kamal 2.0.0.alpha → 2.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/kamal/cli/accessory.rb +44 -20
  4. data/lib/kamal/cli/app/boot.rb +22 -16
  5. data/lib/kamal/cli/app.rb +37 -3
  6. data/lib/kamal/cli/base.rb +9 -48
  7. data/lib/kamal/cli/build.rb +8 -3
  8. data/lib/kamal/cli/healthcheck/barrier.rb +2 -0
  9. data/lib/kamal/cli/healthcheck/poller.rb +18 -39
  10. data/lib/kamal/cli/lock.rb +2 -3
  11. data/lib/kamal/cli/main.rb +54 -51
  12. data/lib/kamal/cli/proxy.rb +224 -0
  13. data/lib/kamal/cli/prune.rb +0 -1
  14. data/lib/kamal/cli/secrets.rb +36 -0
  15. data/lib/kamal/cli/server.rb +0 -2
  16. data/lib/kamal/cli/templates/deploy.yml +0 -11
  17. data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
  18. data/lib/kamal/cli/templates/secrets +16 -0
  19. data/lib/kamal/cli.rb +1 -0
  20. data/lib/kamal/commander/specifics.rb +3 -3
  21. data/lib/kamal/commander.rb +17 -9
  22. data/lib/kamal/commands/accessory.rb +7 -7
  23. data/lib/kamal/commands/app/assets.rb +8 -8
  24. data/lib/kamal/commands/app/proxy.rb +16 -0
  25. data/lib/kamal/commands/app.rb +7 -15
  26. data/lib/kamal/commands/auditor.rb +6 -3
  27. data/lib/kamal/commands/base.rb +8 -0
  28. data/lib/kamal/commands/builder/base.rb +2 -6
  29. data/lib/kamal/commands/builder/hybrid.rb +1 -1
  30. data/lib/kamal/commands/builder/remote.rb +27 -4
  31. data/lib/kamal/commands/builder.rb +1 -1
  32. data/lib/kamal/commands/docker.rb +4 -0
  33. data/lib/kamal/commands/hook.rb +5 -2
  34. data/lib/kamal/commands/lock.rb +2 -6
  35. data/lib/kamal/commands/proxy.rb +77 -0
  36. data/lib/kamal/commands/prune.rb +1 -9
  37. data/lib/kamal/commands/server.rb +11 -1
  38. data/lib/kamal/configuration/accessory.rb +14 -2
  39. data/lib/kamal/configuration/builder.rb +9 -3
  40. data/lib/kamal/configuration/docs/builder.yml +20 -10
  41. data/lib/kamal/configuration/docs/configuration.yml +16 -16
  42. data/lib/kamal/configuration/docs/env.yml +10 -11
  43. data/lib/kamal/configuration/docs/proxy.yml +100 -0
  44. data/lib/kamal/configuration/docs/registry.yml +4 -2
  45. data/lib/kamal/configuration/docs/role.yml +3 -5
  46. data/lib/kamal/configuration/env/tag.rb +4 -3
  47. data/lib/kamal/configuration/env.rb +10 -17
  48. data/lib/kamal/configuration/proxy.rb +66 -0
  49. data/lib/kamal/configuration/registry.rb +3 -2
  50. data/lib/kamal/configuration/role.rb +63 -94
  51. data/lib/kamal/configuration/validator/builder.rb +2 -0
  52. data/lib/kamal/configuration/validator/proxy.rb +11 -0
  53. data/lib/kamal/configuration/validator.rb +3 -1
  54. data/lib/kamal/configuration.rb +90 -33
  55. data/lib/kamal/env_file.rb +4 -0
  56. data/lib/kamal/secrets/adapters/base.rb +18 -0
  57. data/lib/kamal/secrets/adapters/bitwarden.rb +64 -0
  58. data/lib/kamal/secrets/adapters/last_pass.rb +30 -0
  59. data/lib/kamal/secrets/adapters/one_password.rb +61 -0
  60. data/lib/kamal/secrets/adapters/test.rb +10 -0
  61. data/lib/kamal/secrets/adapters.rb +14 -0
  62. data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +32 -0
  63. data/lib/kamal/secrets.rb +37 -0
  64. data/lib/kamal/sshkit_with_ext.rb +1 -0
  65. data/lib/kamal/utils.rb +12 -0
  66. data/lib/kamal/version.rb +1 -1
  67. data/lib/kamal.rb +3 -1
  68. metadata +23 -16
  69. data/lib/kamal/cli/env.rb +0 -54
  70. data/lib/kamal/cli/templates/sample_hooks/post-traefik-reboot.sample +0 -3
  71. data/lib/kamal/cli/templates/template.env +0 -2
  72. data/lib/kamal/cli/traefik.rb +0 -122
  73. data/lib/kamal/commands/app/cord.rb +0 -22
  74. data/lib/kamal/commands/traefik.rb +0 -85
  75. data/lib/kamal/configuration/docs/healthcheck.yml +0 -59
  76. data/lib/kamal/configuration/docs/traefik.yml +0 -62
  77. data/lib/kamal/configuration/healthcheck.rb +0 -63
  78. data/lib/kamal/configuration/traefik.rb +0 -60
  79. /data/lib/kamal/cli/templates/sample_hooks/{pre-traefik-reboot.sample → pre-proxy-reboot.sample} +0 -0
@@ -9,10 +9,6 @@ class Kamal::Cli::Main < Kamal::Cli::Base
9
9
  say "Ensure Docker is installed...", :magenta
10
10
  invoke "kamal:cli:server:bootstrap", [], invoke_options
11
11
 
12
- say "Evaluate and push env files...", :magenta
13
- invoke "kamal:cli:main:envify", [], invoke_options
14
- invoke "kamal:cli:env:push", [], invoke_options
15
-
16
12
  invoke "kamal:cli:accessory:boot", [ "all" ], invoke_options
17
13
  deploy
18
14
  end
@@ -37,10 +33,10 @@ class Kamal::Cli::Main < Kamal::Cli::Base
37
33
  end
38
34
 
39
35
  with_lock do
40
- run_hook "pre-deploy"
36
+ run_hook "pre-deploy", secrets: true
41
37
 
42
- say "Ensure Traefik is running...", :magenta
43
- invoke "kamal:cli:traefik:boot", [], invoke_options
38
+ say "Ensure kamal-proxy is running...", :magenta
39
+ invoke "kamal:cli:proxy:boot", [], invoke_options
44
40
 
45
41
  say "Detect stale containers...", :magenta
46
42
  invoke "kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)
@@ -52,10 +48,10 @@ class Kamal::Cli::Main < Kamal::Cli::Base
52
48
  end
53
49
  end
54
50
 
55
- run_hook "post-deploy", runtime: runtime.round
51
+ run_hook "post-deploy", secrets: true, runtime: runtime.round
56
52
  end
57
53
 
58
- desc "redeploy", "Deploy app to servers without bootstrapping servers, starting Traefik, pruning, and registry login"
54
+ desc "redeploy", "Deploy app to servers without bootstrapping servers, starting kamal-proxy, pruning, and registry login"
59
55
  option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
60
56
  def redeploy
61
57
  runtime = print_runtime do
@@ -70,7 +66,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
70
66
  end
71
67
 
72
68
  with_lock do
73
- run_hook "pre-deploy"
69
+ run_hook "pre-deploy", secrets: true
74
70
 
75
71
  say "Detect stale containers...", :magenta
76
72
  invoke "kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)
@@ -79,7 +75,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
79
75
  end
80
76
  end
81
77
 
82
- run_hook "post-deploy", runtime: runtime.round
78
+ run_hook "post-deploy", secrets: true, runtime: runtime.round
83
79
  end
84
80
 
85
81
  desc "rollback [VERSION]", "Rollback app to VERSION"
@@ -93,7 +89,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
93
89
  old_version = nil
94
90
 
95
91
  if container_available?(version)
96
- run_hook "pre-deploy"
92
+ run_hook "pre-deploy", secrets: true
97
93
 
98
94
  invoke "kamal:cli:app:boot", [], invoke_options.merge(version: version)
99
95
  rolled_back = true
@@ -103,12 +99,12 @@ class Kamal::Cli::Main < Kamal::Cli::Base
103
99
  end
104
100
  end
105
101
 
106
- run_hook "post-deploy", runtime: runtime.round if rolled_back
102
+ run_hook "post-deploy", secrets: true, runtime: runtime.round if rolled_back
107
103
  end
108
104
 
109
105
  desc "details", "Show details about all containers"
110
106
  def details
111
- invoke "kamal:cli:traefik:details"
107
+ invoke "kamal:cli:proxy:details"
112
108
  invoke "kamal:cli:app:details"
113
109
  invoke "kamal:cli:accessory:details", [ "all" ]
114
110
  end
@@ -127,7 +123,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
127
123
  end
128
124
  end
129
125
 
130
- desc "docs", "Show Kamal documentation for configuration setting"
126
+ desc "docs [SECTION]", "Show Kamal configuration documentation"
131
127
  def docs(section = nil)
132
128
  case section
133
129
  when NilClass
@@ -152,9 +148,10 @@ class Kamal::Cli::Main < Kamal::Cli::Base
152
148
  puts "Created configuration file in config/deploy.yml"
153
149
  end
154
150
 
155
- unless (deploy_file = Pathname.new(File.expand_path(".env"))).exist?
156
- FileUtils.cp_r Pathname.new(File.expand_path("templates/template.env", __dir__)), deploy_file
157
- puts "Created .env file"
151
+ unless (secrets_file = Pathname.new(File.expand_path(".kamal/secrets"))).exist?
152
+ FileUtils.mkdir_p secrets_file.dirname
153
+ FileUtils.cp_r Pathname.new(File.expand_path("templates/secrets", __dir__)), secrets_file
154
+ puts "Created .kamal/secrets file"
158
155
  end
159
156
 
160
157
  unless (hooks_dir = Pathname.new(File.expand_path(".kamal/hooks"))).exist?
@@ -179,44 +176,50 @@ class Kamal::Cli::Main < Kamal::Cli::Base
179
176
  end
180
177
  end
181
178
 
182
- desc "envify", "Create .env by evaluating .env.erb (or .env.staging.erb -> .env.staging when using -d staging)"
183
- option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip .env file push"
184
- def envify
185
- if destination = options[:destination]
186
- env_template_path = ".env.#{destination}.erb"
187
- env_path = ".env.#{destination}"
188
- else
189
- env_template_path = ".env.erb"
190
- env_path = ".env"
191
- end
192
-
193
- if Pathname.new(File.expand_path(env_template_path)).exist?
194
- # Ensure existing env doesn't pollute template evaluation
195
- content = with_original_env { ERB.new(File.read(env_template_path), trim_mode: "-").result }
196
- File.write(env_path, content, perm: 0600)
197
-
198
- unless options[:skip_push]
199
- reload_env
200
- invoke "kamal:cli:env:push", options
201
- end
202
- else
203
- puts "Skipping envify (no #{env_template_path} exist)"
204
- end
205
- end
206
-
207
- desc "remove", "Remove Traefik, app, accessories, and registry session from servers"
179
+ desc "remove", "Remove kamal-proxy, app, accessories, and registry session from servers"
208
180
  option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
209
181
  def remove
210
182
  confirming "This will remove all containers and images. Are you sure?" do
211
183
  with_lock do
212
- invoke "kamal:cli:traefik:remove", [], options.without(:confirmed)
213
184
  invoke "kamal:cli:app:remove", [], options.without(:confirmed)
185
+ invoke "kamal:cli:proxy:remove", [], options.without(:confirmed)
214
186
  invoke "kamal:cli:accessory:remove", [ "all" ], options
215
187
  invoke "kamal:cli:registry:logout", [], options.without(:confirmed).merge(skip_local: true)
216
188
  end
217
189
  end
218
190
  end
219
191
 
192
+ desc "upgrade", "Upgrade from Kamal 1.x to 2.0"
193
+ option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
194
+ option :rolling, type: :boolean, default: false, desc: "Upgrade one host at a time"
195
+ def upgrade
196
+ confirming "This will replace Traefik with kamal-proxy and restart all accessories" do
197
+ with_lock do
198
+ if options[:rolling]
199
+ (KAMAL.hosts | KAMAL.accessory_hosts).each do |host|
200
+ KAMAL.with_specific_hosts(host) do
201
+ say "Upgrading #{host}...", :magenta
202
+ if KAMAL.hosts.include?(host)
203
+ invoke "kamal:cli:proxy:upgrade", [], options.merge(confirmed: true, rolling: false)
204
+ reset_invocation(Kamal::Cli::Proxy)
205
+ end
206
+ if KAMAL.accessory_hosts.include?(host)
207
+ invoke "kamal:cli:accessory:upgrade", [ "all" ], options.merge(confirmed: true, rolling: false)
208
+ reset_invocation(Kamal::Cli::Accessory)
209
+ end
210
+ say "Upgraded #{host}", :magenta
211
+ end
212
+ end
213
+ else
214
+ say "Upgrading all hosts...", :magenta
215
+ invoke "kamal:cli:proxy:upgrade", [], options.merge(confirmed: true)
216
+ invoke "kamal:cli:accessory:upgrade", [ "all" ], options.merge(confirmed: true)
217
+ say "Upgraded all hosts", :magenta
218
+ end
219
+ end
220
+ end
221
+ end
222
+
220
223
  desc "version", "Show Kamal version"
221
224
  def version
222
225
  puts Kamal::VERSION
@@ -231,24 +234,24 @@ class Kamal::Cli::Main < Kamal::Cli::Base
231
234
  desc "build", "Build application image"
232
235
  subcommand "build", Kamal::Cli::Build
233
236
 
234
- desc "env", "Manage environment files"
235
- subcommand "env", Kamal::Cli::Env
236
-
237
237
  desc "lock", "Manage the deploy lock"
238
238
  subcommand "lock", Kamal::Cli::Lock
239
239
 
240
+ desc "proxy", "Manage kamal-proxy"
241
+ subcommand "proxy", Kamal::Cli::Proxy
242
+
240
243
  desc "prune", "Prune old application images and containers"
241
244
  subcommand "prune", Kamal::Cli::Prune
242
245
 
243
246
  desc "registry", "Login and -out of the image registry"
244
247
  subcommand "registry", Kamal::Cli::Registry
245
248
 
249
+ desc "secrets", "Helpers for extracting secrets"
250
+ subcommand "secrets", Kamal::Cli::Secrets
251
+
246
252
  desc "server", "Bootstrap servers with curl and Docker"
247
253
  subcommand "server", Kamal::Cli::Server
248
254
 
249
- desc "traefik", "Manage Traefik load balancer"
250
- subcommand "traefik", Kamal::Cli::Traefik
251
-
252
255
  private
253
256
  def container_available?(version)
254
257
  begin
@@ -0,0 +1,224 @@
1
+ class Kamal::Cli::Proxy < Kamal::Cli::Base
2
+ desc "boot", "Boot proxy on servers"
3
+ def boot
4
+ with_lock do
5
+ on(KAMAL.hosts) do |host|
6
+ execute *KAMAL.docker.create_network
7
+ rescue SSHKit::Command::Failed => e
8
+ raise unless e.message.include?("already exists")
9
+ end
10
+
11
+ on(KAMAL.proxy_hosts) do |host|
12
+ execute *KAMAL.registry.login
13
+
14
+ version = capture_with_info(*KAMAL.proxy.version).strip.presence
15
+
16
+ if version && Kamal::Utils.older_version?(version, Kamal::Configuration::PROXY_MINIMUM_VERSION)
17
+ raise "kamal-proxy version #{version} is too old, please reboot to update to at least #{Kamal::Configuration::PROXY_MINIMUM_VERSION}"
18
+ end
19
+ execute *KAMAL.proxy.start_or_run
20
+ end
21
+ end
22
+ end
23
+
24
+ desc "reboot", "Reboot proxy on servers (stop container, remove container, start new container)"
25
+ option :rolling, type: :boolean, default: false, desc: "Reboot proxy on hosts in sequence, rather than in parallel"
26
+ option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
27
+ def reboot
28
+ confirming "This will cause a brief outage on each host. Are you sure?" do
29
+ with_lock do
30
+ host_groups = options[:rolling] ? KAMAL.proxy_hosts : [ KAMAL.proxy_hosts ]
31
+ host_groups.each do |hosts|
32
+ host_list = Array(hosts).join(",")
33
+ run_hook "pre-proxy-reboot", hosts: host_list
34
+ on(hosts) do |host|
35
+ execute *KAMAL.auditor.record("Rebooted proxy"), verbosity: :debug
36
+ execute *KAMAL.registry.login
37
+
38
+ "Stopping and removing Traefik on #{host}, if running..."
39
+ execute *KAMAL.proxy.cleanup_traefik
40
+
41
+ "Stopping and removing kamal-proxy on #{host}, if running..."
42
+ execute *KAMAL.proxy.stop, raise_on_non_zero_exit: false
43
+ execute *KAMAL.proxy.remove_container
44
+
45
+ execute *KAMAL.proxy.run
46
+
47
+ KAMAL.roles_on(host).select(&:running_proxy?).each do |role|
48
+ app = KAMAL.app(role: role, host: host)
49
+
50
+ version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
51
+ endpoint = capture_with_info(*app.container_id_for_version(version)).strip
52
+
53
+ if endpoint.present?
54
+ info "Deploying #{endpoint} for role `#{role}` on #{host}..."
55
+ execute *app.deploy(target: endpoint)
56
+ end
57
+ end
58
+ end
59
+ run_hook "post-proxy-reboot", hosts: host_list
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ desc "upgrade", "Upgrade to kamal-proxy on servers (stop container, remove container, start new container, reboot app)", hide: true
66
+ option :rolling, type: :boolean, default: false, desc: "Reboot proxy on hosts in sequence, rather than in parallel"
67
+ option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
68
+ def upgrade
69
+ invoke_options = { "version" => KAMAL.config.latest_tag }.merge(options)
70
+
71
+ confirming "This will cause a brief outage on each host. Are you sure?" do
72
+ host_groups = options[:rolling] ? KAMAL.hosts : [ KAMAL.hosts ]
73
+ host_groups.each do |hosts|
74
+ host_list = Array(hosts).join(",")
75
+ say "Upgrading proxy on #{host_list}...", :magenta
76
+ run_hook "pre-proxy-reboot", hosts: host_list
77
+ on(hosts) do |host|
78
+ execute *KAMAL.auditor.record("Rebooted proxy"), verbosity: :debug
79
+ execute *KAMAL.registry.login
80
+
81
+ "Stopping and removing Traefik on #{host}, if running..."
82
+ execute *KAMAL.proxy.cleanup_traefik
83
+
84
+ "Stopping and removing kamal-proxy on #{host}, if running..."
85
+ execute *KAMAL.proxy.stop, raise_on_non_zero_exit: false
86
+ execute *KAMAL.proxy.remove_container
87
+ execute *KAMAL.proxy.remove_image
88
+ end
89
+
90
+ KAMAL.with_specific_hosts(hosts) do
91
+ invoke "kamal:cli:proxy:boot", [], invoke_options
92
+ reset_invocation(Kamal::Cli::Proxy)
93
+ invoke "kamal:cli:app:boot", [], invoke_options
94
+ reset_invocation(Kamal::Cli::App)
95
+ invoke "kamal:cli:prune:all", [], invoke_options
96
+ reset_invocation(Kamal::Cli::Prune)
97
+ end
98
+
99
+ run_hook "post-proxy-reboot", hosts: host_list
100
+ say "Upgraded proxy on #{host_list}", :magenta
101
+ end
102
+ end
103
+ end
104
+
105
+ desc "start", "Start existing proxy container on servers"
106
+ def start
107
+ with_lock do
108
+ on(KAMAL.proxy_hosts) do |host|
109
+ execute *KAMAL.auditor.record("Started proxy"), verbosity: :debug
110
+ execute *KAMAL.proxy.start
111
+ end
112
+ end
113
+ end
114
+
115
+ desc "stop", "Stop existing proxy container on servers"
116
+ def stop
117
+ with_lock do
118
+ on(KAMAL.proxy_hosts) do |host|
119
+ execute *KAMAL.auditor.record("Stopped proxy"), verbosity: :debug
120
+ execute *KAMAL.proxy.stop, raise_on_non_zero_exit: false
121
+ end
122
+ end
123
+ end
124
+
125
+ desc "restart", "Restart existing proxy container on servers"
126
+ def restart
127
+ with_lock do
128
+ stop
129
+ start
130
+ end
131
+ end
132
+
133
+ desc "details", "Show details about proxy container from servers"
134
+ def details
135
+ on(KAMAL.proxy_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.proxy.info), type: "Proxy" }
136
+ end
137
+
138
+ desc "logs", "Show log lines from proxy on servers"
139
+ option :since, aliases: "-s", desc: "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)"
140
+ option :lines, type: :numeric, aliases: "-n", desc: "Number of log lines to pull from each server"
141
+ option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
142
+ option :follow, aliases: "-f", desc: "Follow logs on primary server (or specific host set by --hosts)"
143
+ def logs
144
+ grep = options[:grep]
145
+
146
+ if options[:follow]
147
+ run_locally do
148
+ info "Following logs on #{KAMAL.primary_host}..."
149
+ info KAMAL.proxy.follow_logs(host: KAMAL.primary_host, grep: grep)
150
+ exec KAMAL.proxy.follow_logs(host: KAMAL.primary_host, grep: grep)
151
+ end
152
+ else
153
+ since = options[:since]
154
+ lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
155
+
156
+ on(KAMAL.proxy_hosts) do |host|
157
+ puts_by_host host, capture(*KAMAL.proxy.logs(since: since, lines: lines, grep: grep)), type: "Proxy"
158
+ end
159
+ end
160
+ end
161
+
162
+ desc "remove", "Remove proxy container and image from servers"
163
+ option :force, type: :boolean, default: false, desc: "Force removing proxy when apps are still installed"
164
+ def remove
165
+ with_lock do
166
+ if removal_allowed?(options[:force])
167
+ stop
168
+ remove_container
169
+ remove_image
170
+ remove_host_directory
171
+ end
172
+ end
173
+ end
174
+
175
+ desc "remove_container", "Remove proxy container from servers", hide: true
176
+ def remove_container
177
+ with_lock do
178
+ on(KAMAL.proxy_hosts) do
179
+ execute *KAMAL.auditor.record("Removed proxy container"), verbosity: :debug
180
+ execute *KAMAL.proxy.remove_container
181
+ end
182
+ end
183
+ end
184
+
185
+ desc "remove_image", "Remove proxy image from servers", hide: true
186
+ def remove_image
187
+ with_lock do
188
+ on(KAMAL.proxy_hosts) do
189
+ execute *KAMAL.auditor.record("Removed proxy image"), verbosity: :debug
190
+ execute *KAMAL.proxy.remove_image
191
+ end
192
+ end
193
+ end
194
+
195
+ desc "remove_host_directory", "Remove proxy directory from servers", hide: true
196
+ def remove_host_directory
197
+ with_lock do
198
+ on(KAMAL.proxy_hosts) do
199
+ execute *KAMAL.auditor.record("Removed #{KAMAL.config.proxy_directory}"), verbosity: :debug
200
+ execute *KAMAL.proxy.remove_host_directory, raise_on_non_zero_exit: false
201
+ end
202
+ end
203
+ end
204
+
205
+ private
206
+ def removal_allowed?(force)
207
+ on(KAMAL.proxy_hosts) do |host|
208
+ app_count = capture_with_info(*KAMAL.server.app_directory_count).chomp.to_i
209
+ raise "The are other applications installed on #{host}" if app_count > 0
210
+ end
211
+
212
+ true
213
+ rescue SSHKit::Runner::ExecuteError => e
214
+ raise unless e.message.include?("The are other applications installed on")
215
+
216
+ if force
217
+ say "Forcing, so removing the proxy, even though other apps are installed", :magenta
218
+ else
219
+ say "Not removing the proxy, as other apps are installed, ignore this check with kamal proxy remove --force", :magenta
220
+ end
221
+
222
+ force
223
+ end
224
+ end
@@ -28,7 +28,6 @@ class Kamal::Cli::Prune < Kamal::Cli::Base
28
28
  on(KAMAL.hosts) do
29
29
  execute *KAMAL.auditor.record("Pruned containers"), verbosity: :debug
30
30
  execute *KAMAL.prune.app_containers(retain: retain)
31
- execute *KAMAL.prune.healthcheck_containers
32
31
  end
33
32
  end
34
33
  end
@@ -0,0 +1,36 @@
1
+ class Kamal::Cli::Secrets < Kamal::Cli::Base
2
+ desc "fetch [SECRETS...]", "Fetch secrets from a vault"
3
+ option :adapter, type: :string, aliases: "-a", required: true, desc: "Which vault adapter to use"
4
+ option :account, type: :string, required: true, desc: "The account identifier or username"
5
+ option :from, type: :string, required: false, desc: "A vault or folder to fetch the secrets from"
6
+ option :inline, type: :boolean, required: false, hidden: true
7
+ def fetch(*secrets)
8
+ results = adapter(options[:adapter]).fetch(secrets, **options.slice(:account, :from).symbolize_keys)
9
+
10
+ return_or_puts JSON.dump(results).shellescape, inline: options[:inline]
11
+ end
12
+
13
+ desc "extract", "Extract a single secret from the results of a fetch call"
14
+ option :inline, type: :boolean, required: false, hidden: true
15
+ def extract(name, secrets)
16
+ parsed_secrets = JSON.parse(secrets)
17
+ value = parsed_secrets[name] || parsed_secrets.find { |k, v| k.end_with?("/#{name}") }&.last
18
+
19
+ raise "Could not find secret #{name}" if value.nil?
20
+
21
+ return_or_puts value, inline: options[:inline]
22
+ end
23
+
24
+ private
25
+ def adapter(adapter)
26
+ Kamal::Secrets::Adapters.lookup(adapter)
27
+ end
28
+
29
+ def return_or_puts(value, inline: nil)
30
+ if inline
31
+ value
32
+ else
33
+ puts value
34
+ end
35
+ end
36
+ end
@@ -36,8 +36,6 @@ class Kamal::Cli::Server < Kamal::Cli::Base
36
36
  missing << host
37
37
  end
38
38
  end
39
-
40
- execute(*KAMAL.server.ensure_run_directory)
41
39
  end
42
40
 
43
41
  if missing.any?
@@ -57,17 +57,6 @@ builder:
57
57
  # directories:
58
58
  # - data:/data
59
59
 
60
- # Configure custom arguments for Traefik. Be sure to reboot traefik when you modify it.
61
- # traefik:
62
- # args:
63
- # accesslog: true
64
- # accesslog.format: json
65
-
66
- # Configure a custom healthcheck (default is /up on port 3000)
67
- # healthcheck:
68
- # path: /healthz
69
- # port: 4000
70
-
71
60
  # Bridge fingerprinted assets, like JS and CSS, between versions to avoid
72
61
  # hitting 404 on in-flight requests. Combines all files from new and old
73
62
  # version inside the asset_path.
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ echo "Rebooted kamal-proxy on $KAMAL_HOSTS"
@@ -0,0 +1,16 @@
1
+ # WARNING: Avoid adding secrets directly to this file
2
+ # If you must, then add `.kamal/secrets*` to your .gitignore file
3
+
4
+ # Option 1: Read secrets from the environment
5
+ KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
6
+
7
+ # Option 2: Read secrets via a command
8
+ # RAILS_MASTER_KEY=$(cat config/master.key)
9
+
10
+ # Option 3: Read secrets via kamal secrets helpers
11
+ # These will handle logging in and fetching the secrets in as few calls as possible
12
+ # There are adapters for 1Password, LastPass + Bitwarden
13
+ #
14
+ # SECRETS=$(kamal secrets fetch --adapter 1password --account my-account --from MyVault/MyItem KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY)
15
+ # KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD $SECRETS)
16
+ # RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY $SECRETS)
data/lib/kamal/cli.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  module Kamal::Cli
2
+ class BootError < StandardError; end
2
3
  class HookError < StandardError; end
3
4
  class LockError < StandardError; end
4
5
  end
@@ -18,12 +18,12 @@ class Kamal::Commander::Specifics
18
18
  roles.select { |role| role.hosts.include?(host.to_s) }
19
19
  end
20
20
 
21
- def traefik_hosts
22
- config.traefik_hosts & specified_hosts
21
+ def proxy_hosts
22
+ config.proxy_hosts & specified_hosts
23
23
  end
24
24
 
25
25
  def accessory_hosts
26
- specific_hosts || config.accessories.flat_map(&:hosts)
26
+ config.accessories.flat_map(&:hosts) & specified_hosts
27
27
  end
28
28
 
29
29
  private
@@ -1,9 +1,10 @@
1
1
  require "active_support/core_ext/enumerable"
2
2
  require "active_support/core_ext/module/delegation"
3
+ require "active_support/core_ext/object/blank"
3
4
 
4
5
  class Kamal::Commander
5
6
  attr_accessor :verbosity, :holding_lock, :connected
6
- delegate :hosts, :roles, :primary_host, :primary_role, :roles_on, :traefik_hosts, :accessory_hosts, to: :specifics
7
+ delegate :hosts, :roles, :primary_host, :primary_role, :roles_on, :proxy_hosts, :accessory_hosts, to: :specifics
7
8
 
8
9
  def initialize
9
10
  self.verbosity = :info
@@ -23,6 +24,10 @@ class Kamal::Commander
23
24
  @config, @config_kwargs = nil, kwargs
24
25
  end
25
26
 
27
+ def configured?
28
+ @config || @config_kwargs
29
+ end
30
+
26
31
  attr_reader :specific_roles, :specific_hosts
27
32
 
28
33
  def specific_primary!
@@ -60,6 +65,13 @@ class Kamal::Commander
60
65
  end
61
66
  end
62
67
 
68
+ def with_specific_hosts(hosts)
69
+ original_hosts, self.specific_hosts = specific_hosts, hosts
70
+ yield
71
+ ensure
72
+ self.specific_hosts = original_hosts
73
+ end
74
+
63
75
  def accessory_names
64
76
  config.accessories&.collect(&:name) || []
65
77
  end
@@ -89,10 +101,6 @@ class Kamal::Commander
89
101
  @docker ||= Kamal::Commands::Docker.new(config)
90
102
  end
91
103
 
92
- def healthcheck
93
- @healthcheck ||= Kamal::Commands::Healthcheck.new(config)
94
- end
95
-
96
104
  def hook
97
105
  @hook ||= Kamal::Commands::Hook.new(config)
98
106
  end
@@ -101,6 +109,10 @@ class Kamal::Commander
101
109
  @lock ||= Kamal::Commands::Lock.new(config)
102
110
  end
103
111
 
112
+ def proxy
113
+ @proxy ||= Kamal::Commands::Proxy.new(config)
114
+ end
115
+
104
116
  def prune
105
117
  @prune ||= Kamal::Commands::Prune.new(config)
106
118
  end
@@ -113,10 +125,6 @@ class Kamal::Commander
113
125
  @server ||= Kamal::Commands::Server.new(config)
114
126
  end
115
127
 
116
- def traefik
117
- @traefik ||= Kamal::Commands::Traefik.new(config)
118
- end
119
-
120
128
  def alias(name)
121
129
  config.aliases[name]
122
130
  end
@@ -1,7 +1,9 @@
1
1
  class Kamal::Commands::Accessory < Kamal::Commands::Base
2
2
  attr_reader :accessory_config
3
3
  delegate :service_name, :image, :hosts, :port, :files, :directories, :cmd,
4
- :publish_args, :env_args, :volume_args, :label_args, :option_args, to: :accessory_config
4
+ :publish_args, :env_args, :volume_args, :label_args, :option_args,
5
+ :secrets_io, :secrets_path, :env_directory,
6
+ to: :accessory_config
5
7
 
6
8
  def initialize(config, name:)
7
9
  super(config)
@@ -13,6 +15,7 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
13
15
  "--name", service_name,
14
16
  "--detach",
15
17
  "--restart", "unless-stopped",
18
+ "--network", "kamal",
16
19
  *config.logging_args,
17
20
  *publish_args,
18
21
  *env_args,
@@ -61,6 +64,7 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
61
64
  docker :run,
62
65
  ("-it" if interactive),
63
66
  "--rm",
67
+ "--network", "kamal",
64
68
  *env_args,
65
69
  *volume_args,
66
70
  image,
@@ -98,12 +102,8 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
98
102
  docker :image, :rm, "--force", image
99
103
  end
100
104
 
101
- def make_env_directory
102
- make_directory accessory_config.env.secrets_directory
103
- end
104
-
105
- def remove_env_file
106
- [ :rm, "-f", accessory_config.env.secrets_file ]
105
+ def ensure_env_directory
106
+ make_directory env_directory
107
107
  end
108
108
 
109
109
  private