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

View File

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

View File

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

View File

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

View File

@ -24,6 +24,7 @@ module VagrantPlugins
def call(env) def call(env)
entrypoint = env[:plugin_entry_point] entrypoint = env[:plugin_entry_point]
plugin_name = env[:plugin_name] plugin_name = env[:plugin_name]
sources = env[:plugin_sources]
version = env[:plugin_version] version = env[:plugin_version]
# Determine the plugin name we'll look for in the installed set # Determine the plugin name we'll look for in the installed set
@ -52,7 +53,10 @@ module VagrantPlugins
manager = Vagrant::Plugin::Manager.instance manager = Vagrant::Plugin::Manager.instance
plugin_spec = manager.install_plugin( 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 # Record it so we can uninstall if something goes wrong
@installed_plugin_name = plugin_spec.name @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 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 format. This box must be upgraded in order to work properly with
this version of Vagrant. 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: |- cfengine_bootstrap_failed: |-
Failed to bootstrap CFEngine. Please see the output above to Failed to bootstrap CFEngine. Please see the output above to
see what went wrong and address the issue. 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 it "should install the plugin" do
spec = Gem::Specification.new spec = Gem::Specification.new
manager.should_receive(:install_plugin).with( 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 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 it "should specify the version if given" do
spec = Gem::Specification.new spec = Gem::Specification.new
manager.should_receive(:install_plugin).with( 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 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 it "should specify the entrypoint if given" do
spec = Gem::Specification.new spec = Gem::Specification.new
manager.should_receive(:install_plugin).with( 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 app.should_receive(:call).with(env).once
@ -50,6 +50,18 @@ describe VagrantPlugins::CommandPlugin::Action::InstallGem do
env[:plugin_version] = "bar" env[:plugin_version] = "bar"
subject.call(env) subject.call(env)
end 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 end
describe "#recover" do describe "#recover" do

View File

@ -16,12 +16,83 @@ describe Vagrant::Plugin::Manager do
Pathname.new(p) Pathname.new(p)
end end
let(:bundler) { double("bundler") }
after do after do
path.unlink if path.file? path.unlink if path.file?
end end
before do
Vagrant::Bundler.stub(instance: bundler)
end
subject { described_class.new(path) } 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 context "without state" do
describe "#installed_plugins" do describe "#installed_plugins" do
it "is empty initially" do it "is empty initially" do
@ -30,7 +101,6 @@ describe Vagrant::Plugin::Manager do
end end
end end
context "with state" do context "with state" do
before do before do
sf = Vagrant::Plugin::StateFile.new(path) sf = Vagrant::Plugin::StateFile.new(path)

View File

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