diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb index f96108062..ffb084c59 100644 --- a/lib/vagrant/errors.rb +++ b/lib/vagrant/errors.rb @@ -404,6 +404,10 @@ module Vagrant error_key(:local_data_dir_not_accessible) end + class MachineActionLockedError < VagrantError + error_key(:machine_action_locked) + end + class MachineGuestNotReady < VagrantError error_key(:machine_guest_not_ready) end diff --git a/lib/vagrant/machine.rb b/lib/vagrant/machine.rb index 5171165f7..9b646bba9 100644 --- a/lib/vagrant/machine.rb +++ b/lib/vagrant/machine.rb @@ -1,3 +1,4 @@ +require "digest/md5" require "thread" require "log4r" @@ -147,18 +148,28 @@ module Vagrant def action(name, extra_env=nil) @logger.info("Calling action: #{name} on provider #{@provider}") - # Get the callable from the provider. - callable = @provider.action(name) + # Create a deterministic ID for this machine + id = Digest::MD5.hexdigest("#{@env.root_path}#{@name}") - # If this action doesn't exist on the provider, then an exception - # must be raised. - if callable.nil? - raise Errors::UnimplementedProviderAction, - :action => name, - :provider => @provider.to_s + # Lock this machine for the duration of this action + @env.lock("machine-action-#{id}") do + # Get the callable from the provider. + callable = @provider.action(name) + + # If this action doesn't exist on the provider, then an exception + # must be raised. + if callable.nil? + raise Errors::UnimplementedProviderAction, + :action => name, + :provider => @provider.to_s + end + + action_raw(name, callable, extra_env) end - - action_raw(name, callable, extra_env) + rescue Errors::EnvironmentLockedError + raise Errors::MachineActionLockedError, + action: name, + name: @name end # This calls a raw callable in the proper context of the machine using diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 5db009598..b2628a38b 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -744,6 +744,16 @@ en: that the NFS client software is properly installed, and consult any resources specific to the linux distro you're using for more information on how to do this. + machine_action_locked: |- + An action '%{action}' was attempted on the machine '%{name}', + but another process is already executing an action on the machine. + Vagrant locks each machine for access by only one process at a time. + Please wait until the other Vagrant process finishes modifying this + machine, then try again. + + If you believe this message is in error, please check the process + listing for any "ruby" or "vagrant" processes and kill them. Then + try again. machine_guest_not_ready: |- Guest-specific operations were attempted on a machine that is not ready for guest communication. This should not happen and a bug diff --git a/test/unit/vagrant/machine_test.rb b/test/unit/vagrant/machine_test.rb index aa7431b54..e289c4028 100644 --- a/test/unit/vagrant/machine_test.rb +++ b/test/unit/vagrant/machine_test.rb @@ -208,7 +208,7 @@ describe Vagrant::Machine do end end - describe "actions" do + describe "#action" do it "should be able to run an action that exists" do action_name = :up called = false