dash 3.2.1 → 4.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 +4 -4
- data/lib/dash/cli/app/boot.rb +2 -2
- data/lib/dash/cli/app.rb +1 -1
- data/lib/dash/cli/base.rb +1 -1
- data/lib/dash/cli/doctor/endpoint_checks.rb +1 -1
- data/lib/dash/cli/doctor/host_checks.rb +29 -8
- data/lib/dash/cli/main.rb +5 -5
- data/lib/dash/cli/proxy/legacy_rename.rb +58 -0
- data/lib/dash/cli/proxy/loadbalancer_reboot.rb +1 -1
- data/lib/dash/cli/proxy/reboot.rb +8 -8
- data/lib/dash/cli/proxy.rb +14 -10
- data/lib/dash/commander.rb +1 -1
- data/lib/dash/commands/accessory/proxy.rb +1 -1
- data/lib/dash/commands/app/execution.rb +1 -1
- data/lib/dash/commands/app/proxy.rb +1 -1
- data/lib/dash/commands/app.rb +1 -1
- data/lib/dash/commands/docker.rb +35 -1
- data/lib/dash/commands/loadbalancer.rb +47 -17
- data/lib/dash/commands/lock.rb +1 -1
- data/lib/dash/commands/proxy/cert_transfer.rb +4 -4
- data/lib/dash/commands/proxy.rb +117 -15
- data/lib/dash/commands/prune.rb +1 -1
- data/lib/dash/configuration/accessory.rb +2 -2
- data/lib/dash/configuration/docs/accessory.yml +3 -3
- data/lib/dash/configuration/docs/configuration.yml +1 -1
- data/lib/dash/configuration/docs/proxy.yml +38 -38
- data/lib/dash/configuration/loadbalancer.rb +2 -2
- data/lib/dash/configuration/proxy/acme.rb +5 -5
- data/lib/dash/configuration/proxy/boot.rb +2 -4
- data/lib/dash/configuration/proxy/run.rb +10 -10
- data/lib/dash/configuration/proxy.rb +36 -14
- data/lib/dash/configuration/role.rb +2 -2
- data/lib/dash/configuration/validator/proxy.rb +23 -23
- data/lib/dash/configuration.rb +8 -8
- data/lib/dash/utils.rb +1 -1
- data/lib/dash/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8651f7a91510814ccfa26283d3aef0f364caa5c99419a76ee6cdb15820680148
|
|
4
|
+
data.tar.gz: aef6a718f6f4d1427d6e20f73e3096eed8436426e32b6c6ea0a40f52e3588261
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: da7a30a00bf6f622aeeb13889670133285a202c5c4481a7b83cc8d980624db48a0c5d8007e0efaf723489d4ea20672999dc8791e5ff4240b6f4fa65ba54a7380
|
|
7
|
+
data.tar.gz: cdcf6391c305f5e84125687bb6f4174729a7c09819a08a5c8a26bd8de2fd5b10933606387f581187c94151574c2607d803689e3d97c2cffa00beab9e26375f09
|
data/lib/dash/cli/app/boot.rb
CHANGED
|
@@ -58,7 +58,7 @@ class Dash::Cli::App::Boot
|
|
|
58
58
|
raise Dash::Cli::BootError, "Failed to get endpoint for #{role} on #{host}, did the container boot?" if endpoint.empty?
|
|
59
59
|
|
|
60
60
|
run_hook "pre-proxy-deploy", hosts: host.to_s, role: role.name
|
|
61
|
-
info "Deploying #{role} on #{host} via
|
|
61
|
+
info "Deploying #{role} on #{host} via dash-proxy (waiting up to #{DASH.config.deploy_timeout}s for it to become healthy)..."
|
|
62
62
|
execute *app.deploy(target: endpoint)
|
|
63
63
|
run_hook "post-proxy-deploy", hosts: host.to_s, role: role.name
|
|
64
64
|
else
|
|
@@ -84,7 +84,7 @@ class Dash::Cli::App::Boot
|
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
# Every failed boot gets the container log, and the health probe history when the
|
|
87
|
-
# container declares a healthcheck — non-primary roles have no
|
|
87
|
+
# container declares a healthcheck — non-primary roles have no dash-proxy report to fall back on.
|
|
88
88
|
def dump_diagnostics
|
|
89
89
|
error capture_with_info(*app.logs(container_id: app.container_id_for_version(version)))
|
|
90
90
|
|
data/lib/dash/cli/app.rb
CHANGED
|
@@ -283,7 +283,7 @@ class Dash::Cli::App < Dash::Cli::Base
|
|
|
283
283
|
end
|
|
284
284
|
end
|
|
285
285
|
|
|
286
|
-
desc "rollout <deploy|set|stop>", "Manage a canary rollout of a new version through
|
|
286
|
+
desc "rollout <deploy|set|stop>", "Manage a canary rollout of a new version through dash-proxy"
|
|
287
287
|
option :percent, type: :numeric, desc: "Percentage of traffic to send to the rollout target"
|
|
288
288
|
option :list, type: :array, desc: "Send requests whose kamal-rollout cookie matches these values to the rollout target"
|
|
289
289
|
def rollout(action)
|
data/lib/dash/cli/base.rb
CHANGED
|
@@ -170,7 +170,7 @@ module Dash::Cli
|
|
|
170
170
|
raw ? DASH.with_verbosity(:error, &block) : block.call
|
|
171
171
|
end
|
|
172
172
|
|
|
173
|
-
#
|
|
173
|
+
# dash-proxy is one container per host, shared by every destination
|
|
174
174
|
# deployed there, but the deploy lock is per-destination — so two
|
|
175
175
|
# destinations deploying at once take different locks and both mutate the
|
|
176
176
|
# same proxy. Anything touching the proxy takes this lock as well.
|
|
@@ -2,7 +2,7 @@ require "resolv"
|
|
|
2
2
|
require "socket"
|
|
3
3
|
require "openssl"
|
|
4
4
|
|
|
5
|
-
# Local (no SSH) readiness checks for the domains
|
|
5
|
+
# Local (no SSH) readiness checks for the domains dash-proxy will serve:
|
|
6
6
|
# DNS resolution against the configured hosts and TLS certificate expiry.
|
|
7
7
|
class Dash::Cli::Doctor::EndpointChecks
|
|
8
8
|
CERTIFICATE_EXPIRY_WARN_DAYS = 14
|
|
@@ -61,12 +61,17 @@ class Dash::Cli::Doctor::HostChecks
|
|
|
61
61
|
|
|
62
62
|
def proxy_checks
|
|
63
63
|
version, version_error = capture_proxy_version
|
|
64
|
+
# A host that has not been through the stage-3c rename is still running
|
|
65
|
+
# kamal-proxy. Without asking about it too, the version check reports "not
|
|
66
|
+
# running" and the ports check blames a foreign process for the ports that
|
|
67
|
+
# container legitimately holds — failing the very deploy that migrates it.
|
|
68
|
+
legacy_version = version.present? ? nil : capture_legacy_proxy_version
|
|
64
69
|
|
|
65
70
|
{
|
|
66
71
|
proxy_image: proxy_image_check,
|
|
67
|
-
proxy_version: proxy_version_check(version, version_error),
|
|
72
|
+
proxy_version: proxy_version_check(version, version_error, legacy_version),
|
|
68
73
|
proxy_socket: proxy_socket_check(proxy_running: version.present?),
|
|
69
|
-
ports: ports_check(proxy_running: version.present?)
|
|
74
|
+
ports: ports_check(proxy_running: version.present?, legacy_proxy_running: legacy_version.present?)
|
|
70
75
|
}
|
|
71
76
|
end
|
|
72
77
|
|
|
@@ -76,6 +81,14 @@ class Dash::Cli::Doctor::HostChecks
|
|
|
76
81
|
[ nil, e ]
|
|
77
82
|
end
|
|
78
83
|
|
|
84
|
+
# Best-effort: a host with no legacy container simply reports nothing, and
|
|
85
|
+
# any error here must not turn into a failing check of its own.
|
|
86
|
+
def capture_legacy_proxy_version
|
|
87
|
+
capture_with_info(*DASH.proxy(host).version(name: Dash::Configuration::Proxy::LEGACY_CONTAINER_NAME)).strip.presence
|
|
88
|
+
rescue StandardError
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
|
|
79
92
|
def proxy_image_check
|
|
80
93
|
image = expected_proxy_image
|
|
81
94
|
|
|
@@ -88,11 +101,15 @@ class Dash::Cli::Doctor::HostChecks
|
|
|
88
101
|
result :proxy_image, :fail, "#{e.class}: #{e.message}"
|
|
89
102
|
end
|
|
90
103
|
|
|
91
|
-
def proxy_version_check(version, error)
|
|
104
|
+
def proxy_version_check(version, error, legacy_version = nil)
|
|
92
105
|
minimum = Dash::Configuration::Proxy::Run::MINIMUM_VERSION
|
|
93
106
|
|
|
94
107
|
if error
|
|
95
108
|
result :proxy_version, :warn, "could not determine the running version (#{error.message})"
|
|
109
|
+
elsif version.nil? && legacy_version.present?
|
|
110
|
+
result :proxy_version, :ok,
|
|
111
|
+
"#{Dash::Configuration::Proxy::LEGACY_CONTAINER_NAME} #{legacy_version} is running; it is renamed to " \
|
|
112
|
+
"#{Dash::Configuration::Proxy::CONTAINER_NAME} on the next deploy, which briefly interrupts this host"
|
|
96
113
|
elsif version.nil?
|
|
97
114
|
result :proxy_version, :ok, "not running (will be started on deploy)"
|
|
98
115
|
elsif Dash::Utils.older_version?(version, minimum)
|
|
@@ -127,13 +144,13 @@ class Dash::Cli::Doctor::HostChecks
|
|
|
127
144
|
if mounted.include?(expected)
|
|
128
145
|
result :proxy_socket, :ok, "docker socket #{expected} is mounted"
|
|
129
146
|
elsif sleep_configured?
|
|
130
|
-
result :proxy_socket, :fail, "the running
|
|
147
|
+
result :proxy_socket, :fail, "the running dash-proxy has no #{expected} mount, so sleeping services never wake - run `dash proxy reboot`"
|
|
131
148
|
else
|
|
132
149
|
# Nothing sleeps yet, so nothing hangs - drift rather than breakage.
|
|
133
|
-
result :proxy_socket, :warn, "the running
|
|
150
|
+
result :proxy_socket, :warn, "the running dash-proxy has no #{expected} mount - run `dash proxy reboot` to apply the current configuration"
|
|
134
151
|
end
|
|
135
152
|
elsif (stray = mounted.grep(DOCKER_SOCKET_PATTERN).first)
|
|
136
|
-
result :proxy_socket, :warn, "the running
|
|
153
|
+
result :proxy_socket, :warn, "the running dash-proxy mounts #{stray} but the config no longer asks for it - " \
|
|
137
154
|
"the socket is root-equivalent host access; `dash proxy reboot` removes it"
|
|
138
155
|
else
|
|
139
156
|
result :proxy_socket, :ok, "no docker socket configured or mounted"
|
|
@@ -146,7 +163,7 @@ class Dash::Cli::Doctor::HostChecks
|
|
|
146
163
|
DASH.config.roles.any? { |role| role.running_proxy? && role.proxy.proxy_config["sleep"].present? }
|
|
147
164
|
end
|
|
148
165
|
|
|
149
|
-
def ports_check(proxy_running:)
|
|
166
|
+
def ports_check(proxy_running:, legacy_proxy_running: false)
|
|
150
167
|
run_config = DASH.config.proxy_run(host)
|
|
151
168
|
return result(:ports, :ok, "proxy ports are not published, nothing to check") if run_config && !run_config.publish?
|
|
152
169
|
|
|
@@ -154,7 +171,11 @@ class Dash::Cli::Doctor::HostChecks
|
|
|
154
171
|
https_port = run_config&.https_port || Dash::Configuration::Proxy::Run::DEFAULT_HTTPS_PORT
|
|
155
172
|
|
|
156
173
|
if proxy_running
|
|
157
|
-
result :ports, :ok, "ports #{http_port}/#{https_port} held by the running
|
|
174
|
+
result :ports, :ok, "ports #{http_port}/#{https_port} held by the running dash-proxy"
|
|
175
|
+
elsif legacy_proxy_running
|
|
176
|
+
result :ports, :ok,
|
|
177
|
+
"ports #{http_port}/#{https_port} held by the running #{Dash::Configuration::Proxy::LEGACY_CONTAINER_NAME}, " \
|
|
178
|
+
"replaced on the next deploy"
|
|
158
179
|
elsif (busy = busy_ports(http_port, https_port)).any?
|
|
159
180
|
result :ports, :fail, "port(s) #{busy.join(", ")} already in use by another process"
|
|
160
181
|
else
|
data/lib/dash/cli/main.rb
CHANGED
|
@@ -39,7 +39,7 @@ class Dash::Cli::Main < Dash::Cli::Base
|
|
|
39
39
|
modify(lock: true) do
|
|
40
40
|
run_hook "pre-deploy", secrets: true
|
|
41
41
|
|
|
42
|
-
say "Ensure
|
|
42
|
+
say "Ensure dash-proxy is running...", :magenta
|
|
43
43
|
invoke "dash:cli:proxy:boot", [], invoke_options
|
|
44
44
|
|
|
45
45
|
invoke "dash:cli:accessory:boot", [ "all" ], invoke_options if boot_accessories
|
|
@@ -63,7 +63,7 @@ class Dash::Cli::Main < Dash::Cli::Base
|
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
desc "redeploy", "Deploy app to servers without bootstrapping servers, starting
|
|
66
|
+
desc "redeploy", "Deploy app to servers without bootstrapping servers, starting dash-proxy and pruning"
|
|
67
67
|
option :skip_push, aliases: "-P", type: :boolean, default: false, desc: "Skip image build and push"
|
|
68
68
|
option :no_cache, type: :boolean, default: false, desc: "Build without using Docker's build cache"
|
|
69
69
|
def redeploy
|
|
@@ -226,7 +226,7 @@ class Dash::Cli::Main < Dash::Cli::Base
|
|
|
226
226
|
end
|
|
227
227
|
end
|
|
228
228
|
|
|
229
|
-
desc "remove", "Remove
|
|
229
|
+
desc "remove", "Remove dash-proxy, app, accessories, and registry session from servers"
|
|
230
230
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
|
231
231
|
def remove
|
|
232
232
|
confirming "This will remove all containers and images. Are you sure?" do
|
|
@@ -249,7 +249,7 @@ class Dash::Cli::Main < Dash::Cli::Base
|
|
|
249
249
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
|
250
250
|
option :rolling, type: :boolean, default: false, desc: "Upgrade one host at a time"
|
|
251
251
|
def upgrade
|
|
252
|
-
confirming "This will replace Traefik with
|
|
252
|
+
confirming "This will replace Traefik with dash-proxy and restart all accessories" do
|
|
253
253
|
modify(lock: true) do
|
|
254
254
|
if options[:rolling]
|
|
255
255
|
DASH.hosts.each do |host|
|
|
@@ -293,7 +293,7 @@ class Dash::Cli::Main < Dash::Cli::Base
|
|
|
293
293
|
desc "lock", "Manage the deploy lock"
|
|
294
294
|
subcommand "lock", Dash::Cli::Lock
|
|
295
295
|
|
|
296
|
-
desc "proxy", "Manage
|
|
296
|
+
desc "proxy", "Manage dash-proxy"
|
|
297
297
|
subcommand "proxy", Dash::Cli::Proxy
|
|
298
298
|
|
|
299
299
|
desc "prune", "Prune old application images and containers"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Stage 3c migration: brings a host that still carries pre-rename container
|
|
2
|
+
# identity onto the renamed one.
|
|
3
|
+
#
|
|
4
|
+
# Three steps, run in this order on each proxy host before the normal boot:
|
|
5
|
+
#
|
|
6
|
+
# 1. Bridge the network. Docker cannot rename one, so `dash` is created
|
|
7
|
+
# alongside `kamal` and everything still attached to the old network joins
|
|
8
|
+
# the new one. App containers would be replaced by the next deploy anyway;
|
|
9
|
+
# accessories are not, which is the whole reason this exists — without it a
|
|
10
|
+
# renamed proxy cannot reach `db` or `redis`.
|
|
11
|
+
#
|
|
12
|
+
# 2. Copy the config volume. It holds the routing table and the ACME account
|
|
13
|
+
# and certificate cache, so losing it means re-issuing every certificate
|
|
14
|
+
# and spending Let's Encrypt rate limits to get back to where we were. This
|
|
15
|
+
# must happen before the new container starts.
|
|
16
|
+
#
|
|
17
|
+
# 3. Replace the legacy container. A rename means the old container has to
|
|
18
|
+
# release ports 80/443 before the new one can claim them, and no
|
|
19
|
+
# port-holder handoff spans two container names — so this stage accepts a
|
|
20
|
+
# brief outage per host. Deliberate; see zoolutions/dash#124.
|
|
21
|
+
#
|
|
22
|
+
# Every step is idempotent and guarded on its destination not already existing,
|
|
23
|
+
# so a second deploy is a no-op. Nothing here removes the legacy network or
|
|
24
|
+
# volume: an operator who wants them gone removes them by hand, and stage 3d
|
|
25
|
+
# deletes this class outright.
|
|
26
|
+
class Dash::Cli::Proxy::LegacyRename
|
|
27
|
+
attr_reader :host, :sshkit
|
|
28
|
+
delegate :execute, to: :sshkit
|
|
29
|
+
|
|
30
|
+
def initialize(host, sshkit)
|
|
31
|
+
@host = host
|
|
32
|
+
@sshkit = sshkit
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def run
|
|
36
|
+
bridge_network
|
|
37
|
+
adopt_config_volume
|
|
38
|
+
replace_legacy_container
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
def bridge_network
|
|
43
|
+
execute *DASH.docker.connect_legacy_network_containers
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def adopt_config_volume
|
|
47
|
+
execute *DASH.proxy(host).copy_legacy_config_volume
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# The drain timeout the proxy is configured with, so a busy host is not cut
|
|
51
|
+
# off mid-request any more abruptly than a normal reboot would.
|
|
52
|
+
def replace_legacy_container
|
|
53
|
+
proxy = DASH.proxy(host)
|
|
54
|
+
|
|
55
|
+
execute *proxy.remove_legacy_container(timeout: DASH.config.drain_timeout)
|
|
56
|
+
execute *proxy.remove_legacy_holder_container
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -38,7 +38,7 @@ class Dash::Cli::Proxy::LoadbalancerReboot
|
|
|
38
38
|
wait_until_ready
|
|
39
39
|
verify_service if re_register_service
|
|
40
40
|
|
|
41
|
-
#
|
|
41
|
+
# dash-proxy keeps its service state in the config volume, which the
|
|
42
42
|
# replacement container re-mounts - every app's routes survive.
|
|
43
43
|
services = capture_with_info(*DASH.loadbalancer.list).strip
|
|
44
44
|
info "Services registered on the load balancer at #{host} after reboot:\n#{services}"
|
|
@@ -31,7 +31,7 @@ class Dash::Cli::Proxy::Reboot
|
|
|
31
31
|
# Pull before stopping the old container: a registry outage or rate limit
|
|
32
32
|
# can then no longer strand the host with no proxy at all.
|
|
33
33
|
def pull_image
|
|
34
|
-
info "Pulling
|
|
34
|
+
info "Pulling dash-proxy image on #{host}..."
|
|
35
35
|
execute *proxy.pull
|
|
36
36
|
end
|
|
37
37
|
|
|
@@ -64,7 +64,7 @@ class Dash::Cli::Proxy::Reboot
|
|
|
64
64
|
|
|
65
65
|
# Phase 1 replacement: stop the old container, start the new one. Brief gap.
|
|
66
66
|
def stop_and_replace
|
|
67
|
-
info "Stopping and removing
|
|
67
|
+
info "Stopping and removing dash-proxy on #{host}, if running..."
|
|
68
68
|
execute *proxy.stop(timeout: DASH.config.drain_timeout + 10), raise_on_non_zero_exit: false
|
|
69
69
|
execute *proxy.remove_container
|
|
70
70
|
|
|
@@ -87,7 +87,7 @@ class Dash::Cli::Proxy::Reboot
|
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def handoff_generation
|
|
90
|
-
info "Handing off
|
|
90
|
+
info "Handing off dash-proxy on #{host} to a new generation (zero downtime)..."
|
|
91
91
|
execute *proxy.remove_stopped_container(name: proxy.next_container_name), raise_on_non_zero_exit: false
|
|
92
92
|
execute *proxy.run(digest: drift.expected_digest, name: proxy.next_container_name)
|
|
93
93
|
wait_until_ready(name: proxy.next_container_name)
|
|
@@ -102,7 +102,7 @@ class Dash::Cli::Proxy::Reboot
|
|
|
102
102
|
# The old proxy still owns the published ports, so the holder cannot bind
|
|
103
103
|
# them until it is gone. One final replacement with a brief gap.
|
|
104
104
|
def migrate_to_holder
|
|
105
|
-
info "Migrating
|
|
105
|
+
info "Migrating dash-proxy on #{host} to the port-holder architecture (brief gap)..."
|
|
106
106
|
execute *proxy.stop(timeout: DASH.config.drain_timeout + 10), raise_on_non_zero_exit: false
|
|
107
107
|
execute *proxy.remove_container
|
|
108
108
|
|
|
@@ -124,7 +124,7 @@ class Dash::Cli::Proxy::Reboot
|
|
|
124
124
|
begin
|
|
125
125
|
capture_with_info(*proxy.list(**{ name: name }.compact), verbosity: :debug)
|
|
126
126
|
rescue SSHKit::Command::Failed
|
|
127
|
-
raise Dash::Cli::BootError, "
|
|
127
|
+
raise Dash::Cli::BootError, "dash-proxy on #{host} did not become ready within #{READY_TIMEOUT} seconds" if Time.now >= deadline
|
|
128
128
|
sleep 0.5
|
|
129
129
|
retry
|
|
130
130
|
end
|
|
@@ -146,7 +146,7 @@ class Dash::Cli::Proxy::Reboot
|
|
|
146
146
|
endpoint = capture_with_info(*app.container_id_for_version(version, only_running: true), raise_on_non_zero_exit: false).strip.presence
|
|
147
147
|
next unless endpoint
|
|
148
148
|
|
|
149
|
-
info "Re-registering #{role.container_prefix} with
|
|
149
|
+
info "Re-registering #{role.container_prefix} with dash-proxy on #{host}..."
|
|
150
150
|
execute *app.deploy(target: endpoint)
|
|
151
151
|
registered_services << role.container_prefix
|
|
152
152
|
end
|
|
@@ -158,7 +158,7 @@ class Dash::Cli::Proxy::Reboot
|
|
|
158
158
|
target = capture_with_info(*accessory.container_id_for(container_name: accessory_config.service_name, only_running: true), raise_on_non_zero_exit: false).strip.presence
|
|
159
159
|
next unless target
|
|
160
160
|
|
|
161
|
-
info "Re-registering #{accessory_config.service_name} with
|
|
161
|
+
info "Re-registering #{accessory_config.service_name} with dash-proxy on #{host}..."
|
|
162
162
|
execute *accessory.deploy(target: target)
|
|
163
163
|
registered_services << accessory_config.service_name
|
|
164
164
|
end
|
|
@@ -176,7 +176,7 @@ class Dash::Cli::Proxy::Reboot
|
|
|
176
176
|
missing = registered_services - listed
|
|
177
177
|
|
|
178
178
|
if missing.any?
|
|
179
|
-
raise Dash::Cli::BootError, "
|
|
179
|
+
raise Dash::Cli::BootError, "dash-proxy on #{host} is missing services after reboot: #{missing.join(", ")}"
|
|
180
180
|
end
|
|
181
181
|
end
|
|
182
182
|
end
|
data/lib/dash/cli/proxy.rb
CHANGED
|
@@ -21,6 +21,10 @@ class Dash::Cli::Proxy < Dash::Cli::Base
|
|
|
21
21
|
on(proxy_hosts) do |host|
|
|
22
22
|
execute *DASH.registry.login
|
|
23
23
|
|
|
24
|
+
# Before anything reads the new container, volume or network: bring a
|
|
25
|
+
# host still on pre-rename identity across. A no-op once it has been.
|
|
26
|
+
Dash::Cli::Proxy::LegacyRename.new(host, self).run
|
|
27
|
+
|
|
24
28
|
proxy = DASH.proxy(host)
|
|
25
29
|
drift = Dash::Cli::Proxy::Drift.new(host, self)
|
|
26
30
|
|
|
@@ -33,7 +37,7 @@ class Dash::Cli::Proxy < Dash::Cli::Base
|
|
|
33
37
|
version = capture_with_info(*proxy.version).strip.presence
|
|
34
38
|
|
|
35
39
|
if version && Dash::Utils.older_version?(version, Dash::Configuration::Proxy::Run::MINIMUM_VERSION)
|
|
36
|
-
raise "
|
|
40
|
+
raise "dash-proxy version #{version} is too old, run `dash proxy reboot` in order to update to at least #{Dash::Configuration::Proxy::Run::MINIMUM_VERSION}"
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
if (run_config = proxy.proxy_run_config)&.secrets?
|
|
@@ -51,12 +55,12 @@ class Dash::Cli::Proxy < Dash::Cli::Base
|
|
|
51
55
|
end
|
|
52
56
|
|
|
53
57
|
if stale_hosts.any?
|
|
54
|
-
say "
|
|
58
|
+
say "dash-proxy on #{stale_hosts.sort.join(", ")} is running with a configuration that no longer matches the deploy config. " \
|
|
55
59
|
"Automatic reboot is disabled (proxy: reboot_on_deploy: false) - run `dash proxy reboot` to apply the new configuration.", :yellow
|
|
56
60
|
end
|
|
57
61
|
|
|
58
62
|
drifted_hosts.sort.each do |host|
|
|
59
|
-
say "
|
|
63
|
+
say "dash-proxy configuration changed, rebooting on #{host}...", :magenta
|
|
60
64
|
run_hook "pre-proxy-reboot", hosts: host
|
|
61
65
|
on(host) do |h|
|
|
62
66
|
Dash::Cli::Proxy::Reboot.new(h, self).run
|
|
@@ -129,7 +133,7 @@ class Dash::Cli::Proxy < Dash::Cli::Base
|
|
|
129
133
|
end
|
|
130
134
|
end
|
|
131
135
|
|
|
132
|
-
desc "boot_config <set|get|reset>", "Manage
|
|
136
|
+
desc "boot_config <set|get|reset>", "Manage dash-proxy boot configuration"
|
|
133
137
|
option :publish, type: :boolean, default: true, desc: "Publish the proxy ports on the host"
|
|
134
138
|
option :publish_host_ip, type: :string, repeatable: true, default: nil, desc: "Host IP address to bind HTTP/HTTPS traffic to. Defaults to all interfaces"
|
|
135
139
|
option :http_port, type: :numeric, default: Dash::Configuration::Proxy::Run::DEFAULT_HTTP_PORT, desc: "HTTP port to publish on the host"
|
|
@@ -163,7 +167,7 @@ class Dash::Cli::Proxy < Dash::Cli::Base
|
|
|
163
167
|
image_version = options[:image_version]
|
|
164
168
|
|
|
165
169
|
run_command_options = { debug: options[:debug] || nil, "metrics-port": options[:metrics_port] }.compact
|
|
166
|
-
run_command = "
|
|
170
|
+
run_command = "dash-proxy run #{Dash::Utils.optionize(run_command_options).join(" ")}" if run_command_options.any?
|
|
167
171
|
|
|
168
172
|
on(DASH.proxy_hosts) do |host|
|
|
169
173
|
proxy = DASH.proxy(host)
|
|
@@ -229,7 +233,7 @@ class Dash::Cli::Proxy < Dash::Cli::Base
|
|
|
229
233
|
host_list = Array(hosts).join(",")
|
|
230
234
|
run_hook "pre-proxy-reboot", hosts: host_list
|
|
231
235
|
on(hosts) do |host|
|
|
232
|
-
info "Rebooting
|
|
236
|
+
info "Rebooting dash-proxy on #{host}..."
|
|
233
237
|
Dash::Cli::Proxy::Reboot.new(host, self).run
|
|
234
238
|
end
|
|
235
239
|
run_hook "post-proxy-reboot", hosts: host_list
|
|
@@ -249,7 +253,7 @@ class Dash::Cli::Proxy < Dash::Cli::Base
|
|
|
249
253
|
end
|
|
250
254
|
end
|
|
251
255
|
|
|
252
|
-
desc "upgrade", "Upgrade to
|
|
256
|
+
desc "upgrade", "Upgrade to dash-proxy on servers (stop container, remove container, start new container, reboot app)", hide: true
|
|
253
257
|
option :rolling, type: :boolean, default: false, desc: "Reboot proxy on hosts in sequence, rather than in parallel"
|
|
254
258
|
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
|
255
259
|
def upgrade
|
|
@@ -269,7 +273,7 @@ class Dash::Cli::Proxy < Dash::Cli::Base
|
|
|
269
273
|
info "Stopping and removing Traefik on #{host}, if running..."
|
|
270
274
|
execute *proxy.cleanup_traefik
|
|
271
275
|
|
|
272
|
-
info "Stopping and removing
|
|
276
|
+
info "Stopping and removing dash-proxy on #{host}, if running..."
|
|
273
277
|
execute *proxy.stop, raise_on_non_zero_exit: false
|
|
274
278
|
execute *proxy.remove_container
|
|
275
279
|
execute *proxy.remove_image
|
|
@@ -566,7 +570,7 @@ class Dash::Cli::Proxy < Dash::Cli::Base
|
|
|
566
570
|
on(cert_store_host) do |host|
|
|
567
571
|
commands = load_balancing ? DASH.loadbalancer : DASH.proxy(host)
|
|
568
572
|
|
|
569
|
-
#
|
|
573
|
+
# dash-proxy import runs offline against the data directory - importing
|
|
570
574
|
# under a live proxy risks a torn store. --verify only reads the archive.
|
|
571
575
|
unless verify
|
|
572
576
|
if capture_with_info(*commands.container_id(only_running: true), raise_on_non_zero_exit: false).strip.present?
|
|
@@ -652,7 +656,7 @@ class Dash::Cli::Proxy < Dash::Cli::Base
|
|
|
652
656
|
DASH.config.proxy.load_balancing? ? DASH.config.proxy.effective_loadbalancer : DASH.primary_host
|
|
653
657
|
end
|
|
654
658
|
|
|
655
|
-
# Mirrors
|
|
659
|
+
# Mirrors dash-proxy's own flag groups (import.go), so a contradictory
|
|
656
660
|
# invocation fails before anything is uploaded.
|
|
657
661
|
def validate_import_certs_options!
|
|
658
662
|
if options[:traefik_acme].present? == options[:archive].present?
|
data/lib/dash/commander.rb
CHANGED
|
@@ -110,7 +110,7 @@ class Dash::Commander
|
|
|
110
110
|
@commands[:lock] ||= Dash::Commands::Lock.new(config)
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
# Guards the
|
|
113
|
+
# Guards the dash-proxy container, which a host runs once for every
|
|
114
114
|
# destination deployed onto it. Every destination contends for this one.
|
|
115
115
|
def server_lock
|
|
116
116
|
@commands[:server_lock] ||= Dash::Commands::Lock.new(config, scope: :server)
|
data/lib/dash/commands/app.rb
CHANGED
|
@@ -18,7 +18,7 @@ class Dash::Commands::App < Dash::Commands::Base
|
|
|
18
18
|
"--detach",
|
|
19
19
|
"--restart", role.restart_policy,
|
|
20
20
|
"--name", container_name,
|
|
21
|
-
"--network", "
|
|
21
|
+
"--network", "dash",
|
|
22
22
|
*([ "--hostname", hostname ] if hostname),
|
|
23
23
|
"--env", "KAMAL_CONTAINER_NAME=\"#{container_name}\"",
|
|
24
24
|
"--env", "KAMAL_VERSION=\"#{config.version}\"",
|
data/lib/dash/commands/docker.rb
CHANGED
|
@@ -41,10 +41,44 @@ class Dash::Commands::Docker < Dash::Commands::Base
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def create_network
|
|
44
|
-
docker :network, :create,
|
|
44
|
+
docker :network, :create, Dash::Configuration::Proxy::NETWORK
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Stage 3c: docker cannot rename a network, so `dash` is created alongside
|
|
48
|
+
# `kamal` and everything still on the old one is joined to the new one.
|
|
49
|
+
#
|
|
50
|
+
# App containers would be replaced by the next deploy anyway; accessories are
|
|
51
|
+
# not, and that is the whole point — without this a renamed proxy cannot
|
|
52
|
+
# reach `db` or `redis`. Connecting a container that is already attached
|
|
53
|
+
# fails, so each connect tolerates its own failure rather than aborting the
|
|
54
|
+
# sweep. The old network is never removed; that is documented manual cleanup
|
|
55
|
+
# and stage 3d's code change.
|
|
56
|
+
def connect_legacy_network_containers
|
|
57
|
+
any \
|
|
58
|
+
combine(
|
|
59
|
+
legacy_network_exists,
|
|
60
|
+
pipe(legacy_network_container_names, connect_each_to_network)
|
|
61
|
+
),
|
|
62
|
+
[ :true ]
|
|
45
63
|
end
|
|
46
64
|
|
|
47
65
|
private
|
|
66
|
+
def legacy_network_exists
|
|
67
|
+
docker :network, :inspect, Dash::Configuration::Proxy::LEGACY_NETWORK, ">", "/dev/null", "2>&1"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def legacy_network_container_names
|
|
71
|
+
docker :network, :inspect, Dash::Configuration::Proxy::LEGACY_NETWORK,
|
|
72
|
+
"--format", "'{{range .Containers}}{{.Name}}{{\"\\n\"}}{{end}}'"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Already-attached containers make `network connect` fail; tolerate each
|
|
76
|
+
# one's failure rather than letting it abort the sweep.
|
|
77
|
+
def connect_each_to_network
|
|
78
|
+
[ :xargs, "-r", "-I{}", "sh", "-c",
|
|
79
|
+
"'docker network connect #{Dash::Configuration::Proxy::NETWORK} {} 2>/dev/null || true'" ]
|
|
80
|
+
end
|
|
81
|
+
|
|
48
82
|
def get_docker
|
|
49
83
|
shell \
|
|
50
84
|
any \
|