From b25cfa178ba4a78b6bfeb3b21496443672e1dc2c Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Thu, 22 Jun 2023 15:12:43 +0100 Subject: [PATCH 1/4] Limit SSH start concurrency Starting many (90+) SSH connections has caused us some issues such as failed DNS lookups and hitting process file descriptor limits. To mitigate this, patch SSHKit::Backend::Netssh to limit concurrency of connection starts. We'll default to 30 at a time which seems to work without issue, but can be configured via: ``` sshkit: max_concurrent_starts: 10 ``` --- Gemfile.lock | 1 + lib/mrsk/commander.rb | 5 +++- lib/mrsk/configuration.rb | 4 ++++ lib/mrsk/sshkit_with_ext.rb | 48 +++++++++++++++++++++++++++++++++++++ mrsk.gemspec | 1 + test/configuration_test.rb | 5 ++++ 6 files changed, 63 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 26a08348..2765ecd4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,6 +4,7 @@ PATH mrsk (0.15.1) activesupport (>= 7.0) bcrypt_pbkdf (~> 1.0) + concurrent-ruby (~> 1.2) dotenv (~> 2.8) ed25519 (~> 1.2) net-ssh (~> 7.0) diff --git a/lib/mrsk/commander.rb b/lib/mrsk/commander.rb index bdfef962..7b3af31f 100644 --- a/lib/mrsk/commander.rb +++ b/lib/mrsk/commander.rb @@ -143,7 +143,10 @@ class Mrsk::Commander private # Lazy setup of SSHKit def configure_sshkit_with(config) - SSHKit::Backend::Netssh.configure { |ssh| ssh.ssh_options = config.ssh_options } + SSHKit::Backend::Netssh.configure do |sshkit| + sshkit.max_concurrent_starts = config.sshkit_max_concurrent_starts if config.sshkit_max_concurrent_starts + sshkit.ssh_options = config.ssh_options + end SSHKit.config.command_map[:docker] = "docker" # No need to use /usr/bin/env, just clogs up the logs SSHKit.config.output_verbosity = verbosity end diff --git a/lib/mrsk/configuration.rb b/lib/mrsk/configuration.rb index ee6dce05..df389158 100644 --- a/lib/mrsk/configuration.rb +++ b/lib/mrsk/configuration.rb @@ -157,6 +157,10 @@ class Mrsk::Configuration end + def sshkit_max_concurrent_starts + raw_config.sshkit["max_concurrent_starts"] if raw_config.sshkit.present? + end + def healthcheck { "path" => "/up", "port" => 3000, "max_attempts" => 7 }.merge(raw_config.healthcheck || {}) end diff --git a/lib/mrsk/sshkit_with_ext.rb b/lib/mrsk/sshkit_with_ext.rb index 6cd9c3e4..58b1f4e3 100644 --- a/lib/mrsk/sshkit_with_ext.rb +++ b/lib/mrsk/sshkit_with_ext.rb @@ -54,3 +54,51 @@ class SSHKit::Backend::Abstract end prepend CommandEnvMerge end + +class SSHKit::Backend::Netssh::Configuration + DEFAULT_MAX_CONCURRENT_STARTS = 30 + + attr_writer :max_concurrent_starts + + def max_concurrent_starts + @max_concurrent_starts ||= DEFAULT_MAX_CONCURRENT_STARTS + end +end + +class SSHKit::Backend::Netssh + module LimitConcurrentStartsClass + attr_reader :start_semaphore + + def configure(&block) + super &block + # Create this here to avoid lazy creation by multiple threads + @start_semaphore = Concurrent::Semaphore.new(config.max_concurrent_starts) + end + end + + class << self + prepend LimitConcurrentStartsClass + end + + module LimitConcurrentStartsInstance + private + def with_ssh(&block) + host.ssh_options = self.class.config.ssh_options.merge(host.ssh_options || {}) + self.class.pool.with( + method(:start_with_concurrency_limit), + String(host.hostname), + host.username, + host.netssh_options, + &block + ) + end + + def start_with_concurrency_limit(*args) + self.class.start_semaphore.acquire do + Net::SSH.start(*args) + end + end + end + + prepend LimitConcurrentStartsInstance +end diff --git a/mrsk.gemspec b/mrsk.gemspec index 24c40bcf..7a10c2be 100644 --- a/mrsk.gemspec +++ b/mrsk.gemspec @@ -20,6 +20,7 @@ Gem::Specification.new do |spec| spec.add_dependency "zeitwerk", "~> 2.5" spec.add_dependency "ed25519", "~> 1.2" spec.add_dependency "bcrypt_pbkdf", "~> 1.0" + spec.add_dependency "concurrent-ruby", "~> 1.2" spec.add_development_dependency "debug" spec.add_development_dependency "mocha" diff --git a/test/configuration_test.rb b/test/configuration_test.rb index 0f7e12ff..e635cd04 100644 --- a/test/configuration_test.rb +++ b/test/configuration_test.rb @@ -224,6 +224,11 @@ class ConfigurationTest < ActiveSupport::TestCase assert_equal "app@1.2.3.4", @config.ssh_options[:proxy].jump_proxies end + test "sshkit max concurrent starts" do + config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(sshkit: { "max_concurrent_starts" => 50 }) }) + assert_equal 50, @config.sshkit_max_concurrent_starts + end + test "volume_args" do assert_equal ["--volume", "/local/path:/container/path"], @config.volume_args end From f64b5969074f21e702857e056dd27efa6b683b8e Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Thu, 22 Jun 2023 16:09:25 +0100 Subject: [PATCH 2/4] Prevent SSH connection restarts Set a high idle timeout on the sshkit connection pool. This will reduce the incidence of re-connection storms when a deployment has been idle for a while (e.g. when waiting for a docker build). The default timeout was 30 seconds, so we'll enable keepalives at a 30s interval to match. This is to help prevent connections from being killed during long idle periods. --- lib/mrsk/commander.rb | 1 + lib/mrsk/configuration.rb | 11 ++++++++++- test/configuration_test.rb | 25 +++++++++++++++++++++++-- test/integration/main_test.rb | 2 +- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/mrsk/commander.rb b/lib/mrsk/commander.rb index 7b3af31f..94c0aff2 100644 --- a/lib/mrsk/commander.rb +++ b/lib/mrsk/commander.rb @@ -143,6 +143,7 @@ class Mrsk::Commander private # Lazy setup of SSHKit def configure_sshkit_with(config) + SSHKit::Backend::Netssh.pool.idle_timeout = config.sshkit_pool_idle_timeout SSHKit::Backend::Netssh.configure do |sshkit| sshkit.max_concurrent_starts = config.sshkit_max_concurrent_starts if config.sshkit_max_concurrent_starts sshkit.ssh_options = config.ssh_options diff --git a/lib/mrsk/configuration.rb b/lib/mrsk/configuration.rb index df389158..d6badbf3 100644 --- a/lib/mrsk/configuration.rb +++ b/lib/mrsk/configuration.rb @@ -153,7 +153,7 @@ class Mrsk::Configuration end def ssh_options - { user: ssh_user, proxy: ssh_proxy, auth_methods: [ "publickey" ] }.compact + { user: ssh_user, proxy: ssh_proxy, auth_methods: [ "publickey" ], keepalive: true, keepalive_interval: 30 }.compact end @@ -161,6 +161,15 @@ class Mrsk::Configuration raw_config.sshkit["max_concurrent_starts"] if raw_config.sshkit.present? end + def sshkit_pool_idle_timeout + if raw_config.sshkit.present? + raw_config.sshkit["pool_idle_timeout"] || 900 + else + 900 + end + end + + def healthcheck { "path" => "/up", "port" => 3000, "max_attempts" => 7 }.merge(raw_config.healthcheck || {}) end diff --git a/test/configuration_test.rb b/test/configuration_test.rb index e635cd04..47eee321 100644 --- a/test/configuration_test.rb +++ b/test/configuration_test.rb @@ -226,7 +226,13 @@ class ConfigurationTest < ActiveSupport::TestCase test "sshkit max concurrent starts" do config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(sshkit: { "max_concurrent_starts" => 50 }) }) - assert_equal 50, @config.sshkit_max_concurrent_starts + assert_equal 50, config.sshkit_max_concurrent_starts + end + + test "sshkit pool idle timeout" do + assert_equal 900, @config.sshkit_pool_idle_timeout + config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(sshkit: { "pool_idle_timeout" => 600 }) }) + assert_equal 600, config.sshkit_pool_idle_timeout end test "volume_args" do @@ -271,7 +277,22 @@ class ConfigurationTest < ActiveSupport::TestCase end test "to_h" do - assert_equal({ :roles=>["web"], :hosts=>["1.1.1.1", "1.1.1.2"], :primary_host=>"1.1.1.1", :version=>"missing", :repository=>"dhh/app", :absolute_image=>"dhh/app:missing", :service_with_version=>"app-missing", :env_args=>["-e", "REDIS_URL=\"redis://x/y\""], :ssh_options=>{:user=>"root", :auth_methods=>["publickey"]}, :volume_args=>["--volume", "/local/path:/container/path"], :builder=>{}, :logging=>["--log-opt", "max-size=\"10m\""], :healthcheck=>{"path"=>"/up", "port"=>3000, "max_attempts" => 7 }}, @config.to_h) + expected_config = \ + { :roles=>["web"], + :hosts=>["1.1.1.1", "1.1.1.2"], + :primary_host=>"1.1.1.1", + :version=>"missing", + :repository=>"dhh/app", + :absolute_image=>"dhh/app:missing", + :service_with_version=>"app-missing", + :env_args=>["-e", "REDIS_URL=\"redis://x/y\""], + :ssh_options=>{ :user=>"root", :auth_methods=>["publickey"], keepalive: true, keepalive_interval: 30 }, + :volume_args=>["--volume", "/local/path:/container/path"], + :builder=>{}, + :logging=>["--log-opt", "max-size=\"10m\""], + :healthcheck=>{ "path"=>"/up", "port"=>3000, "max_attempts" => 7 }} + + assert_equal expected_config, @config.to_h end test "min version is lower" do diff --git a/test/integration/main_test.rb b/test/integration/main_test.rb index cc7f8bbb..521661bd 100644 --- a/test/integration/main_test.rb +++ b/test/integration/main_test.rb @@ -51,7 +51,7 @@ class MainTest < IntegrationTest assert_equal "app-#{version}", config[:service_with_version] assert_equal [], config[:env_args] assert_equal [], config[:volume_args] - assert_equal({ user: "root", auth_methods: [ "publickey" ] }, config[:ssh_options]) + assert_equal({ user: "root", auth_methods: [ "publickey" ], keepalive: true, keepalive_interval: 30 }, config[:ssh_options]) assert_equal({ "multiarch" => false, "args" => { "COMMIT_SHA" => version } }, config[:builder]) assert_equal [ "--log-opt", "max-size=\"10m\"" ], config[:logging] assert_equal({ "path" => "/up", "port" => 3000, "max_attempts" => 7, "cmd" => "wget -qO- http://localhost > /dev/null" }, config[:healthcheck]) From eb8c97a417634154fad43a51726bcc9ccd54adc2 Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Tue, 25 Jul 2023 10:32:28 +0100 Subject: [PATCH 3/4] Document new sshkit settings --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 5977f50b..4174ba2c 100644 --- a/README.md +++ b/README.md @@ -990,6 +990,20 @@ That'll post a line like the following to a preconfigured chatbot in Basecamp: Set `--skip_hooks` to avoid running the hooks. +## SSH connection management + +Creating SSH connections concurrently can be an issue when deploying to many servers. By default MRSK will limit concurrent connection starts to 30 at a time. + +It also sets a long idle timeout of 900 seconds on connections to prevent re-connection storms after a long idle period, like building an image or waiting for CI. + +You can configure both of these settings: + +```yaml +sshkit: + max_concurrent_starts: 10 + pool_idle_timeout: 300 +``` + ## Stage of development This is beta software. Commands may still move around. But we're live in production at [37signals](https://37signals.com). From 94d6a763a8895c3d5250470e92d4223d7fecab0e Mon Sep 17 00:00:00 2001 From: Donal McBreen Date: Wed, 26 Jul 2023 12:26:23 +0100 Subject: [PATCH 4/4] Extract ssh and sshkit configuration --- lib/mrsk/commander.rb | 6 ++--- lib/mrsk/commands/base.rb | 10 ++++----- lib/mrsk/configuration.rb | 37 +++++-------------------------- lib/mrsk/configuration/ssh.rb | 24 ++++++++++++++++++++ lib/mrsk/configuration/sshkit.rb | 20 +++++++++++++++++ lib/mrsk/sshkit_with_ext.rb | 18 +++++++-------- test/configuration/ssh_test.rb | 32 ++++++++++++++++++++++++++ test/configuration/sshkit_test.rb | 27 ++++++++++++++++++++++ test/configuration_test.rb | 29 +----------------------- 9 files changed, 127 insertions(+), 76 deletions(-) create mode 100644 lib/mrsk/configuration/ssh.rb create mode 100644 lib/mrsk/configuration/sshkit.rb create mode 100644 test/configuration/ssh_test.rb create mode 100644 test/configuration/sshkit_test.rb diff --git a/lib/mrsk/commander.rb b/lib/mrsk/commander.rb index 94c0aff2..4812bc62 100644 --- a/lib/mrsk/commander.rb +++ b/lib/mrsk/commander.rb @@ -143,10 +143,10 @@ class Mrsk::Commander private # Lazy setup of SSHKit def configure_sshkit_with(config) - SSHKit::Backend::Netssh.pool.idle_timeout = config.sshkit_pool_idle_timeout + SSHKit::Backend::Netssh.pool.idle_timeout = config.sshkit.pool_idle_timeout SSHKit::Backend::Netssh.configure do |sshkit| - sshkit.max_concurrent_starts = config.sshkit_max_concurrent_starts if config.sshkit_max_concurrent_starts - sshkit.ssh_options = config.ssh_options + sshkit.max_concurrent_starts = config.sshkit.max_concurrent_starts + sshkit.ssh_options = config.ssh.options end SSHKit.config.command_map[:docker] = "docker" # No need to use /usr/bin/env, just clogs up the logs SSHKit.config.output_verbosity = verbosity diff --git a/lib/mrsk/commands/base.rb b/lib/mrsk/commands/base.rb index bc427ddf..831f5994 100644 --- a/lib/mrsk/commands/base.rb +++ b/lib/mrsk/commands/base.rb @@ -13,12 +13,12 @@ module Mrsk::Commands def run_over_ssh(*command, host:) "ssh".tap do |cmd| - if config.ssh_proxy && config.ssh_proxy.is_a?(Net::SSH::Proxy::Jump) - cmd << " -J #{config.ssh_proxy.jump_proxies}" - elsif config.ssh_proxy && config.ssh_proxy.is_a?(Net::SSH::Proxy::Command) - cmd << " -o ProxyCommand='#{config.ssh_proxy.command_line_template}'" + if config.ssh.proxy && config.ssh.proxy.is_a?(Net::SSH::Proxy::Jump) + cmd << " -J #{config.ssh.proxy.jump_proxies}" + elsif config.ssh.proxy && config.ssh.proxy.is_a?(Net::SSH::Proxy::Command) + cmd << " -o ProxyCommand='#{config.ssh.proxy.command_line_template}'" end - cmd << " -t #{config.ssh_user}@#{host} '#{command.join(" ")}'" + cmd << " -t #{config.ssh.user}@#{host} '#{command.join(" ")}'" end end diff --git a/lib/mrsk/configuration.rb b/lib/mrsk/configuration.rb index d6badbf3..b1bb0370 100644 --- a/lib/mrsk/configuration.rb +++ b/lib/mrsk/configuration.rb @@ -135,38 +135,12 @@ class Mrsk::Configuration end - def ssh_user - if raw_config.ssh.present? - raw_config.ssh["user"] || "root" - else - "root" - end + def ssh + Mrsk::Configuration::Ssh.new(config: self) end - def ssh_proxy - if raw_config.ssh.present? && raw_config.ssh["proxy"] - Net::SSH::Proxy::Jump.new \ - raw_config.ssh["proxy"].include?("@") ? raw_config.ssh["proxy"] : "root@#{raw_config.ssh["proxy"]}" - elsif raw_config.ssh.present? && raw_config.ssh["proxy_command"] - Net::SSH::Proxy::Command.new(raw_config.ssh["proxy_command"]) - end - end - - def ssh_options - { user: ssh_user, proxy: ssh_proxy, auth_methods: [ "publickey" ], keepalive: true, keepalive_interval: 30 }.compact - end - - - def sshkit_max_concurrent_starts - raw_config.sshkit["max_concurrent_starts"] if raw_config.sshkit.present? - end - - def sshkit_pool_idle_timeout - if raw_config.sshkit.present? - raw_config.sshkit["pool_idle_timeout"] || 900 - else - 900 - end + def sshkit + Mrsk::Configuration::Sshkit.new(config: self) end @@ -198,7 +172,8 @@ class Mrsk::Configuration service_with_version: service_with_version, env_args: env_args, volume_args: volume_args, - ssh_options: ssh_options, + ssh_options: ssh.options, + sshkit: sshkit.to_h, builder: builder.to_h, accessories: raw_config.accessories, logging: logging_args, diff --git a/lib/mrsk/configuration/ssh.rb b/lib/mrsk/configuration/ssh.rb new file mode 100644 index 00000000..d328a5fa --- /dev/null +++ b/lib/mrsk/configuration/ssh.rb @@ -0,0 +1,24 @@ +class Mrsk::Configuration::Ssh + def initialize(config:) + @config = config.raw_config.ssh || {} + end + + def user + config.fetch("user", "root") + end + + def proxy + if (proxy = config["proxy"]) + Net::SSH::Proxy::Jump.new(proxy.include?("@") ? proxy : "root@#{proxy}") + elsif (proxy_command = config["proxy_command"]) + Net::SSH::Proxy::Command.new(proxy_command) + end + end + + def options + { user: user, proxy: proxy, auth_methods: [ "publickey" ], keepalive: true, keepalive_interval: 30 }.compact + end + + private + attr_accessor :config +end diff --git a/lib/mrsk/configuration/sshkit.rb b/lib/mrsk/configuration/sshkit.rb new file mode 100644 index 00000000..5371e15d --- /dev/null +++ b/lib/mrsk/configuration/sshkit.rb @@ -0,0 +1,20 @@ +class Mrsk::Configuration::Sshkit + def initialize(config:) + @options = config.raw_config.sshkit || {} + end + + def max_concurrent_starts + options.fetch("max_concurrent_starts", 30) + end + + def pool_idle_timeout + options.fetch("pool_idle_timeout", 900) + end + + def to_h + options + end + + private + attr_accessor :options +end diff --git a/lib/mrsk/sshkit_with_ext.rb b/lib/mrsk/sshkit_with_ext.rb index 58b1f4e3..eb8f359b 100644 --- a/lib/mrsk/sshkit_with_ext.rb +++ b/lib/mrsk/sshkit_with_ext.rb @@ -56,13 +56,7 @@ class SSHKit::Backend::Abstract end class SSHKit::Backend::Netssh::Configuration - DEFAULT_MAX_CONCURRENT_STARTS = 30 - - attr_writer :max_concurrent_starts - - def max_concurrent_starts - @max_concurrent_starts ||= DEFAULT_MAX_CONCURRENT_STARTS - end + attr_accessor :max_concurrent_starts end class SSHKit::Backend::Netssh @@ -72,7 +66,9 @@ class SSHKit::Backend::Netssh def configure(&block) super &block # Create this here to avoid lazy creation by multiple threads - @start_semaphore = Concurrent::Semaphore.new(config.max_concurrent_starts) + if config.max_concurrent_starts + @start_semaphore = Concurrent::Semaphore.new(config.max_concurrent_starts) + end end end @@ -94,7 +90,11 @@ class SSHKit::Backend::Netssh end def start_with_concurrency_limit(*args) - self.class.start_semaphore.acquire do + if self.class.start_semaphore + self.class.start_semaphore.acquire do + Net::SSH.start(*args) + end + else Net::SSH.start(*args) end end diff --git a/test/configuration/ssh_test.rb b/test/configuration/ssh_test.rb new file mode 100644 index 00000000..b958309e --- /dev/null +++ b/test/configuration/ssh_test.rb @@ -0,0 +1,32 @@ +require "test_helper" + +class ConfigurationSshTest < ActiveSupport::TestCase + setup do + @deploy = { + service: "app", image: "dhh/app", + registry: { "username" => "dhh", "password" => "secret" }, + env: { "REDIS_URL" => "redis://x/y" }, + servers: [ "1.1.1.1", "1.1.1.2" ], + volumes: ["/local/path:/container/path"] + } + + @config = Mrsk::Configuration.new(@deploy) + end + + test "ssh options" do + assert_equal "root", @config.ssh.options[:user] + + config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(ssh: { "user" => "app" }) }) + assert_equal "app", @config.ssh.options[:user] + end + + test "ssh options with proxy host" do + config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(ssh: { "proxy" => "1.2.3.4" }) }) + assert_equal "root@1.2.3.4", @config.ssh.options[:proxy].jump_proxies + end + + test "ssh options with proxy host and user" do + config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(ssh: { "proxy" => "app@1.2.3.4" }) }) + assert_equal "app@1.2.3.4", @config.ssh.options[:proxy].jump_proxies + end +end diff --git a/test/configuration/sshkit_test.rb b/test/configuration/sshkit_test.rb new file mode 100644 index 00000000..36c0e05d --- /dev/null +++ b/test/configuration/sshkit_test.rb @@ -0,0 +1,27 @@ +require "test_helper" + +class ConfigurationSshkitTest < ActiveSupport::TestCase + setup do + @deploy = { + service: "app", image: "dhh/app", + registry: { "username" => "dhh", "password" => "secret" }, + env: { "REDIS_URL" => "redis://x/y" }, + servers: [ "1.1.1.1", "1.1.1.2" ], + volumes: ["/local/path:/container/path"] + } + + @config = Mrsk::Configuration.new(@deploy) + end + + test "sshkit max concurrent starts" do + assert_equal 30, @config.sshkit.max_concurrent_starts + @config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(sshkit: { "max_concurrent_starts" => 50 }) }) + assert_equal 50, @config.sshkit.max_concurrent_starts + end + + test "sshkit pool idle timeout" do + assert_equal 900, @config.sshkit.pool_idle_timeout + @config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(sshkit: { "pool_idle_timeout" => 600 }) }) + assert_equal 600, @config.sshkit.pool_idle_timeout + end +end diff --git a/test/configuration_test.rb b/test/configuration_test.rb index 47eee321..bbcd3101 100644 --- a/test/configuration_test.rb +++ b/test/configuration_test.rb @@ -207,34 +207,6 @@ class ConfigurationTest < ActiveSupport::TestCase end end - test "ssh options" do - assert_equal "root", @config.ssh_options[:user] - - config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(ssh: { "user" => "app" }) }) - assert_equal "app", @config.ssh_options[:user] - end - - test "ssh options with proxy host" do - config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(ssh: { "proxy" => "1.2.3.4" }) }) - assert_equal "root@1.2.3.4", @config.ssh_options[:proxy].jump_proxies - end - - test "ssh options with proxy host and user" do - config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(ssh: { "proxy" => "app@1.2.3.4" }) }) - assert_equal "app@1.2.3.4", @config.ssh_options[:proxy].jump_proxies - end - - test "sshkit max concurrent starts" do - config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(sshkit: { "max_concurrent_starts" => 50 }) }) - assert_equal 50, config.sshkit_max_concurrent_starts - end - - test "sshkit pool idle timeout" do - assert_equal 900, @config.sshkit_pool_idle_timeout - config = Mrsk::Configuration.new(@deploy.tap { |c| c.merge!(sshkit: { "pool_idle_timeout" => 600 }) }) - assert_equal 600, config.sshkit_pool_idle_timeout - end - test "volume_args" do assert_equal ["--volume", "/local/path:/container/path"], @config.volume_args end @@ -287,6 +259,7 @@ class ConfigurationTest < ActiveSupport::TestCase :service_with_version=>"app-missing", :env_args=>["-e", "REDIS_URL=\"redis://x/y\""], :ssh_options=>{ :user=>"root", :auth_methods=>["publickey"], keepalive: true, keepalive_interval: 30 }, + :sshkit=>{}, :volume_args=>["--volume", "/local/path:/container/path"], :builder=>{}, :logging=>["--log-opt", "max-size=\"10m\""],