kamal 2.3.0 → 2.7.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 +42 -16
- data/lib/kamal/cli/alias/command.rb +1 -0
- data/lib/kamal/cli/app/{prepare_assets.rb → assets.rb} +1 -1
- data/lib/kamal/cli/app/boot.rb +3 -2
- data/lib/kamal/cli/app/error_pages.rb +33 -0
- data/lib/kamal/cli/app/ssl_certificates.rb +28 -0
- data/lib/kamal/cli/app.rb +94 -29
- data/lib/kamal/cli/base.rb +29 -4
- data/lib/kamal/cli/build.rb +60 -18
- data/lib/kamal/cli/main.rb +8 -10
- data/lib/kamal/cli/proxy.rb +58 -25
- data/lib/kamal/cli/registry.rb +2 -0
- data/lib/kamal/cli/secrets.rb +9 -3
- data/lib/kamal/cli/server.rb +4 -2
- data/lib/kamal/cli/templates/deploy.yml +6 -3
- data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-build.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +1 -1
- data/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +19 -6
- data/lib/kamal/cli.rb +1 -0
- data/lib/kamal/commander/specifics.rb +9 -1
- data/lib/kamal/commander.rb +18 -27
- data/lib/kamal/commands/accessory/proxy.rb +16 -0
- data/lib/kamal/commands/accessory.rb +9 -9
- data/lib/kamal/commands/app/assets.rb +4 -4
- data/lib/kamal/commands/app/containers.rb +2 -2
- data/lib/kamal/commands/app/error_pages.rb +9 -0
- data/lib/kamal/commands/app/execution.rb +6 -4
- data/lib/kamal/commands/app/images.rb +1 -1
- data/lib/kamal/commands/app/logging.rb +14 -4
- data/lib/kamal/commands/app/proxy.rb +17 -1
- data/lib/kamal/commands/app.rb +19 -10
- data/lib/kamal/commands/auditor.rb +11 -5
- data/lib/kamal/commands/base.rb +37 -1
- data/lib/kamal/commands/builder/base.rb +20 -7
- data/lib/kamal/commands/builder/cloud.rb +22 -0
- data/lib/kamal/commands/builder/pack.rb +46 -0
- data/lib/kamal/commands/builder.rb +11 -19
- data/lib/kamal/commands/proxy.rb +55 -15
- data/lib/kamal/commands/registry.rb +9 -7
- data/lib/kamal/configuration/accessory.rb +66 -11
- data/lib/kamal/configuration/builder.rb +20 -0
- data/lib/kamal/configuration/docs/accessory.yml +32 -4
- data/lib/kamal/configuration/docs/alias.yml +2 -2
- data/lib/kamal/configuration/docs/builder.yml +22 -0
- data/lib/kamal/configuration/docs/configuration.yml +6 -0
- data/lib/kamal/configuration/docs/env.yml +31 -0
- data/lib/kamal/configuration/docs/proxy.yml +78 -15
- data/lib/kamal/configuration/docs/registry.yml +4 -0
- data/lib/kamal/configuration/env.rb +13 -4
- data/lib/kamal/configuration/proxy/boot.rb +129 -0
- data/lib/kamal/configuration/proxy.rb +67 -5
- data/lib/kamal/configuration/registry.rb +6 -6
- data/lib/kamal/configuration/role.rb +11 -9
- data/lib/kamal/configuration/servers.rb +8 -1
- data/lib/kamal/configuration/validator/accessory.rb +6 -2
- data/lib/kamal/configuration/validator/builder.rb +2 -0
- data/lib/kamal/configuration/validator/proxy.rb +10 -0
- data/lib/kamal/configuration/validator/role.rb +3 -1
- data/lib/kamal/configuration/validator/servers.rb +1 -1
- data/lib/kamal/configuration/validator.rb +21 -1
- data/lib/kamal/configuration.rb +36 -57
- data/lib/kamal/docker.rb +30 -0
- data/lib/kamal/git.rb +10 -0
- data/lib/kamal/secrets/adapters/aws_secrets_manager.rb +51 -0
- data/lib/kamal/secrets/adapters/base.rb +13 -3
- data/lib/kamal/secrets/adapters/bitwarden.rb +2 -2
- data/lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb +66 -0
- data/lib/kamal/secrets/adapters/doppler.rb +57 -0
- data/lib/kamal/secrets/adapters/enpass.rb +71 -0
- data/lib/kamal/secrets/adapters/gcp_secret_manager.rb +112 -0
- data/lib/kamal/secrets/adapters/last_pass.rb +3 -2
- data/lib/kamal/secrets/adapters/one_password.rb +47 -13
- data/lib/kamal/secrets/adapters/passbolt.rb +130 -0
- data/lib/kamal/secrets/adapters/test.rb +2 -2
- data/lib/kamal/secrets/adapters.rb +2 -0
- data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +2 -1
- data/lib/kamal/secrets.rb +1 -1
- data/lib/kamal/version.rb +1 -1
- metadata +22 -10
data/lib/kamal/cli/build.rb
CHANGED
@@ -5,15 +5,22 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
5
5
|
|
6
6
|
desc "deliver", "Build app and push app image to registry then pull image on servers"
|
7
7
|
def deliver
|
8
|
-
push
|
9
|
-
pull
|
8
|
+
invoke :push
|
9
|
+
invoke :pull
|
10
10
|
end
|
11
11
|
|
12
12
|
desc "push", "Build and push app image to registry"
|
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'."
|
13
14
|
def push
|
14
15
|
cli = self
|
15
16
|
|
16
|
-
|
17
|
+
# Ensure pre-connect hooks run before the build, they may needed for a remote builder
|
18
|
+
# or the pre-build hooks.
|
19
|
+
pre_connect_if_required
|
20
|
+
|
21
|
+
ensure_docker_installed
|
22
|
+
login_to_registry_locally
|
23
|
+
|
17
24
|
run_hook "pre-build"
|
18
25
|
|
19
26
|
uncommitted_changes = Kamal::Git.uncommitted_changes
|
@@ -49,7 +56,7 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
49
56
|
end
|
50
57
|
|
51
58
|
# Get the command here to ensure the Dir.chdir doesn't interfere with it
|
52
|
-
push = KAMAL.builder.push
|
59
|
+
push = KAMAL.builder.push(cli.options[:output])
|
53
60
|
|
54
61
|
KAMAL.with_verbosity(:debug) do
|
55
62
|
Dir.chdir(KAMAL.config.builder.build_directory) { execute *push }
|
@@ -60,14 +67,16 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
60
67
|
|
61
68
|
desc "pull", "Pull app image from registry onto servers"
|
62
69
|
def pull
|
70
|
+
login_to_registry_remotely
|
71
|
+
|
63
72
|
if (first_hosts = mirror_hosts).any?
|
64
73
|
# Pull on a single host per mirror first to seed them
|
65
74
|
say "Pulling image on #{first_hosts.join(", ")} to seed the #{"mirror".pluralize(first_hosts.count)}...", :magenta
|
66
75
|
pull_on_hosts(first_hosts)
|
67
76
|
say "Pulling image on remaining hosts...", :magenta
|
68
|
-
pull_on_hosts(KAMAL.
|
77
|
+
pull_on_hosts(KAMAL.app_hosts - first_hosts)
|
69
78
|
else
|
70
|
-
pull_on_hosts(KAMAL.
|
79
|
+
pull_on_hosts(KAMAL.app_hosts)
|
71
80
|
end
|
72
81
|
end
|
73
82
|
|
@@ -108,21 +117,42 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
108
117
|
end
|
109
118
|
end
|
110
119
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
+
desc "dev", "Build using the working directory, tag it as dirty, and push to local image store."
|
121
|
+
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'."
|
122
|
+
def dev
|
123
|
+
cli = self
|
124
|
+
|
125
|
+
ensure_docker_installed
|
126
|
+
|
127
|
+
docker_included_files = Set.new(Kamal::Docker.included_files)
|
128
|
+
git_uncommitted_files = Set.new(Kamal::Git.uncommitted_files)
|
129
|
+
git_untracked_files = Set.new(Kamal::Git.untracked_files)
|
130
|
+
|
131
|
+
docker_uncommitted_files = docker_included_files & git_uncommitted_files
|
132
|
+
if docker_uncommitted_files.any?
|
133
|
+
say "WARNING: Files with uncommitted changes will be present in the dev container:", :yellow
|
134
|
+
docker_uncommitted_files.sort.each { |f| say " #{f}", :yellow }
|
135
|
+
say
|
136
|
+
end
|
137
|
+
|
138
|
+
docker_untracked_files = docker_included_files & git_untracked_files
|
139
|
+
if docker_untracked_files.any?
|
140
|
+
say "WARNING: Untracked files will be present in the dev container:", :yellow
|
141
|
+
docker_untracked_files.sort.each { |f| say " #{f}", :yellow }
|
142
|
+
say
|
143
|
+
end
|
120
144
|
|
121
|
-
|
145
|
+
with_env(KAMAL.config.builder.secrets) do
|
146
|
+
run_locally do
|
147
|
+
build = KAMAL.builder.push(cli.options[:output], tag_as_dirty: true)
|
148
|
+
KAMAL.with_verbosity(:debug) do
|
149
|
+
execute(*build)
|
122
150
|
end
|
123
151
|
end
|
124
152
|
end
|
153
|
+
end
|
125
154
|
|
155
|
+
private
|
126
156
|
def connect_to_remote_host(remote_host)
|
127
157
|
remote_uri = URI.parse(remote_host)
|
128
158
|
if remote_uri.scheme == "ssh"
|
@@ -137,9 +167,9 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
137
167
|
end
|
138
168
|
|
139
169
|
def mirror_hosts
|
140
|
-
if KAMAL.
|
170
|
+
if KAMAL.app_hosts.many?
|
141
171
|
mirror_hosts = Concurrent::Hash.new
|
142
|
-
on(KAMAL.
|
172
|
+
on(KAMAL.app_hosts) do |host|
|
143
173
|
first_mirror = capture_with_info(*KAMAL.builder.first_mirror).strip.presence
|
144
174
|
mirror_hosts[first_mirror] ||= host.to_s if first_mirror
|
145
175
|
rescue SSHKit::Command::Failed => e
|
@@ -159,4 +189,16 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
159
189
|
execute *KAMAL.builder.validate_image
|
160
190
|
end
|
161
191
|
end
|
192
|
+
|
193
|
+
def login_to_registry_locally
|
194
|
+
run_locally do
|
195
|
+
execute *KAMAL.registry.login
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def login_to_registry_remotely
|
200
|
+
on(KAMAL.app_hosts) do
|
201
|
+
execute *KAMAL.registry.login
|
202
|
+
end
|
203
|
+
end
|
162
204
|
end
|
data/lib/kamal/cli/main.rb
CHANGED
@@ -9,21 +9,17 @@ 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
|
-
|
13
|
-
deploy
|
12
|
+
deploy(boot_accessories: true)
|
14
13
|
end
|
15
14
|
end
|
16
15
|
end
|
17
16
|
|
18
17
|
desc "deploy", "Deploy app to servers"
|
19
18
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
20
|
-
def deploy
|
19
|
+
def deploy(boot_accessories: false)
|
21
20
|
runtime = print_runtime do
|
22
21
|
invoke_options = deploy_options
|
23
22
|
|
24
|
-
say "Log into image registry...", :magenta
|
25
|
-
invoke "kamal:cli:registry:login", [], invoke_options.merge(skip_local: options[:skip_push])
|
26
|
-
|
27
23
|
if options[:skip_push]
|
28
24
|
say "Pull app image...", :magenta
|
29
25
|
invoke "kamal:cli:build:pull", [], invoke_options
|
@@ -38,6 +34,8 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
38
34
|
say "Ensure kamal-proxy is running...", :magenta
|
39
35
|
invoke "kamal:cli:proxy:boot", [], invoke_options
|
40
36
|
|
37
|
+
invoke "kamal:cli:accessory:boot", [ "all" ], invoke_options if boot_accessories
|
38
|
+
|
41
39
|
say "Detect stale containers...", :magenta
|
42
40
|
invoke "kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)
|
43
41
|
|
@@ -51,7 +49,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
51
49
|
run_hook "post-deploy", secrets: true, runtime: runtime.round.to_s
|
52
50
|
end
|
53
51
|
|
54
|
-
desc "redeploy", "Deploy app to servers without bootstrapping servers, starting kamal-proxy
|
52
|
+
desc "redeploy", "Deploy app to servers without bootstrapping servers, starting kamal-proxy and pruning"
|
55
53
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
56
54
|
def redeploy
|
57
55
|
runtime = print_runtime do
|
@@ -196,10 +194,10 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
196
194
|
confirming "This will replace Traefik with kamal-proxy and restart all accessories" do
|
197
195
|
with_lock do
|
198
196
|
if options[:rolling]
|
199
|
-
|
197
|
+
KAMAL.hosts.each do |host|
|
200
198
|
KAMAL.with_specific_hosts(host) do
|
201
199
|
say "Upgrading #{host}...", :magenta
|
202
|
-
if KAMAL.
|
200
|
+
if KAMAL.app_hosts.include?(host)
|
203
201
|
invoke "kamal:cli:proxy:upgrade", [], options.merge(confirmed: true, rolling: false)
|
204
202
|
reset_invocation(Kamal::Cli::Proxy)
|
205
203
|
end
|
@@ -255,7 +253,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
255
253
|
private
|
256
254
|
def container_available?(version)
|
257
255
|
begin
|
258
|
-
on(KAMAL.
|
256
|
+
on(KAMAL.app_hosts) do
|
259
257
|
KAMAL.roles_on(host).each do |role|
|
260
258
|
container_id = capture_with_info(*KAMAL.app(role: role, host: host).container_id_for_version(version))
|
261
259
|
raise "Container not found" unless container_id.present?
|
data/lib/kamal/cli/proxy.rb
CHANGED
@@ -13,9 +13,10 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
13
13
|
|
14
14
|
version = capture_with_info(*KAMAL.proxy.version).strip.presence
|
15
15
|
|
16
|
-
if version && Kamal::Utils.older_version?(version, Kamal::Configuration::
|
17
|
-
raise "kamal-proxy version #{version} is too old, run `kamal proxy reboot` in order to update to at least #{Kamal::Configuration::
|
16
|
+
if version && Kamal::Utils.older_version?(version, Kamal::Configuration::Proxy::Boot::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::Boot::MINIMUM_VERSION}"
|
18
18
|
end
|
19
|
+
execute *KAMAL.proxy.ensure_apps_config_directory
|
19
20
|
execute *KAMAL.proxy.start_or_run
|
20
21
|
end
|
21
22
|
end
|
@@ -23,30 +24,76 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
23
24
|
|
24
25
|
desc "boot_config <set|get|reset>", "Manage kamal-proxy boot configuration"
|
25
26
|
option :publish, type: :boolean, default: true, desc: "Publish the proxy ports on the host"
|
26
|
-
option :
|
27
|
-
option :
|
28
|
-
option :
|
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::Boot::DEFAULT_HTTP_PORT, desc: "HTTP port to publish on the host"
|
29
|
+
option :https_port, type: :numeric, default: Kamal::Configuration::Proxy::Boot::DEFAULT_HTTPS_PORT, desc: "HTTPS port to publish on the host"
|
30
|
+
option :log_max_size, type: :string, default: Kamal::Configuration::Proxy::Boot::DEFAULT_LOG_MAX_SIZE, desc: "Max size of proxy logs"
|
31
|
+
option :registry, type: :string, default: nil, desc: "Registry to use for the proxy image"
|
32
|
+
option :repository, type: :string, default: nil, desc: "Repository for the proxy image"
|
33
|
+
option :image_version, type: :string, default: nil, desc: "Version of the proxy to run"
|
34
|
+
option :metrics_port, type: :numeric, default: nil, desc: "Port to report prometheus metrics on"
|
35
|
+
option :debug, type: :boolean, default: false, desc: "Whether to run the proxy in debug mode"
|
29
36
|
option :docker_options, type: :array, default: [], desc: "Docker options to pass to the proxy container", banner: "option=value option2=value2"
|
30
37
|
def boot_config(subcommand)
|
38
|
+
proxy_boot_config = KAMAL.config.proxy_boot
|
39
|
+
|
31
40
|
case subcommand
|
32
41
|
when "set"
|
33
42
|
boot_options = [
|
34
|
-
*(
|
35
|
-
*(
|
43
|
+
*(proxy_boot_config.publish_args(options[:http_port], options[:https_port], options[:publish_host_ip]) if options[:publish]),
|
44
|
+
*(proxy_boot_config.logging_args(options[:log_max_size])),
|
45
|
+
*("--expose=#{options[:metrics_port]}" if options[:metrics_port]),
|
36
46
|
*options[:docker_options].map { |option| "--#{option}" }
|
37
47
|
]
|
38
48
|
|
49
|
+
image = [
|
50
|
+
options[:registry].presence,
|
51
|
+
options[:repository].presence || proxy_boot_config.repository_name,
|
52
|
+
proxy_boot_config.image_name
|
53
|
+
].compact.join("/")
|
54
|
+
|
55
|
+
image_version = options[:image_version]
|
56
|
+
|
57
|
+
run_command_options = { debug: options[:debug] || nil, "metrics-port": options[:metrics_port] }.compact
|
58
|
+
run_command = "kamal-proxy run #{Kamal::Utils.optionize(run_command_options).join(" ")}" if run_command_options.any?
|
59
|
+
|
39
60
|
on(KAMAL.proxy_hosts) do |host|
|
40
61
|
execute(*KAMAL.proxy.ensure_proxy_directory)
|
41
|
-
|
62
|
+
if boot_options != proxy_boot_config.default_boot_options
|
63
|
+
upload! StringIO.new(boot_options.join(" ")), proxy_boot_config.options_file
|
64
|
+
else
|
65
|
+
execute *KAMAL.proxy.reset_boot_options, raise_on_non_zero_exit: false
|
66
|
+
end
|
67
|
+
|
68
|
+
if image != proxy_boot_config.image_default
|
69
|
+
upload! StringIO.new(image), proxy_boot_config.image_file
|
70
|
+
else
|
71
|
+
execute *KAMAL.proxy.reset_image, raise_on_non_zero_exit: false
|
72
|
+
end
|
73
|
+
|
74
|
+
if image_version
|
75
|
+
upload! StringIO.new(image_version), proxy_boot_config.image_version_file
|
76
|
+
else
|
77
|
+
execute *KAMAL.proxy.reset_image_version, raise_on_non_zero_exit: false
|
78
|
+
end
|
79
|
+
|
80
|
+
if run_command
|
81
|
+
upload! StringIO.new(run_command), proxy_boot_config.run_command_file
|
82
|
+
else
|
83
|
+
execute *KAMAL.proxy.reset_run_command, raise_on_non_zero_exit: false
|
84
|
+
end
|
42
85
|
end
|
43
86
|
when "get"
|
87
|
+
|
44
88
|
on(KAMAL.proxy_hosts) do |host|
|
45
|
-
puts "Host #{host}: #{capture_with_info(*KAMAL.proxy.
|
89
|
+
puts "Host #{host}: #{capture_with_info(*KAMAL.proxy.boot_config)}"
|
46
90
|
end
|
47
91
|
when "reset"
|
48
92
|
on(KAMAL.proxy_hosts) do |host|
|
49
|
-
execute *KAMAL.proxy.reset_boot_options
|
93
|
+
execute *KAMAL.proxy.reset_boot_options, raise_on_non_zero_exit: false
|
94
|
+
execute *KAMAL.proxy.reset_image, raise_on_non_zero_exit: false
|
95
|
+
execute *KAMAL.proxy.reset_image_version, raise_on_non_zero_exit: false
|
96
|
+
execute *KAMAL.proxy.reset_run_command, raise_on_non_zero_exit: false
|
50
97
|
end
|
51
98
|
else
|
52
99
|
raise ArgumentError, "Unknown boot_config subcommand #{subcommand}"
|
@@ -67,26 +114,12 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
67
114
|
execute *KAMAL.auditor.record("Rebooted proxy"), verbosity: :debug
|
68
115
|
execute *KAMAL.registry.login
|
69
116
|
|
70
|
-
"Stopping and removing Traefik on #{host}, if running..."
|
71
|
-
execute *KAMAL.proxy.cleanup_traefik
|
72
|
-
|
73
117
|
"Stopping and removing kamal-proxy on #{host}, if running..."
|
74
118
|
execute *KAMAL.proxy.stop, raise_on_non_zero_exit: false
|
75
119
|
execute *KAMAL.proxy.remove_container
|
120
|
+
execute *KAMAL.proxy.ensure_apps_config_directory
|
76
121
|
|
77
122
|
execute *KAMAL.proxy.run
|
78
|
-
|
79
|
-
KAMAL.roles_on(host).select(&:running_proxy?).each do |role|
|
80
|
-
app = KAMAL.app(role: role, host: host)
|
81
|
-
|
82
|
-
version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
|
83
|
-
endpoint = capture_with_info(*app.container_id_for_version(version)).strip
|
84
|
-
|
85
|
-
if endpoint.present?
|
86
|
-
info "Deploying #{endpoint} for role `#{role}` on #{host}..."
|
87
|
-
execute *app.deploy(target: endpoint)
|
88
|
-
end
|
89
|
-
end
|
90
123
|
end
|
91
124
|
run_hook "post-proxy-reboot", hosts: host_list
|
92
125
|
end
|
data/lib/kamal/cli/registry.rb
CHANGED
@@ -3,6 +3,8 @@ class Kamal::Cli::Registry < Kamal::Cli::Base
|
|
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
5
|
def login
|
6
|
+
ensure_docker_installed unless options[:skip_local]
|
7
|
+
|
6
8
|
run_locally { execute *KAMAL.registry.login } unless options[:skip_local]
|
7
9
|
on(KAMAL.hosts) { execute *KAMAL.registry.login } unless options[:skip_remote]
|
8
10
|
end
|
data/lib/kamal/cli/secrets.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
class Kamal::Cli::Secrets < Kamal::Cli::Base
|
2
2
|
desc "fetch [SECRETS...]", "Fetch secrets from a vault"
|
3
3
|
option :adapter, type: :string, aliases: "-a", required: true, desc: "Which vault adapter to use"
|
4
|
-
option :account, type: :string, required:
|
4
|
+
option :account, type: :string, required: false, desc: "The account identifier or username"
|
5
5
|
option :from, type: :string, required: false, desc: "A vault or folder to fetch the secrets from"
|
6
6
|
option :inline, type: :boolean, required: false, hidden: true
|
7
7
|
def fetch(*secrets)
|
8
|
-
|
8
|
+
adapter = initialize_adapter(options[:adapter])
|
9
|
+
|
10
|
+
if adapter.requires_account? && options[:account].blank?
|
11
|
+
return puts "No value provided for required options '--account'"
|
12
|
+
end
|
13
|
+
|
14
|
+
results = adapter.fetch(secrets, **options.slice(:account, :from).symbolize_keys)
|
9
15
|
|
10
16
|
return_or_puts JSON.dump(results).shellescape, inline: options[:inline]
|
11
17
|
end
|
@@ -29,7 +35,7 @@ class Kamal::Cli::Secrets < Kamal::Cli::Base
|
|
29
35
|
end
|
30
36
|
|
31
37
|
private
|
32
|
-
def
|
38
|
+
def initialize_adapter(adapter)
|
33
39
|
Kamal::Secrets::Adapters.lookup(adapter)
|
34
40
|
end
|
35
41
|
|
data/lib/kamal/cli/server.rb
CHANGED
@@ -2,8 +2,10 @@ 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
4
|
def exec(*cmd)
|
5
|
+
pre_connect_if_required
|
6
|
+
|
5
7
|
cmd = Kamal::Utils.join_commands(cmd)
|
6
|
-
hosts = KAMAL.hosts
|
8
|
+
hosts = KAMAL.hosts
|
7
9
|
|
8
10
|
case
|
9
11
|
when options[:interactive]
|
@@ -27,7 +29,7 @@ class Kamal::Cli::Server < Kamal::Cli::Base
|
|
27
29
|
with_lock do
|
28
30
|
missing = []
|
29
31
|
|
30
|
-
on(KAMAL.hosts
|
32
|
+
on(KAMAL.hosts) do |host|
|
31
33
|
unless execute(*KAMAL.docker.installed?, raise_on_non_zero_exit: false)
|
32
34
|
if execute(*KAMAL.docker.superuser?, raise_on_non_zero_exit: false)
|
33
35
|
info "Missing Docker on #{host}. Installing…"
|
@@ -16,8 +16,8 @@ servers:
|
|
16
16
|
# Enable SSL auto certification via Let's Encrypt and allow for multiple apps on a single web server.
|
17
17
|
# Remove this section when using multiple web servers and ensure you terminate SSL at your load balancer.
|
18
18
|
#
|
19
|
-
# Note: If using Cloudflare, set encryption mode in SSL/TLS setting to "Full" to enable CF-to-app encryption.
|
20
|
-
proxy:
|
19
|
+
# Note: If using Cloudflare, set encryption mode in SSL/TLS setting to "Full" to enable CF-to-app encryption.
|
20
|
+
proxy:
|
21
21
|
ssl: true
|
22
22
|
host: app.example.com
|
23
23
|
# Proxy connects to your container on port 80 by default.
|
@@ -36,6 +36,9 @@ registry:
|
|
36
36
|
# Configure builder setup.
|
37
37
|
builder:
|
38
38
|
arch: amd64
|
39
|
+
# Pass in additional build args needed for your Dockerfile.
|
40
|
+
# args:
|
41
|
+
# RUBY_VERSION: <%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || "#{RUBY_ENGINE}-#{RUBY_ENGINE_VERSION}" %>
|
39
42
|
|
40
43
|
# Inject ENV variables into containers (secrets come from .kamal/secrets).
|
41
44
|
#
|
@@ -46,7 +49,7 @@ builder:
|
|
46
49
|
# - RAILS_MASTER_KEY
|
47
50
|
|
48
51
|
# Aliases are triggered with "bin/kamal <alias>". You can overwrite arguments on invocation:
|
49
|
-
# "bin/kamal logs -r job" will tail logs from the first server in the job section.
|
52
|
+
# "bin/kamal app logs -r job" will tail logs from the first server in the job section.
|
50
53
|
#
|
51
54
|
# aliases:
|
52
55
|
# shell: app exec --interactive --reuse "bash"
|
@@ -13,7 +13,7 @@
|
|
13
13
|
# KAMAL_HOSTS
|
14
14
|
# KAMAL_COMMAND
|
15
15
|
# KAMAL_SUBCOMMAND
|
16
|
-
#
|
16
|
+
# KAMAL_ROLES (if set)
|
17
17
|
# KAMAL_DESTINATION (if set)
|
18
18
|
|
19
19
|
# Only check the build status for production deployments
|
@@ -43,7 +43,7 @@ class GithubStatusChecks
|
|
43
43
|
attr_reader :remote_url, :git_sha, :github_client, :combined_status
|
44
44
|
|
45
45
|
def initialize
|
46
|
-
@remote_url =
|
46
|
+
@remote_url = github_repo_from_remote_url
|
47
47
|
@git_sha = `git rev-parse HEAD`.strip
|
48
48
|
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
|
49
49
|
refresh!
|
@@ -77,16 +77,29 @@ class GithubStatusChecks
|
|
77
77
|
"Build not started..."
|
78
78
|
end
|
79
79
|
end
|
80
|
+
|
81
|
+
private
|
82
|
+
def github_repo_from_remote_url
|
83
|
+
url = `git config --get remote.origin.url`.strip.delete_suffix(".git")
|
84
|
+
if url.start_with?("https://github.com/")
|
85
|
+
url.delete_prefix("https://github.com/")
|
86
|
+
elsif url.start_with?("git@github.com:")
|
87
|
+
url.delete_prefix("git@github.com:")
|
88
|
+
else
|
89
|
+
url
|
90
|
+
end
|
91
|
+
end
|
80
92
|
end
|
81
93
|
|
82
94
|
|
83
95
|
$stdout.sync = true
|
84
96
|
|
85
|
-
puts "Checking build status..."
|
86
|
-
attempts = 0
|
87
|
-
checks = GithubStatusChecks.new
|
88
|
-
|
89
97
|
begin
|
98
|
+
puts "Checking build status..."
|
99
|
+
|
100
|
+
attempts = 0
|
101
|
+
checks = GithubStatusChecks.new
|
102
|
+
|
90
103
|
loop do
|
91
104
|
case checks.state
|
92
105
|
when "success"
|
data/lib/kamal/cli.rb
CHANGED
@@ -11,13 +11,17 @@ class Kamal::Commander::Specifics
|
|
11
11
|
@primary_role = primary_or_first_role(roles_on(primary_host))
|
12
12
|
|
13
13
|
stable_sort!(roles) { |role| role == primary_role ? 0 : 1 }
|
14
|
-
|
14
|
+
sort_primary_role_hosts_first!(hosts)
|
15
15
|
end
|
16
16
|
|
17
17
|
def roles_on(host)
|
18
18
|
roles.select { |role| role.hosts.include?(host.to_s) }
|
19
19
|
end
|
20
20
|
|
21
|
+
def app_hosts
|
22
|
+
@app_hosts ||= sort_primary_role_hosts_first!(config.app_hosts & specified_hosts)
|
23
|
+
end
|
24
|
+
|
21
25
|
def proxy_hosts
|
22
26
|
config.proxy_hosts & specified_hosts
|
23
27
|
end
|
@@ -51,4 +55,8 @@ class Kamal::Commander::Specifics
|
|
51
55
|
specified_hosts
|
52
56
|
end
|
53
57
|
end
|
58
|
+
|
59
|
+
def sort_primary_role_hosts_first!(hosts)
|
60
|
+
stable_sort!(hosts) { |host| roles_on(host).any? { |role| role == primary_role } ? 0 : 1 }
|
61
|
+
end
|
54
62
|
end
|
data/lib/kamal/commander.rb
CHANGED
@@ -4,13 +4,20 @@ require "active_support/core_ext/object/blank"
|
|
4
4
|
|
5
5
|
class Kamal::Commander
|
6
6
|
attr_accessor :verbosity, :holding_lock, :connected
|
7
|
-
|
7
|
+
attr_reader :specific_roles, :specific_hosts
|
8
|
+
delegate :hosts, :roles, :primary_host, :primary_role, :roles_on, :app_hosts, :proxy_hosts, :accessory_hosts, to: :specifics
|
8
9
|
|
9
10
|
def initialize
|
11
|
+
reset
|
12
|
+
end
|
13
|
+
|
14
|
+
def reset
|
10
15
|
self.verbosity = :info
|
11
|
-
self.holding_lock =
|
16
|
+
self.holding_lock = ENV["KAMAL_LOCK"] == "true"
|
12
17
|
self.connected = false
|
13
|
-
@specifics = nil
|
18
|
+
@specifics = @specific_roles = @specific_hosts = nil
|
19
|
+
@config = @config_kwargs = nil
|
20
|
+
@commands = {}
|
14
21
|
end
|
15
22
|
|
16
23
|
def config
|
@@ -28,8 +35,6 @@ class Kamal::Commander
|
|
28
35
|
@config || @config_kwargs
|
29
36
|
end
|
30
37
|
|
31
|
-
attr_reader :specific_roles, :specific_hosts
|
32
|
-
|
33
38
|
def specific_primary!
|
34
39
|
@specifics = nil
|
35
40
|
if specific_roles.present?
|
@@ -76,11 +81,6 @@ class Kamal::Commander
|
|
76
81
|
config.accessories&.collect(&:name) || []
|
77
82
|
end
|
78
83
|
|
79
|
-
def accessories_on(host)
|
80
|
-
config.accessories.select { |accessory| accessory.hosts.include?(host.to_s) }.map(&:name)
|
81
|
-
end
|
82
|
-
|
83
|
-
|
84
84
|
def app(role: nil, host: nil)
|
85
85
|
Kamal::Commands::App.new(config, role: role, host: host)
|
86
86
|
end
|
@@ -94,42 +94,41 @@ class Kamal::Commander
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def builder
|
97
|
-
@builder ||= Kamal::Commands::Builder.new(config)
|
97
|
+
@commands[:builder] ||= Kamal::Commands::Builder.new(config)
|
98
98
|
end
|
99
99
|
|
100
100
|
def docker
|
101
|
-
@docker ||= Kamal::Commands::Docker.new(config)
|
101
|
+
@commands[:docker] ||= Kamal::Commands::Docker.new(config)
|
102
102
|
end
|
103
103
|
|
104
104
|
def hook
|
105
|
-
@hook ||= Kamal::Commands::Hook.new(config)
|
105
|
+
@commands[:hook] ||= Kamal::Commands::Hook.new(config)
|
106
106
|
end
|
107
107
|
|
108
108
|
def lock
|
109
|
-
@lock ||= Kamal::Commands::Lock.new(config)
|
109
|
+
@commands[:lock] ||= Kamal::Commands::Lock.new(config)
|
110
110
|
end
|
111
111
|
|
112
112
|
def proxy
|
113
|
-
@proxy ||= Kamal::Commands::Proxy.new(config)
|
113
|
+
@commands[:proxy] ||= Kamal::Commands::Proxy.new(config)
|
114
114
|
end
|
115
115
|
|
116
116
|
def prune
|
117
|
-
@prune ||= Kamal::Commands::Prune.new(config)
|
117
|
+
@commands[:prune] ||= Kamal::Commands::Prune.new(config)
|
118
118
|
end
|
119
119
|
|
120
120
|
def registry
|
121
|
-
@registry ||= Kamal::Commands::Registry.new(config)
|
121
|
+
@commands[:registry] ||= Kamal::Commands::Registry.new(config)
|
122
122
|
end
|
123
123
|
|
124
124
|
def server
|
125
|
-
@server ||= Kamal::Commands::Server.new(config)
|
125
|
+
@commands[:server] ||= Kamal::Commands::Server.new(config)
|
126
126
|
end
|
127
127
|
|
128
128
|
def alias(name)
|
129
129
|
config.aliases[name]
|
130
130
|
end
|
131
131
|
|
132
|
-
|
133
132
|
def with_verbosity(level)
|
134
133
|
old_level = self.verbosity
|
135
134
|
|
@@ -142,14 +141,6 @@ class Kamal::Commander
|
|
142
141
|
SSHKit.config.output_verbosity = old_level
|
143
142
|
end
|
144
143
|
|
145
|
-
def boot_strategy
|
146
|
-
if config.boot.limit.present?
|
147
|
-
{ in: :groups, limit: config.boot.limit, wait: config.boot.wait }
|
148
|
-
else
|
149
|
-
{}
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
144
|
def holding_lock?
|
154
145
|
self.holding_lock
|
155
146
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Kamal::Commands::Accessory::Proxy
|
2
|
+
delegate :container_name, to: :"config.proxy_boot", prefix: :proxy
|
3
|
+
|
4
|
+
def deploy(target:)
|
5
|
+
proxy_exec :deploy, service_name, *proxy.deploy_command_args(target: target)
|
6
|
+
end
|
7
|
+
|
8
|
+
def remove
|
9
|
+
proxy_exec :remove, service_name
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def proxy_exec(*command)
|
14
|
+
docker :exec, proxy_container_name, "kamal-proxy", *command
|
15
|
+
end
|
16
|
+
end
|