dash 2.12.0 → 3.0.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/kamal/cli/app/boot.rb +47 -11
  3. data/lib/kamal/cli/app/rollout_boot.rb +59 -0
  4. data/lib/kamal/cli/app/ssl_certificates.rb +12 -3
  5. data/lib/kamal/cli/app.rb +74 -7
  6. data/lib/kamal/cli/base.rb +41 -32
  7. data/lib/kamal/cli/doctor/config_checks.rb +28 -0
  8. data/lib/kamal/cli/doctor/endpoint_checks.rb +114 -0
  9. data/lib/kamal/cli/doctor/host_checks.rb +178 -0
  10. data/lib/kamal/cli/doctor.rb +112 -0
  11. data/lib/kamal/cli/healthcheck/drift_error.rb +7 -0
  12. data/lib/kamal/cli/healthcheck/poller.rb +60 -10
  13. data/lib/kamal/cli/main.rb +63 -0
  14. data/lib/kamal/cli/proxy/drift.rb +39 -0
  15. data/lib/kamal/cli/proxy/loadbalancer_claim.rb +70 -0
  16. data/lib/kamal/cli/proxy/loadbalancer_reboot.rb +41 -0
  17. data/lib/kamal/cli/proxy/reboot.rb +172 -0
  18. data/lib/kamal/cli/proxy.rb +200 -26
  19. data/lib/kamal/cli/prune.rb +7 -4
  20. data/lib/kamal/cli/templates/sample_hooks/post-app-stop.sample +9 -0
  21. data/lib/kamal/cli/templates/sample_hooks/post-proxy-deploy.sample +3 -0
  22. data/lib/kamal/cli/templates/sample_hooks/pre-app-stop.sample +12 -0
  23. data/lib/kamal/cli/templates/sample_hooks/pre-proxy-deploy.sample +3 -0
  24. data/lib/kamal/cli.rb +1 -0
  25. data/lib/kamal/commands/app/proxy.rb +12 -0
  26. data/lib/kamal/commands/app.rb +8 -0
  27. data/lib/kamal/commands/base.rb +11 -1
  28. data/lib/kamal/commands/docker.rb +5 -0
  29. data/lib/kamal/commands/loadbalancer.rb +76 -34
  30. data/lib/kamal/commands/proxy.rb +105 -11
  31. data/lib/kamal/commands/prune.rb +16 -2
  32. data/lib/kamal/commands/server.rb +5 -0
  33. data/lib/kamal/configuration/accessory.rb +9 -8
  34. data/lib/kamal/configuration/boot.rb +38 -7
  35. data/lib/kamal/configuration/docs/accessory.yml +28 -1
  36. data/lib/kamal/configuration/docs/boot.yml +17 -2
  37. data/lib/kamal/configuration/docs/configuration.yml +2 -1
  38. data/lib/kamal/configuration/docs/proxy.yml +844 -24
  39. data/lib/kamal/configuration/docs/role.yml +86 -0
  40. data/lib/kamal/configuration/loadbalancer.rb +70 -7
  41. data/lib/kamal/configuration/proxy/acme.rb +69 -0
  42. data/lib/kamal/configuration/proxy/run.rb +194 -5
  43. data/lib/kamal/configuration/proxy.rb +495 -18
  44. data/lib/kamal/configuration/role/healthcheck.rb +95 -0
  45. data/lib/kamal/configuration/role.rb +104 -1
  46. data/lib/kamal/configuration/validator/proxy.rb +623 -10
  47. data/lib/kamal/configuration/validator/role.rb +26 -0
  48. data/lib/kamal/configuration/validator.rb +26 -3
  49. data/lib/kamal/configuration.rb +197 -0
  50. data/lib/kamal/sshkit_with_ext.rb +27 -2
  51. data/lib/kamal/utils.rb +22 -2
  52. data/lib/kamal/version.rb +1 -1
  53. data/lib/kamal.rb +11 -0
  54. metadata +18 -2
@@ -3,7 +3,7 @@ class Kamal::Configuration::Role
3
3
 
4
4
  delegate :argumentize, :optionize, to: Kamal::Utils
5
5
 
6
- attr_reader :name, :config, :specialized_env, :specialized_logging, :specialized_proxy
6
+ attr_reader :name, :config, :specialized_env, :specialized_logging, :specialized_proxy, :healthcheck
7
7
 
8
8
  alias to_s name
9
9
 
@@ -24,6 +24,13 @@ class Kamal::Configuration::Role
24
24
  logging_config: specializations.fetch("logging", {}),
25
25
  context: "servers/#{name}/logging"
26
26
 
27
+ # `healthcheck: false` is an opt-out, not a healthcheck — it leaves @healthcheck nil.
28
+ if healthcheck_config = specializations["healthcheck"]
29
+ @healthcheck = Kamal::Configuration::Role::Healthcheck.new \
30
+ healthcheck_config: healthcheck_config,
31
+ context: "servers/#{name}/healthcheck"
32
+ end
33
+
27
34
  initialize_specialized_proxy
28
35
  end
29
36
 
@@ -39,6 +46,14 @@ class Kamal::Configuration::Role
39
46
  tagged_hosts.fetch(host).collect { |tag| config.env_tag(tag) }.compact
40
47
  end
41
48
 
49
+ # The role's own hosts carrying `tag` in deploy.yml. Accessory `tag:`/`tags:` resolution
50
+ # comes through here rather than re-walking raw_config.servers, so it inherits
51
+ # extract_hosts_from_config's handling of every legal role shape — bare list, `hosts:`
52
+ # mapping, and the top-level `servers:` array.
53
+ def hosts_with_tag(tag)
54
+ tagged_hosts.select { |_host, tags| tags.include?(tag) }.keys
55
+ end
56
+
42
57
  def cmd
43
58
  specializations["cmd"]
44
59
  end
@@ -63,10 +78,40 @@ class Kamal::Configuration::Role
63
78
  logging.args
64
79
  end
65
80
 
81
+ # Kept out of option_args on purpose: Commands::App::Execution splats those into
82
+ # one-shot `kamal app exec` containers, which must not inherit a service healthcheck.
83
+ def healthcheck_args
84
+ healthcheck&.args || []
85
+ end
86
+
66
87
  def logging
67
88
  @logging ||= config.logging.merge(specialized_logging)
68
89
  end
69
90
 
91
+ # nil unless the role paces its own hosts. Deliberately not falling back to the global
92
+ # boot: that limit is already spent slicing the cross-role host list in
93
+ # Cli::App#host_boot_groups, and handing it to the per-role runner as well would sleep
94
+ # `boot.wait` a second time inside every group.
95
+ #
96
+ # Built on first read rather than in the initializer — Servers.new constructs every Role
97
+ # before Kamal::Configuration#initialize has finished assigning its own collaborators.
98
+ def boot
99
+ return @boot if defined?(@boot)
100
+
101
+ @boot =
102
+ if (boot_config = specializations["boot"])
103
+ Kamal::Configuration::Boot.new \
104
+ config: config, boot_config: boot_config, context: "servers/#{name}/boot"
105
+ end
106
+ end
107
+
108
+ # `hosts` is what on_roles is about to pace — `role.hosts & the run's hosts`, so
109
+ # --roles/--hosts have already narrowed it. A percentage limit has to count that, not
110
+ # the role's configured hosts.
111
+ def boot_runner_options(hosts)
112
+ boot&.runner_options_for(hosts) || {}
113
+ end
114
+
70
115
  def proxy
71
116
  @proxy ||= specialized_proxy.merge(config.proxy) if running_proxy?
72
117
  end
@@ -79,6 +124,49 @@ class Kamal::Configuration::Role
79
124
  running_proxy? && proxy.ssl?
80
125
  end
81
126
 
127
+ # Where a deploy of this role actually waits for readiness before stopping the
128
+ # old container. Without a proxy or a docker healthcheck, Healthcheck::Poller
129
+ # only sees `.State.Status`, so staying `running` for the readiness delay is
130
+ # the whole gate. `:healthcheck_exec` is the odd one out: the container declares no
131
+ # docker healthcheck, the deploy host polls the probe itself.
132
+ def readiness_source
133
+ if running_proxy?
134
+ :proxy
135
+ elsif healthcheck&.exec?
136
+ :healthcheck_exec
137
+ elsif healthcheck
138
+ :healthcheck
139
+ elsif health_cmd_option?
140
+ :docker_options
141
+ else
142
+ :none
143
+ end
144
+ end
145
+
146
+ # One-line rendering of readiness_source, shared by the deploy banner and `kamal doctor`
147
+ # so both name the same gate the same way.
148
+ def readiness_description
149
+ case readiness_source
150
+ when :proxy
151
+ [ "kamal-proxy health check", proxy.healthcheck_path ].compact.join(" ")
152
+ when :healthcheck
153
+ healthcheck.port ? "healthcheck #{healthcheck.path}:#{healthcheck.port}" : "healthcheck (custom cmd)"
154
+ when :healthcheck_exec
155
+ "healthcheck exec probe (#{healthcheck.exec})"
156
+ when :docker_options
157
+ "docker healthcheck (options: health-cmd)"
158
+ else
159
+ "NONE (old container stops #{readiness_delay}s after boot)"
160
+ end
161
+ end
162
+
163
+ # Whether the operator has made a readiness decision for this role at all — declared a
164
+ # healthcheck, hand-rolled a health-cmd option, or accepted the gap with `healthcheck: false`.
165
+ # Distinct from readiness_source, which reports what actually gates the deploy.
166
+ def readiness_gated?
167
+ healthcheck.present? || health_cmd_option? || healthcheck_disabled?
168
+ end
169
+
82
170
  def stop_args
83
171
  # When deploying with the proxy, kamal-proxy will drain request before returning so we don't need to wait.
84
172
  timeout = stop_timeout || (running_proxy? ? nil : config.drain_timeout)
@@ -90,6 +178,13 @@ class Kamal::Configuration::Role
90
178
  specializations["stop_timeout"] || config.stop_timeout
91
179
  end
92
180
 
181
+ # How long a role that has no healthcheck must merely keep running before the deploy
182
+ # accepts it. Role-specialized so a role that legitimately opts out can be tuned
183
+ # without slowing every other role down.
184
+ def readiness_delay
185
+ specializations["readiness_delay"] || config.readiness_delay
186
+ end
187
+
93
188
  def env(host)
94
189
  @envs ||= {}
95
190
  @envs[host] ||= [ config.env, specialized_env, *env_tags(host).map(&:env) ].reduce(:merge)
@@ -230,6 +325,14 @@ class Kamal::Configuration::Role
230
325
  docker_options.find { |key, _| key.to_s == "restart" }&.last
231
326
  end
232
327
 
328
+ def health_cmd_option?
329
+ docker_options.any? { |key, _| key.to_s == "health-cmd" }
330
+ end
331
+
332
+ def healthcheck_disabled?
333
+ specializations["healthcheck"] == false
334
+ end
335
+
233
336
  def custom_labels
234
337
  Hash.new.tap do |labels|
235
338
  labels.merge!(config.labels) if config.labels.present?