dash 4.0.5 → 4.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/dash/cli/doctor/host_checks.rb +18 -1
- data/lib/dash/configuration/docs/ssh.yml +8 -0
- data/lib/dash/configuration/docs/sshkit.yml +2 -1
- data/lib/dash/configuration/registry.rb +24 -3
- data/lib/dash/configuration/ssh.rb +8 -1
- data/lib/dash/sshkit_with_ext.rb +15 -4
- data/lib/dash/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a417156134dd79a4724f87192f70baa55fdbc4478a115e021845e3742bacee88
|
|
4
|
+
data.tar.gz: ebdbb8d7f192b692c31c8cdaa9834584c01dec3653b744b1a481672fee179d28
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3b263730b7ccf4eeeb857304477945fd9d2476bdf52cdb30a964b12b09143b36b5f585ae672d776eac29a6e009606add151aa6e9dd98385f9146980bbc40a9db
|
|
7
|
+
data.tar.gz: a3ebb3e0141a0f692bd70205aa3d438f9a3cbd1022d1b92c229bf1daf19294c4826a7420c0fefbb077f5a6d6cb46016ee6ab3926bb08f11383bb7a6eb0aaa23e
|
|
@@ -113,7 +113,12 @@ class Dash::Cli::Doctor::HostChecks
|
|
|
113
113
|
elsif version.nil?
|
|
114
114
|
result :proxy_version, :ok, "not running (will be started on deploy)"
|
|
115
115
|
elsif Dash::Utils.older_version?(version, minimum)
|
|
116
|
-
|
|
116
|
+
if deploy_reboots_proxy?
|
|
117
|
+
result :proxy_version, :ok,
|
|
118
|
+
"#{version} is older than the minimum #{minimum}; the next deploy reboots the proxy to update it"
|
|
119
|
+
else
|
|
120
|
+
result :proxy_version, :fail, "#{version} is older than the minimum #{minimum}, run `dash proxy reboot` to update"
|
|
121
|
+
end
|
|
117
122
|
else
|
|
118
123
|
result :proxy_version, :ok, "#{version} (minimum #{minimum})"
|
|
119
124
|
end
|
|
@@ -121,6 +126,18 @@ class Dash::Cli::Doctor::HostChecks
|
|
|
121
126
|
result :proxy_version, :warn, "running image tag #{version} is not a version number"
|
|
122
127
|
end
|
|
123
128
|
|
|
129
|
+
# Mirrors `proxy boot`: with reboot_on_deploy enabled it reboots a drifted
|
|
130
|
+
# proxy on its own, and a MINIMUM_VERSION bump moves the expected config
|
|
131
|
+
# digest, so a merely-stale proxy is converged by the very deploy this
|
|
132
|
+
# doctor gates. Only a version the deploy would not touch — pinned in the
|
|
133
|
+
# config, or with automatic reboots disabled — is a real failure. Any error
|
|
134
|
+
# probing drift falls back to the conservative answer.
|
|
135
|
+
def deploy_reboots_proxy?
|
|
136
|
+
DASH.config.proxy.reboot_on_deploy? && Dash::Cli::Proxy::Drift.new(host, sshkit).drifted?
|
|
137
|
+
rescue StandardError
|
|
138
|
+
false
|
|
139
|
+
end
|
|
140
|
+
|
|
124
141
|
# What a container path has to look like to count as a container runtime
|
|
125
142
|
# socket when the config no longer names one.
|
|
126
143
|
DOCKER_SOCKET_PATTERN = %r{docker\.sock\z}
|
|
@@ -79,3 +79,11 @@ ssh:
|
|
|
79
79
|
# host or tunnel that does not support agent forwarding (for example,
|
|
80
80
|
# Cloudflare Access for Infrastructure with SSH).
|
|
81
81
|
forward_agent: false
|
|
82
|
+
|
|
83
|
+
# Connect timeout
|
|
84
|
+
#
|
|
85
|
+
# Seconds to wait for the TCP connection and the SSH handshake with a
|
|
86
|
+
# host before giving up with `Net::SSH::ConnectionTimeout`. Defaults to
|
|
87
|
+
# 30. Without a bound, a host that accepts the connection but never
|
|
88
|
+
# answers the handshake hangs the deploy indefinitely.
|
|
89
|
+
connect_timeout: 30
|
|
@@ -23,7 +23,8 @@ sshkit:
|
|
|
23
23
|
#
|
|
24
24
|
# A pooled connection that the network dropped while idle is evicted and the command
|
|
25
25
|
# retried once on a fresh connection, so a long wait elsewhere (a server-lock poll, a
|
|
26
|
-
# loadbalancer reboot) does not fail the next command on another host.
|
|
26
|
+
# loadbalancer reboot) does not fail the next command on another host. A host that
|
|
27
|
+
# stops answering keepalives mid-command is not retried; that deploy fails.
|
|
27
28
|
pool_idle_timeout: 300
|
|
28
29
|
|
|
29
30
|
# DNS retry settings
|
|
@@ -4,6 +4,7 @@ class Dash::Configuration::Registry
|
|
|
4
4
|
def initialize(config:, secrets:, context: "registry")
|
|
5
5
|
@registry_config = config["registry"] || {}
|
|
6
6
|
@secrets = secrets
|
|
7
|
+
@context = context
|
|
7
8
|
validate! registry_config, context: context, with: Dash::Configuration::Validator::Registry
|
|
8
9
|
end
|
|
9
10
|
|
|
@@ -28,13 +29,33 @@ class Dash::Configuration::Registry
|
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
private
|
|
31
|
-
attr_reader :registry_config, :secrets
|
|
32
|
+
attr_reader :registry_config, :secrets, :context
|
|
32
33
|
|
|
34
|
+
# A blank credential would reach the remote host as an empty `docker login -p`,
|
|
35
|
+
# where docker falls back to prompting and dies with the opaque "Cannot perform
|
|
36
|
+
# an interactive login from a non TTY device" — fail locally instead.
|
|
33
37
|
def lookup(key)
|
|
38
|
+
value =
|
|
39
|
+
if registry_config[key].is_a?(Array)
|
|
40
|
+
secrets[registry_config[key].first]
|
|
41
|
+
else
|
|
42
|
+
registry_config[key]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
validate_present! key, value unless local?
|
|
46
|
+
value
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def validate_present!(key, value)
|
|
50
|
+
return if value.present?
|
|
51
|
+
|
|
34
52
|
if registry_config[key].is_a?(Array)
|
|
35
|
-
|
|
53
|
+
secret = registry_config[key].first
|
|
54
|
+
raise Dash::ConfigurationError,
|
|
55
|
+
"#{context}/#{key}: secret '#{secret}' resolved to an empty value — " \
|
|
56
|
+
"if your secrets file forwards it from the environment (#{secret}=$#{secret}), export the variable before running dash"
|
|
36
57
|
else
|
|
37
|
-
|
|
58
|
+
raise Dash::ConfigurationError, "#{context}/#{key}: is blank"
|
|
38
59
|
end
|
|
39
60
|
end
|
|
40
61
|
end
|
|
@@ -57,8 +57,15 @@ class Dash::Configuration::Ssh
|
|
|
57
57
|
ssh_config["forward_agent"]
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
# net-ssh only bounds the TCP connect and the version/key exchange when
|
|
61
|
+
# `timeout` is set. Without it, a host that accepts the TCP connection but
|
|
62
|
+
# never finishes the handshake blocks the deploy forever.
|
|
63
|
+
def connect_timeout
|
|
64
|
+
ssh_config.fetch("connect_timeout", 30)
|
|
65
|
+
end
|
|
66
|
+
|
|
60
67
|
def options
|
|
61
|
-
{ user: user, port: port, proxy: proxy, logger: logger, keepalive: true, keepalive_interval: 30, keys_only: keys_only, keys: keys, key_data: key_data, config: config, forward_agent: forward_agent }.compact
|
|
68
|
+
{ user: user, port: port, proxy: proxy, logger: logger, keepalive: true, keepalive_interval: 30, keys_only: keys_only, keys: keys, key_data: key_data, config: config, forward_agent: forward_agent, timeout: connect_timeout }.compact
|
|
62
69
|
end
|
|
63
70
|
|
|
64
71
|
def to_h
|
data/lib/dash/sshkit_with_ext.rb
CHANGED
|
@@ -181,8 +181,15 @@ class SSHKit::Backend::Netssh
|
|
|
181
181
|
# The block is rerun, so a drop *mid-command* runs the command twice. That
|
|
182
182
|
# is accepted: the errors here are the idle-drop ones, where nothing reached
|
|
183
183
|
# the server, and the alternative is the deploy failing on the next host.
|
|
184
|
+
#
|
|
185
|
+
# Net::SSH::Timeout (a Disconnect subclass) is deliberately excluded. net-ssh
|
|
186
|
+
# raises it when a host has ignored `keepalive_maxcount` keepalives in a row,
|
|
187
|
+
# which is a host that stopped answering mid-command, not a dropped idle
|
|
188
|
+
# socket. Retrying that reconnects to a host that has just proven unresponsive
|
|
189
|
+
# and, if it accepts the connection but never finishes the handshake or the
|
|
190
|
+
# command, hangs the deploy with no further guard. Let it fail.
|
|
184
191
|
module ReconnectOnStaleConnection
|
|
185
|
-
STALE_CONNECTION_ERRORS = [ Errno::ECONNRESET, Errno::EPIPE, Net::SSH::Disconnect
|
|
192
|
+
STALE_CONNECTION_ERRORS = [ Errno::ECONNRESET, Errno::EPIPE, Net::SSH::Disconnect ].freeze
|
|
186
193
|
|
|
187
194
|
private
|
|
188
195
|
def with_ssh
|
|
@@ -191,12 +198,12 @@ class SSHKit::Backend::Netssh
|
|
|
191
198
|
begin
|
|
192
199
|
super do |ssh|
|
|
193
200
|
yield ssh
|
|
194
|
-
rescue *STALE_CONNECTION_ERRORS
|
|
195
|
-
evict_stale_session(ssh)
|
|
201
|
+
rescue *STALE_CONNECTION_ERRORS => e
|
|
202
|
+
evict_stale_session(ssh) if stale_connection_error?(e)
|
|
196
203
|
raise
|
|
197
204
|
end
|
|
198
205
|
rescue *STALE_CONNECTION_ERRORS => e
|
|
199
|
-
raise if reconnected
|
|
206
|
+
raise if reconnected || !stale_connection_error?(e)
|
|
200
207
|
|
|
201
208
|
reconnected = true
|
|
202
209
|
SSHKit.config.output.warn("Reconnecting to #{host}: #{e.message}")
|
|
@@ -204,6 +211,10 @@ class SSHKit::Backend::Netssh
|
|
|
204
211
|
end
|
|
205
212
|
end
|
|
206
213
|
|
|
214
|
+
def stale_connection_error?(error)
|
|
215
|
+
!error.is_a?(Net::SSH::Timeout)
|
|
216
|
+
end
|
|
217
|
+
|
|
207
218
|
# `close` waits for channel-close acknowledgements, which never come over
|
|
208
219
|
# a dead socket. `shutdown!` just closes the socket, and a closed session
|
|
209
220
|
# is what makes the pool drop it instead of caching it again.
|
data/lib/dash/version.rb
CHANGED