summaryrefslogtreecommitdiff
blob: 8e1cde51d59c11743a46898b4c2971e3d41c14d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
require 'open3'
require 'time'
require 'net/http'

# Mirror toolkit functionality and other shared stuff
module MirrorToolkit
  RSYNC_TIMESTAMP_FILE = '/gentoo-portage/metadata/timestamp'
  DISTFILES_TIMESTAMP_FILE = '/distfiles/timestamp.mirmon'

  TYPE_RSYNC = 1
  TYPE_DISTFILES = 2

  module_function

  # Calls `rsync_cat` to fetch a file from rsync
  def rsync_cat(uri)
    rsync_cat = File.join(File.dirname(__FILE__), '..', 'bin', 'rsync-cat')
    stdin, stdout, stderr, wait_thr = Open3.popen3(rsync_cat, uri)
    stdin.close

    if wait_thr.value == 0
      return stdout.gets
    else
      fail "Rsync call unsuccessful: '#{stderr.gets.chomp}'"
    end
  end

  def curl(uri)
    stdin, stdout, stderr, wait_thr = Open3.popen3('curl', uri)
    stdin.close

    if wait_thr.value == 0
      return stdout.gets
    else
      fail "curl call unsuccessful: '#{stderr.gets.chomp}'"
    end
  end

  # Fetches a URI from any of the Gentoo mirror types
  def remote_fetch(url)
    case url.scheme
    when 'rsync'
      rsync_cat(url.to_s)
    when 'http', 'https'
      Net::HTTP.get(url)
    when 'ftp'
      curl(url.to_s)
    else
      fail 'Unknown URI scheme.'
    end
  end

  # Tries to return a Time object for every kind of timestamp used on Gentoo mirrors
  def parse_timestamp(ts)
    if ts.numeric?
      Time.at(ts.to_i).utc
    else
      Time.parse(ts).utc
    end
  end

  # Returns a URI object where to find the timestamp for the given url and mirror type.
  def get_timestamp_url(url, type)
    mirror = URI(url)

    case mirror.scheme
    when 'rsync'
      if type == TYPE_RSYNC
        mirror.path = MirrorToolkit::RSYNC_TIMESTAMP_FILE
      elsif type == TYPE_DISTFILES
        mirror.path += MirrorToolkit::DISTFILES_TIMESTAMP_FILE
      end
    when 'http', 'https', 'ftp'
      mirror.path += MirrorToolkit::DISTFILES_TIMESTAMP_FILE
    end

    mirror
  end

  # Returns the number of seconds a mirror is lagging behind, or nil if it cannot be determined.
  def get_lag(url, type)
    Time.now.utc - parse_timestamp(remote_fetch(get_timestamp_url(url, type)))
  rescue
    nil
  end

  # Renders seconds as a nice human-printable string
  def humanize_seconds(secs)
    [[60, :seconds], [60, :minutes], [24, :hours], [1000, :days]].map do |count, name|
      if secs > 0
        secs, n = secs.divmod(count)
        "#{n.to_i} #{name}"
      end
    end.compact.reverse.join(' ')
  end
end

class String
  def numeric?
    true if Float(self) rescue false
  end
end