diff --git a/lib/vagrant/action/box/download.rb b/lib/vagrant/action/box/download.rb new file mode 100644 index 000000000..65ba6c5bc --- /dev/null +++ b/lib/vagrant/action/box/download.rb @@ -0,0 +1,79 @@ +module Vagrant + class Action + module Box + class Download + BASENAME = "box" + + include Util + + attr_reader :temp_path + + def initialize(app, env) + @app = app + @env = env + @env["download.classes"] ||= [] + @env["download.classes"] << [Downloaders::HTTP, Downloaders::File] + @env["download.classes"].flatten! + end + + def call(env) + @env = env + + return if !instantiate_downloader + download + + @app.call(@env) + + cleanup + end + + def instantiate_downloader + @env["download.classes"].each do |klass| + if klass.match?(@env["download.uri"]) + @env.logger.info "Downloading with #{klass}..." + @downloader = klass.new(@env["download.uri"]) + end + end + + if !@downloader + @env.error!(:box_download_unknown_type) + return false + end + + @downloader.prepare(@env["download.uri"]) + true + end + + def download + with_tempfile do |tempfile| + download_to(tempfile) + @temp_path = @env["download.temp_path"] = tempfile.path + end + end + + def cleanup + if temp_path && File.exist?(temp_path) + @env.logger.info "Cleaning up downloaded box..." + File.unlink(temp_path) + end + end + + def with_tempfile + @env.logger.info "Creating tempfile for storing box file..." + File.open(box_temp_path, Platform.tar_file_options) do |tempfile| + yield tempfile + end + end + + def box_temp_path + File.join(@env.env.tmp_path, BASENAME + Time.now.to_i.to_s) + end + + def download_to(f) + @env.logger.info "Copying box to temporary location..." + @downloader.download!(@env["download.uri"], f) + end + end + end + end +end diff --git a/test/vagrant/action/box/download_test.rb b/test/vagrant/action/box/download_test.rb new file mode 100644 index 000000000..34adae6a7 --- /dev/null +++ b/test/vagrant/action/box/download_test.rb @@ -0,0 +1,139 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class DownloadBoxActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::Box::Download + @app, @env = mock_action_data + + @vm = mock("vm") + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + end + + context "initializing" do + should "initialize download classes" do + @klass.new(@app, @env) + assert_equal [Vagrant::Downloaders::HTTP, Vagrant::Downloaders::File], @env["download.classes"] + end + end + + context "with an instance" do + setup do + @instance = @klass.new(@app, @env) + @env["download.uri"] = "http://google.com" + end + + context "calling" do + should "call the proper methods in sequence" do + seq = sequence("seq") + @instance.expects(:instantiate_downloader).in_sequence(seq).returns(true) + @instance.expects(:download).in_sequence(seq) + @app.expects(:call).with(@env).in_sequence(seq) + @instance.expects(:cleanup).in_sequence(seq) + @instance.call(@env) + end + + should "halt the chain if downloader instantiation fails" do + seq = sequence("seq") + @instance.expects(:instantiate_downloader).in_sequence(seq).returns(false) + @instance.expects(:download).never + @app.expects(:call).with(@env).never + @instance.expects(:cleanup).never + @instance.call(@env) + end + end + + context "instantiating downloader" do + should "instantiate the proper class" do + instance = mock("instance") + Vagrant::Downloaders::HTTP.expects(:new).with(@env["download.uri"]).returns(instance) + instance.expects(:prepare).with(@env["download.uri"]).once + assert @instance.instantiate_downloader + end + + should "error environment if URI is invalid for any downloaders" do + @env["download.uri"] = "foobar" + assert !@instance.instantiate_downloader + assert @env.error? + assert_equal :box_download_unknown_type, @env.error.first + end + end + + context "downloading" do + setup do + @path = "foo" + + @tempfile = mock("tempfile") + @tempfile.stubs(:path).returns(@path) + + @instance.stubs(:with_tempfile).yields(@tempfile) + @instance.stubs(:download_to) + end + + should "make a tempfile and copy the URI contents to it" do + @instance.expects(:with_tempfile).yields(@tempfile) + @instance.expects(:download_to).with(@tempfile) + @instance.download + end + + should "save the tempfile path" do + @instance.download + assert @env.has_key?("download.temp_path") + assert_equal @tempfile.path, @env["download.temp_path"] + assert_equal @tempfile.path, @instance.temp_path + end + end + + context "tempfile" do + should "create a tempfile in the vagrant tmp directory" do + File.expects(:open).with { |name, bitmask| + name =~ /#{Vagrant::Action::Box::Download::BASENAME}/ && name =~ /#{@env.env.tmp_path}/ + }.once + @instance.with_tempfile + end + + should "yield the tempfile object" do + @tempfile = mock("tempfile") + File.expects(:open).yields(@tempfile) + + @instance.with_tempfile do |otherfile| + assert @tempfile.equal?(otherfile) + end + end + end + + context "cleaning up" do + setup do + @temp_path = "foo" + @instance.stubs(:temp_path).returns(@temp_path) + File.stubs(:exist?).returns(true) + end + + should "delete the temporary file if it exists" do + File.expects(:unlink).with(@temp_path).once + @instance.cleanup + end + + should "not delete anything if it doesn't exist" do + File.stubs(:exist?).returns(false) + File.expects(:unlink).never + @instance.cleanup + end + end + + context "downloading to" do + setup do + @downloader = mock("downloader") + @instance.instance_variable_set(:@downloader, @downloader) + end + + should "call download! on the download with the URI and tempfile" do + tempfile = "foo" + @downloader.expects(:download!).with(@env["download.uri"], tempfile) + @instance.download_to(tempfile) + end + end + end +end diff --git a/test/vagrant/actions/box/download_test.rb b/test/vagrant/actions/box/download_test.rb index fd8aa76f7..a0d8faeba 100644 --- a/test/vagrant/actions/box/download_test.rb +++ b/test/vagrant/actions/box/download_test.rb @@ -1,6 +1,6 @@ require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') -class DownloadBoxActionTest < Test::Unit::TestCase +class DownloadBoxActionsTest < Test::Unit::TestCase setup do @uri = "foo.com" @runner, @vm, @action = mock_action(Vagrant::Actions::Box::Download)