kamal 1.8.3 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/kamal/cli/accessory.rb +44 -20
- data/lib/kamal/cli/alias/command.rb +9 -0
- data/lib/kamal/cli/app/boot.rb +22 -16
- data/lib/kamal/cli/app.rb +40 -5
- data/lib/kamal/cli/base.rb +19 -51
- data/lib/kamal/cli/build.rb +12 -13
- 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 +54 -51
- data/lib/kamal/cli/proxy.rb +224 -0
- data/lib/kamal/cli/prune.rb +0 -1
- data/lib/kamal/cli/secrets.rb +36 -0
- data/lib/kamal/cli/server.rb +2 -3
- data/lib/kamal/cli/templates/deploy.yml +4 -21
- data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
- data/lib/kamal/cli/templates/secrets +16 -0
- data/lib/kamal/cli.rb +1 -0
- data/lib/kamal/commander/specifics.rb +3 -3
- data/lib/kamal/commander.rb +24 -8
- data/lib/kamal/commands/accessory.rb +7 -7
- data/lib/kamal/commands/app/assets.rb +8 -8
- data/lib/kamal/commands/app/proxy.rb +16 -0
- data/lib/kamal/commands/app.rb +7 -15
- data/lib/kamal/commands/auditor.rb +6 -3
- data/lib/kamal/commands/base.rb +8 -0
- data/lib/kamal/commands/builder/base.rb +26 -13
- data/lib/kamal/commands/builder/hybrid.rb +21 -0
- data/lib/kamal/commands/builder/local.rb +14 -0
- data/lib/kamal/commands/builder/remote.rb +63 -0
- data/lib/kamal/commands/builder.rb +13 -29
- data/lib/kamal/commands/docker.rb +4 -0
- data/lib/kamal/commands/hook.rb +5 -2
- data/lib/kamal/commands/lock.rb +2 -6
- data/lib/kamal/commands/proxy.rb +77 -0
- data/lib/kamal/commands/prune.rb +1 -9
- data/lib/kamal/commands/server.rb +11 -1
- data/lib/kamal/configuration/accessory.rb +14 -2
- data/lib/kamal/configuration/alias.rb +15 -0
- data/lib/kamal/configuration/builder.rb +52 -18
- data/lib/kamal/configuration/docs/alias.yml +26 -0
- data/lib/kamal/configuration/docs/builder.yml +26 -23
- data/lib/kamal/configuration/docs/configuration.yml +22 -16
- data/lib/kamal/configuration/docs/env.yml +10 -11
- data/lib/kamal/configuration/docs/proxy.yml +100 -0
- data/lib/kamal/configuration/docs/registry.yml +4 -2
- data/lib/kamal/configuration/docs/role.yml +3 -5
- data/lib/kamal/configuration/env/tag.rb +4 -3
- data/lib/kamal/configuration/env.rb +10 -17
- data/lib/kamal/configuration/proxy.rb +66 -0
- data/lib/kamal/configuration/registry.rb +3 -2
- data/lib/kamal/configuration/role.rb +63 -94
- data/lib/kamal/configuration/validator/alias.rb +15 -0
- data/lib/kamal/configuration/validator/builder.rb +4 -0
- data/lib/kamal/configuration/validator/proxy.rb +11 -0
- data/lib/kamal/configuration/validator.rb +42 -24
- data/lib/kamal/configuration.rb +91 -33
- data/lib/kamal/env_file.rb +4 -0
- data/lib/kamal/secrets/adapters/base.rb +18 -0
- data/lib/kamal/secrets/adapters/bitwarden.rb +64 -0
- data/lib/kamal/secrets/adapters/last_pass.rb +30 -0
- data/lib/kamal/secrets/adapters/one_password.rb +61 -0
- data/lib/kamal/secrets/adapters/test.rb +10 -0
- data/lib/kamal/secrets/adapters.rb +14 -0
- data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +32 -0
- data/lib/kamal/secrets.rb +37 -0
- data/lib/kamal/sshkit_with_ext.rb +1 -0
- data/lib/kamal/utils.rb +28 -0
- data/lib/kamal/version.rb +1 -1
- data/lib/kamal.rb +3 -1
- metadata +32 -23
- 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/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/templates/sample_hooks/{pre-traefik-reboot.sample → pre-proxy-reboot.sample} +0 -0
data/lib/kamal/cli/main.rb
CHANGED
@@ -9,10 +9,6 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
9
9
|
say "Ensure Docker is installed...", :magenta
|
10
10
|
invoke "kamal:cli:server:bootstrap", [], invoke_options
|
11
11
|
|
12
|
-
say "Evaluate and push env files...", :magenta
|
13
|
-
invoke "kamal:cli:main:envify", [], invoke_options
|
14
|
-
invoke "kamal:cli:env:push", [], invoke_options
|
15
|
-
|
16
12
|
invoke "kamal:cli:accessory:boot", [ "all" ], invoke_options
|
17
13
|
deploy
|
18
14
|
end
|
@@ -37,10 +33,10 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
37
33
|
end
|
38
34
|
|
39
35
|
with_lock do
|
40
|
-
run_hook "pre-deploy"
|
36
|
+
run_hook "pre-deploy", secrets: true
|
41
37
|
|
42
|
-
say "Ensure
|
43
|
-
invoke "kamal:cli:
|
38
|
+
say "Ensure kamal-proxy is running...", :magenta
|
39
|
+
invoke "kamal:cli:proxy:boot", [], invoke_options
|
44
40
|
|
45
41
|
say "Detect stale containers...", :magenta
|
46
42
|
invoke "kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)
|
@@ -52,10 +48,10 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
52
48
|
end
|
53
49
|
end
|
54
50
|
|
55
|
-
run_hook "post-deploy", runtime: runtime.round
|
51
|
+
run_hook "post-deploy", secrets: true, runtime: runtime.round
|
56
52
|
end
|
57
53
|
|
58
|
-
desc "redeploy", "Deploy app to servers without bootstrapping servers, starting
|
54
|
+
desc "redeploy", "Deploy app to servers without bootstrapping servers, starting kamal-proxy, pruning, and registry login"
|
59
55
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
60
56
|
def redeploy
|
61
57
|
runtime = print_runtime do
|
@@ -70,7 +66,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
70
66
|
end
|
71
67
|
|
72
68
|
with_lock do
|
73
|
-
run_hook "pre-deploy"
|
69
|
+
run_hook "pre-deploy", secrets: true
|
74
70
|
|
75
71
|
say "Detect stale containers...", :magenta
|
76
72
|
invoke "kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)
|
@@ -79,7 +75,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
79
75
|
end
|
80
76
|
end
|
81
77
|
|
82
|
-
run_hook "post-deploy", runtime: runtime.round
|
78
|
+
run_hook "post-deploy", secrets: true, runtime: runtime.round
|
83
79
|
end
|
84
80
|
|
85
81
|
desc "rollback [VERSION]", "Rollback app to VERSION"
|
@@ -93,7 +89,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
93
89
|
old_version = nil
|
94
90
|
|
95
91
|
if container_available?(version)
|
96
|
-
run_hook "pre-deploy"
|
92
|
+
run_hook "pre-deploy", secrets: true
|
97
93
|
|
98
94
|
invoke "kamal:cli:app:boot", [], invoke_options.merge(version: version)
|
99
95
|
rolled_back = true
|
@@ -103,12 +99,12 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
103
99
|
end
|
104
100
|
end
|
105
101
|
|
106
|
-
run_hook "post-deploy", runtime: runtime.round if rolled_back
|
102
|
+
run_hook "post-deploy", secrets: true, runtime: runtime.round if rolled_back
|
107
103
|
end
|
108
104
|
|
109
105
|
desc "details", "Show details about all containers"
|
110
106
|
def details
|
111
|
-
invoke "kamal:cli:
|
107
|
+
invoke "kamal:cli:proxy:details"
|
112
108
|
invoke "kamal:cli:app:details"
|
113
109
|
invoke "kamal:cli:accessory:details", [ "all" ]
|
114
110
|
end
|
@@ -127,7 +123,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
127
123
|
end
|
128
124
|
end
|
129
125
|
|
130
|
-
desc "docs", "Show Kamal
|
126
|
+
desc "docs [SECTION]", "Show Kamal configuration documentation"
|
131
127
|
def docs(section = nil)
|
132
128
|
case section
|
133
129
|
when NilClass
|
@@ -152,9 +148,10 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
152
148
|
puts "Created configuration file in config/deploy.yml"
|
153
149
|
end
|
154
150
|
|
155
|
-
unless (
|
156
|
-
FileUtils.
|
157
|
-
|
151
|
+
unless (secrets_file = Pathname.new(File.expand_path(".kamal/secrets"))).exist?
|
152
|
+
FileUtils.mkdir_p secrets_file.dirname
|
153
|
+
FileUtils.cp_r Pathname.new(File.expand_path("templates/secrets", __dir__)), secrets_file
|
154
|
+
puts "Created .kamal/secrets file"
|
158
155
|
end
|
159
156
|
|
160
157
|
unless (hooks_dir = Pathname.new(File.expand_path(".kamal/hooks"))).exist?
|
@@ -179,44 +176,50 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
179
176
|
end
|
180
177
|
end
|
181
178
|
|
182
|
-
desc "
|
183
|
-
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip .env file push"
|
184
|
-
def envify
|
185
|
-
if destination = options[:destination]
|
186
|
-
env_template_path = ".env.#{destination}.erb"
|
187
|
-
env_path = ".env.#{destination}"
|
188
|
-
else
|
189
|
-
env_template_path = ".env.erb"
|
190
|
-
env_path = ".env"
|
191
|
-
end
|
192
|
-
|
193
|
-
if Pathname.new(File.expand_path(env_template_path)).exist?
|
194
|
-
# Ensure existing env doesn't pollute template evaluation
|
195
|
-
content = with_original_env { ERB.new(File.read(env_template_path), trim_mode: "-").result }
|
196
|
-
File.write(env_path, content, perm: 0600)
|
197
|
-
|
198
|
-
unless options[:skip_push]
|
199
|
-
reload_env
|
200
|
-
invoke "kamal:cli:env:push", options
|
201
|
-
end
|
202
|
-
else
|
203
|
-
puts "Skipping envify (no #{env_template_path} exist)"
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
desc "remove", "Remove Traefik, app, accessories, and registry session from servers"
|
179
|
+
desc "remove", "Remove kamal-proxy, app, accessories, and registry session from servers"
|
208
180
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
209
181
|
def remove
|
210
182
|
confirming "This will remove all containers and images. Are you sure?" do
|
211
183
|
with_lock do
|
212
|
-
invoke "kamal:cli:traefik:remove", [], options.without(:confirmed)
|
213
184
|
invoke "kamal:cli:app:remove", [], options.without(:confirmed)
|
185
|
+
invoke "kamal:cli:proxy:remove", [], options.without(:confirmed)
|
214
186
|
invoke "kamal:cli:accessory:remove", [ "all" ], options
|
215
187
|
invoke "kamal:cli:registry:logout", [], options.without(:confirmed).merge(skip_local: true)
|
216
188
|
end
|
217
189
|
end
|
218
190
|
end
|
219
191
|
|
192
|
+
desc "upgrade", "Upgrade from Kamal 1.x to 2.0"
|
193
|
+
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
194
|
+
option :rolling, type: :boolean, default: false, desc: "Upgrade one host at a time"
|
195
|
+
def upgrade
|
196
|
+
confirming "This will replace Traefik with kamal-proxy and restart all accessories" do
|
197
|
+
with_lock do
|
198
|
+
if options[:rolling]
|
199
|
+
(KAMAL.hosts | KAMAL.accessory_hosts).each do |host|
|
200
|
+
KAMAL.with_specific_hosts(host) do
|
201
|
+
say "Upgrading #{host}...", :magenta
|
202
|
+
if KAMAL.hosts.include?(host)
|
203
|
+
invoke "kamal:cli:proxy:upgrade", [], options.merge(confirmed: true, rolling: false)
|
204
|
+
reset_invocation(Kamal::Cli::Proxy)
|
205
|
+
end
|
206
|
+
if KAMAL.accessory_hosts.include?(host)
|
207
|
+
invoke "kamal:cli:accessory:upgrade", [ "all" ], options.merge(confirmed: true, rolling: false)
|
208
|
+
reset_invocation(Kamal::Cli::Accessory)
|
209
|
+
end
|
210
|
+
say "Upgraded #{host}", :magenta
|
211
|
+
end
|
212
|
+
end
|
213
|
+
else
|
214
|
+
say "Upgrading all hosts...", :magenta
|
215
|
+
invoke "kamal:cli:proxy:upgrade", [], options.merge(confirmed: true)
|
216
|
+
invoke "kamal:cli:accessory:upgrade", [ "all" ], options.merge(confirmed: true)
|
217
|
+
say "Upgraded all hosts", :magenta
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
220
223
|
desc "version", "Show Kamal version"
|
221
224
|
def version
|
222
225
|
puts Kamal::VERSION
|
@@ -231,24 +234,24 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
231
234
|
desc "build", "Build application image"
|
232
235
|
subcommand "build", Kamal::Cli::Build
|
233
236
|
|
234
|
-
desc "env", "Manage environment files"
|
235
|
-
subcommand "env", Kamal::Cli::Env
|
236
|
-
|
237
237
|
desc "lock", "Manage the deploy lock"
|
238
238
|
subcommand "lock", Kamal::Cli::Lock
|
239
239
|
|
240
|
+
desc "proxy", "Manage kamal-proxy"
|
241
|
+
subcommand "proxy", Kamal::Cli::Proxy
|
242
|
+
|
240
243
|
desc "prune", "Prune old application images and containers"
|
241
244
|
subcommand "prune", Kamal::Cli::Prune
|
242
245
|
|
243
246
|
desc "registry", "Login and -out of the image registry"
|
244
247
|
subcommand "registry", Kamal::Cli::Registry
|
245
248
|
|
249
|
+
desc "secrets", "Helpers for extracting secrets"
|
250
|
+
subcommand "secrets", Kamal::Cli::Secrets
|
251
|
+
|
246
252
|
desc "server", "Bootstrap servers with curl and Docker"
|
247
253
|
subcommand "server", Kamal::Cli::Server
|
248
254
|
|
249
|
-
desc "traefik", "Manage Traefik load balancer"
|
250
|
-
subcommand "traefik", Kamal::Cli::Traefik
|
251
|
-
|
252
255
|
private
|
253
256
|
def container_available?(version)
|
254
257
|
begin
|
@@ -0,0 +1,224 @@
|
|
1
|
+
class Kamal::Cli::Proxy < Kamal::Cli::Base
|
2
|
+
desc "boot", "Boot proxy on servers"
|
3
|
+
def boot
|
4
|
+
with_lock do
|
5
|
+
on(KAMAL.hosts) do |host|
|
6
|
+
execute *KAMAL.docker.create_network
|
7
|
+
rescue SSHKit::Command::Failed => e
|
8
|
+
raise unless e.message.include?("already exists")
|
9
|
+
end
|
10
|
+
|
11
|
+
on(KAMAL.proxy_hosts) do |host|
|
12
|
+
execute *KAMAL.registry.login
|
13
|
+
|
14
|
+
version = capture_with_info(*KAMAL.proxy.version).strip.presence
|
15
|
+
|
16
|
+
if version && Kamal::Utils.older_version?(version, Kamal::Configuration::PROXY_MINIMUM_VERSION)
|
17
|
+
raise "kamal-proxy version #{version} is too old, please reboot to update to at least #{Kamal::Configuration::PROXY_MINIMUM_VERSION}"
|
18
|
+
end
|
19
|
+
execute *KAMAL.proxy.start_or_run
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "reboot", "Reboot proxy on servers (stop container, remove container, start new container)"
|
25
|
+
option :rolling, type: :boolean, default: false, desc: "Reboot proxy on hosts in sequence, rather than in parallel"
|
26
|
+
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
27
|
+
def reboot
|
28
|
+
confirming "This will cause a brief outage on each host. Are you sure?" do
|
29
|
+
with_lock do
|
30
|
+
host_groups = options[:rolling] ? KAMAL.proxy_hosts : [ KAMAL.proxy_hosts ]
|
31
|
+
host_groups.each do |hosts|
|
32
|
+
host_list = Array(hosts).join(",")
|
33
|
+
run_hook "pre-proxy-reboot", hosts: host_list
|
34
|
+
on(hosts) do |host|
|
35
|
+
execute *KAMAL.auditor.record("Rebooted proxy"), verbosity: :debug
|
36
|
+
execute *KAMAL.registry.login
|
37
|
+
|
38
|
+
"Stopping and removing Traefik on #{host}, if running..."
|
39
|
+
execute *KAMAL.proxy.cleanup_traefik
|
40
|
+
|
41
|
+
"Stopping and removing kamal-proxy on #{host}, if running..."
|
42
|
+
execute *KAMAL.proxy.stop, raise_on_non_zero_exit: false
|
43
|
+
execute *KAMAL.proxy.remove_container
|
44
|
+
|
45
|
+
execute *KAMAL.proxy.run
|
46
|
+
|
47
|
+
KAMAL.roles_on(host).select(&:running_proxy?).each do |role|
|
48
|
+
app = KAMAL.app(role: role, host: host)
|
49
|
+
|
50
|
+
version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
|
51
|
+
endpoint = capture_with_info(*app.container_id_for_version(version)).strip
|
52
|
+
|
53
|
+
if endpoint.present?
|
54
|
+
info "Deploying #{endpoint} for role `#{role}` on #{host}..."
|
55
|
+
execute *app.deploy(target: endpoint)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
run_hook "post-proxy-reboot", hosts: host_list
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
desc "upgrade", "Upgrade to kamal-proxy on servers (stop container, remove container, start new container, reboot app)", hide: true
|
66
|
+
option :rolling, type: :boolean, default: false, desc: "Reboot proxy on hosts in sequence, rather than in parallel"
|
67
|
+
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
68
|
+
def upgrade
|
69
|
+
invoke_options = { "version" => KAMAL.config.latest_tag }.merge(options)
|
70
|
+
|
71
|
+
confirming "This will cause a brief outage on each host. Are you sure?" do
|
72
|
+
host_groups = options[:rolling] ? KAMAL.hosts : [ KAMAL.hosts ]
|
73
|
+
host_groups.each do |hosts|
|
74
|
+
host_list = Array(hosts).join(",")
|
75
|
+
say "Upgrading proxy on #{host_list}...", :magenta
|
76
|
+
run_hook "pre-proxy-reboot", hosts: host_list
|
77
|
+
on(hosts) do |host|
|
78
|
+
execute *KAMAL.auditor.record("Rebooted proxy"), verbosity: :debug
|
79
|
+
execute *KAMAL.registry.login
|
80
|
+
|
81
|
+
"Stopping and removing Traefik on #{host}, if running..."
|
82
|
+
execute *KAMAL.proxy.cleanup_traefik
|
83
|
+
|
84
|
+
"Stopping and removing kamal-proxy on #{host}, if running..."
|
85
|
+
execute *KAMAL.proxy.stop, raise_on_non_zero_exit: false
|
86
|
+
execute *KAMAL.proxy.remove_container
|
87
|
+
execute *KAMAL.proxy.remove_image
|
88
|
+
end
|
89
|
+
|
90
|
+
KAMAL.with_specific_hosts(hosts) do
|
91
|
+
invoke "kamal:cli:proxy:boot", [], invoke_options
|
92
|
+
reset_invocation(Kamal::Cli::Proxy)
|
93
|
+
invoke "kamal:cli:app:boot", [], invoke_options
|
94
|
+
reset_invocation(Kamal::Cli::App)
|
95
|
+
invoke "kamal:cli:prune:all", [], invoke_options
|
96
|
+
reset_invocation(Kamal::Cli::Prune)
|
97
|
+
end
|
98
|
+
|
99
|
+
run_hook "post-proxy-reboot", hosts: host_list
|
100
|
+
say "Upgraded proxy on #{host_list}", :magenta
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
desc "start", "Start existing proxy container on servers"
|
106
|
+
def start
|
107
|
+
with_lock do
|
108
|
+
on(KAMAL.proxy_hosts) do |host|
|
109
|
+
execute *KAMAL.auditor.record("Started proxy"), verbosity: :debug
|
110
|
+
execute *KAMAL.proxy.start
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
desc "stop", "Stop existing proxy container on servers"
|
116
|
+
def stop
|
117
|
+
with_lock do
|
118
|
+
on(KAMAL.proxy_hosts) do |host|
|
119
|
+
execute *KAMAL.auditor.record("Stopped proxy"), verbosity: :debug
|
120
|
+
execute *KAMAL.proxy.stop, raise_on_non_zero_exit: false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
desc "restart", "Restart existing proxy container on servers"
|
126
|
+
def restart
|
127
|
+
with_lock do
|
128
|
+
stop
|
129
|
+
start
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
desc "details", "Show details about proxy container from servers"
|
134
|
+
def details
|
135
|
+
on(KAMAL.proxy_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.proxy.info), type: "Proxy" }
|
136
|
+
end
|
137
|
+
|
138
|
+
desc "logs", "Show log lines from proxy on servers"
|
139
|
+
option :since, aliases: "-s", desc: "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)"
|
140
|
+
option :lines, type: :numeric, aliases: "-n", desc: "Number of log lines to pull from each server"
|
141
|
+
option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
|
142
|
+
option :follow, aliases: "-f", desc: "Follow logs on primary server (or specific host set by --hosts)"
|
143
|
+
def logs
|
144
|
+
grep = options[:grep]
|
145
|
+
|
146
|
+
if options[:follow]
|
147
|
+
run_locally do
|
148
|
+
info "Following logs on #{KAMAL.primary_host}..."
|
149
|
+
info KAMAL.proxy.follow_logs(host: KAMAL.primary_host, grep: grep)
|
150
|
+
exec KAMAL.proxy.follow_logs(host: KAMAL.primary_host, grep: grep)
|
151
|
+
end
|
152
|
+
else
|
153
|
+
since = options[:since]
|
154
|
+
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
|
155
|
+
|
156
|
+
on(KAMAL.proxy_hosts) do |host|
|
157
|
+
puts_by_host host, capture(*KAMAL.proxy.logs(since: since, lines: lines, grep: grep)), type: "Proxy"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
desc "remove", "Remove proxy container and image from servers"
|
163
|
+
option :force, type: :boolean, default: false, desc: "Force removing proxy when apps are still installed"
|
164
|
+
def remove
|
165
|
+
with_lock do
|
166
|
+
if removal_allowed?(options[:force])
|
167
|
+
stop
|
168
|
+
remove_container
|
169
|
+
remove_image
|
170
|
+
remove_host_directory
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
desc "remove_container", "Remove proxy container from servers", hide: true
|
176
|
+
def remove_container
|
177
|
+
with_lock do
|
178
|
+
on(KAMAL.proxy_hosts) do
|
179
|
+
execute *KAMAL.auditor.record("Removed proxy container"), verbosity: :debug
|
180
|
+
execute *KAMAL.proxy.remove_container
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
desc "remove_image", "Remove proxy image from servers", hide: true
|
186
|
+
def remove_image
|
187
|
+
with_lock do
|
188
|
+
on(KAMAL.proxy_hosts) do
|
189
|
+
execute *KAMAL.auditor.record("Removed proxy image"), verbosity: :debug
|
190
|
+
execute *KAMAL.proxy.remove_image
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
desc "remove_host_directory", "Remove proxy directory from servers", hide: true
|
196
|
+
def remove_host_directory
|
197
|
+
with_lock do
|
198
|
+
on(KAMAL.proxy_hosts) do
|
199
|
+
execute *KAMAL.auditor.record("Removed #{KAMAL.config.proxy_directory}"), verbosity: :debug
|
200
|
+
execute *KAMAL.proxy.remove_host_directory, raise_on_non_zero_exit: false
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
private
|
206
|
+
def removal_allowed?(force)
|
207
|
+
on(KAMAL.proxy_hosts) do |host|
|
208
|
+
app_count = capture_with_info(*KAMAL.server.app_directory_count).chomp.to_i
|
209
|
+
raise "The are other applications installed on #{host}" if app_count > 0
|
210
|
+
end
|
211
|
+
|
212
|
+
true
|
213
|
+
rescue SSHKit::Runner::ExecuteError => e
|
214
|
+
raise unless e.message.include?("The are other applications installed on")
|
215
|
+
|
216
|
+
if force
|
217
|
+
say "Forcing, so removing the proxy, even though other apps are installed", :magenta
|
218
|
+
else
|
219
|
+
say "Not removing the proxy, as other apps are installed, ignore this check with kamal proxy remove --force", :magenta
|
220
|
+
end
|
221
|
+
|
222
|
+
force
|
223
|
+
end
|
224
|
+
end
|
data/lib/kamal/cli/prune.rb
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
class Kamal::Cli::Secrets < Kamal::Cli::Base
|
2
|
+
desc "fetch [SECRETS...]", "Fetch secrets from a vault"
|
3
|
+
option :adapter, type: :string, aliases: "-a", required: true, desc: "Which vault adapter to use"
|
4
|
+
option :account, type: :string, required: true, desc: "The account identifier or username"
|
5
|
+
option :from, type: :string, required: false, desc: "A vault or folder to fetch the secrets from"
|
6
|
+
option :inline, type: :boolean, required: false, hidden: true
|
7
|
+
def fetch(*secrets)
|
8
|
+
results = adapter(options[:adapter]).fetch(secrets, **options.slice(:account, :from).symbolize_keys)
|
9
|
+
|
10
|
+
return_or_puts JSON.dump(results).shellescape, inline: options[:inline]
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "extract", "Extract a single secret from the results of a fetch call"
|
14
|
+
option :inline, type: :boolean, required: false, hidden: true
|
15
|
+
def extract(name, secrets)
|
16
|
+
parsed_secrets = JSON.parse(secrets)
|
17
|
+
value = parsed_secrets[name] || parsed_secrets.find { |k, v| k.end_with?("/#{name}") }&.last
|
18
|
+
|
19
|
+
raise "Could not find secret #{name}" if value.nil?
|
20
|
+
|
21
|
+
return_or_puts value, inline: options[:inline]
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def adapter(adapter)
|
26
|
+
Kamal::Secrets::Adapters.lookup(adapter)
|
27
|
+
end
|
28
|
+
|
29
|
+
def return_or_puts(value, inline: nil)
|
30
|
+
if inline
|
31
|
+
value
|
32
|
+
else
|
33
|
+
puts value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/kamal/cli/server.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
class Kamal::Cli::Server < Kamal::Cli::Base
|
2
2
|
desc "exec", "Run a custom command on the server (use --help to show options)"
|
3
3
|
option :interactive, type: :boolean, aliases: "-i", default: false, desc: "Run the command interactively (use for console/bash)"
|
4
|
-
def exec(cmd)
|
4
|
+
def exec(*cmd)
|
5
|
+
cmd = Kamal::Utils.join_commands(cmd)
|
5
6
|
hosts = KAMAL.hosts | KAMAL.accessory_hosts
|
6
7
|
|
7
8
|
case
|
@@ -35,8 +36,6 @@ class Kamal::Cli::Server < Kamal::Cli::Base
|
|
35
36
|
missing << host
|
36
37
|
end
|
37
38
|
end
|
38
|
-
|
39
|
-
execute(*KAMAL.server.ensure_run_directory)
|
40
39
|
end
|
41
40
|
|
42
41
|
if missing.any?
|
@@ -18,6 +18,10 @@ registry:
|
|
18
18
|
password:
|
19
19
|
- KAMAL_REGISTRY_PASSWORD
|
20
20
|
|
21
|
+
# Configure builder setup.
|
22
|
+
builder:
|
23
|
+
arch: amd64
|
24
|
+
|
21
25
|
# Inject ENV variables into containers (secrets come from .env).
|
22
26
|
# Remember to run `kamal env push` after making changes!
|
23
27
|
# env:
|
@@ -30,16 +34,6 @@ registry:
|
|
30
34
|
# ssh:
|
31
35
|
# user: app
|
32
36
|
|
33
|
-
# Configure builder setup.
|
34
|
-
# builder:
|
35
|
-
# args:
|
36
|
-
# RUBY_VERSION: 3.2.0
|
37
|
-
# secrets:
|
38
|
-
# - GITHUB_TOKEN
|
39
|
-
# remote:
|
40
|
-
# arch: amd64
|
41
|
-
# host: ssh://app@192.168.0.1
|
42
|
-
|
43
37
|
# Use accessory services (secrets come from .env).
|
44
38
|
# accessories:
|
45
39
|
# db:
|
@@ -63,17 +57,6 @@ registry:
|
|
63
57
|
# directories:
|
64
58
|
# - data:/data
|
65
59
|
|
66
|
-
# Configure custom arguments for Traefik. Be sure to reboot traefik when you modify it.
|
67
|
-
# traefik:
|
68
|
-
# args:
|
69
|
-
# accesslog: true
|
70
|
-
# accesslog.format: json
|
71
|
-
|
72
|
-
# Configure a custom healthcheck (default is /up on port 3000)
|
73
|
-
# healthcheck:
|
74
|
-
# path: /healthz
|
75
|
-
# port: 4000
|
76
|
-
|
77
60
|
# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
|
78
61
|
# hitting 404 on in-flight requests. Combines all files from new and old
|
79
62
|
# version inside the asset_path.
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# WARNING: Avoid adding secrets directly to this file
|
2
|
+
# If you must, then add `.kamal/secrets*` to your .gitignore file
|
3
|
+
|
4
|
+
# Option 1: Read secrets from the environment
|
5
|
+
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
|
6
|
+
|
7
|
+
# Option 2: Read secrets via a command
|
8
|
+
# RAILS_MASTER_KEY=$(cat config/master.key)
|
9
|
+
|
10
|
+
# Option 3: Read secrets via kamal secrets helpers
|
11
|
+
# These will handle logging in and fetching the secrets in as few calls as possible
|
12
|
+
# There are adapters for 1Password, LastPass + Bitwarden
|
13
|
+
#
|
14
|
+
# SECRETS=$(kamal secrets fetch --adapter 1password --account my-account --from MyVault/MyItem KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY)
|
15
|
+
# KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD $SECRETS)
|
16
|
+
# RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY $SECRETS)
|
data/lib/kamal/cli.rb
CHANGED
@@ -18,12 +18,12 @@ class Kamal::Commander::Specifics
|
|
18
18
|
roles.select { |role| role.hosts.include?(host.to_s) }
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
config.
|
21
|
+
def proxy_hosts
|
22
|
+
config.proxy_hosts & specified_hosts
|
23
23
|
end
|
24
24
|
|
25
25
|
def accessory_hosts
|
26
|
-
|
26
|
+
config.accessories.flat_map(&:hosts) & specified_hosts
|
27
27
|
end
|
28
28
|
|
29
29
|
private
|
data/lib/kamal/commander.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require "active_support/core_ext/enumerable"
|
2
2
|
require "active_support/core_ext/module/delegation"
|
3
|
+
require "active_support/core_ext/object/blank"
|
3
4
|
|
4
5
|
class Kamal::Commander
|
5
6
|
attr_accessor :verbosity, :holding_lock, :connected
|
6
|
-
delegate :hosts, :roles, :primary_host, :primary_role, :roles_on, :
|
7
|
+
delegate :hosts, :roles, :primary_host, :primary_role, :roles_on, :proxy_hosts, :accessory_hosts, to: :specifics
|
7
8
|
|
8
9
|
def initialize
|
9
10
|
self.verbosity = :info
|
@@ -23,11 +24,19 @@ class Kamal::Commander
|
|
23
24
|
@config, @config_kwargs = nil, kwargs
|
24
25
|
end
|
25
26
|
|
27
|
+
def configured?
|
28
|
+
@config || @config_kwargs
|
29
|
+
end
|
30
|
+
|
26
31
|
attr_reader :specific_roles, :specific_hosts
|
27
32
|
|
28
33
|
def specific_primary!
|
29
34
|
@specifics = nil
|
30
|
-
|
35
|
+
if specific_roles.present?
|
36
|
+
self.specific_hosts = [ specific_roles.first.primary_host ]
|
37
|
+
else
|
38
|
+
self.specific_hosts = [ config.primary_host ]
|
39
|
+
end
|
31
40
|
end
|
32
41
|
|
33
42
|
def specific_roles=(role_names)
|
@@ -56,6 +65,13 @@ class Kamal::Commander
|
|
56
65
|
end
|
57
66
|
end
|
58
67
|
|
68
|
+
def with_specific_hosts(hosts)
|
69
|
+
original_hosts, self.specific_hosts = specific_hosts, hosts
|
70
|
+
yield
|
71
|
+
ensure
|
72
|
+
self.specific_hosts = original_hosts
|
73
|
+
end
|
74
|
+
|
59
75
|
def accessory_names
|
60
76
|
config.accessories&.collect(&:name) || []
|
61
77
|
end
|
@@ -85,10 +101,6 @@ class Kamal::Commander
|
|
85
101
|
@docker ||= Kamal::Commands::Docker.new(config)
|
86
102
|
end
|
87
103
|
|
88
|
-
def healthcheck
|
89
|
-
@healthcheck ||= Kamal::Commands::Healthcheck.new(config)
|
90
|
-
end
|
91
|
-
|
92
104
|
def hook
|
93
105
|
@hook ||= Kamal::Commands::Hook.new(config)
|
94
106
|
end
|
@@ -97,6 +109,10 @@ class Kamal::Commander
|
|
97
109
|
@lock ||= Kamal::Commands::Lock.new(config)
|
98
110
|
end
|
99
111
|
|
112
|
+
def proxy
|
113
|
+
@proxy ||= Kamal::Commands::Proxy.new(config)
|
114
|
+
end
|
115
|
+
|
100
116
|
def prune
|
101
117
|
@prune ||= Kamal::Commands::Prune.new(config)
|
102
118
|
end
|
@@ -109,8 +125,8 @@ class Kamal::Commander
|
|
109
125
|
@server ||= Kamal::Commands::Server.new(config)
|
110
126
|
end
|
111
127
|
|
112
|
-
def
|
113
|
-
|
128
|
+
def alias(name)
|
129
|
+
config.aliases[name]
|
114
130
|
end
|
115
131
|
|
116
132
|
|