diff --git a/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_pb.rb b/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_pb.rb index aa45dab04b3..6616a22be88 100644 --- a/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_pb.rb +++ b/lib/vagrant/protobufs/proto/vagrant_plugin_sdk/plugin_pb.rb @@ -192,6 +192,8 @@ optional :connect_timeout, :int64, 15 proto3_optional :ssh_command, :string, 16 proto3_optional :proxy_command, :string, 17 + proto3_optional :retries, :int64, 18 + proto3_optional :retry_interval, :int64, 19 end add_message "hashicorp.vagrant.sdk.Args.Connection.WinrmInfo" do optional :username, :string, 1 diff --git a/plugins/communicators/ssh/communicator.rb b/plugins/communicators/ssh/communicator.rb index fd15804c694..436bca2c7c7 100644 --- a/plugins/communicators/ssh/communicator.rb +++ b/plugins/communicators/ssh/communicator.rb @@ -400,7 +400,16 @@ def connect(**opts) raise Vagrant::Errors::SSHNotReady if ssh_info.nil? # Default some options - opts[:retries] = 5 if !opts.key?(:retries) + opts[:retries] = if ssh_info.key?(:retries) + ssh_info[:retries] + elsif !opts.key?(:retries) + 5 + end + opts[:retry_interval] = if ssh_info.key?(:retry_interval) + ssh_info[:retry_interval] + else + 10 + end # Set some valid auth methods. We disable the auth methods that # we're not using if we don't have the right auth info. @@ -429,7 +438,7 @@ def connect(**opts) timeout = 60 @logger.info("Attempting SSH connection...") - connection = retryable(tries: opts[:retries], on: SSH_RETRY_EXCEPTIONS) do + connection = retryable(tries: opts[:retries], on: SSH_RETRY_EXCEPTIONS, sleep: opts[:retry_interval]) do Timeout.timeout(timeout) do begin # This logger will get the Net-SSH log data for us. diff --git a/plugins/kernel_v2/config/ssh_connect.rb b/plugins/kernel_v2/config/ssh_connect.rb index 21116d26e91..8dc7c33e5bb 100644 --- a/plugins/kernel_v2/config/ssh_connect.rb +++ b/plugins/kernel_v2/config/ssh_connect.rb @@ -2,6 +2,8 @@ module VagrantPlugins module Kernel_V2 class SSHConnectConfig < Vagrant.plugin("2", :config) DEFAULT_SSH_CONNECT_TIMEOUT = 15 + DEFAULT_SSH_RETRIES = 5 + DEFAULT_SSH_RETRY_INTERVAL = 10 attr_accessor :host attr_accessor :port @@ -18,6 +20,8 @@ class SSHConnectConfig < Vagrant.plugin("2", :config) attr_accessor :dsa_authentication attr_accessor :extra_args attr_accessor :remote_user + attr_accessor :retries + attr_accessor :retry_interval def initialize @host = UNSET_VALUE @@ -35,6 +39,8 @@ def initialize @dsa_authentication = UNSET_VALUE @extra_args = UNSET_VALUE @remote_user = UNSET_VALUE + @retries = UNSET_VALUE + @retry_interval = UNSET_VALUE end def finalize! @@ -52,6 +58,8 @@ def finalize! @extra_args = nil if @extra_args == UNSET_VALUE @config = nil if @config == UNSET_VALUE @connect_timeout = DEFAULT_SSH_CONNECT_TIMEOUT if @connect_timeout == UNSET_VALUE + @retries = DEFAULT_SSH_RETRIES if @retries == UNSET_VALUE + @retry_interval = DEFAULT_SSH_RETRY_INTERVAL if @retry_interval == UNSET_VALUE if @private_key_path && !@private_key_path.is_a?(Array) @private_key_path = [@private_key_path] @@ -134,6 +142,26 @@ def validate(machine) given: @connect_timeout.to_s) end + if !@retries.is_a?(Integer) + errors << I18n.t( + "vagrant.config.ssh.retries_invalid_type", + given: @retries.class.name) + elsif @retries < 1 + errors << I18n.t( + "vagrant.config.ssh.retries_invalid_value", + given: @retries.to_s) + end + + if !@retry_interval.is_a?(Integer) + errors << I18n.t( + "vagrant.config.ssh.retry_interval_invalid_type", + given: @retry_interval.class.name) + elsif @retry_interval < 1 + errors << I18n.t( + "vagrant.config.ssh.retry_interval_invalid_value", + given: @retry_interval.to_s) + end + errors end end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 9e6d7ca1695..cd650fe6a18 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -2014,6 +2014,16 @@ en: `%{given}` type which cannot be converted to an Integer type. connect_timeout_invalid_value: |- The `connect_timeout` key only accepts values greater than 1 (received `%{given}`) + retries_invalid_type: |- + The `retries` key only accepts values of Integer type. Received + `%{given}` type which cannot be converted to an Integer type. + retries_invalid_value: |- + The `retries` key only accepts values greater than or equal to 1 (received `%{given}`) + retry_interval_invalid_type: |- + The `retry_interval` key only accepts values of Integer type. Received + `%{given}` type which cannot be converted to an Integer type. + retry_interval_invalid_value: |- + The `retry_interval` key only accepts values greater than or equal to 1 (received `%{given}`) triggers: bad_trigger_type: |- The type '%{type}' defined for trigger '%{trigger}' is not valid. Must be one of the following types: '%{types}' diff --git a/website/content/docs/vagrantfile/ssh_settings.mdx b/website/content/docs/vagrantfile/ssh_settings.mdx index 8aa431283e7..59d6f68b7f4 100644 --- a/website/content/docs/vagrantfile/ssh_settings.mdx +++ b/website/content/docs/vagrantfile/ssh_settings.mdx @@ -129,6 +129,16 @@ defaults are typically fine, but you can fine tune whatever you would like. net-ssh library (ignored by the `ssh` executable) and should not be used in general. This defaults to the value of `config.ssh.username`. +- `config.ssh.retries` (integer) - Number of times to retry establishing an SSH + connection before failing. The default value is 5. This value should be >= 1. + +- `config.ssh.retry_interval` (integer) - Number seconds to sleep between SSH connection + retries. Using this setting together with `config.ssh.retries` can be useful when waiting + for hosts to boot where they are pingable first, yet the SSH daemon takes a long time to boot. + It is also useful when running provisioning scripts that reboot hosts, allowing time for the + hosts to fully boot and SSH to be available. + The default value is 10. This value should be >= 1. + - `config.ssh.shell` (string) - The shell to use when executing SSH commands from Vagrant. By default this is `bash -l`.