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
data/lib/kamal/cli/build.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require "uri"
|
|
2
|
-
|
|
3
1
|
class Kamal::Cli::Build < Kamal::Cli::Base
|
|
4
2
|
class BuildError < StandardError; end
|
|
5
3
|
|
|
@@ -11,15 +9,16 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
11
9
|
|
|
12
10
|
desc "push", "Build and push app image to registry"
|
|
13
11
|
option :output, type: :string, default: "registry", banner: "export_type", desc: "Exported type for the build result, and may be any exported type supported by 'buildx --output'."
|
|
12
|
+
option :no_cache, type: :boolean, default: false, desc: "Build without using Docker's build cache"
|
|
14
13
|
def push
|
|
15
14
|
cli = self
|
|
16
15
|
|
|
17
|
-
# Ensure pre-connect hooks run before the build, they may needed for a remote builder
|
|
16
|
+
# Ensure pre-connect hooks run before the build, they may be needed for a remote builder
|
|
18
17
|
# or the pre-build hooks.
|
|
19
18
|
pre_connect_if_required
|
|
20
19
|
|
|
21
20
|
ensure_docker_installed
|
|
22
|
-
login_to_registry_locally
|
|
21
|
+
login_to_registry_locally if KAMAL.builder.login_to_registry_locally?
|
|
23
22
|
|
|
24
23
|
run_hook "pre-build"
|
|
25
24
|
|
|
@@ -37,29 +36,31 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
37
36
|
say "Building with uncommitted changes:\n #{uncommitted_changes}", :yellow
|
|
38
37
|
end
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
forward_local_registry_port_for_remote_builder do
|
|
40
|
+
with_env(KAMAL.config.builder.secrets) do
|
|
41
|
+
run_locally do
|
|
42
|
+
begin
|
|
43
|
+
execute *KAMAL.builder.inspect_builder
|
|
44
|
+
rescue SSHKit::Command::Failed => e
|
|
45
|
+
if e.message =~ /(context not found|no builder|no compatible builder|does not exist)/
|
|
46
|
+
warn "Missing compatible builder, so creating a new one first"
|
|
47
|
+
begin
|
|
48
|
+
cli.remove
|
|
49
|
+
rescue SSHKit::Command::Failed
|
|
50
|
+
raise unless e.message =~ /(context not found|no builder|does not exist)/
|
|
51
|
+
end
|
|
52
|
+
cli.create
|
|
53
|
+
else
|
|
54
|
+
raise
|
|
51
55
|
end
|
|
52
|
-
cli.create
|
|
53
|
-
else
|
|
54
|
-
raise
|
|
55
56
|
end
|
|
56
|
-
end
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
# Get the command here to ensure the Dir.chdir doesn't interfere with it
|
|
59
|
+
push = KAMAL.builder.push(cli.options[:output], no_cache: cli.options[:no_cache])
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
KAMAL.with_verbosity(:debug) do
|
|
62
|
+
Dir.chdir(KAMAL.config.builder.build_directory) { execute *push, env: KAMAL.builder.push_env }
|
|
63
|
+
end
|
|
63
64
|
end
|
|
64
65
|
end
|
|
65
66
|
end
|
|
@@ -67,16 +68,18 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
67
68
|
|
|
68
69
|
desc "pull", "Pull app image from registry onto servers"
|
|
69
70
|
def pull
|
|
70
|
-
login_to_registry_remotely
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
login_to_registry_remotely unless KAMAL.registry.local?
|
|
72
|
+
|
|
73
|
+
forward_local_registry_port(KAMAL.hosts, **KAMAL.config.ssh.options) do
|
|
74
|
+
if (first_hosts = mirror_hosts).any?
|
|
75
|
+
# Pull on a single host per mirror first to seed them
|
|
76
|
+
say "Pulling image on #{first_hosts.join(", ")} to seed the #{"mirror".pluralize(first_hosts.count)}...", :magenta
|
|
77
|
+
pull_on_hosts(first_hosts)
|
|
78
|
+
say "Pulling image on remaining hosts...", :magenta
|
|
79
|
+
pull_on_hosts(KAMAL.app_hosts - first_hosts)
|
|
80
|
+
else
|
|
81
|
+
pull_on_hosts(KAMAL.app_hosts)
|
|
82
|
+
end
|
|
80
83
|
end
|
|
81
84
|
end
|
|
82
85
|
|
|
@@ -119,6 +122,7 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
119
122
|
|
|
120
123
|
desc "dev", "Build using the working directory, tag it as dirty, and push to local image store."
|
|
121
124
|
option :output, type: :string, default: "docker", banner: "export_type", desc: "Exported type for the build result, and may be any exported type supported by 'buildx --output'."
|
|
125
|
+
option :no_cache, type: :boolean, default: false, desc: "Build without using Docker's build cache"
|
|
122
126
|
def dev
|
|
123
127
|
cli = self
|
|
124
128
|
|
|
@@ -144,7 +148,7 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
144
148
|
|
|
145
149
|
with_env(KAMAL.config.builder.secrets) do
|
|
146
150
|
run_locally do
|
|
147
|
-
build = KAMAL.builder.push(cli.options[:output], tag_as_dirty: true)
|
|
151
|
+
build = KAMAL.builder.push(cli.options[:output], tag_as_dirty: true, no_cache: cli.options[:no_cache])
|
|
148
152
|
KAMAL.with_verbosity(:debug) do
|
|
149
153
|
execute(*build)
|
|
150
154
|
end
|
|
@@ -192,7 +196,11 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
192
196
|
|
|
193
197
|
def login_to_registry_locally
|
|
194
198
|
run_locally do
|
|
195
|
-
|
|
199
|
+
if KAMAL.registry.local?
|
|
200
|
+
execute *KAMAL.registry.setup
|
|
201
|
+
else
|
|
202
|
+
execute *KAMAL.registry.login
|
|
203
|
+
end
|
|
196
204
|
end
|
|
197
205
|
end
|
|
198
206
|
|
|
@@ -201,4 +209,31 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
|
201
209
|
execute *KAMAL.registry.login
|
|
202
210
|
end
|
|
203
211
|
end
|
|
212
|
+
|
|
213
|
+
def forward_local_registry_port_for_remote_builder(&block)
|
|
214
|
+
if KAMAL.builder.remote?
|
|
215
|
+
remote_uri = URI(KAMAL.config.builder.remote)
|
|
216
|
+
forward_local_registry_port([ remote_uri.host ], **remote_builder_ssh_options(remote_uri), &block)
|
|
217
|
+
else
|
|
218
|
+
yield
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def forward_local_registry_port(hosts, **ssh_options, &block)
|
|
223
|
+
if KAMAL.config.registry.local?
|
|
224
|
+
say "Setting up local registry port forwarding to #{hosts.join(', ')}..."
|
|
225
|
+
PortForwarding.new(hosts, KAMAL.config.registry.local_port, **ssh_options).forward(&block)
|
|
226
|
+
else
|
|
227
|
+
yield
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def remote_builder_ssh_options(remote_uri)
|
|
232
|
+
{ user: remote_uri.user,
|
|
233
|
+
port: remote_uri.port,
|
|
234
|
+
keepalive: KAMAL.config.ssh.options[:keepalive],
|
|
235
|
+
keepalive_interval: KAMAL.config.ssh.options[:keepalive_interval],
|
|
236
|
+
logger: KAMAL.config.ssh.options[:logger]
|
|
237
|
+
}.compact
|
|
238
|
+
end
|
|
204
239
|
end
|
data/lib/kamal/cli/main.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
class Kamal::Cli::Main < Kamal::Cli::Base
|
|
2
2
|
desc "setup", "Setup all accessories, push the env, and deploy app to servers"
|
|
3
3
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
|
4
|
+
option :no_cache, type: :boolean, default: false, desc: "Build without using Docker's build cache"
|
|
4
5
|
def setup
|
|
5
6
|
print_runtime do
|
|
6
7
|
with_lock do
|
|
@@ -16,6 +17,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
|
16
17
|
|
|
17
18
|
desc "deploy", "Deploy app to servers"
|
|
18
19
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
|
20
|
+
option :no_cache, type: :boolean, default: false, desc: "Build without using Docker's build cache"
|
|
19
21
|
def deploy(boot_accessories: false)
|
|
20
22
|
runtime = print_runtime do
|
|
21
23
|
invoke_options = deploy_options
|
|
@@ -51,6 +53,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
|
51
53
|
|
|
52
54
|
desc "redeploy", "Deploy app to servers without bootstrapping servers, starting kamal-proxy and pruning"
|
|
53
55
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
|
56
|
+
option :no_cache, type: :boolean, default: false, desc: "Build without using Docker's build cache"
|
|
54
57
|
def redeploy
|
|
55
58
|
runtime = print_runtime do
|
|
56
59
|
invoke_options = deploy_options
|
|
@@ -109,8 +112,9 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
|
109
112
|
|
|
110
113
|
desc "audit", "Show audit log from servers"
|
|
111
114
|
def audit
|
|
115
|
+
quiet = options[:quiet]
|
|
112
116
|
on(KAMAL.hosts) do |host|
|
|
113
|
-
puts_by_host host, capture_with_info(*KAMAL.auditor.reveal)
|
|
117
|
+
puts_by_host host, capture_with_info(*KAMAL.auditor.reveal), quiet: quiet
|
|
114
118
|
end
|
|
115
119
|
end
|
|
116
120
|
|
|
@@ -182,7 +186,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
|
182
186
|
invoke "kamal:cli:app:remove", [], options.without(:confirmed)
|
|
183
187
|
invoke "kamal:cli:proxy:remove", [], options.without(:confirmed)
|
|
184
188
|
invoke "kamal:cli:accessory:remove", [ "all" ], options
|
|
185
|
-
invoke "kamal:cli:registry:
|
|
189
|
+
invoke "kamal:cli:registry:remove", [], options.without(:confirmed).merge(skip_local: true)
|
|
186
190
|
end
|
|
187
191
|
end
|
|
188
192
|
end
|
|
@@ -272,6 +276,8 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
|
272
276
|
end
|
|
273
277
|
|
|
274
278
|
def deploy_options
|
|
275
|
-
|
|
279
|
+
base_options = options.without("skip_push")
|
|
280
|
+
base_options = base_options.except("no_cache") unless base_options["no_cache"]
|
|
281
|
+
{ "version" => KAMAL.config.version }.merge(base_options)
|
|
276
282
|
end
|
|
277
283
|
end
|
data/lib/kamal/cli/proxy.rb
CHANGED
|
@@ -11,13 +11,13 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
11
11
|
on(KAMAL.proxy_hosts) do |host|
|
|
12
12
|
execute *KAMAL.registry.login
|
|
13
13
|
|
|
14
|
-
version = capture_with_info(*KAMAL.proxy.version).strip.presence
|
|
14
|
+
version = capture_with_info(*KAMAL.proxy(host).version).strip.presence
|
|
15
15
|
|
|
16
|
-
if version && Kamal::Utils.older_version?(version, Kamal::Configuration::Proxy::
|
|
17
|
-
raise "kamal-proxy version #{version} is too old, run `kamal proxy reboot` in order to update to at least #{Kamal::Configuration::Proxy::
|
|
16
|
+
if version && Kamal::Utils.older_version?(version, Kamal::Configuration::Proxy::Run::MINIMUM_VERSION)
|
|
17
|
+
raise "kamal-proxy version #{version} is too old, run `kamal proxy reboot` in order to update to at least #{Kamal::Configuration::Proxy::Run::MINIMUM_VERSION}"
|
|
18
18
|
end
|
|
19
|
-
execute *KAMAL.proxy.ensure_apps_config_directory
|
|
20
|
-
execute *KAMAL.proxy.start_or_run
|
|
19
|
+
execute *KAMAL.proxy(host).ensure_apps_config_directory
|
|
20
|
+
execute *KAMAL.proxy(host).start_or_run
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
|
@@ -25,9 +25,9 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
25
25
|
desc "boot_config <set|get|reset>", "Manage kamal-proxy boot configuration"
|
|
26
26
|
option :publish, type: :boolean, default: true, desc: "Publish the proxy ports on the host"
|
|
27
27
|
option :publish_host_ip, type: :string, repeatable: true, default: nil, desc: "Host IP address to bind HTTP/HTTPS traffic to. Defaults to all interfaces"
|
|
28
|
-
option :http_port, type: :numeric, default: Kamal::Configuration::Proxy::
|
|
29
|
-
option :https_port, type: :numeric, default: Kamal::Configuration::Proxy::
|
|
30
|
-
option :log_max_size, type: :string, default: Kamal::Configuration::Proxy::
|
|
28
|
+
option :http_port, type: :numeric, default: Kamal::Configuration::Proxy::Run::DEFAULT_HTTP_PORT, desc: "HTTP port to publish on the host"
|
|
29
|
+
option :https_port, type: :numeric, default: Kamal::Configuration::Proxy::Run::DEFAULT_HTTPS_PORT, desc: "HTTPS port to publish on the host"
|
|
30
|
+
option :log_max_size, type: :string, default: Kamal::Configuration::Proxy::Run::DEFAULT_LOG_MAX_SIZE, desc: "Max size of proxy logs"
|
|
31
31
|
option :registry, type: :string, default: nil, desc: "Registry to use for the proxy image"
|
|
32
32
|
option :repository, type: :string, default: nil, desc: "Repository for the proxy image"
|
|
33
33
|
option :image_version, type: :string, default: nil, desc: "Version of the proxy to run"
|
|
@@ -35,6 +35,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
35
35
|
option :debug, type: :boolean, default: false, desc: "Whether to run the proxy in debug mode"
|
|
36
36
|
option :docker_options, type: :array, default: [], desc: "Docker options to pass to the proxy container", banner: "option=value option2=value2"
|
|
37
37
|
def boot_config(subcommand)
|
|
38
|
+
say "The proxy boot_config command is deprecated - set the config in the deploy YAML at proxy/run instead", :yellow
|
|
38
39
|
proxy_boot_config = KAMAL.config.proxy_boot
|
|
39
40
|
|
|
40
41
|
case subcommand
|
|
@@ -58,42 +59,44 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
58
59
|
run_command = "kamal-proxy run #{Kamal::Utils.optionize(run_command_options).join(" ")}" if run_command_options.any?
|
|
59
60
|
|
|
60
61
|
on(KAMAL.proxy_hosts) do |host|
|
|
61
|
-
|
|
62
|
+
proxy = KAMAL.proxy(host)
|
|
63
|
+
execute(*proxy.ensure_proxy_directory)
|
|
62
64
|
if boot_options != proxy_boot_config.default_boot_options
|
|
63
65
|
upload! StringIO.new(boot_options.join(" ")), proxy_boot_config.options_file
|
|
64
66
|
else
|
|
65
|
-
execute *
|
|
67
|
+
execute *proxy.reset_boot_options, raise_on_non_zero_exit: false
|
|
66
68
|
end
|
|
67
69
|
|
|
68
70
|
if image != proxy_boot_config.image_default
|
|
69
71
|
upload! StringIO.new(image), proxy_boot_config.image_file
|
|
70
72
|
else
|
|
71
|
-
execute *
|
|
73
|
+
execute *proxy.reset_image, raise_on_non_zero_exit: false
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
if image_version
|
|
75
77
|
upload! StringIO.new(image_version), proxy_boot_config.image_version_file
|
|
76
78
|
else
|
|
77
|
-
execute *
|
|
79
|
+
execute *proxy.reset_image_version, raise_on_non_zero_exit: false
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
if run_command
|
|
81
83
|
upload! StringIO.new(run_command), proxy_boot_config.run_command_file
|
|
82
84
|
else
|
|
83
|
-
execute *
|
|
85
|
+
execute *proxy.reset_run_command, raise_on_non_zero_exit: false
|
|
84
86
|
end
|
|
85
87
|
end
|
|
86
88
|
when "get"
|
|
87
89
|
|
|
88
90
|
on(KAMAL.proxy_hosts) do |host|
|
|
89
|
-
puts "Host #{host}: #{capture_with_info(*KAMAL.proxy.boot_config)}"
|
|
91
|
+
puts "Host #{host}: #{capture_with_info(*KAMAL.proxy(host).boot_config)}"
|
|
90
92
|
end
|
|
91
93
|
when "reset"
|
|
92
94
|
on(KAMAL.proxy_hosts) do |host|
|
|
93
|
-
|
|
94
|
-
execute *
|
|
95
|
-
execute *
|
|
96
|
-
execute *
|
|
95
|
+
proxy = KAMAL.proxy(host)
|
|
96
|
+
execute *proxy.reset_boot_options, raise_on_non_zero_exit: false
|
|
97
|
+
execute *proxy.reset_image, raise_on_non_zero_exit: false
|
|
98
|
+
execute *proxy.reset_image_version, raise_on_non_zero_exit: false
|
|
99
|
+
execute *proxy.reset_run_command, raise_on_non_zero_exit: false
|
|
97
100
|
end
|
|
98
101
|
else
|
|
99
102
|
raise ArgumentError, "Unknown boot_config subcommand #{subcommand}"
|
|
@@ -111,15 +114,16 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
111
114
|
host_list = Array(hosts).join(",")
|
|
112
115
|
run_hook "pre-proxy-reboot", hosts: host_list
|
|
113
116
|
on(hosts) do |host|
|
|
117
|
+
proxy = KAMAL.proxy(host)
|
|
114
118
|
execute *KAMAL.auditor.record("Rebooted proxy"), verbosity: :debug
|
|
115
119
|
execute *KAMAL.registry.login
|
|
116
120
|
|
|
117
121
|
"Stopping and removing kamal-proxy on #{host}, if running..."
|
|
118
|
-
execute *
|
|
119
|
-
execute *
|
|
120
|
-
execute *
|
|
122
|
+
execute *proxy.stop, raise_on_non_zero_exit: false
|
|
123
|
+
execute *proxy.remove_container
|
|
124
|
+
execute *proxy.ensure_apps_config_directory
|
|
121
125
|
|
|
122
|
-
execute *
|
|
126
|
+
execute *proxy.run
|
|
123
127
|
end
|
|
124
128
|
run_hook "post-proxy-reboot", hosts: host_list
|
|
125
129
|
end
|
|
@@ -140,16 +144,17 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
140
144
|
say "Upgrading proxy on #{host_list}...", :magenta
|
|
141
145
|
run_hook "pre-proxy-reboot", hosts: host_list
|
|
142
146
|
on(hosts) do |host|
|
|
147
|
+
proxy = KAMAL.proxy(host)
|
|
143
148
|
execute *KAMAL.auditor.record("Rebooted proxy"), verbosity: :debug
|
|
144
149
|
execute *KAMAL.registry.login
|
|
145
150
|
|
|
146
151
|
"Stopping and removing Traefik on #{host}, if running..."
|
|
147
|
-
execute *
|
|
152
|
+
execute *proxy.cleanup_traefik
|
|
148
153
|
|
|
149
154
|
"Stopping and removing kamal-proxy on #{host}, if running..."
|
|
150
|
-
execute *
|
|
151
|
-
execute *
|
|
152
|
-
execute *
|
|
155
|
+
execute *proxy.stop, raise_on_non_zero_exit: false
|
|
156
|
+
execute *proxy.remove_container
|
|
157
|
+
execute *proxy.remove_image
|
|
153
158
|
end
|
|
154
159
|
|
|
155
160
|
KAMAL.with_specific_hosts(hosts) do
|
|
@@ -172,7 +177,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
172
177
|
with_lock do
|
|
173
178
|
on(KAMAL.proxy_hosts) do |host|
|
|
174
179
|
execute *KAMAL.auditor.record("Started proxy"), verbosity: :debug
|
|
175
|
-
execute *KAMAL.proxy.start
|
|
180
|
+
execute *KAMAL.proxy(host).start
|
|
176
181
|
end
|
|
177
182
|
end
|
|
178
183
|
end
|
|
@@ -182,7 +187,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
182
187
|
with_lock do
|
|
183
188
|
on(KAMAL.proxy_hosts) do |host|
|
|
184
189
|
execute *KAMAL.auditor.record("Stopped proxy"), verbosity: :debug
|
|
185
|
-
execute *KAMAL.proxy.stop, raise_on_non_zero_exit: false
|
|
190
|
+
execute *KAMAL.proxy(host).stop, raise_on_non_zero_exit: false
|
|
186
191
|
end
|
|
187
192
|
end
|
|
188
193
|
end
|
|
@@ -197,7 +202,8 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
197
202
|
|
|
198
203
|
desc "details", "Show details about proxy container from servers"
|
|
199
204
|
def details
|
|
200
|
-
|
|
205
|
+
quiet = options[:quiet]
|
|
206
|
+
on(KAMAL.proxy_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.proxy(host).info), type: "Proxy", quiet: quiet }
|
|
201
207
|
end
|
|
202
208
|
|
|
203
209
|
desc "logs", "Show log lines from proxy on servers"
|
|
@@ -212,16 +218,17 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
212
218
|
|
|
213
219
|
if options[:follow]
|
|
214
220
|
run_locally do
|
|
221
|
+
proxy = KAMAL.proxy(KAMAL.primary_host)
|
|
215
222
|
info "Following logs on #{KAMAL.primary_host}..."
|
|
216
|
-
info
|
|
217
|
-
exec
|
|
223
|
+
info proxy.follow_logs(host: KAMAL.primary_host, timestamps: timestamps, grep: grep)
|
|
224
|
+
exec proxy.follow_logs(host: KAMAL.primary_host, timestamps: timestamps, grep: grep)
|
|
218
225
|
end
|
|
219
226
|
else
|
|
220
227
|
since = options[:since]
|
|
221
228
|
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
|
|
222
229
|
|
|
223
230
|
on(KAMAL.proxy_hosts) do |host|
|
|
224
|
-
puts_by_host host, capture(*KAMAL.proxy.logs(timestamps: timestamps, since: since, lines: lines, grep: grep)), type: "Proxy"
|
|
231
|
+
puts_by_host host, capture(*KAMAL.proxy(host).logs(timestamps: timestamps, since: since, lines: lines, grep: grep)), type: "Proxy"
|
|
225
232
|
end
|
|
226
233
|
end
|
|
227
234
|
end
|
|
@@ -244,7 +251,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
244
251
|
with_lock do
|
|
245
252
|
on(KAMAL.proxy_hosts) do
|
|
246
253
|
execute *KAMAL.auditor.record("Removed proxy container"), verbosity: :debug
|
|
247
|
-
execute *KAMAL.proxy.remove_container
|
|
254
|
+
execute *KAMAL.proxy(host).remove_container
|
|
248
255
|
end
|
|
249
256
|
end
|
|
250
257
|
end
|
|
@@ -254,7 +261,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
254
261
|
with_lock do
|
|
255
262
|
on(KAMAL.proxy_hosts) do
|
|
256
263
|
execute *KAMAL.auditor.record("Removed proxy image"), verbosity: :debug
|
|
257
|
-
execute *KAMAL.proxy.remove_image
|
|
264
|
+
execute *KAMAL.proxy(host).remove_image
|
|
258
265
|
end
|
|
259
266
|
end
|
|
260
267
|
end
|
|
@@ -263,7 +270,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
|
263
270
|
def remove_proxy_directory
|
|
264
271
|
with_lock do
|
|
265
272
|
on(KAMAL.proxy_hosts) do
|
|
266
|
-
execute *KAMAL.proxy.remove_proxy_directory, raise_on_non_zero_exit: false
|
|
273
|
+
execute *KAMAL.proxy(host).remove_proxy_directory, raise_on_non_zero_exit: false
|
|
267
274
|
end
|
|
268
275
|
end
|
|
269
276
|
end
|
data/lib/kamal/cli/registry.rb
CHANGED
|
@@ -1,19 +1,49 @@
|
|
|
1
1
|
class Kamal::Cli::Registry < Kamal::Cli::Base
|
|
2
|
-
desc "
|
|
2
|
+
desc "setup", "Setup local registry or log in to remote registry locally and remotely"
|
|
3
3
|
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
|
4
4
|
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
|
5
|
-
def
|
|
5
|
+
def setup
|
|
6
6
|
ensure_docker_installed unless options[:skip_local]
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
if KAMAL.registry.local?
|
|
9
|
+
run_locally { execute *KAMAL.registry.setup } unless options[:skip_local]
|
|
10
|
+
else
|
|
11
|
+
run_locally { execute *KAMAL.registry.login } unless options[:skip_local]
|
|
12
|
+
on(KAMAL.hosts) { execute *KAMAL.registry.login } unless options[:skip_remote]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
desc "remove", "Remove local registry or log out of remote registry locally and remotely"
|
|
17
|
+
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
|
18
|
+
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
|
19
|
+
def remove
|
|
20
|
+
if KAMAL.registry.local?
|
|
21
|
+
run_locally { execute *KAMAL.registry.remove, raise_on_non_zero_exit: false } unless options[:skip_local]
|
|
22
|
+
else
|
|
23
|
+
run_locally { execute *KAMAL.registry.logout } unless options[:skip_local]
|
|
24
|
+
on(KAMAL.hosts) { execute *KAMAL.registry.logout } unless options[:skip_remote]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc "login", "Log in to remote registry locally and remotely"
|
|
29
|
+
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
|
30
|
+
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
|
31
|
+
def login
|
|
32
|
+
if KAMAL.registry.local?
|
|
33
|
+
raise "Cannot use login command with a local registry. Use `kamal registry setup` instead."
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
setup
|
|
10
37
|
end
|
|
11
38
|
|
|
12
|
-
desc "logout", "Log out of registry locally and remotely"
|
|
39
|
+
desc "logout", "Log out of remote registry locally and remotely"
|
|
13
40
|
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
|
14
41
|
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
|
15
42
|
def logout
|
|
16
|
-
|
|
17
|
-
|
|
43
|
+
if KAMAL.registry.local?
|
|
44
|
+
raise "Cannot use logout command with a local registry. Use `kamal registry remove` instead."
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
remove
|
|
18
48
|
end
|
|
19
49
|
end
|
data/lib/kamal/cli/secrets.rb
CHANGED
|
@@ -12,8 +12,9 @@ class Kamal::Cli::Secrets < Kamal::Cli::Base
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
results = adapter.fetch(secrets, **options.slice(:account, :from).symbolize_keys)
|
|
15
|
+
json = JSON.dump(results)
|
|
15
16
|
|
|
16
|
-
return_or_puts
|
|
17
|
+
return_or_puts options[:inline] ? json.shellescape : json, inline: options[:inline]
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
desc "extract", "Extract a single secret from the results of a fetch call"
|
data/lib/kamal/cli/server.rb
CHANGED
|
@@ -6,6 +6,7 @@ class Kamal::Cli::Server < Kamal::Cli::Base
|
|
|
6
6
|
|
|
7
7
|
cmd = Kamal::Utils.join_commands(cmd)
|
|
8
8
|
hosts = KAMAL.hosts
|
|
9
|
+
quiet = options[:quiet]
|
|
9
10
|
|
|
10
11
|
case
|
|
11
12
|
when options[:interactive]
|
|
@@ -19,7 +20,7 @@ class Kamal::Cli::Server < Kamal::Cli::Base
|
|
|
19
20
|
|
|
20
21
|
on(hosts) do |host|
|
|
21
22
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{host}"), verbosity: :debug
|
|
22
|
-
puts_by_host host, capture_with_info(cmd)
|
|
23
|
+
puts_by_host host, capture_with_info(cmd), quiet: quiet
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
end
|
|
@@ -34,6 +35,16 @@ class Kamal::Cli::Server < Kamal::Cli::Base
|
|
|
34
35
|
if execute(*KAMAL.docker.superuser?, raise_on_non_zero_exit: false)
|
|
35
36
|
info "Missing Docker on #{host}. Installing…"
|
|
36
37
|
execute *KAMAL.docker.install
|
|
38
|
+
|
|
39
|
+
unless execute(*KAMAL.docker.root?, raise_on_non_zero_exit: false) ||
|
|
40
|
+
execute(*KAMAL.docker.in_docker_group?, raise_on_non_zero_exit: false)
|
|
41
|
+
execute *KAMAL.docker.add_to_docker_group
|
|
42
|
+
begin
|
|
43
|
+
execute *KAMAL.docker.refresh_session
|
|
44
|
+
rescue IOError
|
|
45
|
+
info "Session refreshed due to group change."
|
|
46
|
+
end
|
|
47
|
+
end
|
|
37
48
|
else
|
|
38
49
|
missing << host
|
|
39
50
|
end
|
|
@@ -25,13 +25,14 @@ proxy:
|
|
|
25
25
|
|
|
26
26
|
# Credentials for your image host.
|
|
27
27
|
registry:
|
|
28
|
+
server: localhost:5555
|
|
28
29
|
# Specify the registry server, if you're not using Docker Hub
|
|
29
30
|
# server: registry.digitalocean.com / ghcr.io / ...
|
|
30
|
-
username: my-user
|
|
31
|
+
# username: my-user
|
|
31
32
|
|
|
32
33
|
# Always use an access token rather than real password (pulled from .kamal/secrets).
|
|
33
|
-
password:
|
|
34
|
-
|
|
34
|
+
# password:
|
|
35
|
+
# - KAMAL_REGISTRY_PASSWORD
|
|
35
36
|
|
|
36
37
|
# Configure builder setup.
|
|
37
38
|
builder:
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
|
|
4
4
|
|
|
5
5
|
# Option 1: Read secrets from the environment
|
|
6
|
-
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
|
|
6
|
+
# KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
|
|
7
7
|
|
|
8
8
|
# Option 2: Read secrets via a command
|
|
9
9
|
# RAILS_MASTER_KEY=$(cat config/master.key)
|
|
10
|
+
# KAMAL_REGISTRY_PASSWORD=$(rails credentials:fetch kamal.registry_password)
|
|
10
11
|
|
|
11
12
|
# Option 3: Read secrets via kamal secrets helpers
|
|
12
13
|
# These will handle logging in and fetching the secrets in as few calls as possible
|
data/lib/kamal/commander.rb
CHANGED
|
@@ -21,7 +21,7 @@ class Kamal::Commander
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def config
|
|
24
|
-
@config ||= Kamal::Configuration.create_from(**@config_kwargs).tap do |config|
|
|
24
|
+
@config ||= Kamal::Configuration.create_from(**@config_kwargs.to_h).tap do |config|
|
|
25
25
|
@config_kwargs = nil
|
|
26
26
|
configure_sshkit_with(config)
|
|
27
27
|
end
|
|
@@ -46,27 +46,19 @@ class Kamal::Commander
|
|
|
46
46
|
|
|
47
47
|
def specific_roles=(role_names)
|
|
48
48
|
@specifics = nil
|
|
49
|
-
if role_names.present?
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
raise ArgumentError, "No --roles match for #{role_names.join(',')}"
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
@specific_roles
|
|
49
|
+
@specific_roles = if role_names.present?
|
|
50
|
+
filtered = Kamal::Utils.filter_specific_items(role_names, config.roles)
|
|
51
|
+
raise ArgumentError, "No --roles match for #{role_names.join(',')}" if filtered.empty?
|
|
52
|
+
filtered
|
|
57
53
|
end
|
|
58
54
|
end
|
|
59
55
|
|
|
60
56
|
def specific_hosts=(hosts)
|
|
61
57
|
@specifics = nil
|
|
62
|
-
if hosts.present?
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
raise ArgumentError, "No --hosts match for #{hosts.join(',')}"
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
@specific_hosts
|
|
58
|
+
@specific_hosts = if hosts.present?
|
|
59
|
+
filtered = Kamal::Utils.filter_specific_items(hosts, config.all_hosts)
|
|
60
|
+
raise ArgumentError, "No --hosts match for #{hosts.join(',')}" if filtered.empty?
|
|
61
|
+
filtered
|
|
70
62
|
end
|
|
71
63
|
end
|
|
72
64
|
|
|
@@ -109,8 +101,8 @@ class Kamal::Commander
|
|
|
109
101
|
@commands[:lock] ||= Kamal::Commands::Lock.new(config)
|
|
110
102
|
end
|
|
111
103
|
|
|
112
|
-
def proxy
|
|
113
|
-
|
|
104
|
+
def proxy(host)
|
|
105
|
+
Kamal::Commands::Proxy.new(config, host: host)
|
|
114
106
|
end
|
|
115
107
|
|
|
116
108
|
def prune
|
|
@@ -129,6 +121,15 @@ class Kamal::Commander
|
|
|
129
121
|
config.aliases[name]
|
|
130
122
|
end
|
|
131
123
|
|
|
124
|
+
def resolve_alias(name)
|
|
125
|
+
if @config
|
|
126
|
+
@config.aliases[name]&.command
|
|
127
|
+
else
|
|
128
|
+
raw_config = Kamal::Configuration.load_raw_config(**@config_kwargs.to_h.slice(:config_file, :destination))
|
|
129
|
+
raw_config[:aliases]&.dig(name)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
132
133
|
def with_verbosity(level)
|
|
133
134
|
old_level = self.verbosity
|
|
134
135
|
|
|
@@ -155,6 +156,7 @@ class Kamal::Commander
|
|
|
155
156
|
SSHKit::Backend::Netssh.pool.idle_timeout = config.sshkit.pool_idle_timeout
|
|
156
157
|
SSHKit::Backend::Netssh.configure do |sshkit|
|
|
157
158
|
sshkit.max_concurrent_starts = config.sshkit.max_concurrent_starts
|
|
159
|
+
sshkit.dns_retries = config.sshkit.dns_retries
|
|
158
160
|
sshkit.ssh_options = config.ssh.options
|
|
159
161
|
end
|
|
160
162
|
SSHKit.config.command_map[:docker] = "docker" # No need to use /usr/bin/env, just clogs up the logs
|