Class: Vanagon::Component::Source::Http

Inherits:
Local
  • Object
show all
Includes:
Utilities
Defined in:
lib/vanagon/component/source/http.rb

Constant Summary collapse

CHECKSUM_TYPES =

Allowed checksum algorithms to use when validating files

%w[md5 sha1 sha256 sha512].freeze

Constants inherited from Local

Local::ARCHIVE_EXTENSIONS

Instance Attribute Summary collapse

Attributes inherited from Local

#cleanup, #extension, #url, #workdir

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Utilities

#erb_file, #erb_string, #ex, #find_program_on_path, #get_md5sum, #get_sum, #http_request, #http_request_code, #http_request_generic, #local_command, #remote_ssh_command, #retry_with_timeout, #rsync_from, #rsync_to, #ssh_command

Methods inherited from Local

#archive?, archive_extensions, #archive_extensions, #copy, #decompressor, #dirname, #extname, #extract, mangle, #mangle, valid_file?

Constructor Details

#initialize(url, sum:, workdir:, sum_type:, **options) ⇒ Http

Constructor for the Http source type

Parameters:

  • url (String)

    url of the http source to fetch

  • sum (String)

    sum to verify the download against or URL to fetch sum from

  • workdir (String)

    working directory to download into

  • sum_type (String)

    type of sum we are verifying

Raises:

  • (RuntimeError)

    an exception is raised is sum is nil



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
# File 'lib/vanagon/component/source/http.rb', line 52

def initialize(url, sum:, workdir:, sum_type:, **options) # rubocop:disable Metrics/AbcSize
  unless sum
    fail "sum is required to validate the http source"
  end
  unless sum_type
    fail "sum_type is required to validate the http source"
  end
  unless CHECKSUM_TYPES.include? sum_type
    fail %(checksum type "#{sum_type}" is invalid; please use #{CHECKSUM_TYPES.join(', ')})
  end

  @url = url
  @sum = sum
  @workdir = workdir
  @sum_type = sum_type

  if Vanagon::Component::Source::Http.valid_url?(@sum)
    sum_file = download(@sum)
    File.open(File.join(@workdir, sum_file)) do |file|
      # the sha1 files generated during archive creation  are formatted
      # "<sha1sum> <filename>". This will also work for sources that
      # only contain the checksum.
      remote_sum = file.read.split.first
      unless remote_sum
        fail "Downloaded checksum file seems to be empty, make sure you have the correct URL"
      end
      @sum = remote_sum
    end
  end
end

Instance Attribute Details

#sumObject

Accessors :url, :file, :extension, :workdir, :cleanup are inherited from Local



14
15
16
# File 'lib/vanagon/component/source/http.rb', line 14

def sum
  @sum
end

#sum_typeObject

Accessors :url, :file, :extension, :workdir, :cleanup are inherited from Local



14
15
16
# File 'lib/vanagon/component/source/http.rb', line 14

def sum_type
  @sum_type
end

Class Method Details

.valid_url?(target_url) ⇒ Boolean

rubocop:disable Metrics/AbcSize

Returns:

  • (Boolean)


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/vanagon/component/source/http.rb', line 20

def valid_url?(target_url) # rubocop:disable Metrics/AbcSize
  uri = URI.parse(target_url.to_s)
  return false unless ['http', 'https'].include? uri.scheme

  Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
    http.request(Net::HTTP::Head.new(uri)) do |response|
      case response
      when Net::HTTPRedirection
        # By parsing the location header, we get either an absolute
        # URI or a URI with a relative `path`. Adding it to `uri`
        # should correctly update the relative `path` or overwrite
        # the entire URI if it's absolute.
        location = URI.parse(response.header['location'])
        valid_url?(uri + location)
      when Net::HTTPSuccess
        return true
      else
        false
      end
    end
  end
end

Instance Method Details

#download(target_url, target_file = nil, headers = { "Accept-Encoding" => "identity" }) ⇒ Object

Downloads the file from @url into the @workdir

Parameters:

  • target_url (String, URI, Addressable::URI)

    url of an http source to retrieve with GET

Raises:

  • (RuntimeError, Vanagon::Error)

    an exception is raised if the URI scheme cannot be handled



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/vanagon/component/source/http.rb', line 107

def download(target_url, target_file = nil, headers = { "Accept-Encoding" => "identity" }) # rubocop:disable Metrics/AbcSize
  uri = URI.parse(target_url.to_s)
  target_file ||= File.basename(uri.path)

  # Add X-RPROXY-PASS to request header if the environment variable exists
  headers['X-RPROXY-PASS'] = ENV['X-RPROXY-PASS'] if ENV['X-RPROXY-PASS']

  VanagonLogger.info "Downloading file '#{target_file}' from url '#{target_url}'"

  Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
    http.request(Net::HTTP::Get.new(uri, headers)) do |response|
      case response
      when Net::HTTPRedirection
        # By parsing the location header, we get either an absolute
        # URI or a URI with a relative `path`. Adding it to `uri`
        # should correctly update the relative `path` or overwrite
        # the entire URI if it's absolute.
        location = URI.parse(response.header['location'])
        download(uri + location, target_file)
      when Net::HTTPSuccess
        File.open(File.join(@workdir, target_file), 'w') do |io|
          response.read_body { |chunk| io.write(chunk) }
        end
      else
        fail "Error: #{response.code.to_s}. Unable to get source from #{target_url}"
      end
    end
  end

  target_file
rescue Errno::ETIMEDOUT, Timeout::Error, Errno::EINVAL,
  Errno::EACCES, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse,
  Net::HTTPHeaderSyntaxError, Net::ProtocolError => e
  raise Vanagon::Error.wrap(e, "Problem downloading #{target_file} from '#{@url}'. Please verify you have the correct uri specified.")
end

#fetchObject

Download the source from the url specified. Sets the full path to the file as @file and the @extension for the file as a side effect.



85
86
87
# File 'lib/vanagon/component/source/http.rb', line 85

def fetch
  @file = download(@url)
end

#fileObject



89
90
91
# File 'lib/vanagon/component/source/http.rb', line 89

def file
  @file ||= fetch
end

#verifyObject

Verify the downloaded file matches the provided sum

Raises:

  • (RuntimeError)

    an exception is raised if the sum does not match the sum of the file



96
97
98
99
100
101
102
# File 'lib/vanagon/component/source/http.rb', line 96

def verify # rubocop:disable Metrics/AbcSize
  VanagonLogger.info "Verifying file: #{file} against sum: '#{sum}'"
  actual = get_sum(File.join(workdir, file), sum_type)
  return true if sum == actual

  fail "Unable to verify '#{File.join(workdir, file)}': #{sum_type} mismatch (expected '#{sum}', got '#{actual}')"
end