dash 3.0.0 → 3.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6645870f4a0452741a5974d1d822e73fae0d4a04aec353196af3c02b3fb149d2
4
- data.tar.gz: 23d1738ee101bf3eda8c03c363845dacc396403767271f7fbda7cd6656b9f0ff
3
+ metadata.gz: 0c267bf789f49a3eede649d1b17a328fefe83367ae48ddde1ae45feec2185460
4
+ data.tar.gz: 16248804c04178338ca3a4187f5299135c6c25cd1f8691b16394e9b70f61f5ff
5
5
  SHA512:
6
- metadata.gz: 972ac9e339a4b56463c2f6caaf764a272cc01002101345e2696474fb01817d3a36dcb2c825b6f188521da82c764764323ff1b92b34ced280a330eb5a1d1bde0d
7
- data.tar.gz: 7e698f1dbb093d79787f7c48aec3053742c90bfe08261b942d66a208b8051f53b8154969f796c82e29dac893b531dd9bd945a11015f598f4ac76577b271ce1fe
6
+ metadata.gz: 12bf8d9a911c032b75256ef587f796503c062c9988c8fc6ae88448b019ecb3bcab0a06a3055a38a806fa56ca0ecc4cbafa2cdb105428b86a3084ae32972e6bd1
7
+ data.tar.gz: 89013ac78a74c960f6598517216fe536cc3eba9f237bde7f62af00c57eaed164c85c7584ca3aadbb0d566261b5a9cec429125ff39fd5de6792c515be5a8fb02a
@@ -6,6 +6,8 @@
6
6
  # loadbalancer keeps its service state in the config volume, which the
7
7
  # replacement container re-mounts, so every app's routes survive the gap.
8
8
  class Kamal::Cli::Proxy::LoadbalancerReboot
9
+ READY_TIMEOUT = 30
10
+
9
11
  attr_reader :host, :sshkit
10
12
  delegate :execute, :capture_with_info, :info, :upload!, to: :sshkit
11
13
 
@@ -33,9 +35,53 @@ class Kamal::Cli::Proxy::LoadbalancerReboot
33
35
  Kamal::Cli::Proxy::LoadbalancerClaim.new(host, sshkit).claim_run_config(replace: true)
34
36
  execute *KAMAL.loadbalancer.run
35
37
 
38
+ wait_until_ready
39
+ verify_service if re_register_service
40
+
36
41
  # kamal-proxy keeps its service state in the config volume, which the
37
42
  # replacement container re-mounts - every app's routes survive.
38
43
  services = capture_with_info(*KAMAL.loadbalancer.list).strip
39
44
  info "Services registered on the load balancer at #{host} after reboot:\n#{services}"
40
45
  end
46
+
47
+ private
48
+ def wait_until_ready
49
+ deadline = Time.now + READY_TIMEOUT
50
+
51
+ begin
52
+ capture_with_info(*KAMAL.loadbalancer.list, verbosity: :debug)
53
+ rescue SSHKit::Command::Failed
54
+ raise Kamal::Cli::BootError, "the load balancer on #{host} did not become ready within #{READY_TIMEOUT} seconds" if Time.now >= deadline
55
+ sleep 0.5
56
+ retry
57
+ end
58
+ end
59
+
60
+ # A deploy failure after this reboot must not strand a fresh LB with no
61
+ # services: re-register this app's routes now rather than trusting the
62
+ # deploy step that hasn't run yet, mirroring the per-host proxy reboot.
63
+ #
64
+ # Only this app's registration can be rebuilt from this deploy.yml - the
65
+ # owner files of other apps sharing the LB record tokens, not deploy
66
+ # commands - so anything else rides on the state-volume restore (which
67
+ # --recheck-targets-on-restore re-verifies). No owner record, or a foreign
68
+ # one, means nothing of ours to restore.
69
+ def re_register_service
70
+ owner = capture_with_info(*KAMAL.loadbalancer.read_service_owner, raise_on_non_zero_exit: false).strip
71
+ return false unless owner == KAMAL.loadbalancer_config.owner_token
72
+
73
+ info "Re-registering #{KAMAL.config.service} with the load balancer on #{host}..."
74
+ execute *KAMAL.loadbalancer.deploy(targets: KAMAL.loadbalancer_config.target_hosts)
75
+ true
76
+ end
77
+
78
+ # `list --json` returns {"services": {"<name>": ...}} - exact key
79
+ # membership, same as the per-host proxy reboot's verification.
80
+ def verify_service
81
+ listed = JSON.parse(capture_with_info(*KAMAL.loadbalancer.list(json: true))).fetch("services", {}).keys
82
+
83
+ unless listed.include?(KAMAL.config.service)
84
+ raise Kamal::Cli::BootError, "the load balancer on #{host} is missing service #{KAMAL.config.service} after reboot"
85
+ end
86
+ end
41
87
  end
@@ -83,6 +83,7 @@ class Kamal::Cli::Proxy::Reboot
83
83
  execute *proxy.run(digest: drift.expected_digest, name: proxy.next_container_name)
84
84
  wait_until_ready(name: proxy.next_container_name)
85
85
 
86
+ execute *proxy.disable_restart
86
87
  execute *proxy.drain(timeout: KAMAL.config.drain_timeout)
87
88
  execute *proxy.wait_for_exit
88
89
  execute *proxy.remove_stopped_container
@@ -426,14 +426,7 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
426
426
  end
427
427
  when "deploy"
428
428
  if KAMAL.config.proxy.load_balancing?
429
- targets = []
430
- KAMAL.config.roles.each do |role|
431
- next unless role.running_proxy?
432
-
433
- role.hosts.each do |host|
434
- targets << host
435
- end
436
- end
429
+ targets = KAMAL.loadbalancer_config.target_hosts
437
430
 
438
431
  on(KAMAL.config.proxy.effective_loadbalancer) do |host|
439
432
  Kamal::Cli::Proxy::LoadbalancerClaim.new(host, self).claim_service
@@ -44,8 +44,8 @@ class Kamal::Commands::Loadbalancer < Kamal::Commands::Base
44
44
  docker :exec, container_name, "kamal-proxy", "domains", subcommand
45
45
  end
46
46
 
47
- def list
48
- docker :exec, container_name, "kamal-proxy", :list
47
+ def list(json: false)
48
+ docker :exec, container_name, "kamal-proxy", :list, *("--json" if json)
49
49
  end
50
50
 
51
51
  # Cache policy is edge-only under load balancing (see the layering contract),
@@ -113,6 +113,12 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base
113
113
  container_id_for(container_name: proxy_run_config.holder_container_name, only_running: true)
114
114
  end
115
115
 
116
+ # Cancel the restart policy before draining: drain makes the proxy exit on
117
+ # its own, which - unlike `docker stop` - an active restart policy would undo.
118
+ def disable_restart
119
+ docker :update, "--restart=no", container_name
120
+ end
121
+
116
122
  def drain(timeout: nil)
117
123
  docker :exec, container_name, "kamal-proxy", :drain, *("--drain-timeout=#{timeout}s" if timeout)
118
124
  end
@@ -12,12 +12,24 @@ class Kamal::Configuration::Loadbalancer < Kamal::Configuration::Proxy
12
12
 
13
13
  # The load balancer fans a single service out to many targets, so unlike the
14
14
  # per-app proxy deploy (which takes one target) it takes the full list and
15
- # joins them into a single --target flag, honouring app_port for each.
15
+ # joins them into a single --target flag.
16
+ #
17
+ # Each target is a per-host proxy, reached on its published HTTP port
18
+ # (run.http_port, default 80) - the only cross-host surface it exposes.
19
+ # Never app_port: that is how a per-host proxy reaches the app container
20
+ # inside its own docker network, and nothing listens on it across hosts.
16
21
  def deploy_command_args(targets:)
17
- target_arg = targets.map { |target| "#{target}:#{app_port}" }.join(",")
22
+ target_arg = targets.map { |target| "#{target}:#{run.http_port}" }.join(",")
18
23
  optionize ({ target: target_arg }).merge(deploy_options), with: "="
19
24
  end
20
25
 
26
+ # The hosts the load balancer forwards to: every host of every role that
27
+ # runs a proxy. One source of truth for the deploy step and the reboot
28
+ # re-registration, so their target lists cannot diverge.
29
+ def target_hosts
30
+ config.roles.select(&:running_proxy?).flat_map(&:hosts)
31
+ end
32
+
21
33
  def directory
22
34
  File.join config.run_directory, "loadbalancer"
23
35
  end
data/lib/kamal/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Kamal
2
- VERSION = "3.0.0"
2
+ VERSION = "3.0.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dash
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson