kamal 1.5.2 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kamal/cli/accessory.rb +25 -21
  3. data/lib/kamal/cli/app/boot.rb +70 -18
  4. data/lib/kamal/cli/app/prepare_assets.rb +1 -1
  5. data/lib/kamal/cli/app.rb +57 -47
  6. data/lib/kamal/cli/base.rb +26 -28
  7. data/lib/kamal/cli/build/clone.rb +61 -0
  8. data/lib/kamal/cli/build.rb +58 -50
  9. data/lib/kamal/cli/env.rb +5 -5
  10. data/lib/kamal/cli/healthcheck/barrier.rb +31 -0
  11. data/lib/kamal/cli/healthcheck/error.rb +2 -0
  12. data/lib/kamal/cli/healthcheck/poller.rb +4 -5
  13. data/lib/kamal/cli/main.rb +36 -43
  14. data/lib/kamal/cli/prune.rb +3 -3
  15. data/lib/kamal/cli/server.rb +39 -15
  16. data/lib/kamal/cli/traefik.rb +8 -8
  17. data/lib/kamal/commander.rb +6 -6
  18. data/lib/kamal/commands/app/containers.rb +8 -0
  19. data/lib/kamal/commands/app/execution.rb +3 -3
  20. data/lib/kamal/commands/app/logging.rb +2 -2
  21. data/lib/kamal/commands/app.rb +6 -5
  22. data/lib/kamal/commands/base.rb +2 -3
  23. data/lib/kamal/commands/builder/base.rb +6 -12
  24. data/lib/kamal/commands/builder/clone.rb +28 -0
  25. data/lib/kamal/commands/builder/multiarch.rb +9 -9
  26. data/lib/kamal/commands/builder/native/cached.rb +6 -7
  27. data/lib/kamal/commands/builder/native/remote.rb +9 -9
  28. data/lib/kamal/commands/builder/native.rb +6 -7
  29. data/lib/kamal/commands/builder.rb +2 -0
  30. data/lib/kamal/configuration/builder.rb +33 -2
  31. data/lib/kamal/configuration/env/tag.rb +12 -0
  32. data/lib/kamal/configuration/env.rb +1 -1
  33. data/lib/kamal/configuration/role.rb +29 -6
  34. data/lib/kamal/configuration.rb +14 -2
  35. data/lib/kamal/git.rb +4 -0
  36. data/lib/kamal/sshkit_with_ext.rb +36 -0
  37. data/lib/kamal/version.rb +1 -1
  38. metadata +18 -9
  39. data/lib/kamal/cli/healthcheck.rb +0 -21
  40. data/lib/kamal/commands/healthcheck.rb +0 -59
@@ -3,11 +3,12 @@ class Kamal::Commands::App < Kamal::Commands::Base
3
3
 
4
4
  ACTIVE_DOCKER_STATUSES = [ :running, :restarting ]
5
5
 
6
- attr_reader :role, :role
6
+ attr_reader :role, :host
7
7
 
8
- def initialize(config, role: nil)
8
+ def initialize(config, role: nil, host: nil)
9
9
  super(config)
10
10
  @role = role
11
+ @host = host
11
12
  end
12
13
 
13
14
  def run(hostname: nil)
@@ -18,7 +19,7 @@ class Kamal::Commands::App < Kamal::Commands::Base
18
19
  *([ "--hostname", hostname ] if hostname),
19
20
  "-e", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
20
21
  "-e", "KAMAL_VERSION=\"#{config.version}\"",
21
- *role.env_args,
22
+ *role.env_args(host),
22
23
  *role.health_check_args,
23
24
  *role.logging_args,
24
25
  *config.volume_args,
@@ -70,11 +71,11 @@ class Kamal::Commands::App < Kamal::Commands::Base
70
71
 
71
72
 
72
73
  def make_env_directory
73
- make_directory role.env.secrets_directory
74
+ make_directory role.env(host).secrets_directory
74
75
  end
75
76
 
76
77
  def remove_env_file
77
- [ :rm, "-f", role.env.secrets_file ]
78
+ [ :rm, "-f", role.env(host).secrets_file ]
78
79
  end
79
80
 
80
81
 
@@ -3,7 +3,6 @@ module Kamal::Commands
3
3
  delegate :sensitive, :argumentize, to: Kamal::Utils
4
4
 
5
5
  DOCKER_HEALTH_STATUS_FORMAT = "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'"
6
- DOCKER_HEALTH_LOG_FORMAT = "'{{json .State.Health}}'"
7
6
 
8
7
  attr_accessor :config
9
8
 
@@ -78,8 +77,8 @@ module Kamal::Commands
78
77
  args.compact.unshift :docker
79
78
  end
80
79
 
81
- def git(*args)
82
- args.compact.unshift :git
80
+ def git(*args, path: nil)
81
+ [ :git, *([ "-C", path ] if path), *args.compact ]
83
82
  end
84
83
 
85
84
  def tags(**details)
@@ -3,7 +3,7 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
3
3
  class BuilderError < StandardError; end
4
4
 
5
5
  delegate :argumentize, to: Kamal::Utils
6
- delegate :args, :secrets, :dockerfile, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, :ssh, :git_archive?, to: :builder_config
6
+ delegate :args, :secrets, :dockerfile, :target, :local_arch, :local_host, :remote_arch, :remote_host, :cache_from, :cache_to, :ssh, to: :builder_config
7
7
 
8
8
  def clean
9
9
  docker :image, :rm, "--force", config.absolute_image
@@ -13,18 +13,8 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
13
13
  docker :pull, config.absolute_image
14
14
  end
15
15
 
16
- def push
17
- if git_archive?
18
- pipe \
19
- git(:archive, "--format=tar", :HEAD),
20
- build_and_push
21
- else
22
- build_and_push
23
- end
24
- end
25
-
26
16
  def build_options
27
- [ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_ssh ]
17
+ [ *build_tags, *build_cache, *build_labels, *build_args, *build_secrets, *build_dockerfile, *build_target, *build_ssh ]
28
18
  end
29
19
 
30
20
  def build_context
@@ -73,6 +63,10 @@ class Kamal::Commands::Builder::Base < Kamal::Commands::Base
73
63
  end
74
64
  end
75
65
 
66
+ def build_target
67
+ argumentize "--target", target if target.present?
68
+ end
69
+
76
70
  def build_ssh
77
71
  argumentize "--ssh", ssh if ssh.present?
78
72
  end
@@ -0,0 +1,28 @@
1
+ module Kamal::Commands::Builder::Clone
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ delegate :clone_directory, :build_directory, to: :"config.builder"
6
+ end
7
+
8
+ def clone
9
+ git :clone, Kamal::Git.root, path: clone_directory
10
+ end
11
+
12
+ def clone_reset_steps
13
+ [
14
+ git(:remote, "set-url", :origin, Kamal::Git.root, path: build_directory),
15
+ git(:fetch, :origin, path: build_directory),
16
+ git(:reset, "--hard", Kamal::Git.revision, path: build_directory),
17
+ git(:clean, "-fdx", path: build_directory)
18
+ ]
19
+ end
20
+
21
+ def clone_status
22
+ git :status, "--porcelain", path: build_directory
23
+ end
24
+
25
+ def clone_revision
26
+ git :"rev-parse", :HEAD, path: build_directory
27
+ end
28
+ end
@@ -13,6 +13,15 @@ class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base
13
13
  docker(:buildx, :ls)
14
14
  end
15
15
 
16
+ def push
17
+ docker :buildx, :build,
18
+ "--push",
19
+ "--platform", platform_names,
20
+ "--builder", builder_name,
21
+ *build_options,
22
+ build_context
23
+ end
24
+
16
25
  private
17
26
  def builder_name
18
27
  "kamal-#{config.service}-multiarch"
@@ -25,13 +34,4 @@ class Kamal::Commands::Builder::Multiarch < Kamal::Commands::Builder::Base
25
34
  "linux/amd64,linux/arm64"
26
35
  end
27
36
  end
28
-
29
- def build_and_push
30
- docker :buildx, :build,
31
- "--push",
32
- "--platform", platform_names,
33
- "--builder", builder_name,
34
- *build_options,
35
- build_context
36
- end
37
37
  end
@@ -7,11 +7,10 @@ class Kamal::Commands::Builder::Native::Cached < Kamal::Commands::Builder::Nativ
7
7
  docker :buildx, :rm, builder_name
8
8
  end
9
9
 
10
- private
11
- def build_and_push
12
- docker :buildx, :build,
13
- "--push",
14
- *build_options,
15
- build_context
16
- end
10
+ def push
11
+ docker :buildx, :build,
12
+ "--push",
13
+ *build_options,
14
+ build_context
15
+ end
17
16
  end
@@ -17,6 +17,15 @@ class Kamal::Commands::Builder::Native::Remote < Kamal::Commands::Builder::Nativ
17
17
  docker(:buildx, :ls)
18
18
  end
19
19
 
20
+ def push
21
+ docker :buildx, :build,
22
+ "--push",
23
+ "--platform", platform,
24
+ "--builder", builder_name,
25
+ *build_options,
26
+ build_context
27
+ end
28
+
20
29
 
21
30
  private
22
31
  def builder_name
@@ -47,13 +56,4 @@ class Kamal::Commands::Builder::Native::Remote < Kamal::Commands::Builder::Nativ
47
56
  def remove_buildx
48
57
  docker :buildx, :rm, builder_name
49
58
  end
50
-
51
- def build_and_push
52
- docker :buildx, :build,
53
- "--push",
54
- "--platform", platform,
55
- "--builder", builder_name,
56
- *build_options,
57
- build_context
58
- end
59
59
  end
@@ -11,11 +11,10 @@ class Kamal::Commands::Builder::Native < Kamal::Commands::Builder::Base
11
11
  # No-op on native
12
12
  end
13
13
 
14
- private
15
- def build_and_push
16
- combine \
17
- docker(:build, *build_options, build_context),
18
- docker(:push, config.absolute_image),
19
- docker(:push, config.latest_image)
20
- end
14
+ def push
15
+ combine \
16
+ docker(:build, *build_options, build_context),
17
+ docker(:push, config.absolute_image),
18
+ docker(:push, config.latest_image)
19
+ end
21
20
  end
@@ -3,6 +3,8 @@ require "active_support/core_ext/string/filters"
3
3
  class Kamal::Commands::Builder < Kamal::Commands::Base
4
4
  delegate :create, :remove, :push, :clean, :pull, :info, :validate_image, to: :target
5
5
 
6
+ include Clone
7
+
6
8
  def name
7
9
  target.class.to_s.remove("Kamal::Commands::Builder::").underscore.inquiry
8
10
  end
@@ -3,6 +3,8 @@ class Kamal::Configuration::Builder
3
3
  @options = config.raw_config.builder || {}
4
4
  @image = config.image
5
5
  @server = config.registry["server"]
6
+ @service = config.service
7
+ @destination = config.destination
6
8
 
7
9
  valid?
8
10
  end
@@ -39,8 +41,12 @@ class Kamal::Configuration::Builder
39
41
  @options["dockerfile"] || "Dockerfile"
40
42
  end
41
43
 
44
+ def target
45
+ @options["target"]
46
+ end
47
+
42
48
  def context
43
- @options["context"] || (git_archive? ? "-" : ".")
49
+ @options["context"] || "."
44
50
  end
45
51
 
46
52
  def local_arch
@@ -85,10 +91,23 @@ class Kamal::Configuration::Builder
85
91
  @options["ssh"]
86
92
  end
87
93
 
88
- def git_archive?
94
+ def git_clone?
89
95
  Kamal::Git.used? && @options["context"].nil?
90
96
  end
91
97
 
98
+ def clone_directory
99
+ @clone_directory ||= File.join Dir.tmpdir, "kamal-clones", [ @service, pwd_sha ].compact.join("-")
100
+ end
101
+
102
+ def build_directory
103
+ @build_directory ||=
104
+ if git_clone?
105
+ File.join clone_directory, repo_basename, repo_relative_pwd
106
+ else
107
+ "."
108
+ end
109
+ end
110
+
92
111
  private
93
112
  def valid?
94
113
  if @options["cache"] && @options["cache"]["type"]
@@ -119,4 +138,16 @@ class Kamal::Configuration::Builder
119
138
  def cache_to_config_for_registry
120
139
  [ "type=registry", @options["cache"]&.fetch("options", nil), "ref=#{cache_image_ref}" ].compact.join(",")
121
140
  end
141
+
142
+ def repo_basename
143
+ File.basename(Kamal::Git.root)
144
+ end
145
+
146
+ def repo_relative_pwd
147
+ Dir.pwd.delete_prefix(Kamal::Git.root)
148
+ end
149
+
150
+ def pwd_sha
151
+ Digest::SHA256.hexdigest(Dir.pwd)[0..12]
152
+ end
122
153
  end
@@ -0,0 +1,12 @@
1
+ class Kamal::Configuration::Env::Tag
2
+ attr_reader :name, :config
3
+
4
+ def initialize(name, config:)
5
+ @name = name
6
+ @config = config
7
+ end
8
+
9
+ def env
10
+ Kamal::Configuration::Env.from_config(config: config)
11
+ end
12
+ end
@@ -4,7 +4,7 @@ class Kamal::Configuration::Env
4
4
 
5
5
  def self.from_config(config:, secrets_file: nil)
6
6
  secrets_keys = config.fetch("secret", [])
7
- clear = config.fetch("clear", config.key?("secret") ? {} : config)
7
+ clear = config.fetch("clear", config.key?("secret") || config.key?("tags") ? {} : config)
8
8
 
9
9
  new clear: clear, secrets_keys: secrets_keys, secrets_file: secrets_file
10
10
  end
@@ -7,6 +7,7 @@ class Kamal::Configuration::Role
7
7
 
8
8
  def initialize(name, config:)
9
9
  @name, @config = name.inquiry, config
10
+ @tagged_hosts ||= extract_tagged_hosts_from_config
10
11
  end
11
12
 
12
13
  def primary_host
@@ -14,7 +15,11 @@ class Kamal::Configuration::Role
14
15
  end
15
16
 
16
17
  def hosts
17
- @hosts ||= extract_hosts_from_config
18
+ tagged_hosts.keys
19
+ end
20
+
21
+ def env_tags(host)
22
+ tagged_hosts.fetch(host).collect { |tag| config.env_tag(tag) }
18
23
  end
19
24
 
20
25
  def cmd
@@ -50,12 +55,13 @@ class Kamal::Configuration::Role
50
55
  end
51
56
 
52
57
 
53
- def env
54
- @env ||= base_env.merge(specialized_env)
58
+ def env(host)
59
+ @envs ||= {}
60
+ @envs[host] ||= [ base_env, specialized_env, *env_tags(host).map(&:env) ].reduce(:merge)
55
61
  end
56
62
 
57
- def env_args
58
- env.args
63
+ def env_args(host)
64
+ env(host).args
59
65
  end
60
66
 
61
67
  def asset_volume_args
@@ -164,7 +170,24 @@ class Kamal::Configuration::Role
164
170
  end
165
171
 
166
172
  private
167
- attr_accessor :config
173
+ attr_accessor :config, :tagged_hosts
174
+
175
+ def extract_tagged_hosts_from_config
176
+ {}.tap do |tagged_hosts|
177
+ extract_hosts_from_config.map do |host_config|
178
+ if host_config.is_a?(Hash)
179
+ raise ArgumentError, "Multiple hosts found: #{host_config.inspect}" unless host_config.size == 1
180
+
181
+ host, tags = host_config.first
182
+ tagged_hosts[host] = Array(tags)
183
+ elsif host_config.is_a?(String) || host_config.is_a?(Symbol)
184
+ tagged_hosts[host_config] = []
185
+ else
186
+ raise ArgumentError, "Invalid host config: #{host_config.inspect}"
187
+ end
188
+ end
189
+ end
190
+ end
168
191
 
169
192
  def extract_hosts_from_config
170
193
  if config.servers.is_a?(Array)
@@ -188,7 +188,7 @@ class Kamal::Configuration
188
188
 
189
189
 
190
190
  def healthcheck
191
- { "path" => "/up", "port" => 3000, "max_attempts" => 7, "exposed_port" => 3999, "cord" => "/tmp/kamal-cord", "log_lines" => 50 }.merge(raw_config.healthcheck || {})
191
+ { "path" => "/up", "port" => 3000, "max_attempts" => 7, "cord" => "/tmp/kamal-cord", "log_lines" => 50 }.merge(raw_config.healthcheck || {})
192
192
  end
193
193
 
194
194
  def healthcheck_service
@@ -233,6 +233,18 @@ class Kamal::Configuration
233
233
  raw_config.env || {}
234
234
  end
235
235
 
236
+ def env_tags
237
+ @env_tags ||= if (tags = raw_config.env["tags"])
238
+ tags.collect { |name, config| Kamal::Configuration::Env::Tag.new(name, config: config) }
239
+ else
240
+ []
241
+ end
242
+ end
243
+
244
+ def env_tag(name)
245
+ env_tags.detect { |t| t.name == name.to_s }
246
+ end
247
+
236
248
 
237
249
  def valid?
238
250
  ensure_destination_if_required && ensure_required_keys_present && ensure_valid_kamal_version && ensure_retain_containers_valid && ensure_valid_service_name
@@ -328,7 +340,7 @@ class Kamal::Configuration
328
340
  def git_version
329
341
  @git_version ||=
330
342
  if Kamal::Git.used?
331
- if Kamal::Git.uncommitted_changes.present? && !builder.git_archive?
343
+ if Kamal::Git.uncommitted_changes.present? && !builder.git_clone?
332
344
  uncommitted_suffix = "_uncommitted_#{SecureRandom.hex(8)}"
333
345
  end
334
346
  [ Kamal::Git.revision, uncommitted_suffix ].compact.join
data/lib/kamal/git.rb CHANGED
@@ -16,4 +16,8 @@ module Kamal::Git
16
16
  def uncommitted_changes
17
17
  `git status --porcelain`.strip
18
18
  end
19
+
20
+ def root
21
+ `git rev-parse --show-toplevel`.strip
22
+ end
19
23
  end
@@ -103,3 +103,39 @@ class SSHKit::Backend::Netssh
103
103
 
104
104
  prepend LimitConcurrentStartsInstance
105
105
  end
106
+
107
+ class SSHKit::Runner::Parallel
108
+ # SSHKit joins the threads in sequence and fails on the first error it encounters, which means that we wait threads
109
+ # before the first failure to complete but not for ones after.
110
+ #
111
+ # We'll patch it to wait for them all to complete, and to record all the threads that errored so we can see when a
112
+ # problem occurs on multiple hosts.
113
+ module CompleteAll
114
+ def execute
115
+ threads = hosts.map do |host|
116
+ Thread.new(host) do |h|
117
+ backend(h, &block).run
118
+ rescue ::StandardError => e
119
+ e2 = SSHKit::Runner::ExecuteError.new e
120
+ raise e2, "Exception while executing #{host.user ? "as #{host.user}@" : "on host "}#{host}: #{e.message}"
121
+ end
122
+ end
123
+
124
+ exceptions = []
125
+ threads.each do |t|
126
+ begin
127
+ t.join
128
+ rescue SSHKit::Runner::ExecuteError => e
129
+ exceptions << e
130
+ end
131
+ end
132
+ if exceptions.one?
133
+ raise exceptions.first
134
+ elsif exceptions.many?
135
+ raise exceptions.first, [ "Exceptions on #{exceptions.count} hosts:", exceptions.map(&:message) ].join("\n")
136
+ end
137
+ end
138
+ end
139
+
140
+ prepend CompleteAll
141
+ end
data/lib/kamal/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Kamal
2
- VERSION = "1.5.2"
2
+ VERSION = "1.6.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kamal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.2
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-07 00:00:00.000000000 Z
11
+ date: 2024-06-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -28,16 +28,22 @@ dependencies:
28
28
  name: sshkit
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.22.2
34
+ - - "<"
32
35
  - !ruby/object:Gem::Version
33
- version: '1.21'
36
+ version: '2.0'
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
- - - "~>"
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.22.2
44
+ - - "<"
39
45
  - !ruby/object:Gem::Version
40
- version: '1.21'
46
+ version: '2.0'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: net-ssh
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -210,8 +216,10 @@ files:
210
216
  - lib/kamal/cli/app/prepare_assets.rb
211
217
  - lib/kamal/cli/base.rb
212
218
  - lib/kamal/cli/build.rb
219
+ - lib/kamal/cli/build/clone.rb
213
220
  - lib/kamal/cli/env.rb
214
- - lib/kamal/cli/healthcheck.rb
221
+ - lib/kamal/cli/healthcheck/barrier.rb
222
+ - lib/kamal/cli/healthcheck/error.rb
215
223
  - lib/kamal/cli/healthcheck/poller.rb
216
224
  - lib/kamal/cli/lock.rb
217
225
  - lib/kamal/cli/main.rb
@@ -243,13 +251,13 @@ files:
243
251
  - lib/kamal/commands/base.rb
244
252
  - lib/kamal/commands/builder.rb
245
253
  - lib/kamal/commands/builder/base.rb
254
+ - lib/kamal/commands/builder/clone.rb
246
255
  - lib/kamal/commands/builder/multiarch.rb
247
256
  - lib/kamal/commands/builder/multiarch/remote.rb
248
257
  - lib/kamal/commands/builder/native.rb
249
258
  - lib/kamal/commands/builder/native/cached.rb
250
259
  - lib/kamal/commands/builder/native/remote.rb
251
260
  - lib/kamal/commands/docker.rb
252
- - lib/kamal/commands/healthcheck.rb
253
261
  - lib/kamal/commands/hook.rb
254
262
  - lib/kamal/commands/lock.rb
255
263
  - lib/kamal/commands/prune.rb
@@ -261,6 +269,7 @@ files:
261
269
  - lib/kamal/configuration/boot.rb
262
270
  - lib/kamal/configuration/builder.rb
263
271
  - lib/kamal/configuration/env.rb
272
+ - lib/kamal/configuration/env/tag.rb
264
273
  - lib/kamal/configuration/role.rb
265
274
  - lib/kamal/configuration/ssh.rb
266
275
  - lib/kamal/configuration/sshkit.rb
@@ -291,7 +300,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
291
300
  - !ruby/object:Gem::Version
292
301
  version: '0'
293
302
  requirements: []
294
- rubygems_version: 3.5.6
303
+ rubygems_version: 3.5.10
295
304
  signing_key:
296
305
  specification_version: 4
297
306
  summary: Deploy web apps in containers to servers running Docker with zero downtime.
@@ -1,21 +0,0 @@
1
- class Kamal::Cli::Healthcheck < Kamal::Cli::Base
2
- default_command :perform
3
-
4
- desc "perform", "Health check current app version"
5
- def perform
6
- raise "The primary host is not configured to run Traefik" unless KAMAL.config.role(KAMAL.config.primary_role).running_traefik?
7
- on(KAMAL.primary_host) do
8
- begin
9
- execute *KAMAL.healthcheck.run
10
- Poller.wait_for_healthy { capture_with_info(*KAMAL.healthcheck.status) }
11
- rescue Poller::HealthcheckError => e
12
- error capture_with_info(*KAMAL.healthcheck.logs)
13
- error capture_with_pretty_json(*KAMAL.healthcheck.container_health_log)
14
- raise
15
- ensure
16
- execute *KAMAL.healthcheck.stop, raise_on_non_zero_exit: false
17
- execute *KAMAL.healthcheck.remove, raise_on_non_zero_exit: false
18
- end
19
- end
20
- end
21
- end
@@ -1,59 +0,0 @@
1
- class Kamal::Commands::Healthcheck < Kamal::Commands::Base
2
- def run
3
- primary = config.role(config.primary_role)
4
-
5
- docker :run,
6
- "--detach",
7
- "--name", container_name_with_version,
8
- "--publish", "#{exposed_port}:#{config.healthcheck["port"]}",
9
- "--label", "service=#{config.healthcheck_service}",
10
- "-e", "KAMAL_CONTAINER_NAME=\"#{config.healthcheck_service}\"",
11
- *primary.env_args,
12
- *primary.health_check_args(cord: false),
13
- *config.volume_args,
14
- *primary.option_args,
15
- config.absolute_image,
16
- primary.cmd
17
- end
18
-
19
- def status
20
- pipe container_id, xargs(docker(:inspect, "--format", DOCKER_HEALTH_STATUS_FORMAT))
21
- end
22
-
23
- def container_health_log
24
- pipe container_id, xargs(docker(:inspect, "--format", DOCKER_HEALTH_LOG_FORMAT))
25
- end
26
-
27
- def logs
28
- pipe container_id, xargs(docker(:logs, "--tail", log_lines, "2>&1"))
29
- end
30
-
31
- def stop
32
- pipe container_id, xargs(docker(:stop))
33
- end
34
-
35
- def remove
36
- pipe container_id, xargs(docker(:container, :rm))
37
- end
38
-
39
- private
40
- def container_name_with_version
41
- "#{config.healthcheck_service}-#{config.version}"
42
- end
43
-
44
- def container_id
45
- container_id_for(container_name: container_name_with_version)
46
- end
47
-
48
- def health_url
49
- "http://localhost:#{exposed_port}#{config.healthcheck["path"]}"
50
- end
51
-
52
- def exposed_port
53
- config.healthcheck["exposed_port"]
54
- end
55
-
56
- def log_lines
57
- config.healthcheck["log_lines"]
58
- end
59
- end