Download boxes using CURL!

This commit is contained in:
Mitchell Hashimoto 2013-03-17 11:25:22 -07:00
parent 94dd210be7
commit 6c1eb2b007
4 changed files with 44 additions and 42 deletions

View File

@ -1,5 +1,6 @@
require "log4r"
require "vagrant/util/downloader"
require "vagrant/util/platform"
module Vagrant
@ -14,18 +15,21 @@ module Vagrant
end
def call(env)
# Instantiate the downloader
downloader = download_klass(env[:box_url]).new(env[:ui])
env[:ui].info I18n.t("vagrant.actions.box.download.with",
:class => downloader.class.to_s)
@temp_path = env[:tmp_path].join("box" + Time.now.to_i.to_s)
@logger.info("Downloading box to: #{@temp_path}")
# Download the box to a temporary path. We store the temporary
# path as an instance variable so that the `#recover` method can
# access it.
@temp_path = env[:tmp_path].join("box" + Time.now.to_i.to_s)
@logger.info("Downloading box to: #{@temp_path}")
File.open(@temp_path, Vagrant::Util::Platform.tar_file_options) do |f|
downloader.download!(env[:box_url], f)
env[:ui].info(I18n.t("vagrant.actions.box.download.downloading"))
begin
downloader = Util::Downloader.new(env[:box_url], @temp_path, :ui => env[:ui])
downloader.download!
rescue Errors::DownloaderInterrupted
# The downloader was interrupted, so just return, because that
# means we were interrupted as well.
env[:ui].info(I18n.t("vagrant.actions.box.download.interrupted"))
return
end
# Add the box
@ -54,28 +58,8 @@ module Vagrant
@app.call(env)
end
def download_klass(url)
# This is hardcoded for now. In the future I'd like to make this
# pluginnable as well.
classes = [Downloaders::HTTP, Downloaders::File]
# Find the class to use.
classes.each_index do |i|
klass = classes[i]
# Use the class if it matches the given URI or if this
# is the last class...
return klass if classes.length == (i + 1) || klass.match?(url)
end
# If no downloader knows how to download this file, then we
# raise an exception.
raise Errors::BoxDownloadUnknownType
end
def recover(env)
if @temp_path && File.exist?(@temp_path)
env[:ui].info I18n.t("vagrant.actions.box.download.cleaning")
File.unlink(@temp_path)
end
end

View File

@ -95,10 +95,6 @@ module Vagrant
error_key(:already_exists, "vagrant.actions.box.unpackage")
end
class BoxDownloadUnknownType < VagrantError
error_key(:unknown_type, "vagrant.actions.box.download")
end
class BoxMetadataFileNotFound < VagrantError
error_key(:box_metadata_file_not_found)
end
@ -167,6 +163,10 @@ module Vagrant
error_key(:downloader_error)
end
class DownloaderInterrupted < DownloaderError
error_key(:downloader_interrupted)
end
class DownloaderFileDoesntExist < VagrantError
error_key(:file_missing, "vagrant.downloaders.file")
end

View File

@ -1,5 +1,6 @@
require "log4r"
require "vagrant/util/busy"
require "vagrant/util/subprocess"
module Vagrant
@ -10,12 +11,12 @@ module Vagrant
class Downloader
def initialize(source, destination, options=nil)
@logger = Log4r::Logger.new("vagrant::util::downloader")
@source = source
@destination = destination
@source = source.to_s
@destination = destination.to_s
# Get the various optional values
@options ||= {}
@ui = @options[:ui]
options ||= {}
@ui = options[:ui]
end
# This executes the actual download, downloading the source file
@ -70,24 +71,38 @@ module Vagrant
# 11 - Time left
# 12 - Current speed
output = "Progress: #{columns[1]}% (Rate: #{columns[12]}/s, Estimated time remaining: #{columns[11]}"
output = "Progress: #{columns[1]}% (Rate: #{columns[12]}/s, Estimated time remaining: #{columns[11]})"
@ui.clear_line
@ui.info(output, :new_line => false)
end
end
# Create the callback that is called if we are interrupted
interrupted = false
int_callback = Proc.new do
@logger.info("Downloader interrupted!")
interrupted = true
end
@logger.info("Downloader starting download: ")
@logger.info(" -- Source: #{@source}")
@logger.info(" -- Destination: #{@destination}")
# Execute!
result = Subprocess.execute("curl", *options, &data_proc)
result = Busy.busy(int_callback) do
Subprocess.execute("curl", *options, &data_proc)
end
# If the download was interrupted, then raise a specific error
raise Errors::DownloaderInterrupted if interrupted
# If it didn't exit successfully, we need to parse the data and
# show an error message.
if result.exit_code != 0
parts = result.stderr.split(/\ncurl:\s+\(\d+\)\s*/, 2)
raise Errors::DownloaderError, :message => parts[1]
@logger.warn("Downloader exit code: #{result.exit_code}")
parts = result.stderr.split(/\n*curl:\s+\(\d+\)\s*/, 2)
parts[1] ||= ""
raise Errors::DownloaderError, :message => parts[1].chomp
end
# Everything succeeded

View File

@ -136,6 +136,9 @@ en:
again.
%{message}
downloader_interrupted: |-
The download was interrupted by an external signal. It did not
complete.
environment_locked: |-
An instance of Vagrant is already running. Only one instance of Vagrant
may run at any given time to avoid problems with VirtualBox inconsistencies
@ -853,9 +856,9 @@ en:
destroy:
destroying: "Deleting box '%{name}'..."
download:
with: "Downloading with %{class}..."
cleaning: "Cleaning up downloaded box..."
unknown_type: "Unknown or unsupported URI type given for box download."
downloading: "Downloading or copying the box..."
interrupted: "Box download was interrupted. Exiting."
verify:
verifying: "Verifying box..."
failed: |-