kamal 2.7.0 → 2.11.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 +27 -7
- data/lib/kamal/cli/alias/command.rb +2 -2
- data/lib/kamal/cli/app/boot.rb +1 -1
- data/lib/kamal/cli/app.rb +74 -115
- data/lib/kamal/cli/base.rb +19 -6
- data/lib/kamal/cli/build/clone.rb +0 -2
- data/lib/kamal/cli/build/port_forwarding.rb +66 -0
- data/lib/kamal/cli/build.rb +70 -35
- data/lib/kamal/cli/healthcheck/poller.rb +1 -1
- data/lib/kamal/cli/main.rb +9 -3
- data/lib/kamal/cli/proxy.rb +42 -35
- data/lib/kamal/cli/registry.rb +37 -7
- data/lib/kamal/cli/secrets.rb +2 -1
- data/lib/kamal/cli/server.rb +12 -1
- data/lib/kamal/cli/templates/deploy.yml +4 -3
- data/lib/kamal/cli/templates/secrets +2 -1
- data/lib/kamal/commander.rb +21 -19
- data/lib/kamal/commands/accessory.rb +5 -0
- data/lib/kamal/commands/app/execution.rb +7 -1
- data/lib/kamal/commands/app.rb +1 -0
- data/lib/kamal/commands/base.rb +15 -2
- data/lib/kamal/commands/builder/base.rb +20 -1
- data/lib/kamal/commands/builder/hybrid.rb +3 -3
- data/lib/kamal/commands/builder/local.rb +8 -2
- data/lib/kamal/commands/builder/pack.rb +5 -5
- data/lib/kamal/commands/builder/remote.rb +15 -3
- data/lib/kamal/commands/builder.rb +8 -2
- data/lib/kamal/commands/docker.rb +17 -1
- data/lib/kamal/commands/proxy.rb +22 -3
- data/lib/kamal/commands/registry.rb +22 -0
- data/lib/kamal/configuration/accessory.rb +56 -25
- data/lib/kamal/configuration/boot.rb +4 -0
- data/lib/kamal/configuration/builder.rb +10 -3
- data/lib/kamal/configuration/docs/accessory.yml +37 -5
- data/lib/kamal/configuration/docs/alias.yml +3 -0
- data/lib/kamal/configuration/docs/boot.yml +12 -10
- data/lib/kamal/configuration/docs/configuration.yml +30 -1
- data/lib/kamal/configuration/docs/proxy.yml +48 -16
- data/lib/kamal/configuration/docs/registry.yml +12 -4
- data/lib/kamal/configuration/docs/ssh.yml +7 -4
- data/lib/kamal/configuration/docs/sshkit.yml +8 -0
- data/lib/kamal/configuration/env.rb +7 -3
- data/lib/kamal/configuration/proxy/boot.rb +4 -9
- data/lib/kamal/configuration/proxy/run.rb +143 -0
- data/lib/kamal/configuration/proxy.rb +7 -3
- data/lib/kamal/configuration/registry.rb +8 -0
- data/lib/kamal/configuration/role.rb +15 -3
- data/lib/kamal/configuration/ssh.rb +18 -3
- data/lib/kamal/configuration/sshkit.rb +4 -0
- data/lib/kamal/configuration/validator/proxy.rb +20 -0
- data/lib/kamal/configuration/validator/registry.rb +5 -3
- data/lib/kamal/configuration/validator.rb +52 -4
- data/lib/kamal/configuration/volume.rb +11 -4
- data/lib/kamal/configuration.rb +89 -5
- data/lib/kamal/secrets/adapters/one_password.rb +1 -1
- data/lib/kamal/secrets/adapters/passbolt.rb +1 -2
- data/lib/kamal/secrets/adapters/test.rb +3 -1
- data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +15 -1
- data/lib/kamal/secrets.rb +17 -6
- data/lib/kamal/sshkit_with_ext.rb +135 -10
- data/lib/kamal/utils.rb +3 -3
- data/lib/kamal/version.rb +1 -1
- data/lib/kamal.rb +1 -0
- metadata +18 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c8f7525b3ed804be2810d0af353c254b8c7ebf4d5cf5916dbfa97d5bf4615d54
|
|
4
|
+
data.tar.gz: adfac172b3e4b45bc00092317bde87395fcedecf107b30c8500ab58097ef22d8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 05150f9253685ce18dd910cf809a99e51449b7452536e6c3f39c8e37d5ce3db4806bd8e198ed3e29aa872523eb8937db5b58faee036619f8e5b43d1084eebffc
|
|
7
|
+
data.tar.gz: 47e77a8d347c44aff4326f9927a0933c0240b5bfdb90f1b623e85ecdad8ba48c0e3443d43cd062b265aa29e2dbd47d778499be39a079d3cb93d8856cb0388108
|
data/README.md
CHANGED
|
@@ -6,7 +6,7 @@ From bare metal to cloud VMs, deploy web apps anywhere with zero downtime. Kamal
|
|
|
6
6
|
|
|
7
7
|
## Contributing to the documentation
|
|
8
8
|
|
|
9
|
-
Please help us improve Kamal's documentation on
|
|
9
|
+
Please help us improve Kamal's documentation on [the basecamp/kamal-site repository](https://github.com/basecamp/kamal-site).
|
|
10
10
|
|
|
11
11
|
## License
|
|
12
12
|
|
data/lib/kamal/cli/accessory.rb
CHANGED
|
@@ -45,12 +45,14 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
|
45
45
|
with_lock do
|
|
46
46
|
with_accessory(name) do |accessory, hosts|
|
|
47
47
|
on(hosts) do
|
|
48
|
-
accessory.files.each do |(local,
|
|
48
|
+
accessory.files.each do |(local, config)|
|
|
49
|
+
remote = config[:host_path]
|
|
49
50
|
accessory.ensure_local_file_present(local)
|
|
50
51
|
|
|
51
52
|
execute *accessory.make_directory_for(remote)
|
|
52
53
|
upload! local, remote
|
|
53
|
-
execute :chmod,
|
|
54
|
+
execute :chmod, config[:mode], remote
|
|
55
|
+
execute :chown, config[:owner], remote if config[:owner]
|
|
54
56
|
end
|
|
55
57
|
end
|
|
56
58
|
end
|
|
@@ -62,8 +64,10 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
|
62
64
|
with_lock do
|
|
63
65
|
with_accessory(name) do |accessory, hosts|
|
|
64
66
|
on(hosts) do
|
|
65
|
-
accessory.directories.
|
|
66
|
-
execute *accessory.make_directory(
|
|
67
|
+
accessory.directories.each do |(local, config)|
|
|
68
|
+
execute *accessory.make_directory(local)
|
|
69
|
+
execute :chmod, config[:mode], local if config[:mode]
|
|
70
|
+
execute :chown, config[:owner], local if config[:owner]
|
|
67
71
|
end
|
|
68
72
|
end
|
|
69
73
|
end
|
|
@@ -77,6 +81,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
|
77
81
|
KAMAL.accessory_names.each { |accessory_name| reboot(accessory_name) }
|
|
78
82
|
else
|
|
79
83
|
prepare(name)
|
|
84
|
+
pull_image(name)
|
|
80
85
|
stop(name)
|
|
81
86
|
remove_container(name)
|
|
82
87
|
boot(name, prepare: false)
|
|
@@ -127,12 +132,13 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
|
127
132
|
|
|
128
133
|
desc "details [NAME]", "Show details about accessory on host (use NAME=all to show all accessories)"
|
|
129
134
|
def details(name)
|
|
135
|
+
quiet = options[:quiet]
|
|
130
136
|
if name == "all"
|
|
131
137
|
KAMAL.accessory_names.each { |accessory_name| details(accessory_name) }
|
|
132
138
|
else
|
|
133
139
|
type = "Accessory #{name}"
|
|
134
140
|
with_accessory(name) do |accessory, hosts|
|
|
135
|
-
on(hosts) { puts_by_host host, capture_with_info(*accessory.info), type: type }
|
|
141
|
+
on(hosts) { puts_by_host host, capture_with_info(*accessory.info), type: type, quiet: quiet }
|
|
136
142
|
end
|
|
137
143
|
end
|
|
138
144
|
end
|
|
@@ -144,6 +150,8 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
|
144
150
|
pre_connect_if_required
|
|
145
151
|
|
|
146
152
|
cmd = Kamal::Utils.join_commands(cmd)
|
|
153
|
+
quiet = options[:quiet]
|
|
154
|
+
|
|
147
155
|
with_accessory(name) do |accessory, hosts|
|
|
148
156
|
case
|
|
149
157
|
when options[:interactive] && options[:reuse]
|
|
@@ -159,7 +167,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
|
159
167
|
say "Launching command from existing container...", :magenta
|
|
160
168
|
on(hosts) do |host|
|
|
161
169
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
|
|
162
|
-
puts_by_host host, capture_with_info(*accessory.execute_in_existing_container(cmd))
|
|
170
|
+
puts_by_host host, capture_with_info(*accessory.execute_in_existing_container(cmd)), quiet: quiet
|
|
163
171
|
end
|
|
164
172
|
|
|
165
173
|
else
|
|
@@ -167,7 +175,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
|
167
175
|
on(hosts) do |host|
|
|
168
176
|
execute *KAMAL.registry.login
|
|
169
177
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
|
|
170
|
-
puts_by_host host, capture_with_info(*accessory.execute_in_new_container(cmd))
|
|
178
|
+
puts_by_host host, capture_with_info(*accessory.execute_in_new_container(cmd)), quiet: quiet
|
|
171
179
|
end
|
|
172
180
|
end
|
|
173
181
|
end
|
|
@@ -203,6 +211,18 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
|
203
211
|
end
|
|
204
212
|
end
|
|
205
213
|
|
|
214
|
+
desc "pull_image [NAME]", "Pull accessory image on host", hide: true
|
|
215
|
+
def pull_image(name)
|
|
216
|
+
with_lock do
|
|
217
|
+
with_accessory(name) do |accessory, hosts|
|
|
218
|
+
on(hosts) do
|
|
219
|
+
execute *KAMAL.auditor.record("Pull #{name} accessory image"), verbosity: :debug
|
|
220
|
+
execute *accessory.pull_image
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
206
226
|
desc "remove [NAME]", "Remove accessory container, image and data directory from host (use NAME=all to remove all accessories)"
|
|
207
227
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
|
208
228
|
def remove(name)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
class Kamal::Cli::Alias::Command < Thor::DynamicCommand
|
|
2
2
|
def run(instance, args = [])
|
|
3
|
-
if (
|
|
3
|
+
if (command = KAMAL.resolve_alias(name))
|
|
4
4
|
KAMAL.reset
|
|
5
|
-
Kamal::Cli::Main.start(Shellwords.split(
|
|
5
|
+
Kamal::Cli::Main.start(Shellwords.split(command) + ARGV[1..-1])
|
|
6
6
|
else
|
|
7
7
|
super
|
|
8
8
|
end
|
data/lib/kamal/cli/app/boot.rb
CHANGED
|
@@ -56,7 +56,7 @@ class Kamal::Cli::App::Boot
|
|
|
56
56
|
raise Kamal::Cli::BootError, "Failed to get endpoint for #{role} on #{host}, did the container boot?" if endpoint.empty?
|
|
57
57
|
execute *app.deploy(target: endpoint)
|
|
58
58
|
else
|
|
59
|
-
Kamal::Cli::Healthcheck::Poller.wait_for_healthy
|
|
59
|
+
Kamal::Cli::Healthcheck::Poller.wait_for_healthy { capture_with_info(*app.status(version: version)) }
|
|
60
60
|
end
|
|
61
61
|
rescue => e
|
|
62
62
|
error "Failed to boot #{role} on #{host}"
|
data/lib/kamal/cli/app.rb
CHANGED
|
@@ -23,10 +23,8 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
23
23
|
host_list = Array(hosts).join(",")
|
|
24
24
|
run_hook "pre-app-boot", hosts: host_list
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Kamal::Cli::App::Boot.new(host, role, self, version, barrier).run
|
|
29
|
-
end
|
|
26
|
+
on_roles(KAMAL.roles, hosts: hosts, parallel: KAMAL.config.boot.parallel_roles) do |host, role|
|
|
27
|
+
Kamal::Cli::App::Boot.new(host, role, self, version, barrier).run
|
|
30
28
|
end
|
|
31
29
|
|
|
32
30
|
run_hook "post-app-boot", hosts: host_list
|
|
@@ -45,21 +43,17 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
45
43
|
desc "start", "Start existing app container on servers"
|
|
46
44
|
def start
|
|
47
45
|
with_lock do
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
app = KAMAL.app(role: role, host: host)
|
|
53
|
-
execute *KAMAL.auditor.record("Started app version #{KAMAL.config.version}"), verbosity: :debug
|
|
54
|
-
execute *app.start, raise_on_non_zero_exit: false
|
|
46
|
+
on_roles(KAMAL.roles, hosts: KAMAL.app_hosts, parallel: KAMAL.config.boot.parallel_roles) do |host, role|
|
|
47
|
+
app = KAMAL.app(role: role, host: host)
|
|
48
|
+
execute *KAMAL.auditor.record("Started app version #{KAMAL.config.version}"), verbosity: :debug
|
|
49
|
+
execute *app.start, raise_on_non_zero_exit: false
|
|
55
50
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
if role.running_proxy?
|
|
52
|
+
version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
|
|
53
|
+
endpoint = capture_with_info(*app.container_id_for_version(version)).strip
|
|
54
|
+
raise Kamal::Cli::BootError, "Failed to get endpoint for #{role} on #{host}, did the container boot?" if endpoint.empty?
|
|
60
55
|
|
|
61
|
-
|
|
62
|
-
end
|
|
56
|
+
execute *app.deploy(target: endpoint)
|
|
63
57
|
end
|
|
64
58
|
end
|
|
65
59
|
end
|
|
@@ -68,23 +62,19 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
68
62
|
desc "stop", "Stop app container on servers"
|
|
69
63
|
def stop
|
|
70
64
|
with_lock do
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
roles.each do |role|
|
|
75
|
-
app = KAMAL.app(role: role, host: host)
|
|
76
|
-
execute *KAMAL.auditor.record("Stopped app", role: role), verbosity: :debug
|
|
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
|
|
65
|
+
on_roles(KAMAL.roles, hosts: KAMAL.app_hosts, parallel: KAMAL.config.boot.parallel_roles) do |host, role|
|
|
66
|
+
app = KAMAL.app(role: role, host: host)
|
|
67
|
+
execute *KAMAL.auditor.record("Stopped app", role: role), verbosity: :debug
|
|
85
68
|
|
|
86
|
-
|
|
69
|
+
if role.running_proxy?
|
|
70
|
+
version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
|
|
71
|
+
endpoint = capture_with_info(*app.container_id_for_version(version)).strip
|
|
72
|
+
if endpoint.present?
|
|
73
|
+
execute *app.remove, raise_on_non_zero_exit: false
|
|
74
|
+
end
|
|
87
75
|
end
|
|
76
|
+
|
|
77
|
+
execute *app.stop, raise_on_non_zero_exit: false
|
|
88
78
|
end
|
|
89
79
|
end
|
|
90
80
|
end
|
|
@@ -92,12 +82,9 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
92
82
|
# FIXME: Drop in favor of just containers?
|
|
93
83
|
desc "details", "Show details about app containers"
|
|
94
84
|
def details
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
roles.each do |role|
|
|
99
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).info)
|
|
100
|
-
end
|
|
85
|
+
quiet = options[:quiet]
|
|
86
|
+
on_roles(KAMAL.roles, hosts: KAMAL.app_hosts) do |host, role|
|
|
87
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).info), quiet: quiet
|
|
101
88
|
end
|
|
102
89
|
end
|
|
103
90
|
|
|
@@ -120,6 +107,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
120
107
|
cmd = Kamal::Utils.join_commands(cmd)
|
|
121
108
|
env = options[:env]
|
|
122
109
|
detach = options[:detach]
|
|
110
|
+
quiet = options[:quiet]
|
|
123
111
|
case
|
|
124
112
|
when options[:interactive] && options[:reuse]
|
|
125
113
|
say "Get current version of running container...", :magenta unless options[:version]
|
|
@@ -143,13 +131,9 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
143
131
|
using_version(options[:version] || current_running_version) do |version|
|
|
144
132
|
say "Launching command with version #{version} from existing container...", :magenta
|
|
145
133
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
roles.each do |role|
|
|
150
|
-
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}", role: role), verbosity: :debug
|
|
151
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).execute_in_existing_container(cmd, env: env))
|
|
152
|
-
end
|
|
134
|
+
on_roles(KAMAL.roles, hosts: KAMAL.app_hosts) do |host, role|
|
|
135
|
+
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}", role: role), verbosity: :debug
|
|
136
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).execute_in_existing_container(cmd, env: env)), quiet: quiet
|
|
153
137
|
end
|
|
154
138
|
end
|
|
155
139
|
|
|
@@ -157,15 +141,11 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
157
141
|
say "Get most recent version available as an image...", :magenta unless options[:version]
|
|
158
142
|
using_version(version_or_latest) do |version|
|
|
159
143
|
say "Launching command with version #{version} from new container...", :magenta
|
|
160
|
-
on(KAMAL.app_hosts)
|
|
161
|
-
execute *KAMAL.registry.login
|
|
162
|
-
|
|
163
|
-
roles = KAMAL.roles_on(host)
|
|
144
|
+
on(KAMAL.app_hosts) { execute *KAMAL.registry.login }
|
|
164
145
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
end
|
|
146
|
+
on_roles(KAMAL.roles, hosts: KAMAL.app_hosts) do |host, role|
|
|
147
|
+
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
|
|
148
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).execute_in_new_container(cmd, env: env, detach: detach)), quiet: quiet
|
|
169
149
|
end
|
|
170
150
|
end
|
|
171
151
|
end
|
|
@@ -173,30 +153,28 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
173
153
|
|
|
174
154
|
desc "containers", "Show app containers on servers"
|
|
175
155
|
def containers
|
|
176
|
-
|
|
156
|
+
quiet = options[:quiet]
|
|
157
|
+
on(KAMAL.app_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.app.list_containers), quiet: quiet }
|
|
177
158
|
end
|
|
178
159
|
|
|
179
160
|
desc "stale_containers", "Detect app stale containers"
|
|
180
161
|
option :stop, aliases: "-s", type: :boolean, default: false, desc: "Stop the stale containers found"
|
|
181
162
|
def stale_containers
|
|
163
|
+
quiet = options[:quiet]
|
|
182
164
|
stop = options[:stop]
|
|
183
165
|
|
|
184
166
|
with_lock_if_stopping do
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
execute *app.stop(version: version), raise_on_non_zero_exit: false
|
|
197
|
-
else
|
|
198
|
-
puts_by_host host, "Detected stale container for role #{role} with version #{version} (use `kamal app stale_containers --stop` to stop)"
|
|
199
|
-
end
|
|
167
|
+
on_roles(KAMAL.roles, hosts: KAMAL.app_hosts) do |host, role|
|
|
168
|
+
app = KAMAL.app(role: role, host: host)
|
|
169
|
+
versions = capture_with_info(*app.list_versions, raise_on_non_zero_exit: false).split("\n")
|
|
170
|
+
versions -= [ capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip ]
|
|
171
|
+
|
|
172
|
+
versions.each do |version|
|
|
173
|
+
if stop
|
|
174
|
+
puts_by_host host, "Stopping stale container for role #{role} with version #{version}", quiet: quiet
|
|
175
|
+
execute *app.stop(version: version), raise_on_non_zero_exit: false
|
|
176
|
+
else
|
|
177
|
+
puts_by_host host, "Detected stale container for role #{role} with version #{version} (use `kamal app stale_containers --stop` to stop)", quiet: quiet
|
|
200
178
|
end
|
|
201
179
|
end
|
|
202
180
|
end
|
|
@@ -205,7 +183,8 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
205
183
|
|
|
206
184
|
desc "images", "Show app images on servers"
|
|
207
185
|
def images
|
|
208
|
-
|
|
186
|
+
quiet = options[:quiet]
|
|
187
|
+
on(KAMAL.app_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.app.list_images), quiet: quiet }
|
|
209
188
|
end
|
|
210
189
|
|
|
211
190
|
desc "logs", "Show log lines from app on servers (use --help to show options)"
|
|
@@ -224,6 +203,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
224
203
|
since = options[:since]
|
|
225
204
|
container_id = options[:container_id]
|
|
226
205
|
timestamps = !options[:skip_timestamps]
|
|
206
|
+
quiet = options[:quiet]
|
|
227
207
|
|
|
228
208
|
if options[:follow]
|
|
229
209
|
lines = options[:lines].presence || ((since || grep) ? nil : 10) # Default to 10 lines if since or grep isn't set
|
|
@@ -241,15 +221,11 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
241
221
|
else
|
|
242
222
|
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
|
|
243
223
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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))
|
|
250
|
-
rescue SSHKit::Command::Failed
|
|
251
|
-
puts_by_host host, "Nothing found"
|
|
252
|
-
end
|
|
224
|
+
on_roles(KAMAL.roles, hosts: KAMAL.app_hosts) do |host, role|
|
|
225
|
+
begin
|
|
226
|
+
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)), quiet: quiet
|
|
227
|
+
rescue SSHKit::Command::Failed
|
|
228
|
+
puts_by_host host, "Nothing found", quiet: quiet
|
|
253
229
|
end
|
|
254
230
|
end
|
|
255
231
|
end
|
|
@@ -268,12 +244,8 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
268
244
|
desc "live", "Set the app to live mode"
|
|
269
245
|
def live
|
|
270
246
|
with_lock do
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
roles.each do |role|
|
|
275
|
-
execute *KAMAL.app(role: role, host: host).live if role.running_proxy?
|
|
276
|
-
end
|
|
247
|
+
on_roles(KAMAL.roles, hosts: KAMAL.proxy_hosts) do |host, role|
|
|
248
|
+
execute *KAMAL.app(role: role, host: host).live if role.running_proxy?
|
|
277
249
|
end
|
|
278
250
|
end
|
|
279
251
|
end
|
|
@@ -285,12 +257,8 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
285
257
|
maintenance_options = { drain_timeout: options[:drain_timeout] || KAMAL.config.drain_timeout, message: options[:message] }
|
|
286
258
|
|
|
287
259
|
with_lock do
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
roles.each do |role|
|
|
292
|
-
execute *KAMAL.app(role: role, host: host).maintenance(**maintenance_options) if role.running_proxy?
|
|
293
|
-
end
|
|
260
|
+
on_roles(KAMAL.roles, hosts: KAMAL.proxy_hosts) do |host, role|
|
|
261
|
+
execute *KAMAL.app(role: role, host: host).maintenance(**maintenance_options) if role.running_proxy?
|
|
294
262
|
end
|
|
295
263
|
end
|
|
296
264
|
end
|
|
@@ -298,13 +266,9 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
298
266
|
desc "remove_container [VERSION]", "Remove app container with given version from servers", hide: true
|
|
299
267
|
def remove_container(version)
|
|
300
268
|
with_lock do
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
roles.each do |role|
|
|
305
|
-
execute *KAMAL.auditor.record("Removed app container with version #{version}", role: role), verbosity: :debug
|
|
306
|
-
execute *KAMAL.app(role: role, host: host).remove_container(version: version)
|
|
307
|
-
end
|
|
269
|
+
on_roles(KAMAL.roles, hosts: KAMAL.app_hosts) do |host, role|
|
|
270
|
+
execute *KAMAL.auditor.record("Removed app container with version #{version}", role: role), verbosity: :debug
|
|
271
|
+
execute *KAMAL.app(role: role, host: host).remove_container(version: version)
|
|
308
272
|
end
|
|
309
273
|
end
|
|
310
274
|
end
|
|
@@ -312,13 +276,9 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
312
276
|
desc "remove_containers", "Remove all app containers from servers", hide: true
|
|
313
277
|
def remove_containers
|
|
314
278
|
with_lock do
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
roles.each do |role|
|
|
319
|
-
execute *KAMAL.auditor.record("Removed all app containers", role: role), verbosity: :debug
|
|
320
|
-
execute *KAMAL.app(role: role, host: host).remove_containers
|
|
321
|
-
end
|
|
279
|
+
on_roles(KAMAL.roles, hosts: KAMAL.app_hosts) do |host, role|
|
|
280
|
+
execute *KAMAL.auditor.record("Removed all app containers", role: role), verbosity: :debug
|
|
281
|
+
execute *KAMAL.app(role: role, host: host).remove_containers
|
|
322
282
|
end
|
|
323
283
|
end
|
|
324
284
|
end
|
|
@@ -326,7 +286,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
326
286
|
desc "remove_images", "Remove all app images from servers", hide: true
|
|
327
287
|
def remove_images
|
|
328
288
|
with_lock do
|
|
329
|
-
on(
|
|
289
|
+
on(hosts_removing_all_roles) do
|
|
330
290
|
execute *KAMAL.auditor.record("Removed all app images"), verbosity: :debug
|
|
331
291
|
execute *KAMAL.app.remove_images
|
|
332
292
|
end
|
|
@@ -336,14 +296,8 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
336
296
|
desc "remove_app_directories", "Remove the app directories from servers", hide: true
|
|
337
297
|
def remove_app_directories
|
|
338
298
|
with_lock do
|
|
339
|
-
on(
|
|
340
|
-
|
|
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
|
-
|
|
299
|
+
on(hosts_removing_all_roles) do |host|
|
|
300
|
+
execute *KAMAL.server.remove_app_directory, raise_on_non_zero_exit: false
|
|
347
301
|
execute *KAMAL.auditor.record("Removed #{KAMAL.config.app_directory}"), verbosity: :debug
|
|
348
302
|
execute *KAMAL.app.remove_proxy_app_directory, raise_on_non_zero_exit: false
|
|
349
303
|
end
|
|
@@ -352,13 +306,18 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
|
352
306
|
|
|
353
307
|
desc "version", "Show app version currently running on servers"
|
|
354
308
|
def version
|
|
309
|
+
quiet = options[:quiet]
|
|
355
310
|
on(KAMAL.app_hosts) do |host|
|
|
356
311
|
role = KAMAL.roles_on(host).first
|
|
357
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).current_running_version).strip
|
|
312
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).current_running_version).strip, quiet: quiet
|
|
358
313
|
end
|
|
359
314
|
end
|
|
360
315
|
|
|
361
316
|
private
|
|
317
|
+
def hosts_removing_all_roles
|
|
318
|
+
KAMAL.app_hosts.select { |host| KAMAL.roles_on(host).map(&:name).sort == KAMAL.config.host_roles(host.to_s).map(&:name).sort }
|
|
319
|
+
end
|
|
320
|
+
|
|
362
321
|
def using_version(new_version)
|
|
363
322
|
if new_version
|
|
364
323
|
begin
|
data/lib/kamal/cli/base.rb
CHANGED
|
@@ -5,6 +5,8 @@ module Kamal::Cli
|
|
|
5
5
|
class Base < Thor
|
|
6
6
|
include SSHKit::DSL
|
|
7
7
|
|
|
8
|
+
VERBOSITY = { verbose: :debug, quiet: :error }.freeze
|
|
9
|
+
|
|
8
10
|
def self.exit_on_failure?() true end
|
|
9
11
|
def self.dynamic_command_class() Kamal::Cli::Alias::Command end
|
|
10
12
|
|
|
@@ -43,11 +45,11 @@ module Kamal::Cli
|
|
|
43
45
|
KAMAL.tap do |commander|
|
|
44
46
|
if options[:verbose]
|
|
45
47
|
ENV["VERBOSE"] = "1" # For backtraces via cli/start
|
|
46
|
-
commander.verbosity = :
|
|
48
|
+
commander.verbosity = VERBOSITY[:verbose]
|
|
47
49
|
end
|
|
48
50
|
|
|
49
51
|
if options[:quiet]
|
|
50
|
-
commander.verbosity = :
|
|
52
|
+
commander.verbosity = VERBOSITY[:quiet]
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
commander.configure \
|
|
@@ -141,10 +143,21 @@ module Kamal::Cli
|
|
|
141
143
|
subcommand: subcommand
|
|
142
144
|
}.compact
|
|
143
145
|
|
|
144
|
-
|
|
146
|
+
hooks_output = KAMAL.config.hooks_output_for(hook)
|
|
147
|
+
|
|
148
|
+
# CLI flags override config: -q hides all, -v shows all
|
|
149
|
+
# Config setting :verbose forces output, :quiet forces silence
|
|
150
|
+
hook_verbosity = if KAMAL.verbosity == :info && hooks_output
|
|
151
|
+
VERBOSITY.fetch(hooks_output)
|
|
152
|
+
else
|
|
153
|
+
KAMAL.verbosity
|
|
154
|
+
end
|
|
155
|
+
|
|
145
156
|
with_env KAMAL.hook.env(**details, **extra_details) do
|
|
146
|
-
|
|
147
|
-
|
|
157
|
+
KAMAL.with_verbosity(hook_verbosity) do
|
|
158
|
+
run_locally do
|
|
159
|
+
execute *KAMAL.hook.run(hook)
|
|
160
|
+
end
|
|
148
161
|
end
|
|
149
162
|
rescue SSHKit::Command::Failed => e
|
|
150
163
|
raise HookError.new("Hook `#{hook}` failed:\n#{e.message}")
|
|
@@ -160,7 +173,7 @@ module Kamal::Cli
|
|
|
160
173
|
|
|
161
174
|
def pre_connect_if_required
|
|
162
175
|
if !KAMAL.connected?
|
|
163
|
-
run_hook "pre-connect"
|
|
176
|
+
run_hook "pre-connect", secrets: true unless options[:skip_hooks]
|
|
164
177
|
KAMAL.connected = true
|
|
165
178
|
end
|
|
166
179
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require "concurrent/atomic/count_down_latch"
|
|
2
|
+
|
|
3
|
+
class Kamal::Cli::Build::PortForwarding
|
|
4
|
+
attr_reader :hosts, :port, :ssh_options
|
|
5
|
+
|
|
6
|
+
def initialize(hosts, port, **ssh_options)
|
|
7
|
+
@hosts = hosts
|
|
8
|
+
@port = port
|
|
9
|
+
@ssh_options = ssh_options
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def forward
|
|
13
|
+
@done = false
|
|
14
|
+
forward_ports
|
|
15
|
+
|
|
16
|
+
yield
|
|
17
|
+
ensure
|
|
18
|
+
stop
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
def stop
|
|
23
|
+
@done = true
|
|
24
|
+
@threads.to_a.each(&:join)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def forward_ports
|
|
28
|
+
ready = Concurrent::CountDownLatch.new(hosts.size)
|
|
29
|
+
|
|
30
|
+
@threads = hosts.map do |host|
|
|
31
|
+
Thread.new do
|
|
32
|
+
begin
|
|
33
|
+
Net::SSH.start(host, ssh_options[:user], **ssh_options.except(:user)) do |ssh|
|
|
34
|
+
ssh.forward.remote(port, "localhost", port, "127.0.0.1") do |remote_port, bind_address|
|
|
35
|
+
if remote_port == :error
|
|
36
|
+
raise "Failed to establish port forward on #{host}"
|
|
37
|
+
else
|
|
38
|
+
ready.count_down
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
ssh.loop(0.1) do
|
|
43
|
+
if @done
|
|
44
|
+
ssh.forward.cancel_remote(port, "127.0.0.1")
|
|
45
|
+
break
|
|
46
|
+
else
|
|
47
|
+
true
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
rescue Exception => e
|
|
52
|
+
error "Error setting up port forwarding to #{host}: #{e.class}: #{e.message}"
|
|
53
|
+
error e.backtrace.join("\n")
|
|
54
|
+
|
|
55
|
+
raise
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
raise "Timed out waiting for port forwarding to be established" unless ready.wait(30)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def error(message)
|
|
64
|
+
SSHKit.config.output.error(message)
|
|
65
|
+
end
|
|
66
|
+
end
|