diff --git a/lib/vagrant/action/builtin.rb b/lib/vagrant/action/builtin.rb index 962f5662c..ab6a6dfd5 100644 --- a/lib/vagrant/action/builtin.rb +++ b/lib/vagrant/action/builtin.rb @@ -10,6 +10,7 @@ module Vagrant use VM::Customize use VM::ForwardPorts use VM::ShareFolders + use VM::Boot end register :up, up diff --git a/lib/vagrant/action/vm/boot.rb b/lib/vagrant/action/vm/boot.rb new file mode 100644 index 000000000..383913e8b --- /dev/null +++ b/lib/vagrant/action/vm/boot.rb @@ -0,0 +1,46 @@ +module Vagrant + class Action + module VM + class Boot + def initialize(app, env) + @app = app + @env = env + end + + def call(env) + @env = env + + # Start up the VM and wait for it to boot. + boot + return env.error!(:vm_failed_to_boot) if !wait_for_boot + + @app.call(env) + end + + def boot + @env.logger.info "Booting VM..." + @env["vm"].vm.start(@env.env.config.vm.boot_mode) + end + + def wait_for_boot(sleeptime=5) + @env.logger.info "Waiting for VM to boot..." + + @env.env.config.ssh.max_tries.to_i.times do |i| + @env.logger.info "Trying to connect (attempt ##{i+1} of #{@env.env.config[:ssh][:max_tries]})..." + + if @env["vm"].ssh.up? + @env.logger.info "VM booted and ready for use!" + return true + end + + sleep sleeptime + end + + @env.logger.info "Failed to connect to VM! Failed to boot?" + false + end + end + end + end +end + diff --git a/test/vagrant/action/vm/boot_test.rb b/test/vagrant/action/vm/boot_test.rb new file mode 100644 index 000000000..dddcd3c83 --- /dev/null +++ b/test/vagrant/action/vm/boot_test.rb @@ -0,0 +1,58 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper') + +class BootVMActionTest < Test::Unit::TestCase + setup do + @klass = Vagrant::Action::VM::Boot + @app, @env = mock_action_data + + @vm = mock("vm") + @vm.stubs(:ssh).returns(mock("ssh")) + @env["vm"] = @vm + + @internal_vm = mock("internal") + @vm.stubs(:vm).returns(@internal_vm) + + @instance = @klass.new(@app, @env) + end + + context "calling" do + should "run the proper methods on success" do + boot_seq = sequence("boot_seq") + @instance.expects(:boot).in_sequence(boot_seq) + @instance.expects(:wait_for_boot).returns(true).in_sequence(boot_seq) + @app.expects(:call).with(@env).once.in_sequence(boot_seq) + @instance.call(@env) + end + + should "error and halt chain if boot failed" do + boot_seq = sequence("boot_seq") + @instance.expects(:boot).in_sequence(boot_seq) + @instance.expects(:wait_for_boot).returns(false).in_sequence(boot_seq) + @app.expects(:call).never + @instance.call(@env) + end + end + + context "booting" do + should "start the VM in specified mode" do + mode = mock("boot_mode") + @env.env.config.vm.boot_mode = mode + @internal_vm.expects(:start).with(mode).once + @instance.boot + end + end + + context "waiting for boot" do + should "repeatedly ping the SSH port and return false with no response" do + seq = sequence('pings') + @vm.ssh.expects(:up?).times(@env.env.config.ssh.max_tries.to_i - 1).returns(false).in_sequence(seq) + @vm.ssh.expects(:up?).once.returns(true).in_sequence(seq) + assert @instance.wait_for_boot(0) + end + + should "ping the max number of times then just return" do + @vm.ssh.expects(:up?).times(@env.env.config.ssh.max_tries.to_i).returns(false) + assert !@instance.wait_for_boot(0) + end + end +end