lib/omnibus/fetchers/net_fetcher.rb in omnibus-4.0.0 vs lib/omnibus/fetchers/net_fetcher.rb in omnibus-4.1.0

- old
+ new

@@ -14,38 +14,42 @@ # limitations under the License. # require 'fileutils' require 'open-uri' +require 'ruby-progressbar' module Omnibus class NetFetcher < Fetcher # Use 7-zip to extract 7z/zip for Windows WIN_7Z_EXTENSIONS = %w(.7z .zip) # tar probably has compression scheme linked in, otherwise for tarballs TAR_EXTENSIONS = %w(.tar .tar.gz .tgz .bz2 .tar.xz .txz) + # Digest types used for verifying file checksums + DIGESTS = [:sha512, :sha256, :sha1, :md5] + # # A fetch is required if the downloaded_file (such as a tarball) does not # exist on disk, or if the checksum of the downloaded file is different # than the given checksum. # # @return [true, false] # def fetch_required? - !(File.exist?(downloaded_file) && digest(downloaded_file, :md5) == checksum) + !(File.exist?(downloaded_file) && digest(downloaded_file, digest_type) == checksum) end # # The version identifier for this remote location. This is computed using # the name of the software, the version of the software, and the checksum. # # @return [String] # def version_guid - "md5:#{checksum}" + "#{digest_type}:#{checksum}" end # # Clean the project directory by removing the contents from disk. # @@ -79,20 +83,31 @@ verify_checksum! extract end # - # The version for this item in the cache. The is the md5 of downloaded file - # and the URL where it was downloaded from. + # The version for this item in the cache. This is the digest of downloaded + # file and the URL where it was downloaded from. # # @return [String] # def version_for_cache - "download_url:#{source[:url]}|md5:#{source[:md5]}" + "download_url:#{source[:url]}|#{digest_type}:#{checksum}" end # + # Returned the resolved version for the manifest. Since this is a + # remote URL, there is no resolution, the version is what we said + # it is. + # + # @return [String] + # + def self.resolve_version(version, source) + version + end + + # # The path on disk to the downloaded asset. This method requires the # presence of a +source_uri+. # # @return [String] # @@ -100,16 +115,16 @@ filename = File.basename(source[:url], '?*') File.join(Config.cache_dir, filename) end # - # The checksum (+md5+) as defined by the user in the software definition. + # The checksum as defined by the user in the software definition. # # @return [String] # def checksum - source[:md5] + source[digest_type] end private # @@ -121,11 +136,11 @@ # # @return [String] # def download_url if Config.use_s3_caching - "http://#{Config.s3_bucket}.s3.amazonaws.com/#{S3Cache.key_for(software)}" + "http://#{Config.s3_bucket}.s3.amazonaws.com/#{S3Cache.key_for(self)}" else source[:url] end end @@ -145,32 +160,57 @@ log.warn(log_key) { "Permitting unsafe redirects!" } options[:allow_unsafe_redirects] = true end options[:read_timeout] = Omnibus::Config.fetcher_read_timeout + fetcher_retries ||= Omnibus::Config.fetcher_retries + progress_bar = ProgressBar.create( + output: $stdout, + format: '%e %B %p%% (%r KB/sec)', + rate_scale: ->(rate) { rate / 1024 }, + ) + + reported_total = 0 + + options[:content_length_proc] = ->(total) { + reported_total = total + progress_bar.total = total + } + options[:progress_proc] = ->(step) { + downloaded_amount = [step, reported_total].min + progress_bar.progress = downloaded_amount + } + file = open(download_url, options) FileUtils.cp(file.path, downloaded_file) file.close rescue SocketError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ENETUNREACH, + Timeout::Error, OpenURI::HTTPError => e - log.error(log_key) { "Download failed - #{e.class}!" } - raise + if fetcher_retries != 0 + log.debug(log_key) { "Retrying failed download (#{fetcher_retries})..." } + fetcher_retries -= 1 + retry + else + log.error(log_key) { "Download failed - #{e.class}!" } + raise + end end # # Extract the downloaded file, using the magical logic based off of the # ending file extension. In the rare event the file cannot be extracted, it # is copied over as a raw file. # def extract if command = extract_command log.info(log_key) { "Extracting `#{downloaded_file}' to `#{Config.source_dir}'" } - shellout!(extract_command) + shellout!(command) else log.info(log_key) { "`#{downloaded_file}' is not an archive - copying to `#{project_dir}'" } if File.directory?(project_dir) # If the file itself was a directory, copy the whole thing over. This @@ -185,22 +225,33 @@ end end end # + # The digest type defined in the software definition + # + # @return [Symbol] + # + def digest_type + DIGESTS.each do |digest| + return digest if source.key? digest + end + end + + # # Verify the downloaded file has the correct checksum.# # # @raise [ChecksumMismatch] # if the checksum does not match # def verify_checksum! log.info(log_key) { 'Verifying checksum' } expected = checksum - actual = digest(downloaded_file, :md5) + actual = digest(downloaded_file, digest_type) if expected != actual - raise ChecksumMismatch.new(software, expected, actual) + raise ChecksumMismatch.new(self, expected, actual) end end # # The command to use for extracting this piece of software.