kamal 1.4.0 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/kamal/cli/accessory.rb +3 -2
- data/lib/kamal/cli/app/boot.rb +67 -0
- data/lib/kamal/cli/app/prepare_assets.rb +24 -0
- data/lib/kamal/cli/app.rb +20 -61
- data/lib/kamal/cli/base.rb +21 -7
- data/lib/kamal/cli/env.rb +3 -3
- data/lib/kamal/cli/main.rb +1 -1
- data/lib/kamal/cli/templates/deploy.yml +1 -1
- data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +0 -0
- data/lib/kamal/cli/traefik.rb +15 -12
- data/lib/kamal/commander/specifics.rb +49 -0
- data/lib/kamal/commander.rb +9 -33
- data/lib/kamal/commands/accessory.rb +2 -2
- data/lib/kamal/commands/app/assets.rb +4 -4
- data/lib/kamal/commands/app/cord.rb +2 -2
- data/lib/kamal/commands/app/execution.rb +8 -6
- data/lib/kamal/commands/app/images.rb +1 -1
- data/lib/kamal/commands/app.rb +29 -8
- data/lib/kamal/commands/app.rb.orig +127 -0
- data/lib/kamal/commands/auditor.rb +1 -1
- data/lib/kamal/commands/base.rb +6 -2
- data/lib/kamal/commands/builder/base.rb +14 -4
- data/lib/kamal/commands/builder/multiarch.rb +9 -9
- data/lib/kamal/commands/builder/native/cached.rb +7 -6
- data/lib/kamal/commands/builder/native/remote.rb +9 -9
- data/lib/kamal/commands/builder/native.rb +8 -7
- data/lib/kamal/commands/healthcheck.rb +0 -1
- data/lib/kamal/commands/hook.rb +1 -1
- data/lib/kamal/commands/lock.rb +19 -9
- data/lib/kamal/commands/prune.rb +2 -2
- data/lib/kamal/commands/server.rb +1 -1
- data/lib/kamal/commands/traefik.rb +8 -14
- data/lib/kamal/configuration/accessory.rb +9 -19
- data/lib/kamal/configuration/boot.rb +1 -1
- data/lib/kamal/configuration/builder.rb +7 -3
- data/lib/kamal/configuration/env.rb +40 -0
- data/lib/kamal/configuration/role.rb +12 -42
- data/lib/kamal/configuration.rb +20 -8
- data/lib/kamal/env_file.rb +12 -15
- data/lib/kamal/utils.rb +8 -5
- data/lib/kamal/version.rb +1 -1
- data/lib/kamal.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: adf6f739fb85822d7a74b5eac6bdfc373bc842a92acb9dcf9fd5f7e076fe8524
|
4
|
+
data.tar.gz: b2c30a95ee553009a7719760b2f2cb1f8e810bce61800819bf9b66aee3304b3e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b376df173efb5f00c4624b5da611caf0607f294a868b54eb318ff653755934750c19a74139ab978fa9842759812edb69c8d5384e79bd93989e58d121d2cc4ac9
|
7
|
+
data.tar.gz: 289e20a34e4cdbd3b274dd20c8224c4b6ddabe179495a6812b5b2b6a879f612c5f198111925cd3d438c4aaca73de24b32430b8e1ac8fc16b038797d07859b24f
|
data/lib/kamal/cli/accessory.rb
CHANGED
@@ -177,7 +177,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
177
177
|
if name == "all"
|
178
178
|
KAMAL.accessory_names.each { |accessory_name| remove(accessory_name) }
|
179
179
|
else
|
180
|
-
|
180
|
+
confirming "This will remove all containers, images and data directories for #{name}. Are you sure?" do
|
181
181
|
with_accessory(name) do
|
182
182
|
stop(name)
|
183
183
|
remove_container(name)
|
@@ -226,7 +226,8 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
226
226
|
|
227
227
|
private
|
228
228
|
def with_accessory(name)
|
229
|
-
if
|
229
|
+
if KAMAL.config.accessory(name)
|
230
|
+
accessory = KAMAL.accessory(name)
|
230
231
|
yield accessory, accessory_hosts(accessory)
|
231
232
|
else
|
232
233
|
error_on_missing_accessory(name)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Kamal::Cli::App::Boot
|
2
|
+
attr_reader :host, :role, :version, :sshkit
|
3
|
+
delegate :execute, :capture_with_info, :info, to: :sshkit
|
4
|
+
delegate :uses_cord?, :assets?, to: :role
|
5
|
+
|
6
|
+
def initialize(host, role, version, sshkit)
|
7
|
+
@host = host
|
8
|
+
@role = role
|
9
|
+
@version = version
|
10
|
+
@sshkit = sshkit
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
old_version = old_version_renamed_if_clashing
|
15
|
+
|
16
|
+
start_new_version
|
17
|
+
|
18
|
+
if old_version
|
19
|
+
stop_old_version(old_version)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def app
|
25
|
+
@app ||= KAMAL.app(role: role)
|
26
|
+
end
|
27
|
+
|
28
|
+
def auditor
|
29
|
+
@auditor = KAMAL.auditor(role: role)
|
30
|
+
end
|
31
|
+
|
32
|
+
def audit(message)
|
33
|
+
execute *auditor.record(message), verbosity: :debug
|
34
|
+
end
|
35
|
+
|
36
|
+
def old_version_renamed_if_clashing
|
37
|
+
if capture_with_info(*app.container_id_for_version(version), raise_on_non_zero_exit: false).present?
|
38
|
+
renamed_version = "#{version}_replaced_#{SecureRandom.hex(8)}"
|
39
|
+
info "Renaming container #{version} to #{renamed_version} as already deployed on #{host}"
|
40
|
+
audit("Renaming container #{version} to #{renamed_version}")
|
41
|
+
execute *app.rename_container(version: version, new_version: renamed_version)
|
42
|
+
end
|
43
|
+
|
44
|
+
capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip.presence
|
45
|
+
end
|
46
|
+
|
47
|
+
def start_new_version
|
48
|
+
audit "Booted app version #{version}"
|
49
|
+
execute *app.tie_cord(role.cord_host_file) if uses_cord?
|
50
|
+
execute *app.run(hostname: "#{host}-#{SecureRandom.hex(6)}")
|
51
|
+
Kamal::Cli::Healthcheck::Poller.wait_for_healthy(pause_after_ready: true) { capture_with_info(*app.status(version: version)) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop_old_version(version)
|
55
|
+
if uses_cord?
|
56
|
+
cord = capture_with_info(*app.cord(version: version), raise_on_non_zero_exit: false).strip
|
57
|
+
if cord.present?
|
58
|
+
execute *app.cut_cord(cord)
|
59
|
+
Kamal::Cli::Healthcheck::Poller.wait_for_unhealthy(pause_after_ready: true) { capture_with_info(*app.status(version: version)) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
execute *app.stop(version: version), raise_on_non_zero_exit: false
|
64
|
+
|
65
|
+
execute *app.clean_up_assets if assets?
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Kamal::Cli::App::PrepareAssets
|
2
|
+
attr_reader :host, :role, :sshkit
|
3
|
+
delegate :execute, :capture_with_info, :info, to: :sshkit
|
4
|
+
delegate :assets?, to: :role
|
5
|
+
|
6
|
+
def initialize(host, role, sshkit)
|
7
|
+
@host = host
|
8
|
+
@role = role
|
9
|
+
@sshkit = sshkit
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
if assets?
|
14
|
+
execute *app.extract_assets
|
15
|
+
old_version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
|
16
|
+
execute *app.sync_asset_volumes(old_version: old_version)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def app
|
22
|
+
@app ||= KAMAL.app(role: role)
|
23
|
+
end
|
24
|
+
end
|
data/lib/kamal/cli/app.rb
CHANGED
@@ -7,58 +7,23 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
7
7
|
using_version(version_or_latest) do |version|
|
8
8
|
say "Start container with version #{version} using a #{KAMAL.config.readiness_delay}s readiness delay (or reboot if already running)...", :magenta
|
9
9
|
|
10
|
+
# Assets are prepared in a separate step to ensure they are on all hosts before booting
|
10
11
|
on(KAMAL.hosts) do
|
11
|
-
execute *KAMAL.auditor.record("Tagging #{KAMAL.config.absolute_image} as the latest image"), verbosity: :debug
|
12
|
-
execute *KAMAL.app.tag_current_image_as_latest
|
13
|
-
|
14
12
|
KAMAL.roles_on(host).each do |role|
|
15
|
-
|
16
|
-
|
17
|
-
if role.assets?
|
18
|
-
execute *app.extract_assets
|
19
|
-
old_version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
|
20
|
-
execute *app.sync_asset_volumes(old_version: old_version)
|
21
|
-
end
|
13
|
+
Kamal::Cli::App::PrepareAssets.new(host, role, self).run
|
22
14
|
end
|
23
15
|
end
|
24
16
|
|
25
17
|
on(KAMAL.hosts, **KAMAL.boot_strategy) do |host|
|
26
18
|
KAMAL.roles_on(host).each do |role|
|
27
|
-
|
28
|
-
auditor = KAMAL.auditor(role: role)
|
29
|
-
|
30
|
-
if capture_with_info(*app.container_id_for_version(version), raise_on_non_zero_exit: false).present?
|
31
|
-
tmp_version = "#{version}_replaced_#{SecureRandom.hex(8)}"
|
32
|
-
info "Renaming container #{version} to #{tmp_version} as already deployed on #{host}"
|
33
|
-
execute *auditor.record("Renaming container #{version} to #{tmp_version}"), verbosity: :debug
|
34
|
-
execute *app.rename_container(version: version, new_version: tmp_version)
|
35
|
-
end
|
36
|
-
|
37
|
-
old_version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
|
38
|
-
|
39
|
-
execute *app.tie_cord(role.cord_host_file) if role.uses_cord?
|
40
|
-
|
41
|
-
execute *auditor.record("Booted app version #{version}"), verbosity: :debug
|
42
|
-
|
43
|
-
execute *app.run(hostname: "#{host}-#{SecureRandom.hex(6)}")
|
44
|
-
|
45
|
-
Kamal::Cli::Healthcheck::Poller.wait_for_healthy(pause_after_ready: true) { capture_with_info(*app.status(version: version)) }
|
46
|
-
|
47
|
-
if old_version.present?
|
48
|
-
if role.uses_cord?
|
49
|
-
cord = capture_with_info(*app.cord(version: old_version), raise_on_non_zero_exit: false).strip
|
50
|
-
if cord.present?
|
51
|
-
execute *app.cut_cord(cord)
|
52
|
-
Kamal::Cli::Healthcheck::Poller.wait_for_unhealthy(pause_after_ready: true) { capture_with_info(*app.status(version: old_version)) }
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
execute *app.stop(version: old_version), raise_on_non_zero_exit: false
|
57
|
-
|
58
|
-
execute *app.clean_up_assets if role.assets?
|
59
|
-
end
|
19
|
+
Kamal::Cli::App::Boot.new(host, role, version, self).run
|
60
20
|
end
|
61
21
|
end
|
22
|
+
|
23
|
+
on(KAMAL.hosts) do |host|
|
24
|
+
execute *KAMAL.auditor.record("Tagging #{KAMAL.config.absolute_image} as the latest image"), verbosity: :debug
|
25
|
+
execute *KAMAL.app.tag_latest_image
|
26
|
+
end
|
62
27
|
end
|
63
28
|
end
|
64
29
|
end
|
@@ -107,13 +72,15 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
107
72
|
desc "exec [CMD]", "Execute a custom command on servers (use --help to show options)"
|
108
73
|
option :interactive, aliases: "-i", type: :boolean, default: false, desc: "Execute command over ssh for an interactive shell (use for console/bash)"
|
109
74
|
option :reuse, type: :boolean, default: false, desc: "Reuse currently running container instead of starting a new one"
|
75
|
+
option :env, aliases: "-e", type: :hash, desc: "Set environment variables for the command"
|
110
76
|
def exec(cmd)
|
77
|
+
env = options[:env]
|
111
78
|
case
|
112
79
|
when options[:interactive] && options[:reuse]
|
113
80
|
say "Get current version of running container...", :magenta unless options[:version]
|
114
81
|
using_version(options[:version] || current_running_version) do |version|
|
115
82
|
say "Launching interactive command with version #{version} via SSH from existing container on #{KAMAL.primary_host}...", :magenta
|
116
|
-
run_locally { exec KAMAL.app(role: KAMAL.primary_role).execute_in_existing_container_over_ssh(cmd, host: KAMAL.primary_host) }
|
83
|
+
run_locally { exec KAMAL.app(role: KAMAL.primary_role).execute_in_existing_container_over_ssh(cmd, host: KAMAL.primary_host, env: env) }
|
117
84
|
end
|
118
85
|
|
119
86
|
when options[:interactive]
|
@@ -121,7 +88,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
121
88
|
using_version(version_or_latest) do |version|
|
122
89
|
say "Launching interactive command with version #{version} via SSH from new container on #{KAMAL.primary_host}...", :magenta
|
123
90
|
run_locally do
|
124
|
-
exec KAMAL.app(role: KAMAL.primary_role).execute_in_new_container_over_ssh(cmd, host: KAMAL.primary_host)
|
91
|
+
exec KAMAL.app(role: KAMAL.primary_role).execute_in_new_container_over_ssh(cmd, host: KAMAL.primary_host, env: env)
|
125
92
|
end
|
126
93
|
end
|
127
94
|
|
@@ -135,7 +102,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
135
102
|
|
136
103
|
roles.each do |role|
|
137
104
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}", role: role), verbosity: :debug
|
138
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role).execute_in_existing_container(cmd))
|
105
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role).execute_in_existing_container(cmd, env: env))
|
139
106
|
end
|
140
107
|
end
|
141
108
|
end
|
@@ -149,7 +116,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
149
116
|
|
150
117
|
roles.each do |role|
|
151
118
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
|
152
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role).execute_in_new_container(cmd))
|
119
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role).execute_in_new_container(cmd, env: env))
|
153
120
|
end
|
154
121
|
end
|
155
122
|
end
|
@@ -173,7 +140,10 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
173
140
|
roles = KAMAL.roles_on(host)
|
174
141
|
|
175
142
|
roles.each do |role|
|
176
|
-
|
143
|
+
versions = capture_with_info(*KAMAL.app(role: role).list_versions, raise_on_non_zero_exit: false).split("\n")
|
144
|
+
versions -= [ capture_with_info(*KAMAL.app(role: role).current_running_version, raise_on_non_zero_exit: false).strip ]
|
145
|
+
|
146
|
+
versions.each do |version|
|
177
147
|
if stop
|
178
148
|
puts_by_host host, "Stopping stale container for role #{role} with version #{version}"
|
179
149
|
execute *KAMAL.app(role: role).stop(version: version), raise_on_non_zero_exit: false
|
@@ -207,7 +177,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
207
177
|
run_locally do
|
208
178
|
info "Following logs on #{KAMAL.primary_host}..."
|
209
179
|
|
210
|
-
KAMAL.specific_roles ||= ["web"]
|
180
|
+
KAMAL.specific_roles ||= [ "web" ]
|
211
181
|
role = KAMAL.roles_on(KAMAL.primary_host).first
|
212
182
|
|
213
183
|
info KAMAL.app(role: role).follow_logs(host: KAMAL.primary_host, lines: lines, grep: grep)
|
@@ -309,18 +279,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
309
279
|
version.presence
|
310
280
|
end
|
311
281
|
|
312
|
-
def stale_versions(host:, role:)
|
313
|
-
versions = nil
|
314
|
-
on(host) do
|
315
|
-
versions = \
|
316
|
-
capture_with_info(*KAMAL.app(role: role).list_versions, raise_on_non_zero_exit: false)
|
317
|
-
.split("\n")
|
318
|
-
.drop(1)
|
319
|
-
end
|
320
|
-
versions
|
321
|
-
end
|
322
|
-
|
323
282
|
def version_or_latest
|
324
|
-
options[:version] ||
|
283
|
+
options[:version] || KAMAL.config.latest_tag
|
325
284
|
end
|
326
285
|
end
|
data/lib/kamal/cli/base.rb
CHANGED
@@ -73,7 +73,7 @@ module Kamal::Cli
|
|
73
73
|
def print_runtime
|
74
74
|
started_at = Time.now
|
75
75
|
yield
|
76
|
-
|
76
|
+
Time.now - started_at
|
77
77
|
ensure
|
78
78
|
runtime = Time.now - started_at
|
79
79
|
puts " Finished all in #{sprintf("%.1f seconds", runtime)}"
|
@@ -84,7 +84,7 @@ module Kamal::Cli
|
|
84
84
|
|
85
85
|
run_hook "pre-connect"
|
86
86
|
|
87
|
-
|
87
|
+
ensure_run_and_locks_directory
|
88
88
|
|
89
89
|
acquire_lock
|
90
90
|
|
@@ -103,6 +103,16 @@ module Kamal::Cli
|
|
103
103
|
release_lock
|
104
104
|
end
|
105
105
|
|
106
|
+
def confirming(question)
|
107
|
+
return yield if options[:confirmed]
|
108
|
+
|
109
|
+
if ask(question, limited_to: %w[ y N ], default: "N") == "y"
|
110
|
+
yield
|
111
|
+
else
|
112
|
+
say "Aborted", :red
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
106
116
|
def acquire_lock
|
107
117
|
raise_if_locked do
|
108
118
|
say "Acquiring the deploy lock...", :magenta
|
@@ -147,9 +157,9 @@ module Kamal::Cli
|
|
147
157
|
|
148
158
|
say "Running the #{hook} hook...", :magenta
|
149
159
|
run_locally do
|
150
|
-
|
151
|
-
rescue SSHKit::Command::Failed
|
152
|
-
raise HookError.new("Hook `#{hook}` failed")
|
160
|
+
execute *KAMAL.hook.run(hook, **details, **extra_details)
|
161
|
+
rescue SSHKit::Command::Failed => e
|
162
|
+
raise HookError.new("Hook `#{hook}` failed:\n#{e.message}")
|
153
163
|
end
|
154
164
|
end
|
155
165
|
end
|
@@ -176,10 +186,14 @@ module Kamal::Cli
|
|
176
186
|
instance_variable_get("@_invocations").first
|
177
187
|
end
|
178
188
|
|
179
|
-
def
|
189
|
+
def ensure_run_and_locks_directory
|
180
190
|
on(KAMAL.hosts) do
|
181
191
|
execute(*KAMAL.server.ensure_run_directory)
|
182
192
|
end
|
193
|
+
|
194
|
+
on(KAMAL.primary_host) do
|
195
|
+
execute(*KAMAL.lock.ensure_locks_directory)
|
196
|
+
end
|
183
197
|
end
|
184
|
-
|
198
|
+
end
|
185
199
|
end
|
data/lib/kamal/cli/env.rb
CHANGED
@@ -9,20 +9,20 @@ class Kamal::Cli::Env < Kamal::Cli::Base
|
|
9
9
|
|
10
10
|
KAMAL.roles_on(host).each do |role|
|
11
11
|
execute *KAMAL.app(role: role).make_env_directory
|
12
|
-
upload!
|
12
|
+
upload! role.env.secrets_io, role.env.secrets_file, mode: 400
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
on(KAMAL.traefik_hosts) do
|
17
17
|
execute *KAMAL.traefik.make_env_directory
|
18
|
-
upload!
|
18
|
+
upload! KAMAL.traefik.env.secrets_io, KAMAL.traefik.env.secrets_file, mode: 400
|
19
19
|
end
|
20
20
|
|
21
21
|
on(KAMAL.accessory_hosts) do
|
22
22
|
KAMAL.accessories_on(host).each do |accessory|
|
23
23
|
accessory_config = KAMAL.config.accessory(accessory)
|
24
24
|
execute *KAMAL.accessory(accessory).make_env_directory
|
25
|
-
upload!
|
25
|
+
upload! accessory_config.env.secrets_io, accessory_config.env.secrets_file, mode: 400
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
data/lib/kamal/cli/main.rb
CHANGED
@@ -197,7 +197,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
197
197
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
198
198
|
def remove
|
199
199
|
mutating do
|
200
|
-
|
200
|
+
confirming "This will remove all containers and images. Are you sure?" do
|
201
201
|
invoke "kamal:cli:traefik:remove", [], options.without(:confirmed)
|
202
202
|
invoke "kamal:cli:app:remove", [], options.without(:confirmed)
|
203
203
|
invoke "kamal:cli:accessory:remove", [ "all" ], options
|
File without changes
|
data/lib/kamal/cli/traefik.rb
CHANGED
@@ -11,20 +11,23 @@ class Kamal::Cli::Traefik < Kamal::Cli::Base
|
|
11
11
|
|
12
12
|
desc "reboot", "Reboot Traefik on servers (stop container, remove container, start new container)"
|
13
13
|
option :rolling, type: :boolean, default: false, desc: "Reboot traefik on hosts in sequence, rather than in parallel"
|
14
|
+
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
14
15
|
def reboot
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
16
|
+
confirming "This will cause a brief outage on each host. Are you sure?" do
|
17
|
+
mutating do
|
18
|
+
host_groups = options[:rolling] ? KAMAL.traefik_hosts : [ KAMAL.traefik_hosts ]
|
19
|
+
host_groups.each do |hosts|
|
20
|
+
host_list = Array(hosts).join(",")
|
21
|
+
run_hook "pre-traefik-reboot", hosts: host_list
|
22
|
+
on(hosts) do
|
23
|
+
execute *KAMAL.auditor.record("Rebooted traefik"), verbosity: :debug
|
24
|
+
execute *KAMAL.registry.login
|
25
|
+
execute *KAMAL.traefik.stop, raise_on_non_zero_exit: false
|
26
|
+
execute *KAMAL.traefik.remove_container
|
27
|
+
execute *KAMAL.traefik.run
|
28
|
+
end
|
29
|
+
run_hook "post-traefik-reboot", hosts: host_list
|
26
30
|
end
|
27
|
-
run_hook "post-traefik-reboot", hosts: host_list
|
28
31
|
end
|
29
32
|
end
|
30
33
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class Kamal::Commander::Specifics
|
2
|
+
attr_reader :primary_host, :primary_role, :hosts, :roles
|
3
|
+
delegate :stable_sort!, to: Kamal::Utils
|
4
|
+
|
5
|
+
def initialize(config, specific_hosts, specific_roles)
|
6
|
+
@config, @specific_hosts, @specific_roles = config, specific_hosts, specific_roles
|
7
|
+
|
8
|
+
@roles, @hosts = specified_roles, specified_hosts
|
9
|
+
|
10
|
+
@primary_host = specific_hosts&.first || primary_specific_role&.primary_host || config.primary_host
|
11
|
+
@primary_role = primary_or_first_role(roles_on(primary_host))
|
12
|
+
|
13
|
+
stable_sort!(roles) { |role| role == primary_role ? 0 : 1 }
|
14
|
+
stable_sort!(hosts) { |host| roles_on(host).any? { |role| role == primary_role } ? 0 : 1 }
|
15
|
+
end
|
16
|
+
|
17
|
+
def roles_on(host)
|
18
|
+
roles.select { |role| role.hosts.include?(host.to_s) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def traefik_hosts
|
22
|
+
specific_hosts || config.traefik_hosts
|
23
|
+
end
|
24
|
+
|
25
|
+
def accessory_hosts
|
26
|
+
specific_hosts || config.accessories.flat_map(&:hosts)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
attr_reader :config, :specific_hosts, :specific_roles
|
31
|
+
|
32
|
+
def primary_specific_role
|
33
|
+
primary_or_first_role(specific_roles) if specific_roles.present?
|
34
|
+
end
|
35
|
+
|
36
|
+
def primary_or_first_role(roles)
|
37
|
+
roles.detect { |role| role == config.primary_role } || roles.first
|
38
|
+
end
|
39
|
+
|
40
|
+
def specified_roles
|
41
|
+
(specific_roles || config.roles) \
|
42
|
+
.select { |role| ((specific_hosts || config.all_hosts) & role.hosts).any? }
|
43
|
+
end
|
44
|
+
|
45
|
+
def specified_hosts
|
46
|
+
(specific_hosts || config.all_hosts) \
|
47
|
+
.select { |host| (specific_roles || config.roles).flat_map(&:hosts).include?(host) }
|
48
|
+
end
|
49
|
+
end
|
data/lib/kamal/commander.rb
CHANGED
@@ -3,11 +3,13 @@ require "active_support/core_ext/module/delegation"
|
|
3
3
|
|
4
4
|
class Kamal::Commander
|
5
5
|
attr_accessor :verbosity, :holding_lock, :hold_lock_on_error
|
6
|
+
delegate :hosts, :roles, :primary_host, :primary_role, :roles_on, :traefik_hosts, :accessory_hosts, to: :specifics
|
6
7
|
|
7
8
|
def initialize
|
8
9
|
self.verbosity = :info
|
9
10
|
self.holding_lock = false
|
10
11
|
self.hold_lock_on_error = false
|
12
|
+
@specifics = nil
|
11
13
|
end
|
12
14
|
|
13
15
|
def config
|
@@ -24,10 +26,12 @@ class Kamal::Commander
|
|
24
26
|
attr_reader :specific_roles, :specific_hosts
|
25
27
|
|
26
28
|
def specific_primary!
|
29
|
+
@specifics = nil
|
27
30
|
self.specific_hosts = [ config.primary_host ]
|
28
31
|
end
|
29
32
|
|
30
33
|
def specific_roles=(role_names)
|
34
|
+
@specifics = nil
|
31
35
|
if role_names.present?
|
32
36
|
@specific_roles = Kamal::Utils.filter_specific_items(role_names, config.roles)
|
33
37
|
|
@@ -40,6 +44,7 @@ class Kamal::Commander
|
|
40
44
|
end
|
41
45
|
|
42
46
|
def specific_hosts=(hosts)
|
47
|
+
@specifics = nil
|
43
48
|
if hosts.present?
|
44
49
|
@specific_hosts = Kamal::Utils.filter_specific_items(hosts, config.all_hosts)
|
45
50
|
|
@@ -51,39 +56,6 @@ class Kamal::Commander
|
|
51
56
|
end
|
52
57
|
end
|
53
58
|
|
54
|
-
def primary_host
|
55
|
-
# Given a list of specific roles, make an effort to match up with the primary_role
|
56
|
-
specific_hosts&.first || specific_roles&.detect { |role| role == config.primary_role }&.primary_host || specific_roles&.first&.primary_host || config.primary_host
|
57
|
-
end
|
58
|
-
|
59
|
-
def primary_role
|
60
|
-
roles_on(primary_host).first
|
61
|
-
end
|
62
|
-
|
63
|
-
def roles
|
64
|
-
(specific_roles || config.roles).select do |role|
|
65
|
-
((specific_hosts || config.all_hosts) & role.hosts).any?
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def hosts
|
70
|
-
(specific_hosts || config.all_hosts).select do |host|
|
71
|
-
(specific_roles || config.roles).flat_map(&:hosts).include?(host)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def roles_on(host)
|
76
|
-
roles.select { |role| role.hosts.include?(host.to_s) }
|
77
|
-
end
|
78
|
-
|
79
|
-
def traefik_hosts
|
80
|
-
specific_hosts || config.traefik_hosts
|
81
|
-
end
|
82
|
-
|
83
|
-
def accessory_hosts
|
84
|
-
specific_hosts || config.accessories.flat_map(&:hosts)
|
85
|
-
end
|
86
|
-
|
87
59
|
def accessory_names
|
88
60
|
config.accessories&.collect(&:name) || []
|
89
61
|
end
|
@@ -181,4 +153,8 @@ class Kamal::Commander
|
|
181
153
|
SSHKit.config.command_map[:docker] = "docker" # No need to use /usr/bin/env, just clogs up the logs
|
182
154
|
SSHKit.config.output_verbosity = verbosity
|
183
155
|
end
|
156
|
+
|
157
|
+
def specifics
|
158
|
+
@specifics ||= Kamal::Commander::Specifics.new(config, specific_hosts, specific_roles)
|
159
|
+
end
|
184
160
|
end
|
@@ -99,11 +99,11 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def make_env_directory
|
102
|
-
make_directory accessory_config.
|
102
|
+
make_directory accessory_config.env.secrets_directory
|
103
103
|
end
|
104
104
|
|
105
105
|
def remove_env_file
|
106
|
-
[:rm, "-f", accessory_config.
|
106
|
+
[ :rm, "-f", accessory_config.env.secrets_file ]
|
107
107
|
end
|
108
108
|
|
109
109
|
private
|
@@ -4,8 +4,8 @@ module Kamal::Commands::App::Assets
|
|
4
4
|
|
5
5
|
combine \
|
6
6
|
make_directory(role.asset_extracted_path),
|
7
|
-
[*docker(:stop, "-t 1", asset_container, "2> /dev/null"), "|| true"],
|
8
|
-
docker(:run, "--name", asset_container, "--detach", "--rm", config.
|
7
|
+
[ *docker(:stop, "-t 1", asset_container, "2> /dev/null"), "|| true" ],
|
8
|
+
docker(:run, "--name", asset_container, "--detach", "--rm", config.absolute_image, "sleep 1000000"),
|
9
9
|
docker(:cp, "-L", "#{asset_container}:#{role.asset_path}/.", role.asset_extracted_path),
|
10
10
|
docker(:stop, "-t 1", asset_container),
|
11
11
|
by: "&&"
|
@@ -17,7 +17,7 @@ module Kamal::Commands::App::Assets
|
|
17
17
|
old_extracted_path, old_volume_path = role.asset_extracted_path(old_version), role.asset_volume(old_version).host_path
|
18
18
|
end
|
19
19
|
|
20
|
-
commands = [make_directory(new_volume_path), copy_contents(new_extracted_path, new_volume_path)]
|
20
|
+
commands = [ make_directory(new_volume_path), copy_contents(new_extracted_path, new_volume_path) ]
|
21
21
|
|
22
22
|
if old_version.present?
|
23
23
|
commands << copy_contents(new_extracted_path, old_volume_path, continue_on_error: true)
|
@@ -46,6 +46,6 @@ module Kamal::Commands::App::Assets
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def copy_contents(source, destination, continue_on_error: false)
|
49
|
-
[ :cp, "-rnT", "#{source}", destination, *("|| true" if continue_on_error)]
|
49
|
+
[ :cp, "-rnT", "#{source}", destination, *("|| true" if continue_on_error) ]
|
50
50
|
end
|
51
51
|
end
|
@@ -2,7 +2,7 @@ module Kamal::Commands::App::Cord
|
|
2
2
|
def cord(version:)
|
3
3
|
pipe \
|
4
4
|
docker(:inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\\n\" .Source .Destination}}{{ end }}'", container_name(version)),
|
5
|
-
[:awk, "'$2 == \"#{role.cord_volume.container_path}\" {print $1}'"]
|
5
|
+
[ :awk, "'$2 == \"#{role.cord_volume.container_path}\" {print $1}'" ]
|
6
6
|
end
|
7
7
|
|
8
8
|
def tie_cord(cord)
|
@@ -17,6 +17,6 @@ module Kamal::Commands::App::Cord
|
|
17
17
|
def create_empty_file(file)
|
18
18
|
chain \
|
19
19
|
make_directory_for(file),
|
20
|
-
[:touch, file]
|
20
|
+
[ :touch, file ]
|
21
21
|
end
|
22
22
|
end
|
@@ -1,27 +1,29 @@
|
|
1
1
|
module Kamal::Commands::App::Execution
|
2
|
-
def execute_in_existing_container(*command, interactive: false)
|
2
|
+
def execute_in_existing_container(*command, interactive: false, env:)
|
3
3
|
docker :exec,
|
4
4
|
("-it" if interactive),
|
5
|
+
*argumentize("--env", env),
|
5
6
|
container_name,
|
6
7
|
*command
|
7
8
|
end
|
8
9
|
|
9
|
-
def execute_in_new_container(*command, interactive: false)
|
10
|
+
def execute_in_new_container(*command, interactive: false, env:)
|
10
11
|
docker :run,
|
11
12
|
("-it" if interactive),
|
12
13
|
"--rm",
|
13
14
|
*role&.env_args,
|
15
|
+
*argumentize("--env", env),
|
14
16
|
*config.volume_args,
|
15
17
|
*role&.option_args,
|
16
18
|
config.absolute_image,
|
17
19
|
*command
|
18
20
|
end
|
19
21
|
|
20
|
-
def execute_in_existing_container_over_ssh(*command, host:)
|
21
|
-
run_over_ssh execute_in_existing_container(*command, interactive: true), host: host
|
22
|
+
def execute_in_existing_container_over_ssh(*command, host:, env:)
|
23
|
+
run_over_ssh execute_in_existing_container(*command, interactive: true, env: env), host: host
|
22
24
|
end
|
23
25
|
|
24
|
-
def execute_in_new_container_over_ssh(*command, host:)
|
25
|
-
run_over_ssh execute_in_new_container(*command, interactive: true), host: host
|
26
|
+
def execute_in_new_container_over_ssh(*command, host:, env:)
|
27
|
+
run_over_ssh execute_in_new_container(*command, interactive: true, env: env), host: host
|
26
28
|
end
|
27
29
|
end
|