kamal 2.0.0.alpha → 2.0.0.beta1
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 +44 -20
- data/lib/kamal/cli/app/boot.rb +22 -16
- data/lib/kamal/cli/app.rb +37 -3
- data/lib/kamal/cli/base.rb +9 -48
- data/lib/kamal/cli/build.rb +8 -3
- 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 +0 -2
- data/lib/kamal/cli/templates/deploy.yml +0 -11
- 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 +17 -9
- 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 +2 -6
- data/lib/kamal/commands/builder/hybrid.rb +1 -1
- data/lib/kamal/commands/builder/remote.rb +27 -4
- data/lib/kamal/commands/builder.rb +1 -1
- 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/builder.rb +9 -3
- data/lib/kamal/configuration/docs/builder.yml +20 -10
- data/lib/kamal/configuration/docs/configuration.yml +16 -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/builder.rb +2 -0
- data/lib/kamal/configuration/validator/proxy.rb +11 -0
- data/lib/kamal/configuration/validator.rb +3 -1
- data/lib/kamal/configuration.rb +90 -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 +12 -0
- data/lib/kamal/version.rb +1 -1
- data/lib/kamal.rb +3 -1
- metadata +23 -16
- 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/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
@@ -57,17 +57,6 @@ builder:
|
|
57
57
|
# directories:
|
58
58
|
# - data:/data
|
59
59
|
|
60
|
-
# Configure custom arguments for Traefik. Be sure to reboot traefik when you modify it.
|
61
|
-
# traefik:
|
62
|
-
# args:
|
63
|
-
# accesslog: true
|
64
|
-
# accesslog.format: json
|
65
|
-
|
66
|
-
# Configure a custom healthcheck (default is /up on port 3000)
|
67
|
-
# healthcheck:
|
68
|
-
# path: /healthz
|
69
|
-
# port: 4000
|
70
|
-
|
71
60
|
# Bridge fingerprinted assets, like JS and CSS, between versions to avoid
|
72
61
|
# hitting 404 on in-flight requests. Combines all files from new and old
|
73
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,6 +24,10 @@ 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!
|
@@ -60,6 +65,13 @@ class Kamal::Commander
|
|
60
65
|
end
|
61
66
|
end
|
62
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
|
+
|
63
75
|
def accessory_names
|
64
76
|
config.accessories&.collect(&:name) || []
|
65
77
|
end
|
@@ -89,10 +101,6 @@ class Kamal::Commander
|
|
89
101
|
@docker ||= Kamal::Commands::Docker.new(config)
|
90
102
|
end
|
91
103
|
|
92
|
-
def healthcheck
|
93
|
-
@healthcheck ||= Kamal::Commands::Healthcheck.new(config)
|
94
|
-
end
|
95
|
-
|
96
104
|
def hook
|
97
105
|
@hook ||= Kamal::Commands::Hook.new(config)
|
98
106
|
end
|
@@ -101,6 +109,10 @@ class Kamal::Commander
|
|
101
109
|
@lock ||= Kamal::Commands::Lock.new(config)
|
102
110
|
end
|
103
111
|
|
112
|
+
def proxy
|
113
|
+
@proxy ||= Kamal::Commands::Proxy.new(config)
|
114
|
+
end
|
115
|
+
|
104
116
|
def prune
|
105
117
|
@prune ||= Kamal::Commands::Prune.new(config)
|
106
118
|
end
|
@@ -113,10 +125,6 @@ class Kamal::Commander
|
|
113
125
|
@server ||= Kamal::Commands::Server.new(config)
|
114
126
|
end
|
115
127
|
|
116
|
-
def traefik
|
117
|
-
@traefik ||= Kamal::Commands::Traefik.new(config)
|
118
|
-
end
|
119
|
-
|
120
128
|
def alias(name)
|
121
129
|
config.aliases[name]
|
122
130
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
class Kamal::Commands::Accessory < Kamal::Commands::Base
|
2
2
|
attr_reader :accessory_config
|
3
3
|
delegate :service_name, :image, :hosts, :port, :files, :directories, :cmd,
|
4
|
-
:publish_args, :env_args, :volume_args, :label_args, :option_args,
|
4
|
+
:publish_args, :env_args, :volume_args, :label_args, :option_args,
|
5
|
+
:secrets_io, :secrets_path, :env_directory,
|
6
|
+
to: :accessory_config
|
5
7
|
|
6
8
|
def initialize(config, name:)
|
7
9
|
super(config)
|
@@ -13,6 +15,7 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
|
|
13
15
|
"--name", service_name,
|
14
16
|
"--detach",
|
15
17
|
"--restart", "unless-stopped",
|
18
|
+
"--network", "kamal",
|
16
19
|
*config.logging_args,
|
17
20
|
*publish_args,
|
18
21
|
*env_args,
|
@@ -61,6 +64,7 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
|
|
61
64
|
docker :run,
|
62
65
|
("-it" if interactive),
|
63
66
|
"--rm",
|
67
|
+
"--network", "kamal",
|
64
68
|
*env_args,
|
65
69
|
*volume_args,
|
66
70
|
image,
|
@@ -98,12 +102,8 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
|
|
98
102
|
docker :image, :rm, "--force", image
|
99
103
|
end
|
100
104
|
|
101
|
-
def
|
102
|
-
make_directory
|
103
|
-
end
|
104
|
-
|
105
|
-
def remove_env_file
|
106
|
-
[ :rm, "-f", accessory_config.env.secrets_file ]
|
105
|
+
def ensure_env_directory
|
106
|
+
make_directory env_directory
|
107
107
|
end
|
108
108
|
|
109
109
|
private
|