indocker 0.6.1 → 0.6.2
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/Gemfile.lock +1 -1
- data/lib/indocker/launchers/configuration_deployer.rb +12 -0
- data/lib/indocker/rsync.rb +7 -2
- data/lib/indocker/ssh_session.rb +49 -27
- data/lib/indocker/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: 10915e6da5c1e8d2899b510a223d8761a0542880776b61e8078d38d23e261b28
|
|
4
|
+
data.tar.gz: dd047c45c09dd0f26b95ae668f00266c2f45c46ff1c4aecff14ef0155d9e5a59
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b879a49d9defa1cbe4dc56685e0467ff070c9d0310a6fd5531766c941e7b51028aa1ace5817dec1638dec944ccba8afbbceffa062095a658fdb248c615095253
|
|
7
|
+
data.tar.gz: 70814bb24a5a66da13c36a6d1350403686b6a5672f184aa691179cfb40850b74e66b059fc82f249fc052ebe942c2ed26326570ce041270d561f25ca7bb67021c
|
data/Gemfile.lock
CHANGED
|
@@ -46,6 +46,9 @@ class Indocker::Launchers::ConfigurationDeployer
|
|
|
46
46
|
deployer = Indocker::ContainerDeployer.new(configuration: configuration, logger: @logger)
|
|
47
47
|
|
|
48
48
|
@global_logger.info("Establishing ssh sessions to all servers...")
|
|
49
|
+
|
|
50
|
+
warm_jump_hosts(deploy_servers + build_servers)
|
|
51
|
+
|
|
49
52
|
build_server_pool.create_sessions!
|
|
50
53
|
|
|
51
54
|
build_servers = configuration
|
|
@@ -103,6 +106,15 @@ class Indocker::Launchers::ConfigurationDeployer
|
|
|
103
106
|
|
|
104
107
|
private
|
|
105
108
|
|
|
109
|
+
# Opens the shared connection to each jump host before anything runs in parallel, so the
|
|
110
|
+
# fan-out reuses it instead of racing to create one connection per operation.
|
|
111
|
+
def warm_jump_hosts(servers)
|
|
112
|
+
servers.map(&:jump_host).compact.uniq.each do |jump_host|
|
|
113
|
+
@logger.info("Establishing shared connection to jump host #{jump_host}")
|
|
114
|
+
Indocker::SshSession.warm_jump_host(jump_host, logger: @logger)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
106
118
|
def wait_remote_operations(remote_operations)
|
|
107
119
|
remote_operations.each do |remote_operation|
|
|
108
120
|
server = remote_operation.server
|
data/lib/indocker/rsync.rb
CHANGED
|
@@ -44,8 +44,13 @@ class Indocker::Rsync
|
|
|
44
44
|
]
|
|
45
45
|
|
|
46
46
|
# Tunnel through the jump host the server is defined with, so that a server
|
|
47
|
-
# reachable only over its internal address syncs like any other one.
|
|
48
|
-
|
|
47
|
+
# reachable only over its internal address syncs like any other one. ProxyCommand
|
|
48
|
+
# rather than -J: it is the same tunnel every other remote operation uses, including
|
|
49
|
+
# the shared connection that keeps a parallel sync from exhausting the jump host's
|
|
50
|
+
# MaxStartups.
|
|
51
|
+
if session.jump_host
|
|
52
|
+
ssh_command << %Q{-o ProxyCommand="#{Indocker::SshSession.jump_proxy_command(session.jump_host)}"}
|
|
53
|
+
end
|
|
49
54
|
|
|
50
55
|
ssh_command = ssh_command.join(' ')
|
|
51
56
|
command = "rsync --delete-after -a -e '#{ssh_command}' #{from} #{session.user}@#{session.host}:#{to}"
|
data/lib/indocker/ssh_session.rb
CHANGED
|
@@ -2,6 +2,7 @@ require_relative 'shell'
|
|
|
2
2
|
|
|
3
3
|
class Indocker::SshSession
|
|
4
4
|
LOCALHOST = 'localhost'.freeze
|
|
5
|
+
JUMP_CONTROL_PERSIST = '600'.freeze
|
|
5
6
|
|
|
6
7
|
class ExecResult
|
|
7
8
|
attr_reader :stdout_data, :stderr_data, :exit_code, :exit_signal
|
|
@@ -21,6 +22,53 @@ class Indocker::SshSession
|
|
|
21
22
|
|
|
22
23
|
attr_reader :host, :user, :port, :logger, :jump_host
|
|
23
24
|
|
|
25
|
+
# Builds the tunnel to a jump host, shared by Net::SSH sessions and by rsync.
|
|
26
|
+
#
|
|
27
|
+
# Net::SSH::Proxy::Jump would build a bare `ssh -W`: a jump host that asks for a password
|
|
28
|
+
# hangs the deploy, and probing a server whose sshd is still starting prints
|
|
29
|
+
# "channel 0: open failed" from the ssh client on every retry.
|
|
30
|
+
#
|
|
31
|
+
# Multiplexing matters even more. A deploy syncs and deploys to every server in parallel,
|
|
32
|
+
# and each of those opens its own tunnel; sshd drops everything above MaxStartups (10 by
|
|
33
|
+
# default), which surfaces as "connection closed by remote host" on random servers. Sharing
|
|
34
|
+
# one connection to the jump host keeps the whole fan-out down to a single tunnel.
|
|
35
|
+
def self.jump_proxy_command(jump_host)
|
|
36
|
+
require 'digest'
|
|
37
|
+
|
|
38
|
+
host = jump_host
|
|
39
|
+
port = nil
|
|
40
|
+
|
|
41
|
+
if host =~ /\A(.+):(\d+)\z/
|
|
42
|
+
host = $1
|
|
43
|
+
port = $2
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
control_path = "/tmp/indocker-jump-#{Digest::MD5.hexdigest(jump_host)[0, 10]}"
|
|
47
|
+
|
|
48
|
+
command = [
|
|
49
|
+
"ssh -q",
|
|
50
|
+
"-o BatchMode=yes",
|
|
51
|
+
"-o ConnectTimeout=#{Indocker.ssh_connect_timeout}",
|
|
52
|
+
"-o ControlMaster=auto",
|
|
53
|
+
"-o ControlPath=#{control_path}",
|
|
54
|
+
"-o ControlPersist=#{JUMP_CONTROL_PERSIST}",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
command << "-p #{port}" if port
|
|
58
|
+
command << "-W %h:%p"
|
|
59
|
+
command << host
|
|
60
|
+
|
|
61
|
+
command.join(' ')
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Opens the shared connection to a jump host up front. Without it the first wave of
|
|
65
|
+
# parallel operations races to create the master connection and opens a burst of them.
|
|
66
|
+
def self.warm_jump_host(jump_host, logger:)
|
|
67
|
+
command = jump_proxy_command(jump_host).sub("-W %h:%p ", "")
|
|
68
|
+
|
|
69
|
+
Indocker::Shell.command("#{command} true", logger)
|
|
70
|
+
end
|
|
71
|
+
|
|
24
72
|
# Opens a session to a server as it is defined in the configuration, including its
|
|
25
73
|
# jump host, if any. Every remote operation goes through here, so a server reached
|
|
26
74
|
# through a jump host is reached that way by binaries sync, container deploys and
|
|
@@ -56,7 +104,7 @@ class Indocker::SshSession
|
|
|
56
104
|
|
|
57
105
|
if @jump_host
|
|
58
106
|
require 'net/ssh/proxy/command'
|
|
59
|
-
options[:proxy] = Net::SSH::Proxy::Command.new(jump_proxy_command)
|
|
107
|
+
options[:proxy] = Net::SSH::Proxy::Command.new(self.class.jump_proxy_command(@jump_host))
|
|
60
108
|
end
|
|
61
109
|
|
|
62
110
|
@ssh = Net::SSH.start(@host, @user, options)
|
|
@@ -82,32 +130,6 @@ class Indocker::SshSession
|
|
|
82
130
|
end
|
|
83
131
|
|
|
84
132
|
private
|
|
85
|
-
# Net::SSH::Proxy::Jump builds this same `ssh -W` tunnel, but without BatchMode,
|
|
86
|
-
# connect timeout or -q: a jump host that asks for a password hangs the deploy, and
|
|
87
|
-
# every probe of a server whose sshd is still starting prints "channel 0: open failed"
|
|
88
|
-
# from the ssh client.
|
|
89
|
-
def jump_proxy_command
|
|
90
|
-
host = @jump_host
|
|
91
|
-
port = nil
|
|
92
|
-
|
|
93
|
-
if host =~ /\A(.+):(\d+)\z/
|
|
94
|
-
host = $1
|
|
95
|
-
port = $2
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
command = [
|
|
99
|
-
"ssh -q",
|
|
100
|
-
"-o BatchMode=yes",
|
|
101
|
-
"-o ConnectTimeout=#{Indocker.ssh_connect_timeout}",
|
|
102
|
-
]
|
|
103
|
-
|
|
104
|
-
command << "-p #{port}" if port
|
|
105
|
-
command << "-W %h:%p"
|
|
106
|
-
command << host
|
|
107
|
-
|
|
108
|
-
command.join(' ')
|
|
109
|
-
end
|
|
110
|
-
|
|
111
133
|
def exec_locally!(command)
|
|
112
134
|
res = Indocker::Shell.command_with_result(command, @logger, skip_logging: false)
|
|
113
135
|
ExecResult.new(res.stdout, '', res.exit_status, nil)
|
data/lib/indocker/version.rb
CHANGED