kamal 1.5.1 → 1.6.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 +27 -22
- 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 +57 -47
- data/lib/kamal/cli/base.rb +26 -28
- data/lib/kamal/cli/build/clone.rb +61 -0
- data/lib/kamal/cli/build.rb +58 -50
- 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 +4 -5
- data/lib/kamal/cli/main.rb +36 -43
- data/lib/kamal/cli/prune.rb +3 -3
- data/lib/kamal/cli/server.rb +39 -15
- data/lib/kamal/cli/traefik.rb +8 -8
- data/lib/kamal/commander/specifics.rb +1 -1
- data/lib/kamal/commander.rb +6 -6
- 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 +2 -2
- data/lib/kamal/commands/app.rb +6 -5
- data/lib/kamal/commands/base.rb +2 -3
- data/lib/kamal/commands/builder/base.rb +6 -12
- data/lib/kamal/commands/builder/clone.rb +28 -0
- data/lib/kamal/commands/builder/multiarch.rb +9 -9
- data/lib/kamal/commands/builder/native/cached.rb +6 -7
- data/lib/kamal/commands/builder/native/remote.rb +9 -9
- data/lib/kamal/commands/builder/native.rb +6 -7
- data/lib/kamal/commands/builder.rb +2 -0
- data/lib/kamal/configuration/builder.rb +33 -2
- data/lib/kamal/configuration/env/tag.rb +12 -0
- data/lib/kamal/configuration/env.rb +1 -1
- data/lib/kamal/configuration/role.rb +29 -6
- data/lib/kamal/configuration.rb +14 -2
- data/lib/kamal/git.rb +4 -0
- data/lib/kamal/sshkit_with_ext.rb +36 -0
- data/lib/kamal/version.rb +1 -1
- metadata +18 -10
- data/lib/kamal/cli/healthcheck.rb +0 -21
- data/lib/kamal/commands/app.rb.orig +0 -127
- 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: 1386c468f912402e6ed544a4e65bd3c0bdca8840cc3c519c5251b7ec5e89931b
|
4
|
+
data.tar.gz: 025ffc718735a857a03bfb06ee2498e33caf3d04e1e88cfd43baa753413b2fea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 051e11bfe7e3d23bdac103fa39a750b5ce10bfe29b9ce642239bc2a1752080bbef995b5075a2143cd0863cbe57dffb96ce97f75630863ba25f50d03a5e27cf5e
|
7
|
+
data.tar.gz: b0d979ab7b2dc0e1fd8592d39cb5856eae5b8f9aca8e49fe726785132fae5455ee39c7c607b256d8d18ba1546af576e28c4a447c334f59ce8aba8e0e1af56c5e
|
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)
|
@@ -107,8 +107,9 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
107
107
|
if name == "all"
|
108
108
|
KAMAL.accessory_names.each { |accessory_name| details(accessory_name) }
|
109
109
|
else
|
110
|
+
type = "Accessory #{name}"
|
110
111
|
with_accessory(name) do |accessory, hosts|
|
111
|
-
on(hosts) {
|
112
|
+
on(hosts) { puts_by_host host, capture_with_info(*accessory.info), type: type }
|
112
113
|
end
|
113
114
|
end
|
114
115
|
end
|
@@ -173,17 +174,12 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
173
174
|
desc "remove [NAME]", "Remove accessory container, image and data directory from host (use NAME=all to remove all accessories)"
|
174
175
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
175
176
|
def remove(name)
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
stop(name)
|
183
|
-
remove_container(name)
|
184
|
-
remove_image(name)
|
185
|
-
remove_service_directory(name)
|
186
|
-
end
|
177
|
+
confirming "This will remove all containers, images and data directories for #{name}. Are you sure?" do
|
178
|
+
with_lock do
|
179
|
+
if name == "all"
|
180
|
+
KAMAL.accessory_names.each { |accessory_name| remove_accessory(accessory_name) }
|
181
|
+
else
|
182
|
+
remove_accessory(name)
|
187
183
|
end
|
188
184
|
end
|
189
185
|
end
|
@@ -191,7 +187,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
191
187
|
|
192
188
|
desc "remove_container [NAME]", "Remove accessory container from host", hide: true
|
193
189
|
def remove_container(name)
|
194
|
-
|
190
|
+
with_lock do
|
195
191
|
with_accessory(name) do |accessory, hosts|
|
196
192
|
on(hosts) do
|
197
193
|
execute *KAMAL.auditor.record("Remove #{name} accessory container"), verbosity: :debug
|
@@ -203,7 +199,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
203
199
|
|
204
200
|
desc "remove_image [NAME]", "Remove accessory image from host", hide: true
|
205
201
|
def remove_image(name)
|
206
|
-
|
202
|
+
with_lock do
|
207
203
|
with_accessory(name) do |accessory, hosts|
|
208
204
|
on(hosts) do
|
209
205
|
execute *KAMAL.auditor.record("Removed #{name} accessory image"), verbosity: :debug
|
@@ -215,7 +211,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
215
211
|
|
216
212
|
desc "remove_service_directory [NAME]", "Remove accessory directory used for uploaded files and data directories from host", hide: true
|
217
213
|
def remove_service_directory(name)
|
218
|
-
|
214
|
+
with_lock do
|
219
215
|
with_accessory(name) do |accessory, hosts|
|
220
216
|
on(hosts) do
|
221
217
|
execute *accessory.remove_service_directory
|
@@ -249,4 +245,13 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
249
245
|
accessory.hosts
|
250
246
|
end
|
251
247
|
end
|
248
|
+
|
249
|
+
def remove_accessory(name)
|
250
|
+
with_accessory(name) do
|
251
|
+
stop(name)
|
252
|
+
remove_container(name)
|
253
|
+
remove_image(name)
|
254
|
+
remove_service_directory(name)
|
255
|
+
end
|
256
|
+
end
|
252
257
|
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
|
@@ -180,8 +181,9 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
180
181
|
KAMAL.specific_roles ||= [ "web" ]
|
181
182
|
role = KAMAL.roles_on(KAMAL.primary_host).first
|
182
183
|
|
183
|
-
|
184
|
-
|
184
|
+
app = KAMAL.app(role: role, host: host)
|
185
|
+
info app.follow_logs(host: KAMAL.primary_host, lines: lines, grep: grep)
|
186
|
+
exec app.follow_logs(host: KAMAL.primary_host, lines: lines, grep: grep)
|
185
187
|
end
|
186
188
|
else
|
187
189
|
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
|
@@ -191,7 +193,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
191
193
|
|
192
194
|
roles.each do |role|
|
193
195
|
begin
|
194
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role).logs(since: since, lines: lines, grep: grep))
|
196
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).logs(since: since, lines: lines, grep: grep))
|
195
197
|
rescue SSHKit::Command::Failed
|
196
198
|
puts_by_host host, "Nothing found"
|
197
199
|
end
|
@@ -202,7 +204,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
202
204
|
|
203
205
|
desc "remove", "Remove app containers and images from servers"
|
204
206
|
def remove
|
205
|
-
|
207
|
+
with_lock do
|
206
208
|
stop
|
207
209
|
remove_containers
|
208
210
|
remove_images
|
@@ -211,13 +213,13 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
211
213
|
|
212
214
|
desc "remove_container [VERSION]", "Remove app container with given version from servers", hide: true
|
213
215
|
def remove_container(version)
|
214
|
-
|
216
|
+
with_lock do
|
215
217
|
on(KAMAL.hosts) do |host|
|
216
218
|
roles = KAMAL.roles_on(host)
|
217
219
|
|
218
220
|
roles.each do |role|
|
219
221
|
execute *KAMAL.auditor.record("Removed app container with version #{version}", role: role), verbosity: :debug
|
220
|
-
execute *KAMAL.app(role: role).remove_container(version: version)
|
222
|
+
execute *KAMAL.app(role: role, host: host).remove_container(version: version)
|
221
223
|
end
|
222
224
|
end
|
223
225
|
end
|
@@ -225,13 +227,13 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
225
227
|
|
226
228
|
desc "remove_containers", "Remove all app containers from servers", hide: true
|
227
229
|
def remove_containers
|
228
|
-
|
230
|
+
with_lock do
|
229
231
|
on(KAMAL.hosts) do |host|
|
230
232
|
roles = KAMAL.roles_on(host)
|
231
233
|
|
232
234
|
roles.each do |role|
|
233
235
|
execute *KAMAL.auditor.record("Removed all app containers", role: role), verbosity: :debug
|
234
|
-
execute *KAMAL.app(role: role).remove_containers
|
236
|
+
execute *KAMAL.app(role: role, host: host).remove_containers
|
235
237
|
end
|
236
238
|
end
|
237
239
|
end
|
@@ -239,7 +241,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
239
241
|
|
240
242
|
desc "remove_images", "Remove all app images from servers", hide: true
|
241
243
|
def remove_images
|
242
|
-
|
244
|
+
with_lock do
|
243
245
|
on(KAMAL.hosts) do
|
244
246
|
execute *KAMAL.auditor.record("Removed all app images"), verbosity: :debug
|
245
247
|
execute *KAMAL.app.remove_images
|
@@ -251,7 +253,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
251
253
|
def version
|
252
254
|
on(KAMAL.hosts) do |host|
|
253
255
|
role = KAMAL.roles_on(host).first
|
254
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role).current_running_version).strip
|
256
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).current_running_version).strip
|
255
257
|
end
|
256
258
|
end
|
257
259
|
|
@@ -274,7 +276,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
274
276
|
version = nil
|
275
277
|
on(host) do
|
276
278
|
role = KAMAL.roles_on(host).first
|
277
|
-
version = capture_with_info(*KAMAL.app(role: role).current_running_version).strip
|
279
|
+
version = capture_with_info(*KAMAL.app(role: role, host: host).current_running_version).strip
|
278
280
|
end
|
279
281
|
version.presence
|
280
282
|
end
|
@@ -282,4 +284,12 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
282
284
|
def version_or_latest
|
283
285
|
options[:version] || KAMAL.config.latest_tag
|
284
286
|
end
|
287
|
+
|
288
|
+
def with_lock_if_stopping
|
289
|
+
if options[:stop]
|
290
|
+
with_lock { yield }
|
291
|
+
else
|
292
|
+
yield
|
293
|
+
end
|
294
|
+
end
|
285
295
|
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
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "uri"
|
2
|
+
|
3
|
+
class Kamal::Cli::Build::Clone
|
4
|
+
attr_reader :sshkit
|
5
|
+
delegate :info, :error, :execute, :capture_with_info, to: :sshkit
|
6
|
+
|
7
|
+
def initialize(sshkit)
|
8
|
+
@sshkit = sshkit
|
9
|
+
end
|
10
|
+
|
11
|
+
def prepare
|
12
|
+
begin
|
13
|
+
clone_repo
|
14
|
+
rescue SSHKit::Command::Failed => e
|
15
|
+
if e.message =~ /already exists and is not an empty directory/
|
16
|
+
reset
|
17
|
+
else
|
18
|
+
raise Kamal::Cli::Build::BuildError, "Failed to clone repo: #{e.message}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
validate!
|
23
|
+
rescue Kamal::Cli::Build::BuildError => e
|
24
|
+
error "Error preparing clone: #{e.message}, deleting and retrying..."
|
25
|
+
|
26
|
+
FileUtils.rm_rf KAMAL.config.builder.clone_directory
|
27
|
+
clone_repo
|
28
|
+
validate!
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def clone_repo
|
33
|
+
info "Cloning repo into build directory `#{KAMAL.config.builder.build_directory}`..."
|
34
|
+
|
35
|
+
FileUtils.mkdir_p KAMAL.config.builder.clone_directory
|
36
|
+
execute *KAMAL.builder.clone
|
37
|
+
end
|
38
|
+
|
39
|
+
def reset
|
40
|
+
info "Resetting local clone as `#{KAMAL.config.builder.build_directory}` already exists..."
|
41
|
+
|
42
|
+
KAMAL.builder.clone_reset_steps.each { |step| execute *step }
|
43
|
+
rescue SSHKit::Command::Failed => e
|
44
|
+
raise Kamal::Cli::Build::BuildError, "Failed to clone repo: #{e.message}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate!
|
48
|
+
status = capture_with_info(*KAMAL.builder.clone_status).strip
|
49
|
+
|
50
|
+
unless status.empty?
|
51
|
+
raise Kamal::Cli::Build::BuildError, "Clone in #{KAMAL.config.builder.build_directory} is dirty, #{status}"
|
52
|
+
end
|
53
|
+
|
54
|
+
revision = capture_with_info(*KAMAL.builder.clone_revision).strip
|
55
|
+
if revision != Kamal::Git.revision
|
56
|
+
raise Kamal::Cli::Build::BuildError, "Clone in #{KAMAL.config.builder.build_directory} is not on the correct revision, expected `#{Kamal::Git.revision}` but got `#{revision}`"
|
57
|
+
end
|
58
|
+
rescue SSHKit::Command::Failed => e
|
59
|
+
raise Kamal::Cli::Build::BuildError, "Failed to validate clone: #{e.message}"
|
60
|
+
end
|
61
|
+
end
|