kamal 2.3.0 → 2.5.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 +24 -9
- data/lib/kamal/cli/alias/command.rb +1 -0
- data/lib/kamal/cli/app/boot.rb +2 -2
- data/lib/kamal/cli/app.rb +28 -8
- data/lib/kamal/cli/base.rb +16 -1
- data/lib/kamal/cli/build.rb +36 -14
- data/lib/kamal/cli/main.rb +4 -3
- data/lib/kamal/cli/proxy.rb +2 -4
- data/lib/kamal/cli/registry.rb +2 -0
- data/lib/kamal/cli/secrets.rb +9 -3
- 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/pre-app-boot.sample +3 -0
- data/lib/kamal/cli.rb +1 -0
- data/lib/kamal/commander.rb +16 -25
- data/lib/kamal/commands/accessory/proxy.rb +16 -0
- data/lib/kamal/commands/accessory.rb +4 -4
- data/lib/kamal/commands/app/assets.rb +4 -4
- data/lib/kamal/commands/app/containers.rb +2 -2
- data/lib/kamal/commands/app/execution.rb +4 -2
- data/lib/kamal/commands/app/images.rb +1 -1
- data/lib/kamal/commands/app/logging.rb +14 -4
- data/lib/kamal/commands/app.rb +15 -7
- data/lib/kamal/commands/base.rb +25 -1
- data/lib/kamal/commands/builder/base.rb +17 -6
- data/lib/kamal/commands/builder/cloud.rb +22 -0
- data/lib/kamal/commands/builder.rb +6 -20
- data/lib/kamal/commands/registry.rb +9 -7
- data/lib/kamal/configuration/accessory.rb +41 -9
- data/lib/kamal/configuration/builder.rb +8 -0
- data/lib/kamal/configuration/docs/accessory.yml +26 -3
- data/lib/kamal/configuration/docs/alias.yml +2 -2
- data/lib/kamal/configuration/docs/builder.yml +9 -0
- data/lib/kamal/configuration/docs/proxy.yml +13 -10
- data/lib/kamal/configuration/docs/registry.yml +4 -0
- data/lib/kamal/configuration/registry.rb +6 -6
- data/lib/kamal/configuration/role.rb +6 -6
- data/lib/kamal/configuration/validator/role.rb +1 -1
- data/lib/kamal/configuration.rb +31 -14
- data/lib/kamal/docker.rb +30 -0
- data/lib/kamal/git.rb +10 -0
- data/lib/kamal/secrets/adapters/aws_secrets_manager.rb +50 -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 +72 -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 +2 -2
- data/lib/kamal/secrets/adapters/test.rb +2 -2
- data/lib/kamal/secrets/adapters.rb +2 -0
- data/lib/kamal/secrets.rb +1 -1
- data/lib/kamal/version.rb +1 -1
- metadata +13 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '01499d423cd415dea520fe78d0e5f17b0d67d3ef2d241e56abb84e2deaeb3f65'
|
4
|
+
data.tar.gz: 826b542e67f360b9d019d886a454fff796b73cda57b5e201edc2981e5059a1dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bbc7aa2632e0e5810666cb6d93ce698461fd3290dd5813c11bb88dd9faa4d6f7f015140c24a928b588df34316b814aec6ce710cbb4a28aa19713cc3420c15ae3
|
7
|
+
data.tar.gz: 2c818511055983038c9cbc9674a6f9c122de98296e27bf47441b02f3f9474176416e07cf1928bfc5b333648e51bb9ff4d857249173df82127e2ed29a124a97a7
|
data/lib/kamal/cli/accessory.rb
CHANGED
@@ -18,6 +18,11 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
18
18
|
execute *accessory.ensure_env_directory
|
19
19
|
upload! accessory.secrets_io, accessory.secrets_path, mode: "0600"
|
20
20
|
execute *accessory.run
|
21
|
+
|
22
|
+
if accessory.running_proxy?
|
23
|
+
target = capture_with_info(*accessory.container_id_for(container_name: accessory.service_name, only_running: true)).strip
|
24
|
+
execute *accessory.deploy(target: target)
|
25
|
+
end
|
21
26
|
end
|
22
27
|
end
|
23
28
|
end
|
@@ -75,6 +80,10 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
75
80
|
on(hosts) do
|
76
81
|
execute *KAMAL.auditor.record("Started #{name} accessory"), verbosity: :debug
|
77
82
|
execute *accessory.start
|
83
|
+
if accessory.running_proxy?
|
84
|
+
target = capture_with_info(*accessory.container_id_for(container_name: accessory.service_name, only_running: true)).strip
|
85
|
+
execute *accessory.deploy(target: target)
|
86
|
+
end
|
78
87
|
end
|
79
88
|
end
|
80
89
|
end
|
@@ -87,6 +96,11 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
87
96
|
on(hosts) do
|
88
97
|
execute *KAMAL.auditor.record("Stopped #{name} accessory"), verbosity: :debug
|
89
98
|
execute *accessory.stop, raise_on_non_zero_exit: false
|
99
|
+
|
100
|
+
if accessory.running_proxy?
|
101
|
+
target = capture_with_info(*accessory.container_id_for(container_name: accessory.service_name, only_running: true)).strip
|
102
|
+
execute *accessory.remove if target
|
103
|
+
end
|
90
104
|
end
|
91
105
|
end
|
92
106
|
end
|
@@ -112,14 +126,15 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
112
126
|
end
|
113
127
|
end
|
114
128
|
|
115
|
-
desc "exec [NAME] [CMD]", "Execute a custom command on servers (use --help to show options)"
|
129
|
+
desc "exec [NAME] [CMD...]", "Execute a custom command on servers within the accessory container (use --help to show options)"
|
116
130
|
option :interactive, aliases: "-i", type: :boolean, default: false, desc: "Execute command over ssh for an interactive shell (use for console/bash)"
|
117
131
|
option :reuse, type: :boolean, default: false, desc: "Reuse currently running container instead of starting a new one"
|
118
|
-
def exec(name, cmd)
|
132
|
+
def exec(name, *cmd)
|
133
|
+
cmd = Kamal::Utils.join_commands(cmd)
|
119
134
|
with_accessory(name) do |accessory, hosts|
|
120
135
|
case
|
121
136
|
when options[:interactive] && options[:reuse]
|
122
|
-
say "Launching interactive command
|
137
|
+
say "Launching interactive command via SSH from existing container...", :magenta
|
123
138
|
run_locally { exec accessory.execute_in_existing_container_over_ssh(cmd) }
|
124
139
|
|
125
140
|
when options[:interactive]
|
@@ -128,16 +143,16 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
128
143
|
|
129
144
|
when options[:reuse]
|
130
145
|
say "Launching command from existing container...", :magenta
|
131
|
-
on(hosts) do
|
146
|
+
on(hosts) do |host|
|
132
147
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
|
133
|
-
capture_with_info(*accessory.execute_in_existing_container(cmd))
|
148
|
+
puts_by_host host, capture_with_info(*accessory.execute_in_existing_container(cmd))
|
134
149
|
end
|
135
150
|
|
136
151
|
else
|
137
152
|
say "Launching command from new container...", :magenta
|
138
|
-
on(hosts) do
|
153
|
+
on(hosts) do |host|
|
139
154
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{name} accessory"), verbosity: :debug
|
140
|
-
capture_with_info(*accessory.execute_in_new_container(cmd))
|
155
|
+
puts_by_host host, capture_with_info(*accessory.execute_in_new_container(cmd))
|
141
156
|
end
|
142
157
|
end
|
143
158
|
end
|
@@ -147,7 +162,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
147
162
|
option :since, aliases: "-s", desc: "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)"
|
148
163
|
option :lines, type: :numeric, aliases: "-n", desc: "Number of log lines to pull from each server"
|
149
164
|
option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
|
150
|
-
option :grep_options,
|
165
|
+
option :grep_options, desc: "Additional options supplied to grep"
|
151
166
|
option :follow, aliases: "-f", desc: "Follow logs on primary server (or specific host set by --hosts)"
|
152
167
|
option :skip_timestamps, type: :boolean, aliases: "-T", desc: "Skip appending timestamps to logging output"
|
153
168
|
def logs(name)
|
@@ -277,7 +292,7 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
|
|
277
292
|
def prepare(name)
|
278
293
|
with_accessory(name) do |accessory, hosts|
|
279
294
|
on(hosts) do
|
280
|
-
execute *KAMAL.registry.login
|
295
|
+
execute *KAMAL.registry.login(registry_config: accessory.registry)
|
281
296
|
execute *KAMAL.docker.create_network
|
282
297
|
rescue SSHKit::Command::Failed => e
|
283
298
|
raise unless e.message.include?("already exists")
|
data/lib/kamal/cli/app/boot.rb
CHANGED
@@ -45,7 +45,7 @@ class Kamal::Cli::App::Boot
|
|
45
45
|
|
46
46
|
def start_new_version
|
47
47
|
audit "Booted app version #{version}"
|
48
|
-
hostname = "#{host.to_s[0...51].
|
48
|
+
hostname = "#{host.to_s[0...51].chomp(".")}-#{SecureRandom.hex(6)}"
|
49
49
|
|
50
50
|
execute *app.ensure_env_directory
|
51
51
|
upload! role.secrets_io(host), role.secrets_path, mode: "0600"
|
@@ -91,7 +91,7 @@ class Kamal::Cli::App::Boot
|
|
91
91
|
if barrier.close
|
92
92
|
info "First #{KAMAL.primary_role} container is unhealthy on #{host}, not booting any other roles"
|
93
93
|
begin
|
94
|
-
error capture_with_info(*app.logs(
|
94
|
+
error capture_with_info(*app.logs(container_id: app.container_id_for_version(version)))
|
95
95
|
error capture_with_info(*app.container_health_log(version: version))
|
96
96
|
rescue SSHKit::Command::Failed
|
97
97
|
error "Could not fetch logs for #{version}"
|
data/lib/kamal/cli/app.rb
CHANGED
@@ -16,10 +16,18 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
16
16
|
# Primary hosts and roles are returned first, so they can open the barrier
|
17
17
|
barrier = Kamal::Cli::Healthcheck::Barrier.new
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
host_boot_groups.each do |hosts|
|
20
|
+
host_list = Array(hosts).join(",")
|
21
|
+
run_hook "pre-app-boot", hosts: host_list
|
22
|
+
|
23
|
+
on(hosts) do |host|
|
24
|
+
KAMAL.roles_on(host).each do |role|
|
25
|
+
Kamal::Cli::App::Boot.new(host, role, self, version, barrier).run
|
26
|
+
end
|
22
27
|
end
|
28
|
+
|
29
|
+
run_hook "post-app-boot", hosts: host_list
|
30
|
+
sleep KAMAL.config.boot.wait if KAMAL.config.boot.wait
|
23
31
|
end
|
24
32
|
|
25
33
|
# Tag once the app booted on all hosts
|
@@ -94,9 +102,15 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
94
102
|
option :interactive, aliases: "-i", type: :boolean, default: false, desc: "Execute command over ssh for an interactive shell (use for console/bash)"
|
95
103
|
option :reuse, type: :boolean, default: false, desc: "Reuse currently running container instead of starting a new one"
|
96
104
|
option :env, aliases: "-e", type: :hash, desc: "Set environment variables for the command"
|
105
|
+
option :detach, type: :boolean, default: false, desc: "Execute command in a detached container"
|
97
106
|
def exec(*cmd)
|
107
|
+
if (incompatible_options = [ :interactive, :reuse ].select { |key| options[:detach] && options[key] }.presence)
|
108
|
+
raise ArgumentError, "Detach is not compatible with #{incompatible_options.join(" or ")}"
|
109
|
+
end
|
110
|
+
|
98
111
|
cmd = Kamal::Utils.join_commands(cmd)
|
99
112
|
env = options[:env]
|
113
|
+
detach = options[:detach]
|
100
114
|
case
|
101
115
|
when options[:interactive] && options[:reuse]
|
102
116
|
say "Get current version of running container...", :magenta unless options[:version]
|
@@ -138,7 +152,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
138
152
|
|
139
153
|
roles.each do |role|
|
140
154
|
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on app version #{version}"), verbosity: :debug
|
141
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).execute_in_new_container(cmd, env: env))
|
155
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).execute_in_new_container(cmd, env: env, detach: detach))
|
142
156
|
end
|
143
157
|
end
|
144
158
|
end
|
@@ -186,15 +200,17 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
186
200
|
option :since, aliases: "-s", desc: "Show lines since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)"
|
187
201
|
option :lines, type: :numeric, aliases: "-n", desc: "Number of lines to show from each server"
|
188
202
|
option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
|
189
|
-
option :grep_options,
|
203
|
+
option :grep_options, desc: "Additional options supplied to grep"
|
190
204
|
option :follow, aliases: "-f", desc: "Follow log on primary server (or specific host set by --hosts)"
|
191
205
|
option :skip_timestamps, type: :boolean, aliases: "-T", desc: "Skip appending timestamps to logging output"
|
206
|
+
option :container_id, desc: "Docker container ID to fetch logs"
|
192
207
|
def logs
|
193
208
|
# FIXME: Catch when app containers aren't running
|
194
209
|
|
195
210
|
grep = options[:grep]
|
196
211
|
grep_options = options[:grep_options]
|
197
212
|
since = options[:since]
|
213
|
+
container_id = options[:container_id]
|
198
214
|
timestamps = !options[:skip_timestamps]
|
199
215
|
|
200
216
|
if options[:follow]
|
@@ -207,8 +223,8 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
207
223
|
role = KAMAL.roles_on(KAMAL.primary_host).first
|
208
224
|
|
209
225
|
app = KAMAL.app(role: role, host: host)
|
210
|
-
info app.follow_logs(host: KAMAL.primary_host, timestamps: timestamps, lines: lines, grep: grep, grep_options: grep_options)
|
211
|
-
exec app.follow_logs(host: KAMAL.primary_host, timestamps: timestamps, lines: lines, grep: grep, grep_options: grep_options)
|
226
|
+
info app.follow_logs(host: KAMAL.primary_host, container_id: container_id, timestamps: timestamps, lines: lines, grep: grep, grep_options: grep_options)
|
227
|
+
exec app.follow_logs(host: KAMAL.primary_host, container_id: container_id, timestamps: timestamps, lines: lines, grep: grep, grep_options: grep_options)
|
212
228
|
end
|
213
229
|
else
|
214
230
|
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
|
@@ -218,7 +234,7 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
218
234
|
|
219
235
|
roles.each do |role|
|
220
236
|
begin
|
221
|
-
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).logs(timestamps: timestamps, since: since, lines: lines, grep: grep, grep_options: grep_options))
|
237
|
+
puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).logs(container_id: container_id, timestamps: timestamps, since: since, lines: lines, grep: grep, grep_options: grep_options))
|
222
238
|
rescue SSHKit::Command::Failed
|
223
239
|
puts_by_host host, "Nothing found"
|
224
240
|
end
|
@@ -332,4 +348,8 @@ class Kamal::Cli::App < Kamal::Cli::Base
|
|
332
348
|
yield
|
333
349
|
end
|
334
350
|
end
|
351
|
+
|
352
|
+
def host_boot_groups
|
353
|
+
KAMAL.config.boot.limit ? KAMAL.hosts.each_slice(KAMAL.config.boot.limit).to_a : [ KAMAL.hosts ]
|
354
|
+
end
|
335
355
|
end
|
data/lib/kamal/cli/base.rb
CHANGED
@@ -5,7 +5,7 @@ module Kamal::Cli
|
|
5
5
|
class Base < Thor
|
6
6
|
include SSHKit::DSL
|
7
7
|
|
8
|
-
def self.exit_on_failure?()
|
8
|
+
def self.exit_on_failure?() true end
|
9
9
|
def self.dynamic_command_class() Kamal::Cli::Alias::Command end
|
10
10
|
|
11
11
|
class_option :verbose, type: :boolean, aliases: "-v", desc: "Detailed logging"
|
@@ -30,6 +30,7 @@ module Kamal::Cli
|
|
30
30
|
else
|
31
31
|
super
|
32
32
|
end
|
33
|
+
|
33
34
|
initialize_commander unless KAMAL.configured?
|
34
35
|
end
|
35
36
|
|
@@ -194,5 +195,19 @@ module Kamal::Cli
|
|
194
195
|
ENV.clear
|
195
196
|
ENV.update(current_env)
|
196
197
|
end
|
198
|
+
|
199
|
+
def ensure_docker_installed
|
200
|
+
run_locally do
|
201
|
+
begin
|
202
|
+
execute *KAMAL.builder.ensure_docker_installed
|
203
|
+
rescue SSHKit::Command::Failed => e
|
204
|
+
error = e.message =~ /command not found/ ?
|
205
|
+
"Docker is not installed locally" :
|
206
|
+
"Docker buildx plugin is not installed locally"
|
207
|
+
|
208
|
+
raise DependencyError, error
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
197
212
|
end
|
198
213
|
end
|
data/lib/kamal/cli/build.rb
CHANGED
@@ -5,15 +5,16 @@ 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_docker_installed
|
17
18
|
run_hook "pre-build"
|
18
19
|
|
19
20
|
uncommitted_changes = Kamal::Git.uncommitted_changes
|
@@ -49,7 +50,7 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
49
50
|
end
|
50
51
|
|
51
52
|
# Get the command here to ensure the Dir.chdir doesn't interfere with it
|
52
|
-
push = KAMAL.builder.push
|
53
|
+
push = KAMAL.builder.push(cli.options[:output])
|
53
54
|
|
54
55
|
KAMAL.with_verbosity(:debug) do
|
55
56
|
Dir.chdir(KAMAL.config.builder.build_directory) { execute *push }
|
@@ -108,21 +109,42 @@ class Kamal::Cli::Build < Kamal::Cli::Base
|
|
108
109
|
end
|
109
110
|
end
|
110
111
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
112
|
+
desc "dev", "Build using the working directory, tag it as dirty, and push to local image store."
|
113
|
+
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'."
|
114
|
+
def dev
|
115
|
+
cli = self
|
116
|
+
|
117
|
+
ensure_docker_installed
|
118
|
+
|
119
|
+
docker_included_files = Set.new(Kamal::Docker.included_files)
|
120
|
+
git_uncommitted_files = Set.new(Kamal::Git.uncommitted_files)
|
121
|
+
git_untracked_files = Set.new(Kamal::Git.untracked_files)
|
122
|
+
|
123
|
+
docker_uncommitted_files = docker_included_files & git_uncommitted_files
|
124
|
+
if docker_uncommitted_files.any?
|
125
|
+
say "WARNING: Files with uncommitted changes will be present in the dev container:", :yellow
|
126
|
+
docker_uncommitted_files.sort.each { |f| say " #{f}", :yellow }
|
127
|
+
say
|
128
|
+
end
|
129
|
+
|
130
|
+
docker_untracked_files = docker_included_files & git_untracked_files
|
131
|
+
if docker_untracked_files.any?
|
132
|
+
say "WARNING: Untracked files will be present in the dev container:", :yellow
|
133
|
+
docker_untracked_files.sort.each { |f| say " #{f}", :yellow }
|
134
|
+
say
|
135
|
+
end
|
120
136
|
|
121
|
-
|
137
|
+
with_env(KAMAL.config.builder.secrets) do
|
138
|
+
run_locally do
|
139
|
+
build = KAMAL.builder.push(cli.options[:output], tag_as_dirty: true)
|
140
|
+
KAMAL.with_verbosity(:debug) do
|
141
|
+
execute(*build)
|
122
142
|
end
|
123
143
|
end
|
124
144
|
end
|
145
|
+
end
|
125
146
|
|
147
|
+
private
|
126
148
|
def connect_to_remote_host(remote_host)
|
127
149
|
remote_uri = URI.parse(remote_host)
|
128
150
|
if remote_uri.scheme == "ssh"
|
data/lib/kamal/cli/main.rb
CHANGED
@@ -9,15 +9,14 @@ 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
|
|
@@ -38,6 +37,8 @@ class Kamal::Cli::Main < Kamal::Cli::Base
|
|
38
37
|
say "Ensure kamal-proxy is running...", :magenta
|
39
38
|
invoke "kamal:cli:proxy:boot", [], invoke_options
|
40
39
|
|
40
|
+
invoke "kamal:cli:accessory:boot", [ "all" ], invoke_options if boot_accessories
|
41
|
+
|
41
42
|
say "Detect stale containers...", :magenta
|
42
43
|
invoke "kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)
|
43
44
|
|
data/lib/kamal/cli/proxy.rb
CHANGED
@@ -23,6 +23,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
23
23
|
|
24
24
|
desc "boot_config <set|get|reset>", "Manage kamal-proxy boot configuration"
|
25
25
|
option :publish, type: :boolean, default: true, desc: "Publish the proxy ports on the host"
|
26
|
+
option :publish_host_ip, type: :string, repeatable: true, default: nil, desc: "Host IP address to bind HTTP/HTTPS traffic to. Defaults to all interfaces"
|
26
27
|
option :http_port, type: :numeric, default: Kamal::Configuration::PROXY_HTTP_PORT, desc: "HTTP port to publish on the host"
|
27
28
|
option :https_port, type: :numeric, default: Kamal::Configuration::PROXY_HTTPS_PORT, desc: "HTTPS port to publish on the host"
|
28
29
|
option :log_max_size, type: :string, default: Kamal::Configuration::PROXY_LOG_MAX_SIZE, desc: "Max size of proxy logs"
|
@@ -31,7 +32,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
31
32
|
case subcommand
|
32
33
|
when "set"
|
33
34
|
boot_options = [
|
34
|
-
*(KAMAL.config.proxy_publish_args(options[:http_port], options[:https_port]) if options[:publish]),
|
35
|
+
*(KAMAL.config.proxy_publish_args(options[:http_port], options[:https_port], options[:publish_host_ip]) if options[:publish]),
|
35
36
|
*(KAMAL.config.proxy_logging_args(options[:log_max_size])),
|
36
37
|
*options[:docker_options].map { |option| "--#{option}" }
|
37
38
|
]
|
@@ -67,9 +68,6 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
67
68
|
execute *KAMAL.auditor.record("Rebooted proxy"), verbosity: :debug
|
68
69
|
execute *KAMAL.registry.login
|
69
70
|
|
70
|
-
"Stopping and removing Traefik on #{host}, if running..."
|
71
|
-
execute *KAMAL.proxy.cleanup_traefik
|
72
|
-
|
73
71
|
"Stopping and removing kamal-proxy on #{host}, if running..."
|
74
72
|
execute *KAMAL.proxy.stop, raise_on_non_zero_exit: false
|
75
73
|
execute *KAMAL.proxy.remove_container
|
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
|
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
|
|
@@ -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"
|
data/lib/kamal/cli.rb
CHANGED
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
|
+
attr_reader :specific_roles, :specific_hosts
|
7
8
|
delegate :hosts, :roles, :primary_host, :primary_role, :roles_on, :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
16
|
self.holding_lock = false
|
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 :proxy_container_name, to: :config
|
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
|
@@ -1,9 +1,12 @@
|
|
1
1
|
class Kamal::Commands::Accessory < Kamal::Commands::Base
|
2
|
+
include Proxy
|
3
|
+
|
2
4
|
attr_reader :accessory_config
|
3
5
|
delegate :service_name, :image, :hosts, :port, :files, :directories, :cmd,
|
4
6
|
:network_args, :publish_args, :env_args, :volume_args, :label_args, :option_args,
|
5
|
-
:secrets_io, :secrets_path, :env_directory,
|
7
|
+
:secrets_io, :secrets_path, :env_directory, :proxy, :running_proxy?, :registry,
|
6
8
|
to: :accessory_config
|
9
|
+
delegate :proxy_container_name, to: :config
|
7
10
|
|
8
11
|
def initialize(config, name:)
|
9
12
|
super(config)
|
@@ -38,7 +41,6 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
|
|
38
41
|
docker :ps, *service_filter
|
39
42
|
end
|
40
43
|
|
41
|
-
|
42
44
|
def logs(timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil)
|
43
45
|
pipe \
|
44
46
|
docker(:logs, service_name, (" --since #{since}" if since), (" --tail #{lines}" if lines), ("--timestamps" if timestamps), "2>&1"),
|
@@ -52,7 +54,6 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
|
|
52
54
|
(%(grep "#{grep}"#{" #{grep_options}" if grep_options}) if grep)
|
53
55
|
end
|
54
56
|
|
55
|
-
|
56
57
|
def execute_in_existing_container(*command, interactive: false)
|
57
58
|
docker :exec,
|
58
59
|
("-it" if interactive),
|
@@ -83,7 +84,6 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
|
|
83
84
|
super command, host: hosts.first
|
84
85
|
end
|
85
86
|
|
86
|
-
|
87
87
|
def ensure_local_file_present(local_file)
|
88
88
|
if !local_file.is_a?(StringIO) && !Pathname.new(local_file).exist?
|
89
89
|
raise "Missing file: #{local_file}"
|