core: support plugin sources, and mask Bundler errors

This commit is contained in:
Mitchell Hashimoto 2014-01-05 23:13:49 -08:00
parent 84ecca5c15
commit f612ec7549
9 changed files with 128 additions and 9 deletions

View File

@ -1,4 +1,5 @@
require "pathname"
require "set"
require "tempfile"
require "bundler"
@ -94,9 +95,15 @@ module Vagrant
f.tap do |gemfile|
gemfile.puts(%Q[source "https://rubygems.org"])
gemfile.puts(%Q[source "http://gems.hashicorp.com"])
gemfile.puts(%Q[gem "vagrant", "= #{Vagrant::VERSION}"])
gemfile.puts("group :plugins do")
sources = plugins.values.map { |p| p["sources"] }.flatten.compact.uniq
sources.each do |source|
next if source == ""
gemfile.puts(%Q[source "#{source}"])
end
gemfile.puts(%Q[gem "vagrant", "= #{Vagrant::VERSION}"])
gemfile.puts("group :plugins do")
plugins.each do |name, plugin|
version = plugin["gem_version"]
version = nil if version == ""
@ -108,9 +115,11 @@ module Vagrant
gemfile.puts(%Q[gem "#{name}", #{version.inspect}, #{opts.inspect}])
end
gemfile.puts("end")
gemfile.close
puts File.read(gemfile.path)
end
end

View File

@ -168,6 +168,10 @@ module Vagrant
error_key(:failed, "vagrant.actions.box.verify")
end
class BundlerError < VagrantError
error_key(:bundler_error)
end
class CFEngineBootstrapFailed < VagrantError
error_key(:cfengine_bootstrap_failed)
end

View File

@ -33,6 +33,7 @@ module Vagrant
plugins[name] = {
"require" => opts[:require],
"gem_version" => opts[:version],
"sources" => opts[:sources],
}
result = nil
@ -44,11 +45,17 @@ module Vagrant
# Add the plugin to the state file
@global_file.add_plugin(
result.name, version: opts[:version], require: opts[:require])
result.name,
version: opts[:version],
require: opts[:require],
sources: opts[:sources],
)
result
rescue ::Bundler::GemNotFound
raise Errors::PluginGemNotFound, name: name
rescue ::Bundler::BundlerError => e
raise Errors::BundlerError, message: e.to_s
end
# Uninstalls the plugin with the given name.
@ -59,11 +66,15 @@ module Vagrant
# Clean the environment, removing any old plugins
Vagrant::Bundler.instance.clean(installed_plugins)
rescue ::Bundler::BundlerError => e
raise Errors::BundlerError, message: e.to_s
end
# Updates all or a specific set of plugins.
def update_plugins(specific)
Vagrant::Bundler.instance.update(installed_plugins, specific)
rescue ::Bundler::BundlerError => e
raise Errors::BundlerError, message: e.to_s
end
# This returns the list of plugins that should be enabled.

View File

@ -33,6 +33,7 @@ module Vagrant
"vagrant_version" => Vagrant::VERSION,
"gem_version" => opts[:version] || "",
"require" => opts[:require] || "",
"sources" => opts[:sources] || [],
}
save!

View File

@ -24,6 +24,7 @@ module VagrantPlugins
def call(env)
entrypoint = env[:plugin_entry_point]
plugin_name = env[:plugin_name]
sources = env[:plugin_sources]
version = env[:plugin_version]
# Determine the plugin name we'll look for in the installed set
@ -52,7 +53,10 @@ module VagrantPlugins
manager = Vagrant::Plugin::Manager.instance
plugin_spec = manager.install_plugin(
plugin_name, version: version, require: entrypoint)
plugin_name,
version: version,
require: entrypoint,
sources: sources,)
# Record it so we can uninstall if something goes wrong
@installed_plugin_name = plugin_spec.name

View File

@ -254,6 +254,13 @@ en:
The box '%{name}' is still stored on disk in the Vagrant 1.0.x
format. This box must be upgraded in order to work properly with
this version of Vagrant.
bundler_error: |-
Bundler, the underlying system Vagrant uses to install plugins,
reported an error. The error is shown below. These errors are usually
caused by misconfigured plugin installations or transient network
issues. The error from Bundler is:
%{message}
cfengine_bootstrap_failed: |-
Failed to bootstrap CFEngine. Please see the output above to
see what went wrong and address the issue.

View File

@ -18,7 +18,7 @@ describe VagrantPlugins::CommandPlugin::Action::InstallGem do
it "should install the plugin" do
spec = Gem::Specification.new
manager.should_receive(:install_plugin).with(
"foo", version: nil, require: nil).once.and_return(spec)
"foo", version: nil, require: nil, sources: nil).once.and_return(spec)
app.should_receive(:call).with(env).once
@ -29,7 +29,7 @@ describe VagrantPlugins::CommandPlugin::Action::InstallGem do
it "should specify the version if given" do
spec = Gem::Specification.new
manager.should_receive(:install_plugin).with(
"foo", version: "bar", require: nil).once.and_return(spec)
"foo", version: "bar", require: nil, sources: nil).once.and_return(spec)
app.should_receive(:call).with(env).once
@ -41,7 +41,7 @@ describe VagrantPlugins::CommandPlugin::Action::InstallGem do
it "should specify the entrypoint if given" do
spec = Gem::Specification.new
manager.should_receive(:install_plugin).with(
"foo", version: "bar", require: "baz").once.and_return(spec)
"foo", version: "bar", require: "baz", sources: nil).once.and_return(spec)
app.should_receive(:call).with(env).once
@ -50,6 +50,18 @@ describe VagrantPlugins::CommandPlugin::Action::InstallGem do
env[:plugin_version] = "bar"
subject.call(env)
end
it "should specify the sources if given" do
spec = Gem::Specification.new
manager.should_receive(:install_plugin).with(
"foo", version: nil, require: nil, sources: ["foo"]).once.and_return(spec)
app.should_receive(:call).with(env).once
env[:plugin_name] = "foo"
env[:plugin_sources] = ["foo"]
subject.call(env)
end
end
describe "#recover" do

View File

@ -16,12 +16,83 @@ describe Vagrant::Plugin::Manager do
Pathname.new(p)
end
let(:bundler) { double("bundler") }
after do
path.unlink if path.file?
end
before do
Vagrant::Bundler.stub(instance: bundler)
end
subject { described_class.new(path) }
describe "#install_plugin" do
it "installs the plugin and adds it to the state file" do
specs = Array.new(5) { Gem::Specification.new }
specs[3].name = "foo"
bundler.should_receive(:install).once.with do |plugins|
expect(plugins).to have_key("foo")
end.and_return(specs)
result = subject.install_plugin("foo")
# It should return the spec of the installed plugin
expect(result).to eql(specs[3])
# It should've added the plugin to the state
expect(subject.installed_plugins).to have_key("foo")
end
it "masks GemNotFound with our error" do
bundler.should_receive(:install).and_raise(Bundler::GemNotFound)
expect { subject.install_plugin("foo") }.
to raise_error(Vagrant::Errors::PluginGemNotFound)
end
it "masks bundler errors with our own error" do
bundler.should_receive(:install).and_raise(Bundler::InstallError)
expect { subject.install_plugin("foo") }.
to raise_error(Vagrant::Errors::BundlerError)
end
end
describe "#uninstall_plugin" do
it "removes the plugin from the state" do
sf = Vagrant::Plugin::StateFile.new(path)
sf.add_plugin("foo")
# Sanity
expect(subject.installed_plugins).to have_key("foo")
# Test
bundler.should_receive(:clean).once.with({})
# Remove it
subject.uninstall_plugin("foo")
expect(subject.installed_plugins).to_not have_key("foo")
end
it "masks bundler errors with our own error" do
bundler.should_receive(:clean).and_raise(Bundler::InstallError)
expect { subject.uninstall_plugin("foo") }.
to raise_error(Vagrant::Errors::BundlerError)
end
end
describe "#update_plugins" do
it "masks bundler errors with our own error" do
bundler.should_receive(:update).and_raise(Bundler::InstallError)
expect { subject.update_plugins([]) }.
to raise_error(Vagrant::Errors::BundlerError)
end
end
context "without state" do
describe "#installed_plugins" do
it "is empty initially" do
@ -30,7 +101,6 @@ describe Vagrant::Plugin::Manager do
end
end
context "with state" do
before do
sf = Vagrant::Plugin::StateFile.new(path)

View File

@ -34,6 +34,7 @@ describe Vagrant::Plugin::StateFile do
"vagrant_version" => Vagrant::VERSION,
"gem_version" => "",
"require" => "",
"sources" => [],
})
end