diff --git a/lib/vagrant/action/builtin.rb b/lib/vagrant/action/builtin.rb index 9a6003aa2..2ffc4b007 100644 --- a/lib/vagrant/action/builtin.rb +++ b/lib/vagrant/action/builtin.rb @@ -18,7 +18,12 @@ module Vagrant use VM::Boot end + down = Builder.new do + use VM::Halt + end + register :up, up + register :down, down end end end diff --git a/lib/vagrant/action/vm/halt.rb b/lib/vagrant/action/vm/halt.rb new file mode 100644 index 000000000..4958a3e6c --- /dev/null +++ b/lib/vagrant/action/vm/halt.rb @@ -0,0 +1,24 @@ +module Vagrant + class Action + module VM + class Halt + def initialize(app, env) + @app = app + end + + def call(env) + return env.error!(:vm_not_running) unless env["vm"].vm.running? + + env["vm"].system.halt if !env["force"] + + if env["vm"].vm.state(true) != :powered_off + env.logger.info "Forcing shutdown of VM..." + env["vm"].vm.stop + end + + @app.call(env) + end + end + end + end +end diff --git a/test/vagrant/action/vm/halt_test.rb b/test/vagrant/action/vm/halt_test.rb new file mode 100644 index 000000000..4abb92ea9 --- /dev/null +++ b/test/vagrant/action/vm/halt_test.rb @@ -0,0 +1,62 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class HaltVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Halt + @app, @env = mock_action_data + + @vm = mock("vm") + @vm.stubs(:name).returns("foo") + @vm.stubs(:ssh).returns(mock("ssh")) + @vm.stubs(:system).returns(mock("system")) + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + context "calling" do + setup do + @internal_vm.stubs(:running?).returns(true) + + @vm.system.stubs(:halt) + @internal_vm.stubs(:stop) + @internal_vm.stubs(:state).returns(:powered_off) + end + + should "halt with the system and NOT force VM to stop if powered off" do + @internal_vm.expects(:state).with(true).returns(:powered_off) + @vm.system.expects(:halt).once + @internal_vm.expects(:stop).never + @app.expects(:call).once + + @instance.call(@env) + end + + should "halt with the system and force VM to stop if NOT powered off" do + @internal_vm.expects(:state).with(true).returns(:running) + @vm.system.expects(:halt).once + @internal_vm.expects(:stop).once + @app.expects(:call).once + + @instance.call(@env) + end + + should "not call halt on the system if forcing" do + @env["force"] = true + @vm.system.expects(:halt).never + @instance.call(@env) + end + + should "raise an ActionException if VM is not running" do + @internal_vm.stubs(:running?).returns(false) + @internal_vm.expects(:stop).never + @app.expects(:call).never + @instance.call(@env) + assert @env.error? + assert_equal :vm_not_running, @env.error.first + end + end +end