From 2c1483ae807eb3012bae00741900e5f370ee8eb5 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 11 Mar 2010 17:59:25 -0800 Subject: [PATCH] SSH.up? timeout slowly ramps up based on configuration values, giving later attempts more time. --- config/default.rb | 4 +- lib/vagrant/actions/vm/boot.rb | 7 +++- lib/vagrant/config.rb | 2 + lib/vagrant/ssh.rb | 8 ++-- test/test_helper.rb | 2 + test/vagrant/actions/vm/boot_test.rb | 22 +++++++++++ test/vagrant/ssh_test.rb | 55 ++++++++++++++++++++++------ 7 files changed, 84 insertions(+), 16 deletions(-) diff --git a/config/default.rb b/config/default.rb index 55e64872f..317bd564c 100644 --- a/config/default.rb +++ b/config/default.rb @@ -9,7 +9,9 @@ Vagrant::Config.run do |config| config.ssh.host = "localhost" config.ssh.forwarded_port_key = "ssh" config.ssh.max_tries = 10 - config.ssh.timeout = 30 + config.ssh.timeout = 5 + config.ssh.retry_timeout_delta = 5 + config.ssh.max_timeout_delta = 30 config.vm.box_ovf = "box.ovf" config.vm.base_mac = "0800279C2E42" diff --git a/lib/vagrant/actions/vm/boot.rb b/lib/vagrant/actions/vm/boot.rb index 65b358ff6..08b47e9a2 100644 --- a/lib/vagrant/actions/vm/boot.rb +++ b/lib/vagrant/actions/vm/boot.rb @@ -29,13 +29,18 @@ error def wait_for_boot logger.info "Waiting for VM to boot..." + current_timeout = Vagrant.config.ssh.timeout + max_timeout = current_timeout + Vagrant.config.ssh.max_timeout_delta + Vagrant.config[:ssh][:max_tries].to_i.times do |i| logger.info "Trying to connect (attempt ##{i+1} of #{Vagrant.config[:ssh][:max_tries]})..." - if Vagrant::SSH.up? + if Vagrant::SSH.up?(current_timeout) logger.info "VM booted and ready for use!" return true end + + current_timeout += Vagrant.config.ssh.retry_timeout_delta unless current_timeout >= max_timeout end logger.info "Failed to connect to VM! Failed to boot?" diff --git a/lib/vagrant/config.rb b/lib/vagrant/config.rb index 0b6459536..3a81811a1 100644 --- a/lib/vagrant/config.rb +++ b/lib/vagrant/config.rb @@ -64,6 +64,8 @@ module Vagrant attr_accessor :forwarded_port_key attr_accessor :max_tries attr_accessor :timeout + attr_accessor :retry_timeout_delta + attr_accessor :max_timeout_delta end class VMConfig < Base diff --git a/lib/vagrant/ssh.rb b/lib/vagrant/ssh.rb index 9da69b3f7..00b7e7ec9 100644 --- a/lib/vagrant/ssh.rb +++ b/lib/vagrant/ssh.rb @@ -25,11 +25,13 @@ module Vagrant end end - def up? + def up?(timeout=nil) + timeout ||= Vagrant.config.ssh.timeout + check_thread = Thread.new do begin Thread.current[:result] = false - Net::SSH.start(Vagrant.config.ssh.host, Vagrant.config.ssh.username, :port => port, :password => Vagrant.config.ssh.password, :timeout => Vagrant.config.ssh.timeout) do |ssh| + Net::SSH.start(Vagrant.config.ssh.host, Vagrant.config.ssh.username, :port => port, :password => Vagrant.config.ssh.password, :timeout => timeout) do |ssh| Thread.current[:result] = true end rescue Errno::ECONNREFUSED, Net::SSH::Disconnect @@ -37,7 +39,7 @@ module Vagrant end end - check_thread.join(Vagrant.config.ssh.timeout) + check_thread.join(timeout) return check_thread[:result] end diff --git a/test/test_helper.rb b/test/test_helper.rb index 4c14f3b43..f9094a12f 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -31,6 +31,8 @@ class Test::Unit::TestCase config.ssh.forwarded_port_key = "ssh" config.ssh.max_tries = 10 config.ssh.timeout = 10 + config.ssh.retry_timeout_delta = 5 + config.ssh.max_timeout_delta = 30 config.vm.box = "foo" config.vm.box_ovf = "box.ovf" diff --git a/test/vagrant/actions/vm/boot_test.rb b/test/vagrant/actions/vm/boot_test.rb index 7a11ba77f..649a6d52c 100644 --- a/test/vagrant/actions/vm/boot_test.rb +++ b/test/vagrant/actions/vm/boot_test.rb @@ -51,5 +51,27 @@ class BootActionTest < Test::Unit::TestCase Vagrant::SSH.expects(:up?).times(Vagrant.config[:ssh][:max_tries].to_i).returns(false) assert !@action.wait_for_boot end + + should "slowly increase timeout given to up? up to maximum specified" do + @timeout = 30 + @retry_delta = 5 + @max_delta = 30 + + mock_config do |config| + config.ssh.timeout = @timeout + config.ssh.retry_timeout_delta = @retry_delta + config.ssh.max_timeout_delta = @max_delta + end + + up_sequence = sequence("up_seq") + current_timeout = @timeout + max_timeout = @timeout + @max_delta + Vagrant.config.ssh.max_tries.times do |i| + Vagrant::SSH.expects(:up?).with(current_timeout).returns(false).in_sequence(up_sequence) + current_timeout += @retry_delta unless current_timeout >= max_timeout + end + + assert !@action.wait_for_boot + end end end diff --git a/test/vagrant/ssh_test.rb b/test/vagrant/ssh_test.rb index aafe07344..38662042b 100644 --- a/test/vagrant/ssh_test.rb +++ b/test/vagrant/ssh_test.rb @@ -56,26 +56,59 @@ class SshTest < Test::Unit::TestCase context "checking if host is up" do setup do mock_config - end - should "return true if SSH connection works" do - Net::SSH.expects(:start).yields("success") - assert Vagrant::SSH.up? - end + @timeout = 7 - should "return false if SSH connection times out" do - Net::SSH.expects(:start) - assert !Vagrant::SSH.up? - end - - should "allow the thread the configured timeout time" do @thread = mock("thread") @thread.stubs(:[]) + @thread.stubs(:[]=) + @thread.stubs(:join) + Thread.stubs(:current).returns(@thread) + Thread.stubs(:new).returns(@thread).yields + + Net::SSH.stubs(:start) + end + + should "return the value of result in thread" do + result = mock("result") + @thread.expects(:[]).with(:result).returns(result) + assert_equal result, Vagrant::SSH.up? + end + + should "start SSH with proper configuration" do + Net::SSH.expects(:start).with(Vagrant.config.ssh.host, Vagrant.config.ssh.username, :port => Vagrant::SSH.port, :password => Vagrant.config.ssh.password, :timeout => Vagrant.config.ssh.timeout).once + Vagrant::SSH.up? + end + + should "start SSH with proper timeout value if given" do + Net::SSH.expects(:start).with(Vagrant.config.ssh.host, Vagrant.config.ssh.username, :port => Vagrant::SSH.port, :password => Vagrant.config.ssh.password, :timeout => @timeout).once + Vagrant::SSH.up?(@timeout) + end + + should "set result to true if SSH connection works" do + @thread.expects(:[]=).with(:result, true) + Net::SSH.expects(:start).yields("success") + Vagrant::SSH.up? + end + + should "set result to false if SSH connection times out" do + @thread.expects(:[]=).with(:result, false) + Net::SSH.expects(:start) + Vagrant::SSH.up? + end + + should "allow the thread the configured timeout time by default" do Thread.expects(:new).returns(@thread) @thread.expects(:join).with(Vagrant.config.ssh.timeout).once Vagrant::SSH.up? end + should "allow the thread the given timeout time if given" do + Thread.expects(:new).returns(@thread) + @thread.expects(:join).with(@timeout).once + Vagrant::SSH.up?(@timeout) + end + should "return false if the connection is refused" do Net::SSH.expects(:start).raises(Errno::ECONNREFUSED) assert_nothing_raised {