diff --git a/lib/vagrant/action/builtin/box_add.rb b/lib/vagrant/action/builtin/box_add.rb index 522925fa2..9dfcdebc7 100644 --- a/lib/vagrant/action/builtin/box_add.rb +++ b/lib/vagrant/action/builtin/box_add.rb @@ -57,8 +57,17 @@ module Vagrant end end + # Call the hook to transform URLs into authenticated URLs. + # In the case we don't have a plugin that does this, then it + # will just return the same URLs. + hook_env = env[:hook].call(:authenticate_box_url, box_urls: url) + authed_urls = hook_env[:box_urls] + if !authed_urls || authed_urls.length != url.length + raise "Bad box authentication hook, did not generate proper results." + end + # Test if any of our URLs point to metadata - is_metadata_results = url.map do |u| + is_metadata_results = authed_urls.map do |u| begin metadata_url?(u, env) rescue Errors::DownloaderError => e @@ -86,7 +95,8 @@ module Vagrant end if is_metadata - add_from_metadata(url.first, env, expanded) + url = [url.first, authed_urls.first] + add_from_metadata(url, env, expanded) else add_direct(url, env) end @@ -118,12 +128,28 @@ module Vagrant end # Adds a box given that the URL is a metadata document. + # + # @param [String | Array] url The URL of the metadata for + # the box to add. If this is an array, then it must be a two-element + # array where the first element is the original URL and the second + # element is an authenticated URL. + # @param [Hash] env + # @param [Bool] expanded True if the metadata URL was expanded with + # a Vagrant Cloud server URL. def add_from_metadata(url, env, expanded) original_url = env[:box_url] provider = env[:box_provider] provider = Array(provider) if provider version = env[:box_version] + authenticated_url = url + if url.is_a?(Array) + # We have both a normal URL and "authenticated" URL. Split + # them up. + authenticated_url = url[1] + url = url[0] + end + env[:ui].output(I18n.t( "vagrant.box_loading_metadata", name: Array(original_url).first)) @@ -134,7 +160,8 @@ module Vagrant metadata = nil begin - metadata_path = download(url, env, json: true, ui: false) + metadata_path = download( + authenticated_url, env, json: true, ui: false) File.open(metadata_path) do |f| metadata = BoxMetadata.new(f) diff --git a/lib/vagrant/box_collection.rb b/lib/vagrant/box_collection.rb index f25658fa8..5ab5465a0 100644 --- a/lib/vagrant/box_collection.rb +++ b/lib/vagrant/box_collection.rb @@ -44,6 +44,7 @@ module Vagrant options ||= {} @directory = directory + @hook = options[:hook] @lock = Monitor.new @temp_root = options[:temp_dir_root] @logger = Log4r::Logger.new("vagrant::box_collection") @@ -289,6 +290,12 @@ module Vagrant metadata_url_file = box_directory.join("metadata_url") metadata_url = metadata_url_file.read if metadata_url_file.file? + if metadata_url && @hook + hook_env = @hook.call( + :authenticate_box_url, box_urls: [metadata_url]) + metadata_url = hook_env[:box_urls].first + end + return Box.new( name, provider, v.to_s, provider_dir, metadata_url: metadata_url, diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb index d302a986e..f4520ed29 100644 --- a/lib/vagrant/environment.rb +++ b/lib/vagrant/environment.rb @@ -258,7 +258,10 @@ module Vagrant # # @return [BoxCollection] def boxes - @_boxes ||= BoxCollection.new(boxes_path, temp_dir_root: tmp_path) + @_boxes ||= BoxCollection.new( + boxes_path, + hook: method(:hook), + temp_dir_root: tmp_path) end # Returns the {Config::Loader} that can be used to load Vagrantflies diff --git a/test/unit/plugins/commands/box/command/update_test.rb b/test/unit/plugins/commands/box/command/update_test.rb index 8571d6ba3..1ba9cdc2c 100644 --- a/test/unit/plugins/commands/box/command/update_test.rb +++ b/test/unit/plugins/commands/box/command/update_test.rb @@ -41,9 +41,18 @@ describe VagrantPlugins::CommandBox::Command::Update do end it "doesn't update if they're up to date" do - action_runner.should_receive(:run).never + called = false + action_runner.stub(:run) do |callable, opts| + if opts[:box_provider] + called = true + end + + opts + end subject.execute + + expect(called).to be_false end it "does update if there is an update" do @@ -69,14 +78,21 @@ describe VagrantPlugins::CommandBox::Command::Update do RAW end - action_runner.should_receive(:run).with do |action, opts| - expect(opts[:box_url]).to eq(metadata_url.to_s) - expect(opts[:box_provider]).to eq("virtualbox") - expect(opts[:box_version]).to eq("1.1") - true + action_called = false + action_runner.stub(:run) do |action, opts| + if opts[:box_provider] + action_called = true + expect(opts[:box_url]).to eq(metadata_url.to_s) + expect(opts[:box_provider]).to eq("virtualbox") + expect(opts[:box_version]).to eq("1.1") + end + + opts end subject.execute + + expect(action_called).to be_true end it "raises an error if there are multiple providers" do @@ -116,14 +132,21 @@ describe VagrantPlugins::CommandBox::Command::Update do test_iso_env.box3("foo", "1.0", :vmware) - action_runner.should_receive(:run).with do |action, opts| - expect(opts[:box_url]).to eq(metadata_url.to_s) - expect(opts[:box_provider]).to eq("vmware") - expect(opts[:box_version]).to eq("1.1") - true + action_called = false + action_runner.stub(:run) do |action, opts| + if opts[:box_provider] + action_called = true + expect(opts[:box_url]).to eq(metadata_url.to_s) + expect(opts[:box_provider]).to eq("vmware") + expect(opts[:box_version]).to eq("1.1") + end + + opts end subject.execute + + expect(action_called).to be_true end it "raises an error if that provider doesn't exist" do diff --git a/test/unit/vagrant/action/builtin/box_add_test.rb b/test/unit/vagrant/action/builtin/box_add_test.rb index b2b31b403..db870e077 100644 --- a/test/unit/vagrant/action/builtin/box_add_test.rb +++ b/test/unit/vagrant/action/builtin/box_add_test.rb @@ -14,6 +14,7 @@ describe Vagrant::Action::Builtin::BoxAdd do let(:app) { lambda { |env| } } let(:env) { { box_collection: box_collection, + hook: Proc.new { |name, env| env }, tmp_path: Pathname.new(Dir.mktmpdir), ui: Vagrant::UI::Silent.new, } } @@ -262,6 +263,60 @@ describe Vagrant::Action::Builtin::BoxAdd do end end + it "authenticates HTTP URLs and adds them" do + box_path = iso_env.box2_file(:virtualbox) + tf = Tempfile.new(["vagrant", ".json"]).tap do |f| + f.write(<<-RAW) + { + "name": "foo/bar", + "versions": [ + { + "version": "0.5" + }, + { + "version": "0.7", + "providers": [ + { + "name": "virtualbox", + "url": "#{box_path}" + } + ] + } + ] + } + RAW + f.close + end + + md_path = Pathname.new(tf.path) + with_web_server(md_path) do |port| + real_url = "http://127.0.0.1:#{port}/#{md_path.basename}" + + # Set the box URL to something fake so we can modify it in place + env[:box_url] = "foo" + + env[:hook] = double("hook") + env[:hook].should_receive(:call) do |name, opts| + expect(name).to eq(:authenticate_box_url) + expect(opts[:box_urls]).to eq(["foo"]) + { box_urls: [real_url] } + end + + box_collection.should_receive(:add).with do |path, name, version, **opts| + expect(name).to eq("foo/bar") + expect(version).to eq("0.7") + expect(checksum(path)).to eq(checksum(box_path)) + expect(opts[:metadata_url]).to eq(env[:box_url]) + true + end.and_return(box) + + app.should_receive(:call).with(env) + + subject.call(env) + end + end + + it "raises an error if no Vagrant server is set" do tf = Tempfile.new("foo") tf.close diff --git a/test/unit/vagrant/box_collection_test.rb b/test/unit/vagrant/box_collection_test.rb index 7cdb242bd..78324e232 100644 --- a/test/unit/vagrant/box_collection_test.rb +++ b/test/unit/vagrant/box_collection_test.rb @@ -56,6 +56,42 @@ describe Vagrant::BoxCollection do expect(result).to_not be_nil expect(result).to be_kind_of(box_class) expect(result.name).to eq("foo") + expect(result.metadata_url).to be_nil + end + + it "sets a metadata URL if it has one" do + # Create the "box" + environment.box3("foo", "0", :virtualbox, + metadata_url: "foourl") + + # Actual test + result = subject.find("foo", :virtualbox, ">= 0") + expect(result).to_not be_nil + expect(result).to be_kind_of(box_class) + expect(result.name).to eq("foo") + expect(result.metadata_url).to eq("foourl") + end + + it "sets the metadata URL to an authenticated URL if it has one" do + hook = double("hook") + subject = described_class.new(environment.boxes_dir, hook: hook) + + # Create the "box" + environment.box3("foo", "0", :virtualbox, + metadata_url: "foourl") + + hook.should_receive(:call).with do |name, env| + expect(name).to eq(:authenticate_box_url) + expect(env[:box_urls]).to eq(["foourl"]) + true + end.and_return(box_urls: ["bar"]) + + # Actual test + result = subject.find("foo", :virtualbox, ">= 0") + expect(result).to_not be_nil + expect(result).to be_kind_of(box_class) + expect(result.name).to eq("foo") + expect(result.metadata_url).to eq("bar") end it "returns latest version matching constraint" do diff --git a/test/unit/vagrant/environment_test.rb b/test/unit/vagrant/environment_test.rb index 42cce8572..f95cc8744 100644 --- a/test/unit/vagrant/environment_test.rb +++ b/test/unit/vagrant/environment_test.rb @@ -757,6 +757,11 @@ VF collection = instance.boxes collection.should be_kind_of(Vagrant::BoxCollection) collection.directory.should == instance.boxes_path + + # Reach into some internal state here but not sure how else + # to test this at the moment. + expect(collection.instance_variable_get(:@hook)). + to eq(instance.method(:hook)) end describe "action runner" do