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 "log4r"
require "vagrant/util/downloader"
require "vagrant/util/platform" require "vagrant/util/platform"
module Vagrant module Vagrant
@ -14,18 +15,21 @@ module Vagrant
end end
def call(env) def call(env)
# Instantiate the downloader @temp_path = env[:tmp_path].join("box" + Time.now.to_i.to_s)
downloader = download_klass(env[:box_url]).new(env[:ui]) @logger.info("Downloading box to: #{@temp_path}")
env[:ui].info I18n.t("vagrant.actions.box.download.with",
:class => downloader.class.to_s)
# Download the box to a temporary path. We store the temporary # Download the box to a temporary path. We store the temporary
# path as an instance variable so that the `#recover` method can # path as an instance variable so that the `#recover` method can
# access it. # access it.
@temp_path = env[:tmp_path].join("box" + Time.now.to_i.to_s) env[:ui].info(I18n.t("vagrant.actions.box.download.downloading"))
@logger.info("Downloading box to: #{@temp_path}") begin
File.open(@temp_path, Vagrant::Util::Platform.tar_file_options) do |f| downloader = Util::Downloader.new(env[:box_url], @temp_path, :ui => env[:ui])
downloader.download!(env[:box_url], f) 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 end
# Add the box # Add the box
@ -54,28 +58,8 @@ module Vagrant
@app.call(env) @app.call(env)
end 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) def recover(env)
if @temp_path && File.exist?(@temp_path) if @temp_path && File.exist?(@temp_path)
env[:ui].info I18n.t("vagrant.actions.box.download.cleaning")
File.unlink(@temp_path) File.unlink(@temp_path)
end end
end end

View File

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

View File

@ -1,5 +1,6 @@
require "log4r" require "log4r"
require "vagrant/util/busy"
require "vagrant/util/subprocess" require "vagrant/util/subprocess"
module Vagrant module Vagrant
@ -10,12 +11,12 @@ module Vagrant
class Downloader class Downloader
def initialize(source, destination, options=nil) def initialize(source, destination, options=nil)
@logger = Log4r::Logger.new("vagrant::util::downloader") @logger = Log4r::Logger.new("vagrant::util::downloader")
@source = source @source = source.to_s
@destination = destination @destination = destination.to_s
# Get the various optional values # Get the various optional values
@options ||= {} options ||= {}
@ui = @options[:ui] @ui = options[:ui]
end end
# This executes the actual download, downloading the source file # This executes the actual download, downloading the source file
@ -70,24 +71,38 @@ module Vagrant
# 11 - Time left # 11 - Time left
# 12 - Current speed # 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.clear_line
@ui.info(output, :new_line => false) @ui.info(output, :new_line => false)
end end
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("Downloader starting download: ")
@logger.info(" -- Source: #{@source}") @logger.info(" -- Source: #{@source}")
@logger.info(" -- Destination: #{@destination}") @logger.info(" -- Destination: #{@destination}")
# Execute! # 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 # If it didn't exit successfully, we need to parse the data and
# show an error message. # show an error message.
if result.exit_code != 0 if result.exit_code != 0
parts = result.stderr.split(/\ncurl:\s+\(\d+\)\s*/, 2) @logger.warn("Downloader exit code: #{result.exit_code}")
raise Errors::DownloaderError, :message => parts[1] parts = result.stderr.split(/\n*curl:\s+\(\d+\)\s*/, 2)
parts[1] ||= ""
raise Errors::DownloaderError, :message => parts[1].chomp
end end
# Everything succeeded # Everything succeeded

View File

@ -136,6 +136,9 @@ en:
again. again.
%{message} %{message}
downloader_interrupted: |-
The download was interrupted by an external signal. It did not
complete.
environment_locked: |- environment_locked: |-
An instance of Vagrant is already running. Only one instance of Vagrant An instance of Vagrant is already running. Only one instance of Vagrant
may run at any given time to avoid problems with VirtualBox inconsistencies may run at any given time to avoid problems with VirtualBox inconsistencies
@ -853,9 +856,9 @@ en:
destroy: destroy:
destroying: "Deleting box '%{name}'..." destroying: "Deleting box '%{name}'..."
download: download:
with: "Downloading with %{class}..."
cleaning: "Cleaning up downloaded box..." 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: verify:
verifying: "Verifying box..." verifying: "Verifying box..."
failed: |- failed: |-