kamal 1.8.3 → 2.7.0
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/README.md +1 -1
- data/lib/kamal/cli/accessory.rb +92 -38
- data/lib/kamal/cli/alias/command.rb +10 -0
- data/lib/kamal/cli/app/{prepare_assets.rb → assets.rb} +1 -1
- data/lib/kamal/cli/app/boot.rb +23 -16
- data/lib/kamal/cli/app/error_pages.rb +33 -0
- data/lib/kamal/cli/app/ssl_certificates.rb +28 -0
- data/lib/kamal/cli/app.rb +132 -30
- data/lib/kamal/cli/base.rb +57 -53
- data/lib/kamal/cli/build.rb +81 -38
- data/lib/kamal/cli/healthcheck/barrier.rb +2 -0
- data/lib/kamal/cli/healthcheck/poller.rb +18 -39
- data/lib/kamal/cli/lock.rb +2 -3
- data/lib/kamal/cli/main.rb +60 -59
- data/lib/kamal/cli/proxy.rb +290 -0
- data/lib/kamal/cli/prune.rb +0 -1
- data/lib/kamal/cli/registry.rb +2 -0
- data/lib/kamal/cli/secrets.rb +49 -0
- data/lib/kamal/cli/server.rb +6 -5
- data/lib/kamal/cli/templates/deploy.yml +53 -53
- data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +2 -12
- data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-build.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +19 -6
- data/lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample +3 -0
- data/lib/kamal/cli/templates/secrets +17 -0
- data/lib/kamal/cli.rb +2 -0
- data/lib/kamal/commander/specifics.rb +19 -6
- data/lib/kamal/commander.rb +39 -32
- data/lib/kamal/commands/accessory/proxy.rb +16 -0
- data/lib/kamal/commands/accessory.rb +19 -19
- data/lib/kamal/commands/app/assets.rb +10 -10
- data/lib/kamal/commands/app/containers.rb +2 -2
- data/lib/kamal/commands/app/error_pages.rb +9 -0
- data/lib/kamal/commands/app/execution.rb +7 -4
- data/lib/kamal/commands/app/images.rb +1 -1
- data/lib/kamal/commands/app/logging.rb +16 -6
- data/lib/kamal/commands/app/proxy.rb +32 -0
- data/lib/kamal/commands/app.rb +25 -24
- data/lib/kamal/commands/auditor.rb +12 -3
- data/lib/kamal/commands/base.rb +54 -8
- data/lib/kamal/commands/builder/base.rb +46 -16
- data/lib/kamal/commands/builder/clone.rb +16 -14
- data/lib/kamal/commands/builder/cloud.rb +22 -0
- data/lib/kamal/commands/builder/hybrid.rb +21 -0
- data/lib/kamal/commands/builder/local.rb +14 -0
- data/lib/kamal/commands/builder/pack.rb +46 -0
- data/lib/kamal/commands/builder/remote.rb +63 -0
- data/lib/kamal/commands/builder.rb +21 -45
- data/lib/kamal/commands/docker.rb +4 -0
- data/lib/kamal/commands/hook.rb +8 -2
- data/lib/kamal/commands/lock.rb +2 -6
- data/lib/kamal/commands/proxy.rb +127 -0
- data/lib/kamal/commands/prune.rb +1 -9
- data/lib/kamal/commands/registry.rb +9 -7
- data/lib/kamal/commands/server.rb +11 -1
- data/lib/kamal/configuration/accessory.rb +89 -12
- data/lib/kamal/configuration/alias.rb +15 -0
- data/lib/kamal/configuration/builder.rb +73 -15
- data/lib/kamal/configuration/docs/accessory.yml +53 -15
- data/lib/kamal/configuration/docs/alias.yml +26 -0
- data/lib/kamal/configuration/docs/boot.yml +3 -3
- data/lib/kamal/configuration/docs/builder.yml +63 -38
- data/lib/kamal/configuration/docs/configuration.yml +62 -46
- data/lib/kamal/configuration/docs/env.yml +61 -17
- data/lib/kamal/configuration/docs/logging.yml +3 -3
- data/lib/kamal/configuration/docs/proxy.yml +168 -0
- data/lib/kamal/configuration/docs/registry.yml +20 -13
- data/lib/kamal/configuration/docs/role.yml +14 -13
- data/lib/kamal/configuration/docs/servers.yml +2 -2
- data/lib/kamal/configuration/docs/ssh.yml +23 -19
- data/lib/kamal/configuration/docs/sshkit.yml +4 -4
- data/lib/kamal/configuration/env/tag.rb +4 -3
- data/lib/kamal/configuration/env.rb +19 -17
- data/lib/kamal/configuration/proxy/boot.rb +129 -0
- data/lib/kamal/configuration/proxy.rb +124 -0
- data/lib/kamal/configuration/registry.rb +7 -6
- data/lib/kamal/configuration/role.rb +69 -98
- data/lib/kamal/configuration/servers.rb +8 -1
- data/lib/kamal/configuration/validator/accessory.rb +6 -2
- data/lib/kamal/configuration/validator/alias.rb +15 -0
- data/lib/kamal/configuration/validator/builder.rb +6 -0
- data/lib/kamal/configuration/validator/proxy.rb +25 -0
- data/lib/kamal/configuration/validator/role.rb +3 -1
- data/lib/kamal/configuration/validator/servers.rb +1 -1
- data/lib/kamal/configuration/validator.rb +62 -24
- data/lib/kamal/configuration.rb +96 -50
- data/lib/kamal/docker.rb +30 -0
- data/lib/kamal/env_file.rb +7 -1
- data/lib/kamal/git.rb +10 -0
- data/lib/kamal/secrets/adapters/aws_secrets_manager.rb +51 -0
- data/lib/kamal/secrets/adapters/base.rb +33 -0
- data/lib/kamal/secrets/adapters/bitwarden.rb +81 -0
- data/lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb +66 -0
- data/lib/kamal/secrets/adapters/doppler.rb +57 -0
- data/lib/kamal/secrets/adapters/enpass.rb +71 -0
- data/lib/kamal/secrets/adapters/gcp_secret_manager.rb +112 -0
- data/lib/kamal/secrets/adapters/last_pass.rb +40 -0
- data/lib/kamal/secrets/adapters/one_password.rb +104 -0
- data/lib/kamal/secrets/adapters/passbolt.rb +130 -0
- data/lib/kamal/secrets/adapters/test.rb +14 -0
- data/lib/kamal/secrets/adapters.rb +16 -0
- data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +33 -0
- data/lib/kamal/secrets.rb +42 -0
- data/lib/kamal/sshkit_with_ext.rb +1 -0
- data/lib/kamal/utils.rb +30 -0
- data/lib/kamal/version.rb +1 -1
- data/lib/kamal.rb +3 -1
- metadata +63 -36
- data/lib/kamal/cli/env.rb +0 -54
- data/lib/kamal/cli/templates/sample_hooks/post-traefik-reboot.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/pre-traefik-reboot.sample +0 -3
- data/lib/kamal/cli/templates/template.env +0 -2
- data/lib/kamal/cli/traefik.rb +0 -122
- data/lib/kamal/commands/app/cord.rb +0 -22
- data/lib/kamal/commands/builder/multiarch/remote.rb +0 -65
- data/lib/kamal/commands/builder/multiarch.rb +0 -41
- data/lib/kamal/commands/builder/native/cached.rb +0 -25
- data/lib/kamal/commands/builder/native/remote.rb +0 -67
- data/lib/kamal/commands/builder/native.rb +0 -20
- data/lib/kamal/commands/traefik.rb +0 -85
- data/lib/kamal/configuration/docs/healthcheck.yml +0 -59
- data/lib/kamal/configuration/docs/traefik.yml +0 -62
- data/lib/kamal/configuration/healthcheck.rb +0 -63
- data/lib/kamal/configuration/traefik.rb +0 -60
data/lib/kamal/cli/app.rb
CHANGED
@@ -4,26 +4,37 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
4
4
|
with_lock do
|
5
5
|
say "Get most recent version available as an image...", :magenta unless options[:version]
|
6
6
|
using_version(version_or_latest) do |version|
|
7
|
-
say "Start container with version #{version}
|
7
|
+
say "Start container with version #{version} (or reboot if already running)...", :magenta
|
8
8
|
|
9
9
|
# Assets are prepared in a separate step to ensure they are on all hosts before booting
|
10
|
-
on(KAMAL.
|
10
|
+
on(KAMAL.app_hosts) do
|
11
|
+
Kamal::Cli::App::ErrorPages.new(host, self).run
|
12
|
+
|
11
13
|
KAMAL.roles_on(host).each do |role|
|
12
|
-
Kamal::Cli::App::
|
14
|
+
Kamal::Cli::App::Assets.new(host, role, self).run
|
15
|
+
Kamal::Cli::App::SslCertificates.new(host, role, self).run
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
16
19
|
# Primary hosts and roles are returned first, so they can open the barrier
|
17
20
|
barrier = Kamal::Cli::Healthcheck::Barrier.new
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
host_boot_groups.each do |hosts|
|
23
|
+
host_list = Array(hosts).join(",")
|
24
|
+
run_hook "pre-app-boot", hosts: host_list
|
25
|
+
|
26
|
+
on(hosts) do |host|
|
27
|
+
KAMAL.roles_on(host).each do |role|
|
28
|
+
Kamal::Cli::App::Boot.new(host, role, self, version, barrier).run
|
29
|
+
end
|
22
30
|
end
|
31
|
+
|
32
|
+
run_hook "post-app-boot", hosts: host_list
|
33
|
+
sleep KAMAL.config.boot.wait if KAMAL.config.boot.wait
|
23
34
|
end
|
24
35
|
|
25
36
|
# Tag once the app booted on all hosts
|
26
|
-
on(KAMAL.
|
37
|
+
on(KAMAL.app_hosts) do |host|
|
27
38
|
execute *KAMAL.auditor.record("Tagging #{KAMAL.config.absolute_image} as the latest image"), verbosity: :debug
|
28
39
|
execute *KAMAL.app.tag_latest_image
|
29
40
|
end
|
@@ -34,12 +45,21 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
34
45
|
desc "start", "Start existing app container on servers"
|
35
46
|
def start
|
36
47
|
with_lock do
|
37
|
-
on(KAMAL.
|
48
|
+
on(KAMAL.app_hosts) do |host|
|
38
49
|
roles = KAMAL.roles_on(host)
|
39
50
|
|
40
51
|
roles.each do |role|
|
52
|
+
app = KAMAL.app(role: role, host: host)
|
41
53
|
execute *KAMAL.auditor.record("Started app version #{KAMAL.config.version}"), verbosity: :debug
|
42
|
-
execute *
|
54
|
+
execute *app.start, raise_on_non_zero_exit: false
|
55
|
+
|
56
|
+
if role.running_proxy?
|
57
|
+
version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
|
58
|
+
endpoint = capture_with_info(*app.container_id_for_version(version)).strip
|
59
|
+
raise Kamal::Cli::BootError, "Failed to get endpoint for #{role} on #{host}, did the container boot?" if endpoint.empty?
|
60
|
+
|
61
|
+
execute *app.deploy(target: endpoint)
|
62
|
+
end
|
43
63
|
end
|
44
64
|
end
|
45
65
|
end
|
@@ -48,12 +68,22 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
48
68
|
desc "stop", "Stop app container on servers"
|
49
69
|
def stop
|
50
70
|
with_lock do
|
51
|
-
on(KAMAL.
|
71
|
+
on(KAMAL.app_hosts) do |host|
|
52
72
|
roles = KAMAL.roles_on(host)
|
53
73
|
|
54
74
|
roles.each do |role|
|
75
|
+
app = KAMAL.app(role: role, host: host)
|
55
76
|
execute *KAMAL.auditor.record("Stopped app", role: role), verbosity: :debug
|
56
|
-
|
77
|
+
|
78
|
+
if role.running_proxy?
|
79
|
+
version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
|
80
|
+
endpoint = capture_with_info(*app.container_id_for_version(version)).strip
|
81
|
+
if endpoint.present?
|
82
|
+
execute *app.remove, raise_on_non_zero_exit: false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
execute *app.stop, raise_on_non_zero_exit: false
|
57
87
|
end
|
58
88
|
end
|
59
89
|
end
|
@@ -62,7 +92,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
62
92
|
# FIXME: Drop in favor of just containers?
|
63
93
|
desc "details", "Show details about app containers"
|
64
94
|
def details
|
65
|
-
on(KAMAL.
|
95
|
+
on(KAMAL.app_hosts) do |host|
|
66
96
|
roles = KAMAL.roles_on(host)
|
67
97
|
|
68
98
|
roles.each do |role|
|
@@ -71,12 +101,25 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
71
101
|
end
|
72
102
|
end
|
73
103
|
|
74
|
-
desc "exec [CMD]", "Execute a custom command on servers within the app container (use --help to show options)"
|
104
|
+
desc "exec [CMD...]", "Execute a custom command on servers within the app container (use --help to show options)"
|
75
105
|
option :interactive, aliases: "-i", type: :boolean, default: false, desc: "Execute command over ssh for an interactive shell (use for console/bash)"
|
76
106
|
option :reuse, type: :boolean, default: false, desc: "Reuse currently running container instead of starting a new one"
|
77
107
|
option :env, aliases: "-e", type: :hash, desc: "Set environment variables for the command"
|
78
|
-
|
108
|
+
option :detach, type: :boolean, default: false, desc: "Execute command in a detached container"
|
109
|
+
def exec(*cmd)
|
110
|
+
pre_connect_if_required
|
111
|
+
|
112
|
+
if (incompatible_options = [ :interactive, :reuse ].select { |key| options[:detach] && options[key] }.presence)
|
113
|
+
raise ArgumentError, "Detach is not compatible with #{incompatible_options.join(" or ")}"
|
114
|
+
end
|
115
|
+
|
116
|
+
if cmd.empty?
|
117
|
+
raise ArgumentError, "No command provided. You must specify a command to execute."
|
118
|
+
end
|
119
|
+
|
120
|
+
cmd = Kamal::Utils.join_commands(cmd)
|
79
121
|
env = options[:env]
|
122
|
+
detach = options[:detach]
|
80
123
|
case
|
81
124
|
when options[:interactive] && options[:reuse]
|
82
125
|
say "Get current version of running container...", :magenta unless options[:version]
|
@@ -89,6 +132,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
89
132
|
say "Get most recent version available as an image...", :magenta unless options[:version]
|
90
133
|
using_version(version_or_latest) do |version|
|
91
134
|
say "Launching interactive command with version #{version} via SSH from new container on #{KAMAL.primary_host}...", :magenta
|
135
|
+
on(KAMAL.primary_host) { execute *KAMAL.registry.login }
|
92
136
|
run_locally do
|
93
137
|
exec KAMAL.app(role: KAMAL.primary_role, host: KAMAL.primary_host).execute_in_new_container_over_ssh(cmd, env: env)
|
94
138
|
end
|
@@ -99,7 +143,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
99
143
|
using_version(options[:version] || current_running_version) do |version|
|
100
144
|
say "Launching command with version #{version} from existing container...", :magenta
|
101
145
|
|
102
|
-
on(KAMAL.
|
146
|
+
on(KAMAL.app_hosts) do |host|
|
103
147
|
roles = KAMAL.roles_on(host)
|
104
148
|
|
105
149
|
roles.each do |role|
|
@@ -113,12 +157,14 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
113
157
|
say "Get most recent version available as an image...", :magenta unless options[:version]
|
114
158
|
using_version(version_or_latest) do |version|
|
115
159
|
say "Launching command with version #{version} from new container...", :magenta
|
116
|
-
on(KAMAL.
|
160
|
+
on(KAMAL.app_hosts) do |host|
|
161
|
+
execute *KAMAL.registry.login
|
162
|
+
|
117
163
|
roles = KAMAL.roles_on(host)
|
118
164
|
|
119
165
|
roles.each do |role|
|
120
166
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
|
121
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).execute_in_new_container(cmd, env: env))
|
167
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).execute_in_new_container(cmd, env: env, detach: detach))
|
122
168
|
end
|
123
169
|
end
|
124
170
|
end
|
@@ -127,7 +173,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
127
173
|
|
128
174
|
desc "containers", "Show app containers on servers"
|
129
175
|
def containers
|
130
|
-
on(KAMAL.
|
176
|
+
on(KAMAL.app_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.app.list_containers) }
|
131
177
|
end
|
132
178
|
|
133
179
|
desc "stale_containers", "Detect app stale containers"
|
@@ -136,7 +182,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
136
182
|
stop = options[:stop]
|
137
183
|
|
138
184
|
with_lock_if_stopping do
|
139
|
-
on(KAMAL.
|
185
|
+
on(KAMAL.app_hosts) do |host|
|
140
186
|
roles = KAMAL.roles_on(host)
|
141
187
|
|
142
188
|
roles.each do |role|
|
@@ -159,21 +205,25 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
159
205
|
|
160
206
|
desc "images", "Show app images on servers"
|
161
207
|
def images
|
162
|
-
on(KAMAL.
|
208
|
+
on(KAMAL.app_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.app.list_images) }
|
163
209
|
end
|
164
210
|
|
165
211
|
desc "logs", "Show log lines from app on servers (use --help to show options)"
|
166
212
|
option :since, aliases: "-s", desc: "Show lines since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)"
|
167
213
|
option :lines, type: :numeric, aliases: "-n", desc: "Number of lines to show from each server"
|
168
214
|
option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
|
169
|
-
option :grep_options,
|
215
|
+
option :grep_options, desc: "Additional options supplied to grep"
|
170
216
|
option :follow, aliases: "-f", desc: "Follow log on primary server (or specific host set by --hosts)"
|
217
|
+
option :skip_timestamps, type: :boolean, aliases: "-T", desc: "Skip appending timestamps to logging output"
|
218
|
+
option :container_id, desc: "Docker container ID to fetch logs"
|
171
219
|
def logs
|
172
220
|
# FIXME: Catch when app containers aren't running
|
173
221
|
|
174
222
|
grep = options[:grep]
|
175
223
|
grep_options = options[:grep_options]
|
176
224
|
since = options[:since]
|
225
|
+
container_id = options[:container_id]
|
226
|
+
timestamps = !options[:skip_timestamps]
|
177
227
|
|
178
228
|
if options[:follow]
|
179
229
|
lines = options[:lines].presence || ((since || grep) ? nil : 10) # Default to 10 lines if since or grep isn't set
|
@@ -181,22 +231,22 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
181
231
|
run_locally do
|
182
232
|
info "Following logs on #{KAMAL.primary_host}..."
|
183
233
|
|
184
|
-
KAMAL.specific_roles ||= [
|
234
|
+
KAMAL.specific_roles ||= [ KAMAL.primary_role.name ]
|
185
235
|
role = KAMAL.roles_on(KAMAL.primary_host).first
|
186
236
|
|
187
237
|
app = KAMAL.app(role: role, host: host)
|
188
|
-
info app.follow_logs(host: KAMAL.primary_host, lines: lines, grep: grep, grep_options: grep_options)
|
189
|
-
exec app.follow_logs(host: KAMAL.primary_host, lines: lines, grep: grep, grep_options: grep_options)
|
238
|
+
info app.follow_logs(host: KAMAL.primary_host, container_id: container_id, timestamps: timestamps, lines: lines, grep: grep, grep_options: grep_options)
|
239
|
+
exec app.follow_logs(host: KAMAL.primary_host, container_id: container_id, timestamps: timestamps, lines: lines, grep: grep, grep_options: grep_options)
|
190
240
|
end
|
191
241
|
else
|
192
242
|
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
|
193
243
|
|
194
|
-
on(KAMAL.
|
244
|
+
on(KAMAL.app_hosts) do |host|
|
195
245
|
roles = KAMAL.roles_on(host)
|
196
246
|
|
197
247
|
roles.each do |role|
|
198
248
|
begin
|
199
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).logs(since: since, lines: lines, grep: grep, grep_options: grep_options))
|
249
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).logs(container_id: container_id, timestamps: timestamps, since: since, lines: lines, grep: grep, grep_options: grep_options))
|
200
250
|
rescue SSHKit::Command::Failed
|
201
251
|
puts_by_host host, "Nothing found"
|
202
252
|
end
|
@@ -211,13 +261,44 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
211
261
|
stop
|
212
262
|
remove_containers
|
213
263
|
remove_images
|
264
|
+
remove_app_directories
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
desc "live", "Set the app to live mode"
|
269
|
+
def live
|
270
|
+
with_lock do
|
271
|
+
on(KAMAL.proxy_hosts) do |host|
|
272
|
+
roles = KAMAL.roles_on(host)
|
273
|
+
|
274
|
+
roles.each do |role|
|
275
|
+
execute *KAMAL.app(role: role, host: host).live if role.running_proxy?
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
desc "maintenance", "Set the app to maintenance mode"
|
282
|
+
option :drain_timeout, type: :numeric, desc: "How long to allow in-flight requests to complete (defaults to drain_timeout from config)"
|
283
|
+
option :message, type: :string, desc: "Message to display to clients while stopped"
|
284
|
+
def maintenance
|
285
|
+
maintenance_options = { drain_timeout: options[:drain_timeout] || KAMAL.config.drain_timeout, message: options[:message] }
|
286
|
+
|
287
|
+
with_lock do
|
288
|
+
on(KAMAL.proxy_hosts) do |host|
|
289
|
+
roles = KAMAL.roles_on(host)
|
290
|
+
|
291
|
+
roles.each do |role|
|
292
|
+
execute *KAMAL.app(role: role, host: host).maintenance(**maintenance_options) if role.running_proxy?
|
293
|
+
end
|
294
|
+
end
|
214
295
|
end
|
215
296
|
end
|
216
297
|
|
217
298
|
desc "remove_container [VERSION]", "Remove app container with given version from servers", hide: true
|
218
299
|
def remove_container(version)
|
219
300
|
with_lock do
|
220
|
-
on(KAMAL.
|
301
|
+
on(KAMAL.app_hosts) do |host|
|
221
302
|
roles = KAMAL.roles_on(host)
|
222
303
|
|
223
304
|
roles.each do |role|
|
@@ -231,7 +312,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
231
312
|
desc "remove_containers", "Remove all app containers from servers", hide: true
|
232
313
|
def remove_containers
|
233
314
|
with_lock do
|
234
|
-
on(KAMAL.
|
315
|
+
on(KAMAL.app_hosts) do |host|
|
235
316
|
roles = KAMAL.roles_on(host)
|
236
317
|
|
237
318
|
roles.each do |role|
|
@@ -245,16 +326,33 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
245
326
|
desc "remove_images", "Remove all app images from servers", hide: true
|
246
327
|
def remove_images
|
247
328
|
with_lock do
|
248
|
-
on(KAMAL.
|
329
|
+
on(KAMAL.app_hosts) do
|
249
330
|
execute *KAMAL.auditor.record("Removed all app images"), verbosity: :debug
|
250
331
|
execute *KAMAL.app.remove_images
|
251
332
|
end
|
252
333
|
end
|
253
334
|
end
|
254
335
|
|
336
|
+
desc "remove_app_directories", "Remove the app directories from servers", hide: true
|
337
|
+
def remove_app_directories
|
338
|
+
with_lock do
|
339
|
+
on(KAMAL.app_hosts) do |host|
|
340
|
+
roles = KAMAL.roles_on(host)
|
341
|
+
|
342
|
+
roles.each do |role|
|
343
|
+
execute *KAMAL.auditor.record("Removed #{KAMAL.config.app_directory}", role: role), verbosity: :debug
|
344
|
+
execute *KAMAL.server.remove_app_directory, raise_on_non_zero_exit: false
|
345
|
+
end
|
346
|
+
|
347
|
+
execute *KAMAL.auditor.record("Removed #{KAMAL.config.app_directory}"), verbosity: :debug
|
348
|
+
execute *KAMAL.app.remove_proxy_app_directory, raise_on_non_zero_exit: false
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
255
353
|
desc "version", "Show app version currently running on servers"
|
256
354
|
def version
|
257
|
-
on(KAMAL.
|
355
|
+
on(KAMAL.app_hosts) do |host|
|
258
356
|
role = KAMAL.roles_on(host).first
|
259
357
|
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).current_running_version).strip
|
260
358
|
end
|
@@ -295,4 +393,8 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
295
393
|
yield
|
296
394
|
end
|
297
395
|
end
|
396
|
+
|
397
|
+
def host_boot_groups
|
398
|
+
KAMAL.config.boot.limit ? KAMAL.app_hosts.each_slice(KAMAL.config.boot.limit).to_a : [ KAMAL.app_hosts ]
|
399
|
+
end
|
298
400
|
end
|
data/lib/kamal/cli/base.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require "thor"
|
2
|
-
require "dotenv"
|
3
2
|
require "kamal/sshkit_with_ext"
|
4
3
|
|
5
4
|
module Kamal::Cli
|
@@ -7,6 +6,7 @@ module Kamal::Cli
|
|
7
6
|
include SSHKit::DSL
|
8
7
|
|
9
8
|
def self.exit_on_failure?() true end
|
9
|
+
def self.dynamic_command_class() Kamal::Cli::Alias::Command end
|
10
10
|
|
11
11
|
class_option :verbose, type: :boolean, aliases: "-v", desc: "Detailed logging"
|
12
12
|
class_option :quiet, type: :boolean, aliases: "-q", desc: "Minimal logging"
|
@@ -22,55 +22,24 @@ module Kamal::Cli
|
|
22
22
|
|
23
23
|
class_option :skip_hooks, aliases: "-H", type: :boolean, default: false, desc: "Don't run hooks"
|
24
24
|
|
25
|
-
def initialize(
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
private
|
33
|
-
def reload_env
|
34
|
-
reset_env
|
35
|
-
load_env
|
36
|
-
end
|
37
|
-
|
38
|
-
def load_env
|
39
|
-
if destination = options[:destination]
|
40
|
-
Dotenv.load(".env.#{destination}", ".env")
|
41
|
-
else
|
42
|
-
Dotenv.load(".env")
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def reset_env
|
47
|
-
replace_env @original_env
|
48
|
-
end
|
49
|
-
|
50
|
-
def replace_env(env)
|
51
|
-
ENV.clear
|
52
|
-
ENV.update(env)
|
53
|
-
end
|
54
|
-
|
55
|
-
def with_original_env
|
56
|
-
keeping_current_env do
|
57
|
-
reset_env
|
58
|
-
yield
|
59
|
-
end
|
25
|
+
def initialize(args = [], local_options = {}, config = {})
|
26
|
+
if config[:current_command].is_a?(Kamal::Cli::Alias::Command)
|
27
|
+
# When Thor generates a dynamic command, it doesn't attempt to parse the arguments.
|
28
|
+
# For our purposes, it means the arguments are passed in args rather than local_options.
|
29
|
+
super([], args, config)
|
30
|
+
else
|
31
|
+
super
|
60
32
|
end
|
61
33
|
|
62
|
-
|
63
|
-
|
64
|
-
yield
|
65
|
-
ensure
|
66
|
-
replace_env(current_env)
|
67
|
-
end
|
34
|
+
initialize_commander unless KAMAL.configured?
|
35
|
+
end
|
68
36
|
|
37
|
+
private
|
69
38
|
def options_with_subcommand_class_options
|
70
39
|
options.merge(@_initializer.last[:class_options] || {})
|
71
40
|
end
|
72
41
|
|
73
|
-
def initialize_commander
|
42
|
+
def initialize_commander
|
74
43
|
KAMAL.tap do |commander|
|
75
44
|
if options[:verbose]
|
76
45
|
ENV["VERBOSE"] = "1" # For backtraces via cli/start
|
@@ -105,8 +74,6 @@ module Kamal::Cli
|
|
105
74
|
if KAMAL.holding_lock?
|
106
75
|
yield
|
107
76
|
else
|
108
|
-
ensure_run_and_locks_directory
|
109
|
-
|
110
77
|
acquire_lock
|
111
78
|
|
112
79
|
begin
|
@@ -135,6 +102,8 @@ module Kamal::Cli
|
|
135
102
|
end
|
136
103
|
|
137
104
|
def acquire_lock
|
105
|
+
ensure_run_directory
|
106
|
+
|
138
107
|
raise_if_locked do
|
139
108
|
say "Acquiring the deploy lock...", :magenta
|
140
109
|
on(KAMAL.primary_host) { execute *KAMAL.lock.acquire("Automatic deploy lock", KAMAL.config.version), verbosity: :debug }
|
@@ -164,11 +133,19 @@ module Kamal::Cli
|
|
164
133
|
|
165
134
|
def run_hook(hook, **extra_details)
|
166
135
|
if !options[:skip_hooks] && KAMAL.hook.hook_exists?(hook)
|
167
|
-
details = {
|
136
|
+
details = {
|
137
|
+
hosts: KAMAL.hosts.join(","),
|
138
|
+
roles: KAMAL.specific_roles&.join(","),
|
139
|
+
lock: KAMAL.holding_lock?.to_s,
|
140
|
+
command: command,
|
141
|
+
subcommand: subcommand
|
142
|
+
}.compact
|
168
143
|
|
169
144
|
say "Running the #{hook} hook...", :magenta
|
170
|
-
|
171
|
-
|
145
|
+
with_env KAMAL.hook.env(**details, **extra_details) do
|
146
|
+
run_locally do
|
147
|
+
execute *KAMAL.hook.run(hook)
|
148
|
+
end
|
172
149
|
rescue SSHKit::Command::Failed => e
|
173
150
|
raise HookError.new("Hook `#{hook}` failed:\n#{e.message}")
|
174
151
|
end
|
@@ -176,12 +153,16 @@ module Kamal::Cli
|
|
176
153
|
end
|
177
154
|
|
178
155
|
def on(*args, &block)
|
156
|
+
pre_connect_if_required
|
157
|
+
|
158
|
+
super
|
159
|
+
end
|
160
|
+
|
161
|
+
def pre_connect_if_required
|
179
162
|
if !KAMAL.connected?
|
180
163
|
run_hook "pre-connect"
|
181
164
|
KAMAL.connected = true
|
182
165
|
end
|
183
|
-
|
184
|
-
super
|
185
166
|
end
|
186
167
|
|
187
168
|
def command
|
@@ -206,13 +187,36 @@ module Kamal::Cli
|
|
206
187
|
instance_variable_get("@_invocations").first
|
207
188
|
end
|
208
189
|
|
209
|
-
def
|
190
|
+
def reset_invocation(cli_class)
|
191
|
+
instance_variable_get("@_invocations")[cli_class].pop
|
192
|
+
end
|
193
|
+
|
194
|
+
def ensure_run_directory
|
210
195
|
on(KAMAL.hosts) do
|
211
196
|
execute(*KAMAL.server.ensure_run_directory)
|
212
197
|
end
|
198
|
+
end
|
213
199
|
|
214
|
-
|
215
|
-
|
200
|
+
def with_env(env)
|
201
|
+
current_env = ENV.to_h.dup
|
202
|
+
ENV.update(env)
|
203
|
+
yield
|
204
|
+
ensure
|
205
|
+
ENV.clear
|
206
|
+
ENV.update(current_env)
|
207
|
+
end
|
208
|
+
|
209
|
+
def ensure_docker_installed
|
210
|
+
run_locally do
|
211
|
+
begin
|
212
|
+
execute *KAMAL.builder.ensure_docker_installed
|
213
|
+
rescue SSHKit::Command::Failed => e
|
214
|
+
error = e.message =~ /command not found/ ?
|
215
|
+
"Docker is not installed locally" :
|
216
|
+
"Docker buildx plugin is not installed locally"
|
217
|
+
|
218
|
+
raise DependencyError, error
|
219
|
+
end
|
216
220
|
end
|
217
221
|
end
|
218
222
|
end
|