kamal 1.5.2 → 1.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/lib/kamal/cli/accessory.rb +30 -24
- data/lib/kamal/cli/app/boot.rb +70 -18
- data/lib/kamal/cli/app/prepare_assets.rb +1 -1
- data/lib/kamal/cli/app.rb +60 -47
- data/lib/kamal/cli/base.rb +26 -28
- data/lib/kamal/cli/build/clone.rb +61 -0
- data/lib/kamal/cli/build.rb +64 -53
- data/lib/kamal/cli/env.rb +5 -5
- data/lib/kamal/cli/healthcheck/barrier.rb +31 -0
- data/lib/kamal/cli/healthcheck/error.rb +2 -0
- data/lib/kamal/cli/healthcheck/poller.rb +6 -7
- data/lib/kamal/cli/main.rb +49 -44
- data/lib/kamal/cli/prune.rb +3 -3
- data/lib/kamal/cli/registry.rb +9 -10
- data/lib/kamal/cli/server.rb +39 -15
- data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +1 -1
- data/lib/kamal/cli/traefik.rb +13 -11
- data/lib/kamal/cli.rb +1 -1
- data/lib/kamal/commander.rb +6 -6
- data/lib/kamal/commands/accessory.rb +4 -4
- data/lib/kamal/commands/app/containers.rb +8 -0
- data/lib/kamal/commands/app/execution.rb +3 -3
- data/lib/kamal/commands/app/logging.rb +5 -5
- data/lib/kamal/commands/app.rb +6 -5
- data/lib/kamal/commands/base.rb +2 -3
- data/lib/kamal/commands/builder/base.rb +19 -12
- data/lib/kamal/commands/builder/clone.rb +28 -0
- data/lib/kamal/commands/builder/multiarch/remote.rb +10 -0
- data/lib/kamal/commands/builder/multiarch.rb +13 -9
- data/lib/kamal/commands/builder/native/cached.rb +14 -6
- data/lib/kamal/commands/builder/native/remote.rb +17 -9
- data/lib/kamal/commands/builder/native.rb +6 -7
- data/lib/kamal/commands/builder.rb +19 -11
- data/lib/kamal/commands/registry.rb +4 -13
- data/lib/kamal/commands/traefik.rb +8 -47
- data/lib/kamal/configuration/accessory.rb +30 -41
- data/lib/kamal/configuration/boot.rb +9 -4
- data/lib/kamal/configuration/builder.rb +61 -30
- data/lib/kamal/configuration/docs/accessory.yml +90 -0
- data/lib/kamal/configuration/docs/boot.yml +19 -0
- data/lib/kamal/configuration/docs/builder.yml +107 -0
- data/lib/kamal/configuration/docs/configuration.yml +157 -0
- data/lib/kamal/configuration/docs/env.yml +72 -0
- data/lib/kamal/configuration/docs/healthcheck.yml +59 -0
- data/lib/kamal/configuration/docs/logging.yml +21 -0
- data/lib/kamal/configuration/docs/registry.yml +49 -0
- data/lib/kamal/configuration/docs/role.yml +52 -0
- data/lib/kamal/configuration/docs/servers.yml +27 -0
- data/lib/kamal/configuration/docs/ssh.yml +46 -0
- data/lib/kamal/configuration/docs/sshkit.yml +23 -0
- data/lib/kamal/configuration/docs/traefik.yml +62 -0
- data/lib/kamal/configuration/env/tag.rb +12 -0
- data/lib/kamal/configuration/env.rb +10 -14
- data/lib/kamal/configuration/healthcheck.rb +63 -0
- data/lib/kamal/configuration/logging.rb +33 -0
- data/lib/kamal/configuration/registry.rb +31 -0
- data/lib/kamal/configuration/role.rb +72 -61
- data/lib/kamal/configuration/servers.rb +18 -0
- data/lib/kamal/configuration/ssh.rb +11 -8
- data/lib/kamal/configuration/sshkit.rb +9 -7
- data/lib/kamal/configuration/traefik.rb +60 -0
- data/lib/kamal/configuration/validation.rb +27 -0
- data/lib/kamal/configuration/validator/accessory.rb +9 -0
- data/lib/kamal/configuration/validator/builder.rb +9 -0
- data/lib/kamal/configuration/validator/env.rb +54 -0
- data/lib/kamal/configuration/validator/registry.rb +25 -0
- data/lib/kamal/configuration/validator/role.rb +11 -0
- data/lib/kamal/configuration/validator/servers.rb +7 -0
- data/lib/kamal/configuration/validator.rb +140 -0
- data/lib/kamal/configuration.rb +50 -63
- data/lib/kamal/git.rb +4 -0
- data/lib/kamal/sshkit_with_ext.rb +36 -0
- data/lib/kamal/version.rb +1 -1
- data/lib/kamal.rb +2 -0
- metadata +64 -9
- data/lib/kamal/cli/healthcheck.rb +0 -21
- data/lib/kamal/commands/healthcheck.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30a8c6964442e363d08edf8ad8c8b5ae835308392b330ec9e1ba8c659916bb04
|
4
|
+
data.tar.gz: fce971fac71c529d90bebf6ae1b28fb617d8c34a18cc7d244e0a845238891f22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '097ba4ef7a4956a22b89354a648319152224b48e77a8c32e0757462402508773b61d1b238b34d9b8f16f9d24c8dad12e118ba23e669b6a1388d5a09f73296925'
|
7
|
+
data.tar.gz: f13f8f4dd704a559940313c245e5bde92915c77a716d01e3f69b5f825ef2c122bd840b9a9f47a4eb54f5555a87bced74dcf8c46394f53941211fdc0d66d5567b
|
data/lib/kamal/cli/accessory.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
class Kamal::Cli::Accessory < Kamal::Cli::Base
|
2
2
|
desc "boot [NAME]", "Boot new accessory service on host (use NAME=all to boot all accessories)"
|
3
3
|
def boot(name, login: true)
|
4
|
-
|
4
|
+
with_lock do
|
5
5
|
if name == "all"
|
6
6
|
KAMAL.accessory_names.each { |accessory_name| boot(accessory_name) }
|
7
7
|
else
|
@@ -21,7 +21,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
21
21
|
|
22
22
|
desc "upload [NAME]", "Upload accessory files to host", hide: true
|
23
23
|
def upload(name)
|
24
|
-
|
24
|
+
with_lock do
|
25
25
|
with_accessory(name) do |accessory, hosts|
|
26
26
|
on(hosts) do
|
27
27
|
accessory.files.each do |(local, remote)|
|
@@ -38,7 +38,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
38
38
|
|
39
39
|
desc "directories [NAME]", "Create accessory directories on host", hide: true
|
40
40
|
def directories(name)
|
41
|
-
|
41
|
+
with_lock do
|
42
42
|
with_accessory(name) do |accessory, hosts|
|
43
43
|
on(hosts) do
|
44
44
|
accessory.directories.keys.each do |host_path|
|
@@ -51,7 +51,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
51
51
|
|
52
52
|
desc "reboot [NAME]", "Reboot existing accessory on host (stop container, remove container, start new container; use NAME=all to boot all accessories)"
|
53
53
|
def reboot(name)
|
54
|
-
|
54
|
+
with_lock do
|
55
55
|
if name == "all"
|
56
56
|
KAMAL.accessory_names.each { |accessory_name| reboot(accessory_name) }
|
57
57
|
else
|
@@ -70,7 +70,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
70
70
|
|
71
71
|
desc "start [NAME]", "Start existing accessory container on host"
|
72
72
|
def start(name)
|
73
|
-
|
73
|
+
with_lock do
|
74
74
|
with_accessory(name) do |accessory, hosts|
|
75
75
|
on(hosts) do
|
76
76
|
execute *KAMAL.auditor.record("Started #{name} accessory"), verbosity: :debug
|
@@ -82,7 +82,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
82
82
|
|
83
83
|
desc "stop [NAME]", "Stop existing accessory container on host"
|
84
84
|
def stop(name)
|
85
|
-
|
85
|
+
with_lock do
|
86
86
|
with_accessory(name) do |accessory, hosts|
|
87
87
|
on(hosts) do
|
88
88
|
execute *KAMAL.auditor.record("Stopped #{name} accessory"), verbosity: :debug
|
@@ -94,7 +94,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
94
94
|
|
95
95
|
desc "restart [NAME]", "Restart existing accessory container on host"
|
96
96
|
def restart(name)
|
97
|
-
|
97
|
+
with_lock do
|
98
98
|
with_accessory(name) do
|
99
99
|
stop(name)
|
100
100
|
start(name)
|
@@ -149,23 +149,25 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
149
149
|
option :since, aliases: "-s", desc: "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)"
|
150
150
|
option :lines, type: :numeric, aliases: "-n", desc: "Number of log lines to pull from each server"
|
151
151
|
option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
|
152
|
+
option :grep_options, aliases: "-o", desc: "Additional options supplied to grep"
|
152
153
|
option :follow, aliases: "-f", desc: "Follow logs on primary server (or specific host set by --hosts)"
|
153
154
|
def logs(name)
|
154
155
|
with_accessory(name) do |accessory, hosts|
|
155
156
|
grep = options[:grep]
|
157
|
+
grep_options = options[:grep_options]
|
156
158
|
|
157
159
|
if options[:follow]
|
158
160
|
run_locally do
|
159
161
|
info "Following logs on #{hosts}..."
|
160
|
-
info accessory.follow_logs(grep: grep)
|
161
|
-
exec accessory.follow_logs(grep: grep)
|
162
|
+
info accessory.follow_logs(grep: grep, grep_options: grep_options)
|
163
|
+
exec accessory.follow_logs(grep: grep, grep_options: grep_options)
|
162
164
|
end
|
163
165
|
else
|
164
166
|
since = options[:since]
|
165
167
|
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
|
166
168
|
|
167
169
|
on(hosts) do
|
168
|
-
puts capture_with_info(*accessory.logs(since: since, lines: lines, grep: grep))
|
170
|
+
puts capture_with_info(*accessory.logs(since: since, lines: lines, grep: grep, grep_options: grep_options))
|
169
171
|
end
|
170
172
|
end
|
171
173
|
end
|
@@ -174,17 +176,12 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
174
176
|
desc "remove [NAME]", "Remove accessory container, image and data directory from host (use NAME=all to remove all accessories)"
|
175
177
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
176
178
|
def remove(name)
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
stop(name)
|
184
|
-
remove_container(name)
|
185
|
-
remove_image(name)
|
186
|
-
remove_service_directory(name)
|
187
|
-
end
|
179
|
+
confirming "This will remove all containers, images and data directories for #{name}. Are you sure?" do
|
180
|
+
with_lock do
|
181
|
+
if name == "all"
|
182
|
+
KAMAL.accessory_names.each { |accessory_name| remove_accessory(accessory_name) }
|
183
|
+
else
|
184
|
+
remove_accessory(name)
|
188
185
|
end
|
189
186
|
end
|
190
187
|
end
|
@@ -192,7 +189,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
192
189
|
|
193
190
|
desc "remove_container [NAME]", "Remove accessory container from host", hide: true
|
194
191
|
def remove_container(name)
|
195
|
-
|
192
|
+
with_lock do
|
196
193
|
with_accessory(name) do |accessory, hosts|
|
197
194
|
on(hosts) do
|
198
195
|
execute *KAMAL.auditor.record("Remove #{name} accessory container"), verbosity: :debug
|
@@ -204,7 +201,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
204
201
|
|
205
202
|
desc "remove_image [NAME]", "Remove accessory image from host", hide: true
|
206
203
|
def remove_image(name)
|
207
|
-
|
204
|
+
with_lock do
|
208
205
|
with_accessory(name) do |accessory, hosts|
|
209
206
|
on(hosts) do
|
210
207
|
execute *KAMAL.auditor.record("Removed #{name} accessory image"), verbosity: :debug
|
@@ -216,7 +213,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
216
213
|
|
217
214
|
desc "remove_service_directory [NAME]", "Remove accessory directory used for uploaded files and data directories from host", hide: true
|
218
215
|
def remove_service_directory(name)
|
219
|
-
|
216
|
+
with_lock do
|
220
217
|
with_accessory(name) do |accessory, hosts|
|
221
218
|
on(hosts) do
|
222
219
|
execute *accessory.remove_service_directory
|
@@ -250,4 +247,13 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
250
247
|
accessory.hosts
|
251
248
|
end
|
252
249
|
end
|
250
|
+
|
251
|
+
def remove_accessory(name)
|
252
|
+
with_accessory(name) do
|
253
|
+
stop(name)
|
254
|
+
remove_container(name)
|
255
|
+
remove_image(name)
|
256
|
+
remove_service_directory(name)
|
257
|
+
end
|
258
|
+
end
|
253
259
|
end
|
data/lib/kamal/cli/app/boot.rb
CHANGED
@@ -1,19 +1,30 @@
|
|
1
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
|
2
|
+
attr_reader :host, :role, :version, :barrier, :sshkit
|
3
|
+
delegate :execute, :capture_with_info, :capture_with_pretty_json, :info, :error, to: :sshkit
|
4
|
+
delegate :uses_cord?, :assets?, :running_traefik?, to: :role
|
5
5
|
|
6
|
-
def initialize(host, role, version,
|
6
|
+
def initialize(host, role, sshkit, version, barrier)
|
7
7
|
@host = host
|
8
8
|
@role = role
|
9
9
|
@version = version
|
10
|
+
@barrier = barrier
|
10
11
|
@sshkit = sshkit
|
11
12
|
end
|
12
13
|
|
13
14
|
def run
|
14
15
|
old_version = old_version_renamed_if_clashing
|
15
16
|
|
16
|
-
|
17
|
+
wait_at_barrier if queuer?
|
18
|
+
|
19
|
+
begin
|
20
|
+
start_new_version
|
21
|
+
rescue => e
|
22
|
+
close_barrier if gatekeeper?
|
23
|
+
stop_new_version
|
24
|
+
raise
|
25
|
+
end
|
26
|
+
|
27
|
+
release_barrier if gatekeeper?
|
17
28
|
|
18
29
|
if old_version
|
19
30
|
stop_old_version(old_version)
|
@@ -21,18 +32,6 @@ class Kamal::Cli::App::Boot
|
|
21
32
|
end
|
22
33
|
|
23
34
|
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
35
|
def old_version_renamed_if_clashing
|
37
36
|
if capture_with_info(*app.container_id_for_version(version), raise_on_non_zero_exit: false).present?
|
38
37
|
renamed_version = "#{version}_replaced_#{SecureRandom.hex(8)}"
|
@@ -46,11 +45,17 @@ class Kamal::Cli::App::Boot
|
|
46
45
|
|
47
46
|
def start_new_version
|
48
47
|
audit "Booted app version #{version}"
|
48
|
+
|
49
49
|
execute *app.tie_cord(role.cord_host_file) if uses_cord?
|
50
|
-
|
50
|
+
hostname = "#{host.to_s[0...51].gsub(/\.+$/, '')}-#{SecureRandom.hex(6)}"
|
51
|
+
execute *app.run(hostname: hostname)
|
51
52
|
Kamal::Cli::Healthcheck::Poller.wait_for_healthy(pause_after_ready: true) { capture_with_info(*app.status(version: version)) }
|
52
53
|
end
|
53
54
|
|
55
|
+
def stop_new_version
|
56
|
+
execute *app.stop(version: version), raise_on_non_zero_exit: false
|
57
|
+
end
|
58
|
+
|
54
59
|
def stop_old_version(version)
|
55
60
|
if uses_cord?
|
56
61
|
cord = capture_with_info(*app.cord(version: version), raise_on_non_zero_exit: false).strip
|
@@ -64,4 +69,51 @@ class Kamal::Cli::App::Boot
|
|
64
69
|
|
65
70
|
execute *app.clean_up_assets if assets?
|
66
71
|
end
|
72
|
+
|
73
|
+
def release_barrier
|
74
|
+
if barrier.open
|
75
|
+
info "First #{KAMAL.primary_role} container is healthy on #{host}, booting other roles"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def wait_at_barrier
|
80
|
+
info "Waiting for the first healthy #{KAMAL.primary_role} container before booting #{role} on #{host}..."
|
81
|
+
barrier.wait
|
82
|
+
info "First #{KAMAL.primary_role} container is healthy, booting #{role} on #{host}..."
|
83
|
+
rescue Kamal::Cli::Healthcheck::Error
|
84
|
+
info "First #{KAMAL.primary_role} container is unhealthy, not booting #{role} on #{host}"
|
85
|
+
raise
|
86
|
+
end
|
87
|
+
|
88
|
+
def close_barrier
|
89
|
+
if barrier.close
|
90
|
+
info "First #{KAMAL.primary_role} container is unhealthy on #{host}, not booting other roles"
|
91
|
+
error capture_with_info(*app.logs(version: version))
|
92
|
+
error capture_with_info(*app.container_health_log(version: version))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def barrier_role?
|
97
|
+
role == KAMAL.primary_role
|
98
|
+
end
|
99
|
+
|
100
|
+
def app
|
101
|
+
@app ||= KAMAL.app(role: role, host: host)
|
102
|
+
end
|
103
|
+
|
104
|
+
def auditor
|
105
|
+
@auditor = KAMAL.auditor(role: role)
|
106
|
+
end
|
107
|
+
|
108
|
+
def audit(message)
|
109
|
+
execute *auditor.record(message), verbosity: :debug
|
110
|
+
end
|
111
|
+
|
112
|
+
def gatekeeper?
|
113
|
+
barrier && barrier_role?
|
114
|
+
end
|
115
|
+
|
116
|
+
def queuer?
|
117
|
+
barrier && !barrier_role?
|
118
|
+
end
|
67
119
|
end
|
data/lib/kamal/cli/app.rb
CHANGED
@@ -1,43 +1,45 @@
|
|
1
1
|
class Kamal::Cli::App < Kamal::Cli::Base
|
2
2
|
desc "boot", "Boot app on servers (or reboot app if already running)"
|
3
3
|
def boot
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
say "Start container with version #{version} using a #{KAMAL.config.readiness_delay}s readiness delay (or reboot if already running)...", :magenta
|
9
|
-
|
10
|
-
# Assets are prepared in a separate step to ensure they are on all hosts before booting
|
11
|
-
on(KAMAL.hosts) do
|
12
|
-
KAMAL.roles_on(host).each do |role|
|
13
|
-
Kamal::Cli::App::PrepareAssets.new(host, role, self).run
|
14
|
-
end
|
15
|
-
end
|
4
|
+
with_lock do
|
5
|
+
say "Get most recent version available as an image...", :magenta unless options[:version]
|
6
|
+
using_version(version_or_latest) do |version|
|
7
|
+
say "Start container with version #{version} using a #{KAMAL.config.readiness_delay}s readiness delay (or reboot if already running)...", :magenta
|
16
8
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
9
|
+
# Assets are prepared in a separate step to ensure they are on all hosts before booting
|
10
|
+
on(KAMAL.hosts) do
|
11
|
+
KAMAL.roles_on(host).each do |role|
|
12
|
+
Kamal::Cli::App::PrepareAssets.new(host, role, self).run
|
21
13
|
end
|
14
|
+
end
|
22
15
|
|
23
|
-
|
24
|
-
|
25
|
-
|
16
|
+
# Primary hosts and roles are returned first, so they can open the barrier
|
17
|
+
barrier = Kamal::Cli::Healthcheck::Barrier.new if KAMAL.roles.many?
|
18
|
+
|
19
|
+
on(KAMAL.hosts, **KAMAL.boot_strategy) do |host|
|
20
|
+
KAMAL.roles_on(host).each do |role|
|
21
|
+
Kamal::Cli::App::Boot.new(host, role, self, version, barrier).run
|
26
22
|
end
|
27
23
|
end
|
24
|
+
|
25
|
+
# Tag once the app booted on all hosts
|
26
|
+
on(KAMAL.hosts) do |host|
|
27
|
+
execute *KAMAL.auditor.record("Tagging #{KAMAL.config.absolute_image} as the latest image"), verbosity: :debug
|
28
|
+
execute *KAMAL.app.tag_latest_image
|
29
|
+
end
|
28
30
|
end
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
32
34
|
desc "start", "Start existing app container on servers"
|
33
35
|
def start
|
34
|
-
|
36
|
+
with_lock do
|
35
37
|
on(KAMAL.hosts) do |host|
|
36
38
|
roles = KAMAL.roles_on(host)
|
37
39
|
|
38
40
|
roles.each do |role|
|
39
41
|
execute *KAMAL.auditor.record("Started app version #{KAMAL.config.version}"), verbosity: :debug
|
40
|
-
execute *KAMAL.app(role: role).start, raise_on_non_zero_exit: false
|
42
|
+
execute *KAMAL.app(role: role, host: host).start, raise_on_non_zero_exit: false
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
@@ -45,13 +47,13 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
45
47
|
|
46
48
|
desc "stop", "Stop app container on servers"
|
47
49
|
def stop
|
48
|
-
|
50
|
+
with_lock do
|
49
51
|
on(KAMAL.hosts) do |host|
|
50
52
|
roles = KAMAL.roles_on(host)
|
51
53
|
|
52
54
|
roles.each do |role|
|
53
55
|
execute *KAMAL.auditor.record("Stopped app", role: role), verbosity: :debug
|
54
|
-
execute *KAMAL.app(role: role).stop, raise_on_non_zero_exit: false
|
56
|
+
execute *KAMAL.app(role: role, host: host).stop, raise_on_non_zero_exit: false
|
55
57
|
end
|
56
58
|
end
|
57
59
|
end
|
@@ -64,12 +66,12 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
64
66
|
roles = KAMAL.roles_on(host)
|
65
67
|
|
66
68
|
roles.each do |role|
|
67
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role).info)
|
69
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).info)
|
68
70
|
end
|
69
71
|
end
|
70
72
|
end
|
71
73
|
|
72
|
-
desc "exec [CMD]", "Execute a custom command on servers (use --help to show options)"
|
74
|
+
desc "exec [CMD]", "Execute a custom command on servers within the app container (use --help to show options)"
|
73
75
|
option :interactive, aliases: "-i", type: :boolean, default: false, desc: "Execute command over ssh for an interactive shell (use for console/bash)"
|
74
76
|
option :reuse, type: :boolean, default: false, desc: "Reuse currently running container instead of starting a new one"
|
75
77
|
option :env, aliases: "-e", type: :hash, desc: "Set environment variables for the command"
|
@@ -80,7 +82,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
80
82
|
say "Get current version of running container...", :magenta unless options[:version]
|
81
83
|
using_version(options[:version] || current_running_version) do |version|
|
82
84
|
say "Launching interactive command with version #{version} via SSH from existing container on #{KAMAL.primary_host}...", :magenta
|
83
|
-
run_locally { exec KAMAL.app(role: KAMAL.primary_role
|
85
|
+
run_locally { exec KAMAL.app(role: KAMAL.primary_role, host: KAMAL.primary_host).execute_in_existing_container_over_ssh(cmd, env: env) }
|
84
86
|
end
|
85
87
|
|
86
88
|
when options[:interactive]
|
@@ -88,7 +90,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
88
90
|
using_version(version_or_latest) do |version|
|
89
91
|
say "Launching interactive command with version #{version} via SSH from new container on #{KAMAL.primary_host}...", :magenta
|
90
92
|
run_locally do
|
91
|
-
exec KAMAL.app(role: KAMAL.primary_role
|
93
|
+
exec KAMAL.app(role: KAMAL.primary_role, host: KAMAL.primary_host).execute_in_new_container_over_ssh(cmd, env: env)
|
92
94
|
end
|
93
95
|
end
|
94
96
|
|
@@ -102,7 +104,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
102
104
|
|
103
105
|
roles.each do |role|
|
104
106
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}", role: role), verbosity: :debug
|
105
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role).execute_in_existing_container(cmd, env: env))
|
107
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).execute_in_existing_container(cmd, env: env))
|
106
108
|
end
|
107
109
|
end
|
108
110
|
end
|
@@ -116,7 +118,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
116
118
|
|
117
119
|
roles.each do |role|
|
118
120
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
|
119
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role).execute_in_new_container(cmd, env: env))
|
121
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).execute_in_new_container(cmd, env: env))
|
120
122
|
end
|
121
123
|
end
|
122
124
|
end
|
@@ -131,22 +133,21 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
131
133
|
desc "stale_containers", "Detect app stale containers"
|
132
134
|
option :stop, aliases: "-s", type: :boolean, default: false, desc: "Stop the stale containers found"
|
133
135
|
def stale_containers
|
134
|
-
|
135
|
-
stop = options[:stop]
|
136
|
-
|
137
|
-
cli = self
|
136
|
+
stop = options[:stop]
|
138
137
|
|
138
|
+
with_lock_if_stopping do
|
139
139
|
on(KAMAL.hosts) do |host|
|
140
140
|
roles = KAMAL.roles_on(host)
|
141
141
|
|
142
142
|
roles.each do |role|
|
143
|
-
|
144
|
-
versions
|
143
|
+
app = KAMAL.app(role: role, host: host)
|
144
|
+
versions = capture_with_info(*app.list_versions, raise_on_non_zero_exit: false).split("\n")
|
145
|
+
versions -= [ capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip ]
|
145
146
|
|
146
147
|
versions.each do |version|
|
147
148
|
if stop
|
148
149
|
puts_by_host host, "Stopping stale container for role #{role} with version #{version}"
|
149
|
-
execute *
|
150
|
+
execute *app.stop(version: version), raise_on_non_zero_exit: false
|
150
151
|
else
|
151
152
|
puts_by_host host, "Detected stale container for role #{role} with version #{version} (use `kamal app stale_containers --stop` to stop)"
|
152
153
|
end
|
@@ -165,12 +166,15 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
165
166
|
option :since, aliases: "-s", desc: "Show lines since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)"
|
166
167
|
option :lines, type: :numeric, aliases: "-n", desc: "Number of lines to show from each server"
|
167
168
|
option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
|
169
|
+
option :grep_options, aliases: "-o", desc: "Additional options supplied to grep"
|
168
170
|
option :follow, aliases: "-f", desc: "Follow log on primary server (or specific host set by --hosts)"
|
169
171
|
def logs
|
170
172
|
# FIXME: Catch when app containers aren't running
|
171
173
|
|
172
174
|
grep = options[:grep]
|
175
|
+
grep_options = options[:grep_options]
|
173
176
|
since = options[:since]
|
177
|
+
|
174
178
|
if options[:follow]
|
175
179
|
lines = options[:lines].presence || ((since || grep) ? nil : 10) # Default to 10 lines if since or grep isn't set
|
176
180
|
|
@@ -180,8 +184,9 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
180
184
|
KAMAL.specific_roles ||= [ "web" ]
|
181
185
|
role = KAMAL.roles_on(KAMAL.primary_host).first
|
182
186
|
|
183
|
-
|
184
|
-
|
187
|
+
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)
|
185
190
|
end
|
186
191
|
else
|
187
192
|
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
|
@@ -191,7 +196,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
191
196
|
|
192
197
|
roles.each do |role|
|
193
198
|
begin
|
194
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role).logs(since: since, lines: lines, grep: grep))
|
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))
|
195
200
|
rescue SSHKit::Command::Failed
|
196
201
|
puts_by_host host, "Nothing found"
|
197
202
|
end
|
@@ -202,7 +207,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
202
207
|
|
203
208
|
desc "remove", "Remove app containers and images from servers"
|
204
209
|
def remove
|
205
|
-
|
210
|
+
with_lock do
|
206
211
|
stop
|
207
212
|
remove_containers
|
208
213
|
remove_images
|
@@ -211,13 +216,13 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
211
216
|
|
212
217
|
desc "remove_container [VERSION]", "Remove app container with given version from servers", hide: true
|
213
218
|
def remove_container(version)
|
214
|
-
|
219
|
+
with_lock do
|
215
220
|
on(KAMAL.hosts) do |host|
|
216
221
|
roles = KAMAL.roles_on(host)
|
217
222
|
|
218
223
|
roles.each do |role|
|
219
224
|
execute *KAMAL.auditor.record("Removed app container with version #{version}", role: role), verbosity: :debug
|
220
|
-
execute *KAMAL.app(role: role).remove_container(version: version)
|
225
|
+
execute *KAMAL.app(role: role, host: host).remove_container(version: version)
|
221
226
|
end
|
222
227
|
end
|
223
228
|
end
|
@@ -225,13 +230,13 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
225
230
|
|
226
231
|
desc "remove_containers", "Remove all app containers from servers", hide: true
|
227
232
|
def remove_containers
|
228
|
-
|
233
|
+
with_lock do
|
229
234
|
on(KAMAL.hosts) do |host|
|
230
235
|
roles = KAMAL.roles_on(host)
|
231
236
|
|
232
237
|
roles.each do |role|
|
233
238
|
execute *KAMAL.auditor.record("Removed all app containers", role: role), verbosity: :debug
|
234
|
-
execute *KAMAL.app(role: role).remove_containers
|
239
|
+
execute *KAMAL.app(role: role, host: host).remove_containers
|
235
240
|
end
|
236
241
|
end
|
237
242
|
end
|
@@ -239,7 +244,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
239
244
|
|
240
245
|
desc "remove_images", "Remove all app images from servers", hide: true
|
241
246
|
def remove_images
|
242
|
-
|
247
|
+
with_lock do
|
243
248
|
on(KAMAL.hosts) do
|
244
249
|
execute *KAMAL.auditor.record("Removed all app images"), verbosity: :debug
|
245
250
|
execute *KAMAL.app.remove_images
|
@@ -251,7 +256,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
251
256
|
def version
|
252
257
|
on(KAMAL.hosts) do |host|
|
253
258
|
role = KAMAL.roles_on(host).first
|
254
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role).current_running_version).strip
|
259
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).current_running_version).strip
|
255
260
|
end
|
256
261
|
end
|
257
262
|
|
@@ -274,7 +279,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
274
279
|
version = nil
|
275
280
|
on(host) do
|
276
281
|
role = KAMAL.roles_on(host).first
|
277
|
-
version = capture_with_info(*KAMAL.app(role: role).current_running_version).strip
|
282
|
+
version = capture_with_info(*KAMAL.app(role: role, host: host).current_running_version).strip
|
278
283
|
end
|
279
284
|
version.presence
|
280
285
|
end
|
@@ -282,4 +287,12 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
282
287
|
def version_or_latest
|
283
288
|
options[:version] || KAMAL.config.latest_tag
|
284
289
|
end
|
290
|
+
|
291
|
+
def with_lock_if_stopping
|
292
|
+
if options[:stop]
|
293
|
+
with_lock { yield }
|
294
|
+
else
|
295
|
+
yield
|
296
|
+
end
|
297
|
+
end
|
285
298
|
end
|
data/lib/kamal/cli/base.rb
CHANGED
@@ -79,28 +79,27 @@ module Kamal::Cli
|
|
79
79
|
puts " Finished all in #{sprintf("%.1f seconds", runtime)}"
|
80
80
|
end
|
81
81
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
run_hook "pre-connect"
|
86
|
-
|
87
|
-
ensure_run_and_locks_directory
|
88
|
-
|
89
|
-
acquire_lock
|
90
|
-
|
91
|
-
begin
|
82
|
+
def with_lock
|
83
|
+
if KAMAL.holding_lock?
|
92
84
|
yield
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
85
|
+
else
|
86
|
+
ensure_run_and_locks_directory
|
87
|
+
|
88
|
+
acquire_lock
|
89
|
+
|
90
|
+
begin
|
91
|
+
yield
|
92
|
+
rescue
|
93
|
+
begin
|
94
|
+
release_lock
|
95
|
+
rescue => e
|
96
|
+
say "Error releasing the deploy lock: #{e.message}", :red
|
97
|
+
end
|
98
|
+
raise
|
98
99
|
end
|
99
100
|
|
100
|
-
|
101
|
+
release_lock
|
101
102
|
end
|
102
|
-
|
103
|
-
release_lock
|
104
103
|
end
|
105
104
|
|
106
105
|
def confirming(question)
|
@@ -141,16 +140,6 @@ module Kamal::Cli
|
|
141
140
|
end
|
142
141
|
end
|
143
142
|
|
144
|
-
def hold_lock_on_error
|
145
|
-
if KAMAL.hold_lock_on_error?
|
146
|
-
yield
|
147
|
-
else
|
148
|
-
KAMAL.hold_lock_on_error = true
|
149
|
-
yield
|
150
|
-
KAMAL.hold_lock_on_error = false
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
143
|
def run_hook(hook, **extra_details)
|
155
144
|
if !options[:skip_hooks] && KAMAL.hook.hook_exists?(hook)
|
156
145
|
details = { hosts: KAMAL.hosts.join(","), command: command, subcommand: subcommand }
|
@@ -164,6 +153,15 @@ module Kamal::Cli
|
|
164
153
|
end
|
165
154
|
end
|
166
155
|
|
156
|
+
def on(*args, &block)
|
157
|
+
if !KAMAL.connected?
|
158
|
+
run_hook "pre-connect"
|
159
|
+
KAMAL.connected = true
|
160
|
+
end
|
161
|
+
|
162
|
+
super
|
163
|
+
end
|
164
|
+
|
167
165
|
def command
|
168
166
|
@kamal_command ||= begin
|
169
167
|
invocation_class, invocation_commands = *first_invocation
|