kamal 1.5.2 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kamal/cli/accessory.rb +30 -24
  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 +60 -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 +64 -53
  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 +6 -7
  13. data/lib/kamal/cli/main.rb +49 -44
  14. data/lib/kamal/cli/prune.rb +3 -3
  15. data/lib/kamal/cli/registry.rb +9 -10
  16. data/lib/kamal/cli/server.rb +39 -15
  17. data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +1 -1
  18. data/lib/kamal/cli/traefik.rb +13 -11
  19. data/lib/kamal/cli.rb +1 -1
  20. data/lib/kamal/commander.rb +6 -6
  21. data/lib/kamal/commands/accessory.rb +4 -4
  22. data/lib/kamal/commands/app/containers.rb +8 -0
  23. data/lib/kamal/commands/app/execution.rb +3 -3
  24. data/lib/kamal/commands/app/logging.rb +5 -5
  25. data/lib/kamal/commands/app.rb +6 -5
  26. data/lib/kamal/commands/base.rb +2 -3
  27. data/lib/kamal/commands/builder/base.rb +19 -12
  28. data/lib/kamal/commands/builder/clone.rb +28 -0
  29. data/lib/kamal/commands/builder/multiarch/remote.rb +10 -0
  30. data/lib/kamal/commands/builder/multiarch.rb +13 -9
  31. data/lib/kamal/commands/builder/native/cached.rb +14 -6
  32. data/lib/kamal/commands/builder/native/remote.rb +17 -9
  33. data/lib/kamal/commands/builder/native.rb +6 -7
  34. data/lib/kamal/commands/builder.rb +19 -11
  35. data/lib/kamal/commands/registry.rb +4 -13
  36. data/lib/kamal/commands/traefik.rb +8 -47
  37. data/lib/kamal/configuration/accessory.rb +30 -41
  38. data/lib/kamal/configuration/boot.rb +9 -4
  39. data/lib/kamal/configuration/builder.rb +61 -30
  40. data/lib/kamal/configuration/docs/accessory.yml +90 -0
  41. data/lib/kamal/configuration/docs/boot.yml +19 -0
  42. data/lib/kamal/configuration/docs/builder.yml +107 -0
  43. data/lib/kamal/configuration/docs/configuration.yml +157 -0
  44. data/lib/kamal/configuration/docs/env.yml +72 -0
  45. data/lib/kamal/configuration/docs/healthcheck.yml +59 -0
  46. data/lib/kamal/configuration/docs/logging.yml +21 -0
  47. data/lib/kamal/configuration/docs/registry.yml +49 -0
  48. data/lib/kamal/configuration/docs/role.yml +52 -0
  49. data/lib/kamal/configuration/docs/servers.yml +27 -0
  50. data/lib/kamal/configuration/docs/ssh.yml +46 -0
  51. data/lib/kamal/configuration/docs/sshkit.yml +23 -0
  52. data/lib/kamal/configuration/docs/traefik.yml +62 -0
  53. data/lib/kamal/configuration/env/tag.rb +12 -0
  54. data/lib/kamal/configuration/env.rb +10 -14
  55. data/lib/kamal/configuration/healthcheck.rb +63 -0
  56. data/lib/kamal/configuration/logging.rb +33 -0
  57. data/lib/kamal/configuration/registry.rb +31 -0
  58. data/lib/kamal/configuration/role.rb +72 -61
  59. data/lib/kamal/configuration/servers.rb +18 -0
  60. data/lib/kamal/configuration/ssh.rb +11 -8
  61. data/lib/kamal/configuration/sshkit.rb +9 -7
  62. data/lib/kamal/configuration/traefik.rb +60 -0
  63. data/lib/kamal/configuration/validation.rb +27 -0
  64. data/lib/kamal/configuration/validator/accessory.rb +9 -0
  65. data/lib/kamal/configuration/validator/builder.rb +9 -0
  66. data/lib/kamal/configuration/validator/env.rb +54 -0
  67. data/lib/kamal/configuration/validator/registry.rb +25 -0
  68. data/lib/kamal/configuration/validator/role.rb +11 -0
  69. data/lib/kamal/configuration/validator/servers.rb +7 -0
  70. data/lib/kamal/configuration/validator.rb +140 -0
  71. data/lib/kamal/configuration.rb +50 -63
  72. data/lib/kamal/git.rb +4 -0
  73. data/lib/kamal/sshkit_with_ext.rb +36 -0
  74. data/lib/kamal/version.rb +1 -1
  75. data/lib/kamal.rb +2 -0
  76. metadata +64 -9
  77. data/lib/kamal/cli/healthcheck.rb +0 -21
  78. data/lib/kamal/commands/healthcheck.rb +0 -59
@@ -0,0 +1,140 @@
1
+ class Kamal::Configuration::Validator
2
+ attr_reader :config, :example, :context
3
+
4
+ def initialize(config, example:, context:)
5
+ @config = config
6
+ @example = example
7
+ @context = context
8
+ end
9
+
10
+ def validate!
11
+ validate_against_example! config, example
12
+ end
13
+
14
+ private
15
+ def validate_against_example!(validation_config, example)
16
+ validate_type! validation_config, Hash
17
+
18
+ if (unknown_keys = validation_config.keys - example.keys).any?
19
+ unknown_keys_error unknown_keys
20
+ end
21
+
22
+ validation_config.each do |key, value|
23
+ with_context(key) do
24
+ example_value = example[key]
25
+
26
+ if example_value == "..."
27
+ validate_type! value, *(Array if key == :servers), Hash
28
+ elsif key == "hosts"
29
+ validate_servers! value
30
+ elsif example_value.is_a?(Array)
31
+ validate_array_of! value, example_value.first.class
32
+ elsif example_value.is_a?(Hash)
33
+ case key.to_s
34
+ when "options"
35
+ validate_type! value, Hash
36
+ when "args", "labels"
37
+ validate_hash_of! value, example_value.first[1].class
38
+ else
39
+ validate_against_example! value, example_value
40
+ end
41
+ else
42
+ validate_type! value, example_value.class
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+
49
+ def valid_type?(value, type)
50
+ value.is_a?(type) ||
51
+ (type == String && stringish?(value)) ||
52
+ (boolean?(type) && boolean?(value.class))
53
+ end
54
+
55
+ def type_description(type)
56
+ if type == Integer || type == Array
57
+ "an #{type.name.downcase}"
58
+ elsif type == TrueClass || type == FalseClass
59
+ "a boolean"
60
+ else
61
+ "a #{type.name.downcase}"
62
+ end
63
+ end
64
+
65
+ def boolean?(type)
66
+ type == TrueClass || type == FalseClass
67
+ end
68
+
69
+ def stringish?(value)
70
+ value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(TrueClass) || value.is_a?(FalseClass)
71
+ end
72
+
73
+ def validate_array_of!(array, type)
74
+ validate_type! array, Array
75
+
76
+ array.each_with_index do |value, index|
77
+ with_context(index) do
78
+ validate_type! value, type
79
+ end
80
+ end
81
+ end
82
+
83
+ def validate_hash_of!(hash, type)
84
+ validate_type! hash, Hash
85
+
86
+ hash.each do |key, value|
87
+ with_context(key) do
88
+ validate_type! value, type
89
+ end
90
+ end
91
+ end
92
+
93
+ def validate_servers!(servers)
94
+ validate_type! servers, Array
95
+
96
+ servers.each_with_index do |server, index|
97
+ with_context(index) do
98
+ validate_type! server, String, Hash
99
+
100
+ if server.is_a?(Hash)
101
+ error "multiple hosts found" unless server.size == 1
102
+ host, tags = server.first
103
+
104
+ with_context(host) do
105
+ validate_type! tags, String, Array
106
+ validate_array_of! tags, String if tags.is_a?(Array)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ def validate_type!(value, *types)
114
+ type_error(*types) unless types.any? { |type| valid_type?(value, type) }
115
+ end
116
+
117
+ def error(message)
118
+ raise Kamal::ConfigurationError, "#{error_context}#{message}"
119
+ end
120
+
121
+ def type_error(*expected_types)
122
+ error "should be #{expected_types.map { |type| type_description(type) }.join(" or ")}"
123
+ end
124
+
125
+ def unknown_keys_error(unknown_keys)
126
+ error "unknown #{"key".pluralize(unknown_keys.count)}: #{unknown_keys.join(", ")}"
127
+ end
128
+
129
+ def error_context
130
+ "#{context}: " if context.present?
131
+ end
132
+
133
+ def with_context(context)
134
+ old_context = @context
135
+ @context = [ @context, context ].select(&:present?).join("/")
136
+ yield
137
+ ensure
138
+ @context = old_context
139
+ end
140
+ end
@@ -1,15 +1,19 @@
1
1
  require "active_support/ordered_options"
2
2
  require "active_support/core_ext/string/inquiry"
3
3
  require "active_support/core_ext/module/delegation"
4
+ require "active_support/core_ext/hash/keys"
4
5
  require "pathname"
5
6
  require "erb"
6
7
  require "net/ssh/proxy/jump"
7
8
 
8
9
  class Kamal::Configuration
9
- delegate :service, :image, :servers, :labels, :registry, :stop_wait_time, :hooks_path, :logging, to: :raw_config, allow_nil: true
10
+ delegate :service, :image, :labels, :stop_wait_time, :hooks_path, to: :raw_config, allow_nil: true
10
11
  delegate :argumentize, :optionize, to: Kamal::Utils
11
12
 
12
13
  attr_reader :destination, :raw_config
14
+ attr_reader :accessories, :boot, :builder, :env, :healthcheck, :logging, :traefik, :servers, :ssh, :sshkit, :registry
15
+
16
+ include Validation
13
17
 
14
18
  class << self
15
19
  def create_from(config_file:, destination: nil, version: nil)
@@ -42,7 +46,29 @@ class Kamal::Configuration
42
46
  @raw_config = ActiveSupport::InheritableOptions.new(raw_config)
43
47
  @destination = destination
44
48
  @declared_version = version
45
- valid? if validate
49
+
50
+ validate! raw_config, example: validation_yml.symbolize_keys, context: ""
51
+
52
+ # Eager load config to validate it, these are first as they have dependencies later on
53
+ @servers = Servers.new(config: self)
54
+ @registry = Registry.new(config: self)
55
+
56
+ @accessories = @raw_config.accessories&.keys&.collect { |name| Accessory.new(name, config: self) } || []
57
+ @boot = Boot.new(config: self)
58
+ @builder = Builder.new(config: self)
59
+ @env = Env.new(config: @raw_config.env || {})
60
+
61
+ @healthcheck = Healthcheck.new(healthcheck_config: @raw_config.healthcheck)
62
+ @logging = Logging.new(logging_config: @raw_config.logging)
63
+ @traefik = Traefik.new(config: self)
64
+ @ssh = Ssh.new(config: self)
65
+ @sshkit = Sshkit.new(config: self)
66
+
67
+ ensure_destination_if_required
68
+ ensure_required_keys_present
69
+ ensure_valid_kamal_version
70
+ ensure_retain_containers_valid
71
+ ensure_valid_service_name
46
72
  end
47
73
 
48
74
 
@@ -71,17 +97,13 @@ class Kamal::Configuration
71
97
 
72
98
 
73
99
  def roles
74
- @roles ||= role_names.collect { |role_name| Role.new(role_name, config: self) }
100
+ servers.roles
75
101
  end
76
102
 
77
103
  def role(name)
78
104
  roles.detect { |r| r.name == name.to_s }
79
105
  end
80
106
 
81
- def accessories
82
- @accessories ||= raw_config.accessories&.keys&.collect { |name| Kamal::Configuration::Accessory.new(name, config: self) } || []
83
- end
84
-
85
107
  def accessory(name)
86
108
  accessories.detect { |a| a.name == name.to_s }
87
109
  end
@@ -120,7 +142,7 @@ class Kamal::Configuration
120
142
  end
121
143
 
122
144
  def repository
123
- [ raw_config.registry["server"], image ].compact.join("/")
145
+ [ registry.server, image ].compact.join("/")
124
146
  end
125
147
 
126
148
  def absolute_image
@@ -157,39 +179,9 @@ class Kamal::Configuration
157
179
  end
158
180
 
159
181
  def logging_args
160
- if logging.present?
161
- optionize({ "log-driver" => logging["driver"] }.compact) +
162
- argumentize("--log-opt", logging["options"])
163
- else
164
- argumentize("--log-opt", { "max-size" => "10m" })
165
- end
166
- end
167
-
168
-
169
- def boot
170
- Kamal::Configuration::Boot.new(config: self)
182
+ logging.args
171
183
  end
172
184
 
173
- def builder
174
- Kamal::Configuration::Builder.new(config: self)
175
- end
176
-
177
- def traefik
178
- raw_config.traefik || {}
179
- end
180
-
181
- def ssh
182
- Kamal::Configuration::Ssh.new(config: self)
183
- end
184
-
185
- def sshkit
186
- Kamal::Configuration::Sshkit.new(config: self)
187
- end
188
-
189
-
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 || {})
192
- end
193
185
 
194
186
  def healthcheck_service
195
187
  [ "healthcheck", service, destination ].compact.join("-")
@@ -229,15 +221,19 @@ class Kamal::Configuration
229
221
  File.join(run_directory, "env")
230
222
  end
231
223
 
232
- def env
233
- raw_config.env || {}
224
+ def env_tags
225
+ @env_tags ||= if (tags = raw_config.env["tags"])
226
+ tags.collect { |name, config| Env::Tag.new(name, config: config) }
227
+ else
228
+ []
229
+ end
234
230
  end
235
231
 
236
-
237
- def valid?
238
- ensure_destination_if_required && ensure_required_keys_present && ensure_valid_kamal_version && ensure_retain_containers_valid && ensure_valid_service_name
232
+ def env_tag(name)
233
+ env_tags.detect { |t| t.name == name.to_s }
239
234
  end
240
235
 
236
+
241
237
  def to_h
242
238
  {
243
239
  roles: role_names,
@@ -253,11 +249,10 @@ class Kamal::Configuration
253
249
  builder: builder.to_h,
254
250
  accessories: raw_config.accessories,
255
251
  logging: logging_args,
256
- healthcheck: healthcheck
252
+ healthcheck: healthcheck.to_h
257
253
  }.compact
258
254
  end
259
255
 
260
-
261
256
  private
262
257
  # Will raise ArgumentError if any required config keys are missing
263
258
  def ensure_destination_if_required
@@ -270,29 +265,21 @@ class Kamal::Configuration
270
265
 
271
266
  def ensure_required_keys_present
272
267
  %i[ service image registry servers ].each do |key|
273
- raise ArgumentError, "Missing required configuration for #{key}" unless raw_config[key].present?
274
- end
275
-
276
- if raw_config.registry["username"].blank?
277
- raise ArgumentError, "You must specify a username for the registry in config/deploy.yml"
278
- end
279
-
280
- if raw_config.registry["password"].blank?
281
- raise ArgumentError, "You must specify a password for the registry in config/deploy.yml (or set the ENV variable if that's used)"
268
+ raise Kamal::ConfigurationError, "Missing required configuration for #{key}" unless raw_config[key].present?
282
269
  end
283
270
 
284
- unless role_names.include?(primary_role_name)
285
- raise ArgumentError, "The primary_role #{primary_role_name} isn't defined"
271
+ unless role(primary_role_name).present?
272
+ raise Kamal::ConfigurationError, "The primary_role #{primary_role_name} isn't defined"
286
273
  end
287
274
 
288
275
  if primary_role.hosts.empty?
289
- raise ArgumentError, "No servers specified for the #{primary_role.name} primary_role"
276
+ raise Kamal::ConfigurationError, "No servers specified for the #{primary_role.name} primary_role"
290
277
  end
291
278
 
292
279
  unless allow_empty_roles?
293
280
  roles.each do |role|
294
281
  if role.hosts.empty?
295
- raise ArgumentError, "No servers specified for the #{role.name} role. You can ignore this with allow_empty_roles: true"
282
+ raise Kamal::ConfigurationError, "No servers specified for the #{role.name} role. You can ignore this with allow_empty_roles: true"
296
283
  end
297
284
  end
298
285
  end
@@ -301,21 +288,21 @@ class Kamal::Configuration
301
288
  end
302
289
 
303
290
  def ensure_valid_service_name
304
- raise ArgumentError, "Service name can only include alphanumeric characters, hyphens, and underscores" unless raw_config[:service] =~ /^[a-z0-9_-]+$/i
291
+ raise Kamal::ConfigurationError, "Service name can only include alphanumeric characters, hyphens, and underscores" unless raw_config[:service] =~ /^[a-z0-9_-]+$/i
305
292
 
306
293
  true
307
294
  end
308
295
 
309
296
  def ensure_valid_kamal_version
310
297
  if minimum_version && Gem::Version.new(minimum_version) > Gem::Version.new(Kamal::VERSION)
311
- raise ArgumentError, "Current version is #{Kamal::VERSION}, minimum required is #{minimum_version}"
298
+ raise Kamal::ConfigurationError, "Current version is #{Kamal::VERSION}, minimum required is #{minimum_version}"
312
299
  end
313
300
 
314
301
  true
315
302
  end
316
303
 
317
304
  def ensure_retain_containers_valid
318
- raise ArgumentError, "Must retain at least 1 container" if retain_containers < 1
305
+ raise Kamal::ConfigurationError, "Must retain at least 1 container" if retain_containers < 1
319
306
 
320
307
  true
321
308
  end
@@ -328,7 +315,7 @@ class Kamal::Configuration
328
315
  def git_version
329
316
  @git_version ||=
330
317
  if Kamal::Git.used?
331
- if Kamal::Git.uncommitted_changes.present? && !builder.git_archive?
318
+ if Kamal::Git.uncommitted_changes.present? && !builder.git_clone?
332
319
  uncommitted_suffix = "_uncommitted_#{SecureRandom.hex(8)}"
333
320
  end
334
321
  [ 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.7.0"
3
3
  end
data/lib/kamal.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  module Kamal
2
+ class ConfigurationError < StandardError; end
2
3
  end
3
4
 
4
5
  require "active_support"
5
6
  require "zeitwerk"
7
+ require "yaml"
6
8
 
7
9
  loader = Zeitwerk::Loader.for_gem
8
10
  loader.ignore(File.join(__dir__, "kamal", "sshkit_with_ext.rb"))
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.7.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-18 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
@@ -108,6 +114,26 @@ dependencies:
108
114
  - - "~>"
109
115
  - !ruby/object:Gem::Version
110
116
  version: '1.2'
117
+ - !ruby/object:Gem::Dependency
118
+ name: x25519
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '1.0'
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: 1.0.10
127
+ type: :runtime
128
+ prerelease: false
129
+ version_requirements: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - "~>"
132
+ - !ruby/object:Gem::Version
133
+ version: '1.0'
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: 1.0.10
111
137
  - !ruby/object:Gem::Dependency
112
138
  name: bcrypt_pbkdf
113
139
  requirement: !ruby/object:Gem::Requirement
@@ -210,8 +236,10 @@ files:
210
236
  - lib/kamal/cli/app/prepare_assets.rb
211
237
  - lib/kamal/cli/base.rb
212
238
  - lib/kamal/cli/build.rb
239
+ - lib/kamal/cli/build/clone.rb
213
240
  - lib/kamal/cli/env.rb
214
- - lib/kamal/cli/healthcheck.rb
241
+ - lib/kamal/cli/healthcheck/barrier.rb
242
+ - lib/kamal/cli/healthcheck/error.rb
215
243
  - lib/kamal/cli/healthcheck/poller.rb
216
244
  - lib/kamal/cli/lock.rb
217
245
  - lib/kamal/cli/main.rb
@@ -243,13 +271,13 @@ files:
243
271
  - lib/kamal/commands/base.rb
244
272
  - lib/kamal/commands/builder.rb
245
273
  - lib/kamal/commands/builder/base.rb
274
+ - lib/kamal/commands/builder/clone.rb
246
275
  - lib/kamal/commands/builder/multiarch.rb
247
276
  - lib/kamal/commands/builder/multiarch/remote.rb
248
277
  - lib/kamal/commands/builder/native.rb
249
278
  - lib/kamal/commands/builder/native/cached.rb
250
279
  - lib/kamal/commands/builder/native/remote.rb
251
280
  - lib/kamal/commands/docker.rb
252
- - lib/kamal/commands/healthcheck.rb
253
281
  - lib/kamal/commands/hook.rb
254
282
  - lib/kamal/commands/lock.rb
255
283
  - lib/kamal/commands/prune.rb
@@ -260,10 +288,37 @@ files:
260
288
  - lib/kamal/configuration/accessory.rb
261
289
  - lib/kamal/configuration/boot.rb
262
290
  - lib/kamal/configuration/builder.rb
291
+ - lib/kamal/configuration/docs/accessory.yml
292
+ - lib/kamal/configuration/docs/boot.yml
293
+ - lib/kamal/configuration/docs/builder.yml
294
+ - lib/kamal/configuration/docs/configuration.yml
295
+ - lib/kamal/configuration/docs/env.yml
296
+ - lib/kamal/configuration/docs/healthcheck.yml
297
+ - lib/kamal/configuration/docs/logging.yml
298
+ - lib/kamal/configuration/docs/registry.yml
299
+ - lib/kamal/configuration/docs/role.yml
300
+ - lib/kamal/configuration/docs/servers.yml
301
+ - lib/kamal/configuration/docs/ssh.yml
302
+ - lib/kamal/configuration/docs/sshkit.yml
303
+ - lib/kamal/configuration/docs/traefik.yml
263
304
  - lib/kamal/configuration/env.rb
305
+ - lib/kamal/configuration/env/tag.rb
306
+ - lib/kamal/configuration/healthcheck.rb
307
+ - lib/kamal/configuration/logging.rb
308
+ - lib/kamal/configuration/registry.rb
264
309
  - lib/kamal/configuration/role.rb
310
+ - lib/kamal/configuration/servers.rb
265
311
  - lib/kamal/configuration/ssh.rb
266
312
  - lib/kamal/configuration/sshkit.rb
313
+ - lib/kamal/configuration/traefik.rb
314
+ - lib/kamal/configuration/validation.rb
315
+ - lib/kamal/configuration/validator.rb
316
+ - lib/kamal/configuration/validator/accessory.rb
317
+ - lib/kamal/configuration/validator/builder.rb
318
+ - lib/kamal/configuration/validator/env.rb
319
+ - lib/kamal/configuration/validator/registry.rb
320
+ - lib/kamal/configuration/validator/role.rb
321
+ - lib/kamal/configuration/validator/servers.rb
267
322
  - lib/kamal/configuration/volume.rb
268
323
  - lib/kamal/env_file.rb
269
324
  - lib/kamal/git.rb
@@ -291,7 +346,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
291
346
  - !ruby/object:Gem::Version
292
347
  version: '0'
293
348
  requirements: []
294
- rubygems_version: 3.5.6
349
+ rubygems_version: 3.5.11
295
350
  signing_key:
296
351
  specification_version: 4
297
352
  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