kamal 2.7.0 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/kamal/cli/accessory.rb +13 -0
- data/lib/kamal/cli/build.rb +33 -15
- data/lib/kamal/cli/main.rb +7 -2
- data/lib/kamal/cli/port_forwarding.rb +42 -0
- data/lib/kamal/cli/registry.rb +16 -8
- data/lib/kamal/cli/templates/deploy.yml +4 -3
- data/lib/kamal/cli/templates/secrets +1 -1
- data/lib/kamal/commander.rb +1 -1
- data/lib/kamal/commands/accessory.rb +4 -0
- data/lib/kamal/commands/app.rb +1 -0
- data/lib/kamal/commands/builder/base.rb +10 -1
- data/lib/kamal/commands/builder/local.rb +15 -2
- data/lib/kamal/commands/builder/pack.rb +5 -5
- data/lib/kamal/commands/builder/remote.rb +9 -1
- data/lib/kamal/commands/builder.rb +8 -2
- data/lib/kamal/commands/registry.rb +22 -0
- data/lib/kamal/configuration/docs/proxy.yml +24 -16
- data/lib/kamal/configuration/proxy.rb +5 -1
- data/lib/kamal/configuration/registry.rb +8 -0
- data/lib/kamal/configuration/validator/registry.rb +5 -3
- data/lib/kamal/configuration/validator.rb +3 -3
- data/lib/kamal/configuration.rb +11 -2
- data/lib/kamal/secrets/adapters/one_password.rb +1 -1
- data/lib/kamal/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80ba6d51041312c99d659a1bfaac13bfa0ed7d1e758bde65e5475c0e51d88238
|
4
|
+
data.tar.gz: f0cc94a905da2cfb5dcf4f5d9ead2194df1b0ed46fd3b8671a59bfc333b10fbf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e33ddb40f46e587364d9121bbd2f2d829beddeb956f4bf1d8399e5ac835285180914f51e1365bee96ec4b82a02c0e88df8ff513a68bf95e44a8dc7e0ce081509
|
7
|
+
data.tar.gz: 8a0b1a90bbacd96d072cbbe5cec3371e5f7fe6d213cfa5dac3203f6d4ef02fadbddd1e3458909f3051adcd9218d69d6edb5b193f86fca6fac28b49b00bd2dd91
|
data/lib/kamal/cli/accessory.rb
CHANGED
@@ -77,6 +77,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
77
77
|
KAMAL.accessory_names.each { |accessory_name| reboot(accessory_name) }
|
78
78
|
else
|
79
79
|
prepare(name)
|
80
|
+
pull_image(name)
|
80
81
|
stop(name)
|
81
82
|
remove_container(name)
|
82
83
|
boot(name, prepare: false)
|
@@ -203,6 +204,18 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
203
204
|
end
|
204
205
|
end
|
205
206
|
|
207
|
+
desc "pull_image [NAME]", "Pull accessory image on host", hide: true
|
208
|
+
def pull_image(name)
|
209
|
+
with_lock do
|
210
|
+
with_accessory(name) do |accessory, hosts|
|
211
|
+
on(hosts) do
|
212
|
+
execute *KAMAL.auditor.record("Pull #{name} accessory image"), verbosity: :debug
|
213
|
+
execute *accessory.pull_image
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
206
219
|
desc "remove [NAME]", "Remove accessory container, image and data directory from host (use NAME=all to remove all accessories)"
|
207
220
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
208
221
|
def remove(name)
|
data/lib/kamal/cli/build.rb
CHANGED
@@ -11,6 +11,7 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
11
11
|
|
12
12
|
desc "push", "Build and push app image to registry"
|
13
13
|
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'."
|
14
|
+
option :no_cache, type: :boolean, default: false, desc: "Build without using Docker's build cache"
|
14
15
|
def push
|
15
16
|
cli = self
|
16
17
|
|
@@ -19,7 +20,7 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
19
20
|
pre_connect_if_required
|
20
21
|
|
21
22
|
ensure_docker_installed
|
22
|
-
login_to_registry_locally
|
23
|
+
login_to_registry_locally if KAMAL.builder.login_to_registry_locally?
|
23
24
|
|
24
25
|
run_hook "pre-build"
|
25
26
|
|
@@ -56,10 +57,10 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
56
57
|
end
|
57
58
|
|
58
59
|
# Get the command here to ensure the Dir.chdir doesn't interfere with it
|
59
|
-
push = KAMAL.builder.push(cli.options[:output])
|
60
|
+
push = KAMAL.builder.push(cli.options[:output], no_cache: cli.options[:no_cache])
|
60
61
|
|
61
62
|
KAMAL.with_verbosity(:debug) do
|
62
|
-
Dir.chdir(KAMAL.config.builder.build_directory) { execute *push }
|
63
|
+
Dir.chdir(KAMAL.config.builder.build_directory) { execute *push, env: KAMAL.builder.push_env }
|
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 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,14 @@ 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(&block)
|
214
|
+
if KAMAL.config.registry.local?
|
215
|
+
Kamal::Cli::PortForwarding.
|
216
|
+
new(KAMAL.hosts, KAMAL.config.registry.local_port).
|
217
|
+
forward(&block)
|
218
|
+
else
|
219
|
+
yield
|
220
|
+
end
|
221
|
+
end
|
204
222
|
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
|
@@ -182,7 +185,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
182
185
|
invoke "kamal:cli:app:remove", [], options.without(:confirmed)
|
183
186
|
invoke "kamal:cli:proxy:remove", [], options.without(:confirmed)
|
184
187
|
invoke "kamal:cli:accessory:remove", [ "all" ], options
|
185
|
-
invoke "kamal:cli:registry:
|
188
|
+
invoke "kamal:cli:registry:remove", [], options.without(:confirmed).merge(skip_local: true)
|
186
189
|
end
|
187
190
|
end
|
188
191
|
end
|
@@ -272,6 +275,8 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
272
275
|
end
|
273
276
|
|
274
277
|
def deploy_options
|
275
|
-
|
278
|
+
base_options = options.without("skip_push")
|
279
|
+
base_options = base_options.except("no_cache") unless base_options["no_cache"]
|
280
|
+
{ "version" => KAMAL.config.version }.merge(base_options)
|
276
281
|
end
|
277
282
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class Kamal::Cli::PortForwarding
|
2
|
+
attr_reader :hosts, :port
|
3
|
+
|
4
|
+
def initialize(hosts, port)
|
5
|
+
@hosts = hosts
|
6
|
+
@port = port
|
7
|
+
end
|
8
|
+
|
9
|
+
def forward
|
10
|
+
@done = false
|
11
|
+
forward_ports
|
12
|
+
|
13
|
+
yield
|
14
|
+
ensure
|
15
|
+
stop
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def stop
|
21
|
+
@done = true
|
22
|
+
@threads.to_a.each(&:join)
|
23
|
+
end
|
24
|
+
|
25
|
+
def forward_ports
|
26
|
+
@threads = hosts.map do |host|
|
27
|
+
Thread.new do
|
28
|
+
Net::SSH.start(host, KAMAL.config.ssh.user, **{ proxy: KAMAL.config.ssh.proxy }.compact) do |ssh|
|
29
|
+
ssh.forward.remote(port, "localhost", port, "localhost")
|
30
|
+
ssh.loop(0.1) do
|
31
|
+
if @done
|
32
|
+
ssh.forward.cancel_remote(port, "localhost")
|
33
|
+
break
|
34
|
+
else
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/kamal/cli/registry.rb
CHANGED
@@ -1,19 +1,27 @@
|
|
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
|
10
14
|
end
|
11
15
|
|
12
|
-
desc "
|
16
|
+
desc "remove", "Remove local registry or log out of remote registry locally and remotely"
|
13
17
|
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
14
18
|
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
15
|
-
def
|
16
|
-
|
17
|
-
|
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
|
18
26
|
end
|
19
27
|
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,7 +3,7 @@
|
|
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)
|
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
|
data/lib/kamal/commands/app.rb
CHANGED
@@ -23,6 +23,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
|
|
23
23
|
"--env", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
|
24
24
|
"--env", "KAMAL_VERSION=\"#{config.version}\"",
|
25
25
|
"--env", "KAMAL_HOST=\"#{host}\"",
|
26
|
+
"--env", "KAMAL_DESTINATION=\"#{config.destination}\"",
|
26
27
|
*role.env_args(host),
|
27
28
|
*role.logging_args,
|
28
29
|
*config.volume_args,
|
@@ -14,13 +14,14 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|
14
14
|
docker :image, :rm, "--force", config.absolute_image
|
15
15
|
end
|
16
16
|
|
17
|
-
def push(export_action = "registry", tag_as_dirty: false)
|
17
|
+
def push(export_action = "registry", tag_as_dirty: false, no_cache: false)
|
18
18
|
docker :buildx, :build,
|
19
19
|
"--output=type=#{export_action}",
|
20
20
|
*platform_options(arches),
|
21
21
|
*([ "--builder", builder_name ] unless docker_driver?),
|
22
22
|
*build_tag_options(tag_as_dirty: tag_as_dirty),
|
23
23
|
*build_options,
|
24
|
+
*([ "--no-cache" ] if no_cache),
|
24
25
|
build_context,
|
25
26
|
"2>&1"
|
26
27
|
end
|
@@ -60,6 +61,14 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
|
|
60
61
|
docker(:info, "--format '{{index .RegistryConfig.Mirrors 0}}'")
|
61
62
|
end
|
62
63
|
|
64
|
+
def login_to_registry_locally?
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
def push_env
|
69
|
+
{}
|
70
|
+
end
|
71
|
+
|
63
72
|
private
|
64
73
|
def build_tag_names(tag_as_dirty: false)
|
65
74
|
tag_names = [ config.absolute_image, config.latest_image ]
|
@@ -1,6 +1,15 @@
|
|
1
1
|
class Kamal::Commands::Builder::Local < Kamal::Commands::Builder::Base
|
2
2
|
def create
|
3
|
-
|
3
|
+
return if docker_driver?
|
4
|
+
|
5
|
+
options =
|
6
|
+
if KAMAL.registry.local?
|
7
|
+
"--driver=#{driver} --driver-opt network=host"
|
8
|
+
else
|
9
|
+
"--driver=#{driver}"
|
10
|
+
end
|
11
|
+
|
12
|
+
docker :buildx, :create, "--name", builder_name, options
|
4
13
|
end
|
5
14
|
|
6
15
|
def remove
|
@@ -9,6 +18,10 @@ class Kamal::Commands::Builder::Local < Kamal::Commands::Builder::Base
|
|
9
18
|
|
10
19
|
private
|
11
20
|
def builder_name
|
12
|
-
|
21
|
+
if KAMAL.registry.local?
|
22
|
+
"kamal-local-registry-#{driver}"
|
23
|
+
else
|
24
|
+
"kamal-local-#{driver}"
|
25
|
+
end
|
13
26
|
end
|
14
27
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class Kamal::Commands::Builder::Pack < Kamal::Commands::Builder::Base
|
2
|
-
def push(export_action = "registry")
|
2
|
+
def push(export_action = "registry", tag_as_dirty: false, no_cache: false)
|
3
3
|
combine \
|
4
|
-
build,
|
4
|
+
build(tag_as_dirty: tag_as_dirty, no_cache: no_cache),
|
5
5
|
export(export_action)
|
6
6
|
end
|
7
7
|
|
@@ -13,15 +13,15 @@ class Kamal::Commands::Builder::Pack < Kamal::Commands::Builder::Base
|
|
13
13
|
alias_method :inspect_builder, :info
|
14
14
|
|
15
15
|
private
|
16
|
-
def build
|
16
|
+
def build(tag_as_dirty: false, no_cache: false)
|
17
17
|
pack(:build,
|
18
18
|
config.repository,
|
19
19
|
"--platform", platform,
|
20
20
|
"--creation-time", "now",
|
21
21
|
"--builder", pack_builder,
|
22
22
|
buildpacks,
|
23
|
-
|
24
|
-
"-
|
23
|
+
*build_tag_options(tag_as_dirty: tag_as_dirty),
|
24
|
+
*([ "--clear-cache" ] if no_cache),
|
25
25
|
"--env", "BP_IMAGE_LABELS=service=#{config.service}",
|
26
26
|
*argumentize("--env", args),
|
27
27
|
*argumentize("--env", secrets, sensitive: true),
|
@@ -19,11 +19,19 @@ class Kamal::Commands::Builder::Remote < Kamal::Commands::Builder::Base
|
|
19
19
|
|
20
20
|
def inspect_builder
|
21
21
|
combine \
|
22
|
-
combine
|
22
|
+
combine(inspect_buildx, inspect_remote_context),
|
23
23
|
[ "(echo no compatible builder && exit 1)" ],
|
24
24
|
by: "||"
|
25
25
|
end
|
26
26
|
|
27
|
+
def login_to_registry_locally?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def push_env
|
32
|
+
{ "BUILDKIT_NO_CLIENT_TOKEN" => "1" }
|
33
|
+
end
|
34
|
+
|
27
35
|
private
|
28
36
|
def builder_name
|
29
37
|
"kamal-remote-#{remote.gsub(/[^a-z0-9_-]/, "-")}"
|
@@ -1,8 +1,14 @@
|
|
1
1
|
require "active_support/core_ext/string/filters"
|
2
2
|
|
3
3
|
class Kamal::Commands::Builder < Kamal::Commands::Base
|
4
|
-
delegate
|
5
|
-
|
4
|
+
delegate \
|
5
|
+
:create, :remove, :dev, :push, :clean, :pull, :info, :inspect_builder,
|
6
|
+
:validate_image, :first_mirror, :login_to_registry_locally?, :push_env,
|
7
|
+
to: :target
|
8
|
+
|
9
|
+
delegate \
|
10
|
+
:local?, :remote?, :pack?, :cloud?,
|
11
|
+
to: "config.builder"
|
6
12
|
|
7
13
|
include Clone
|
8
14
|
|
@@ -2,6 +2,8 @@ class Kamal::Commands::Registry < Kamal::Commands::Base
|
|
2
2
|
def login(registry_config: nil)
|
3
3
|
registry_config ||= config.registry
|
4
4
|
|
5
|
+
return if registry_config.local?
|
6
|
+
|
5
7
|
docker :login,
|
6
8
|
registry_config.server,
|
7
9
|
"-u", sensitive(Kamal::Utils.escape_shell_value(registry_config.username)),
|
@@ -13,4 +15,24 @@ class Kamal::Commands::Registry < Kamal::Commands::Base
|
|
13
15
|
|
14
16
|
docker :logout, registry_config.server
|
15
17
|
end
|
18
|
+
|
19
|
+
def setup(registry_config: nil)
|
20
|
+
registry_config ||= config.registry
|
21
|
+
|
22
|
+
combine \
|
23
|
+
docker(:start, "kamal-docker-registry"),
|
24
|
+
docker(:run, "--detach", "-p", "127.0.0.1:#{registry_config.local_port}:5000", "--name", "kamal-docker-registry", "registry:3"),
|
25
|
+
by: "||"
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove
|
29
|
+
combine \
|
30
|
+
docker(:stop, "kamal-docker-registry"),
|
31
|
+
docker(:rm, "kamal-docker-registry"),
|
32
|
+
by: "&&"
|
33
|
+
end
|
34
|
+
|
35
|
+
def local?
|
36
|
+
config.registry.local?
|
37
|
+
end
|
16
38
|
end
|
@@ -45,27 +45,23 @@ proxy:
|
|
45
45
|
# unless you explicitly set `forward_headers: true`
|
46
46
|
#
|
47
47
|
# Defaults to `false`:
|
48
|
-
ssl:
|
48
|
+
ssl: true
|
49
49
|
|
50
50
|
# Custom SSL certificate
|
51
51
|
#
|
52
52
|
# In some cases, using Let's Encrypt for automatic certificate management is not an
|
53
|
-
# option, for example if you are running from
|
54
|
-
# have SSL certificates issued by a different Certificate Authority (CA).
|
55
|
-
# Kamal supports loading custom SSL certificates
|
56
|
-
# directly from secrets.
|
57
|
-
#
|
58
|
-
# Examples:
|
59
|
-
# ssl: true # Enable SSL with Let's Encrypt
|
60
|
-
# ssl: false # Disable SSL
|
61
|
-
# ssl: # Enable custom SSL
|
62
|
-
# certificate_pem: CERTIFICATE_PEM
|
63
|
-
# private_key_pem: PRIVATE_KEY_PEM
|
53
|
+
# option, for example if you are running from more than one host.
|
64
54
|
#
|
55
|
+
# Or you may already have SSL certificates issued by a different Certificate Authority (CA).
|
56
|
+
#
|
57
|
+
# Kamal supports loading custom SSL certificates directly from secrets. You should
|
58
|
+
# pass a hash mapping the `certificate_pem` and `private_key_pem` to the secret names.
|
59
|
+
ssl:
|
60
|
+
certificate_pem: CERTIFICATE_PEM
|
61
|
+
private_key_pem: PRIVATE_KEY_PEM
|
65
62
|
# ### Notes
|
66
|
-
# - If the certificate or key is missing or invalid,
|
67
|
-
# - Always handle SSL certificates and private keys securely. Avoid hard-coding them in
|
68
|
-
# - For automated certificate management, consider using the built-in Let's Encrypt integration instead.
|
63
|
+
# - If the certificate or key is missing or invalid, deployments will fail.
|
64
|
+
# - Always handle SSL certificates and private keys securely. Avoid hard-coding them in source control.
|
69
65
|
|
70
66
|
# SSL redirect
|
71
67
|
#
|
@@ -93,9 +89,21 @@ proxy:
|
|
93
89
|
#
|
94
90
|
# For applications that split their traffic to different services based on the request path,
|
95
91
|
# you can use path-based routing to mount services under different path prefixes.
|
96
|
-
path_prefix: '/api'
|
92
|
+
# Usage sample: path_prefix: '/api'
|
93
|
+
#
|
94
|
+
# You can also specify multiple paths in two ways.
|
95
|
+
#
|
96
|
+
# When using path_prefix you can supply multiple routes separated by commas.
|
97
|
+
path_prefix: "/api,/oauth_callback"
|
98
|
+
# You can also specify paths as a list of paths, the configuration will be
|
99
|
+
# rolled together into a comma separated string.
|
100
|
+
path_prefixes:
|
101
|
+
- "/api"
|
102
|
+
- "/oauth_callback"
|
97
103
|
# By default, the path prefix will be stripped from the request before it is forwarded upstream.
|
104
|
+
#
|
98
105
|
# So in the example above, a request to /api/users/123 will be forwarded to web-1 as /users/123.
|
106
|
+
#
|
99
107
|
# To instead forward the request with the original path (including the prefix),
|
100
108
|
# specify --strip-path-prefix=false
|
101
109
|
strip_path_prefix: false
|
@@ -63,6 +63,10 @@ class Kamal::Configuration::Proxy
|
|
63
63
|
tls_path(config.proxy_boot.tls_container_directory, "key.pem") if custom_ssl_certificate?
|
64
64
|
end
|
65
65
|
|
66
|
+
def path_prefixes
|
67
|
+
proxy_config["path_prefixes"] || proxy_config["path_prefix"]&.split(",") || []
|
68
|
+
end
|
69
|
+
|
66
70
|
def deploy_options
|
67
71
|
{
|
68
72
|
host: hosts,
|
@@ -80,7 +84,7 @@ class Kamal::Configuration::Proxy
|
|
80
84
|
"buffer-memory": proxy_config.dig("buffering", "memory"),
|
81
85
|
"max-request-body": proxy_config.dig("buffering", "max_request_body"),
|
82
86
|
"max-response-body": proxy_config.dig("buffering", "max_response_body"),
|
83
|
-
"path-prefix":
|
87
|
+
"path-prefix": path_prefixes,
|
84
88
|
"strip-path-prefix": proxy_config.dig("strip_path_prefix"),
|
85
89
|
"forward-headers": proxy_config.dig("forward_headers"),
|
86
90
|
"tls-redirect": proxy_config.dig("ssl_redirect"),
|
@@ -19,6 +19,14 @@ class Kamal::Configuration::Registry
|
|
19
19
|
lookup("password")
|
20
20
|
end
|
21
21
|
|
22
|
+
def local?
|
23
|
+
server.to_s.match?("^localhost[:$]")
|
24
|
+
end
|
25
|
+
|
26
|
+
def local_port
|
27
|
+
local? ? (server.split(":").last.to_i || 80) : nil
|
28
|
+
end
|
29
|
+
|
22
30
|
private
|
23
31
|
attr_reader :registry_config, :secrets
|
24
32
|
|
@@ -15,10 +15,12 @@ class Kamal::Configuration::Validator::Registry < Kamal::Configuration::Validato
|
|
15
15
|
with_context(key) do
|
16
16
|
value = config[key]
|
17
17
|
|
18
|
-
|
18
|
+
unless config["server"]&.match?("^localhost[:$]")
|
19
|
+
error "is required" unless value.present?
|
19
20
|
|
20
|
-
|
21
|
-
|
21
|
+
unless value.is_a?(String) || (value.is_a?(Array) && value.size == 1 && value.first.is_a?(String))
|
22
|
+
error "should be a string or an array with one string (for secret lookup)"
|
23
|
+
end
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
@@ -24,11 +24,11 @@ class Kamal::Configuration::Validator
|
|
24
24
|
example_value = example[key]
|
25
25
|
|
26
26
|
if example_value == "..."
|
27
|
-
|
28
|
-
validate_type! value, TrueClass, FalseClass, Hash
|
29
|
-
elsif key.to_s != "proxy" || !boolean?(value.class)
|
27
|
+
unless key.to_s == "proxy" && boolean?(value.class)
|
30
28
|
validate_type! value, *(Array if key == :servers), Hash
|
31
29
|
end
|
30
|
+
elsif key.to_s == "ssl"
|
31
|
+
validate_type! value, TrueClass, FalseClass, Hash
|
32
32
|
elsif key == "hosts"
|
33
33
|
validate_servers! value
|
34
34
|
elsif example_value.is_a?(Array)
|
data/lib/kamal/configuration.rb
CHANGED
@@ -6,7 +6,7 @@ require "erb"
|
|
6
6
|
require "net/ssh/proxy/jump"
|
7
7
|
|
8
8
|
class Kamal::Configuration
|
9
|
-
delegate :service, :
|
9
|
+
delegate :service, :labels, :hooks_path, to: :raw_config, allow_nil: true
|
10
10
|
delegate :argumentize, :optionize, to: Kamal::Utils
|
11
11
|
|
12
12
|
attr_reader :destination, :raw_config, :secrets
|
@@ -157,6 +157,13 @@ class Kamal::Configuration
|
|
157
157
|
(proxy_roles.flat_map(&:hosts) + proxy_accessories.flat_map(&:hosts)).uniq
|
158
158
|
end
|
159
159
|
|
160
|
+
def image
|
161
|
+
name = raw_config&.image.presence
|
162
|
+
name ||= raw_config&.service if registry.local?
|
163
|
+
|
164
|
+
name
|
165
|
+
end
|
166
|
+
|
160
167
|
def repository
|
161
168
|
[ registry.server, image ].compact.join("/")
|
162
169
|
end
|
@@ -282,10 +289,12 @@ class Kamal::Configuration
|
|
282
289
|
end
|
283
290
|
|
284
291
|
def ensure_required_keys_present
|
285
|
-
%i[ service
|
292
|
+
%i[ service registry ].each do |key|
|
286
293
|
raise Kamal::ConfigurationError, "Missing required configuration for #{key}" unless raw_config[key].present?
|
287
294
|
end
|
288
295
|
|
296
|
+
raise Kamal::ConfigurationError, "Missing required configuration for image" if image.blank?
|
297
|
+
|
289
298
|
if raw_config.servers.nil?
|
290
299
|
raise Kamal::ConfigurationError, "No servers or accessories specified" unless raw_config.accessories.present?
|
291
300
|
else
|
@@ -17,7 +17,7 @@ class Kamal::Secrets::Adapters::OnePassword < Kamal::Secrets::Adapters::Base
|
|
17
17
|
|
18
18
|
def fetch_secrets(secrets, from:, account:, session:)
|
19
19
|
if secrets.blank?
|
20
|
-
fetch_all_secrets(from: from, account: account, session: session)
|
20
|
+
fetch_all_secrets(from: from, account: account, session: session)
|
21
21
|
else
|
22
22
|
fetch_specified_secrets(secrets, from: from, account: account, session: session)
|
23
23
|
end
|
data/lib/kamal/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kamal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
@@ -229,6 +229,7 @@ files:
|
|
229
229
|
- lib/kamal/cli/healthcheck/poller.rb
|
230
230
|
- lib/kamal/cli/lock.rb
|
231
231
|
- lib/kamal/cli/main.rb
|
232
|
+
- lib/kamal/cli/port_forwarding.rb
|
232
233
|
- lib/kamal/cli/proxy.rb
|
233
234
|
- lib/kamal/cli/prune.rb
|
234
235
|
- lib/kamal/cli/registry.rb
|
@@ -355,7 +356,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
355
356
|
- !ruby/object:Gem::Version
|
356
357
|
version: '0'
|
357
358
|
requirements: []
|
358
|
-
rubygems_version: 3.6.
|
359
|
+
rubygems_version: 3.6.9
|
359
360
|
specification_version: 4
|
360
361
|
summary: Deploy web apps in containers to servers running Docker with zero downtime.
|
361
362
|
test_files: []
|