Class: Dash::Commands::Proxy
Defined Under Namespace
Modules: CertTransfer
Constant Summary
collapse
- CONFIG_DIGEST_LABEL =
"org.dash.proxy-config-digest"
- LEGACY_CONFIG_DIGEST_LABEL =
Containers booted before the stage-3b rename carry the old key. New ones are
labelled with CONFIG_DIGEST_LABEL only, but reads fall back to the legacy key
so upgrading doesn't read as config drift and reboot every proxy for nothing.
Both the legacy constant and the fallback go away in stage 3d.
"org.kamal.proxy-config-digest"
- CONFIG_DIGEST_FORMAT =
"'{{ with index .Config.Labels \"#{CONFIG_DIGEST_LABEL}\" }}{{ . }}" \
"{{ else }}{{ index .Config.Labels \"#{LEGACY_CONFIG_DIGEST_LABEL}\" }}{{ end }}'"
CertTransfer::CERT_ARCHIVE_FILENAME, CertTransfer::CERT_IMPORT_STAGING_FILENAME, CertTransfer::CONTAINER_IMPORT_PATH
Constants inherited
from Base
Base::DOCKER_HEALTH_STATUS_FORMAT, Base::NO_HEALTHCHECK
Instance Attribute Summary collapse
Attributes inherited from Base
#config
Instance Method Summary
collapse
-
#boot_config ⇒ Object
-
#cache_purge(service, path_prefix: nil) ⇒ Object
-
#cache_stats(count: false, json: false) ⇒ Object
-
#cleanup_traefik ⇒ Object
-
#config_digest ⇒ Object
-
#container_id(only_running: false) ⇒ Object
-
#copy_legacy_config_volume(volume: Dash::Configuration::Proxy::CONFIG_VOLUME, legacy: Dash::Configuration::Proxy::LEGACY_CONFIG_VOLUME) ⇒ Object
Copies the pre-rename config volume into the new one, before anything starts.
-
#disable_restart ⇒ Object
Cancel the restart policy before draining: drain makes the proxy exit on its own, which - unlike docker stop - an active restart policy would undo.
-
#domains(subcommand, *args) ⇒ Object
retry takes a host, or --all; the rest take no arguments.
-
#drain(timeout: nil) ⇒ Object
-
#ensure_apps_config_directory ⇒ Object
-
#ensure_proxy_directory ⇒ Object
-
#follow_logs(host:, timestamps: true, grep: nil, grep_options: nil) ⇒ Object
-
#holder_container_id ⇒ Object
-
#info ⇒ Object
-
#initialize(config, host:) ⇒ Proxy
constructor
-
#list(name: container_name, json: false) ⇒ Object
-
#loadbalancer ⇒ Object
-
#logs(timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil) ⇒ Object
-
#mount_destinations ⇒ Object
One mount destination per line - what the running container was actually booted with, as opposed to what the current configuration would mount.
-
#next_container_name ⇒ Object
-
#port_holder? ⇒ Boolean
Zero-downtime handoff commands (proxy/run port_holder mode).
-
#promote_next_container ⇒ Object
-
#pull ⇒ Object
-
#read_boot_options ⇒ Object
-
#read_image ⇒ Object
-
#read_image_version ⇒ Object
-
#read_run_command ⇒ Object
-
#remove_container ⇒ Object
Docker ANDs multiple --filter label= values, so matching both the current and the pre-rename image title takes two commands rather than one filter with two values.
-
#remove_image ⇒ Object
-
#remove_legacy_container(timeout: nil) ⇒ Object
Stops and removes a pre-rename proxy container so the renamed one can claim ports 80/443.
-
#remove_legacy_holder_container ⇒ Object
-
#remove_proxy_directory ⇒ Object
-
#remove_proxy_secrets_file ⇒ Object
Static path rather than proxy_run_config.secrets_path: the file must be removable precisely when the run config (or its secrets) is gone.
-
#remove_stopped_container(name: container_name) ⇒ Object
-
#reset_boot_options ⇒ Object
-
#reset_image ⇒ Object
-
#reset_image_version ⇒ Object
-
#reset_run_command ⇒ Object
-
#run(digest: nil, name: nil) ⇒ Object
-
#run_holder ⇒ Object
-
#start ⇒ Object
-
#start_holder_or_run ⇒ Object
-
#start_or_run(digest: nil) ⇒ Object
-
#stop(name: container_name, timeout: nil) ⇒ Object
-
#version ⇒ Object
-
#wait_for_exit(name: container_name) ⇒ Object
#certs_archive_container_path, #certs_archive_host_path, #certs_import_host_path, #export_certs, #export_certs_offline, #import_certs, #remove_certs_archive, #remove_certs_import
Methods inherited from Base
#container_id_for, #ensure_docker_installed, #ensure_run_directory, #make_directory, #make_directory_for, #read_file, #remove_directory, #remove_file, #run_over_ssh
Constructor Details
#initialize(config, host:) ⇒ Proxy
Returns a new instance of Proxy.
18
19
20
21
|
# File 'lib/dash/commands/proxy.rb', line 18
def initialize(config, host:)
super(config)
@proxy_run_config = config.proxy_run(host)
end
|
Instance Attribute Details
#proxy_run_config ⇒ Object
Returns the value of attribute proxy_run_config.
5
6
7
|
# File 'lib/dash/commands/proxy.rb', line 5
def proxy_run_config
@proxy_run_config
end
|
Instance Method Details
#boot_config ⇒ Object
263
264
265
|
# File 'lib/dash/commands/proxy.rb', line 263
def boot_config
[ :echo, "#{substitute(read_boot_options)} #{substitute(read_image)}:#{substitute(read_image_version)} #{substitute(read_run_command)}" ]
end
|
#cache_purge(service, path_prefix: nil) ⇒ Object
139
140
141
|
# File 'lib/dash/commands/proxy.rb', line 139
def cache_purge(service, path_prefix: nil)
docker :exec, container_name, "dash-proxy", :cache, :purge, service, *optionize({ "path-prefix": path_prefix }.compact)
end
|
#cache_stats(count: false, json: false) ⇒ Object
135
136
137
|
# File 'lib/dash/commands/proxy.rb', line 135
def cache_stats(count: false, json: false)
docker :exec, container_name, "dash-proxy", :cache, :stats, *optionize({ count: count || nil, json: json || nil }.compact)
end
|
#cleanup_traefik ⇒ Object
236
237
238
239
240
241
242
243
|
# File 'lib/dash/commands/proxy.rb', line 236
def cleanup_traefik
chain \
docker(:container, :stop, "traefik"),
combine(
docker(:container, :prune, "--force", "--filter", "label=org.opencontainers.image.title=Traefik"),
docker(:image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=Traefik")
)
end
|
#config_digest ⇒ Object
115
116
117
|
# File 'lib/dash/commands/proxy.rb', line 115
def config_digest
docker :inspect, container_name, "--format", CONFIG_DIGEST_FORMAT
end
|
#container_id(only_running: false) ⇒ Object
119
120
121
|
# File 'lib/dash/commands/proxy.rb', line 119
def container_id(only_running: false)
container_id_for(container_name: container_name, only_running: only_running)
end
|
#copy_legacy_config_volume(volume: Dash::Configuration::Proxy::CONFIG_VOLUME, legacy: Dash::Configuration::Proxy::LEGACY_CONFIG_VOLUME) ⇒ Object
Copies the pre-rename config volume into the new one, before anything
starts. The volume holds the routing table and the ACME account and
certificate cache; losing it means re-issuing every certificate and
spending Let's Encrypt rate limits to get back where we were.
Runs in the dash-proxy image itself — already pulled by this point in the
boot sequence, and its ubuntu base has sh and cp. --user root because the
image's own user cannot write the destination volume; cp -a preserves the
uid, which the rename leaves at 1001.
The guard is negated and leads the chain, with || true last, because
shell && and || share precedence and associate left: written as
exists || legacy_exists && create && copy it would parse as
((exists || legacy_exists) && create) && copy and re-copy the legacy
volume over live state on every deploy. Leading with ! exists makes the
whole chain a single left-associative AND, which short-circuits correctly.
#disable_restart ⇒ Object
Cancel the restart policy before draining: drain makes the proxy exit on
its own, which - unlike docker stop - an active restart policy would undo.
181
182
183
|
# File 'lib/dash/commands/proxy.rb', line 181
def disable_restart
docker :update, "--restart=no", container_name
end
|
#domains(subcommand, *args) ⇒ Object
retry takes a host, or --all; the rest take no arguments.
215
216
217
|
# File 'lib/dash/commands/proxy.rb', line 215
def domains(subcommand, *args)
docker :exec, container_name, "dash-proxy", "domains", subcommand, *args
end
|
#drain(timeout: nil) ⇒ Object
185
186
187
|
# File 'lib/dash/commands/proxy.rb', line 185
def drain(timeout: nil)
docker :exec, container_name, "dash-proxy", :drain, *("--drain-timeout=#{timeout}s" if timeout)
end
|
#ensure_apps_config_directory ⇒ Object
253
254
255
|
# File 'lib/dash/commands/proxy.rb', line 253
def ensure_apps_config_directory
make_directory config.proxy_boot.apps_directory
end
|
#ensure_proxy_directory ⇒ Object
245
246
247
|
# File 'lib/dash/commands/proxy.rb', line 245
def ensure_proxy_directory
make_directory config.proxy_boot.host_directory
end
|
#follow_logs(host:, timestamps: true, grep: nil, grep_options: nil) ⇒ Object
207
208
209
210
211
212
|
# File 'lib/dash/commands/proxy.rb', line 207
def follow_logs(host:, timestamps: true, grep: nil, grep_options: nil)
run_over_ssh pipe(
docker(:logs, container_name, ("--timestamps" if timestamps), "--tail", "10", "--follow", "2>&1"),
(%(grep "#{grep}"#{" #{grep_options}" if grep_options}) if grep)
).join(" "), host: host
end
|
#holder_container_id ⇒ Object
175
176
177
|
# File 'lib/dash/commands/proxy.rb', line 175
def holder_container_id
container_id_for(container_name: proxy_run_config.holder_container_name, only_running: true)
end
|
#info ⇒ Object
105
106
107
|
# File 'lib/dash/commands/proxy.rb', line 105
def info
docker :ps, "--filter", "'name=^#{container_name}$'"
end
|
#list(name: container_name, json: false) ⇒ Object
131
132
133
|
# File 'lib/dash/commands/proxy.rb', line 131
def list(name: container_name, json: false)
docker :exec, name, "dash-proxy", :list, *("--json" if json)
end
|
#loadbalancer ⇒ Object
299
300
301
|
# File 'lib/dash/commands/proxy.rb', line 299
def loadbalancer
@loadbalancer ||= Dash::Commands::Loadbalancer.new(config, loadbalancer_config: DASH.loadbalancer_config)
end
|
#logs(timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil) ⇒ Object
201
202
203
204
205
|
# File 'lib/dash/commands/proxy.rb', line 201
def logs(timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil)
pipe \
docker(:logs, container_name, ("--since #{since}" if since), ("--tail #{lines}" if lines), ("--timestamps" if timestamps), "2>&1"),
("grep '#{grep}'#{" #{grep_options}" if grep_options}" if grep)
end
|
#mount_destinations ⇒ Object
One mount destination per line - what the running container was actually
booted with, as opposed to what the current configuration would mount.
145
146
147
|
# File 'lib/dash/commands/proxy.rb', line 145
def mount_destinations
docker :inspect, container_name, "--format", "'{{range .Mounts}}{{println .Destination}}{{end}}'"
end
|
#next_container_name ⇒ Object
155
156
157
|
# File 'lib/dash/commands/proxy.rb', line 155
def next_container_name
"#{container_name}-next"
end
|
#port_holder? ⇒ Boolean
Zero-downtime handoff commands (proxy/run port_holder mode)
151
152
153
|
# File 'lib/dash/commands/proxy.rb', line 151
def port_holder?
proxy_run_config&.port_holder? || false
end
|
197
198
199
|
# File 'lib/dash/commands/proxy.rb', line 197
def promote_next_container
docker :container, :rename, next_container_name, container_name
end
|
#pull ⇒ Object
123
124
125
126
127
128
129
|
# File 'lib/dash/commands/proxy.rb', line 123
def pull
if proxy_run_config
docker :pull, proxy_run_config.image
else
docker :pull, "#{substitute(read_image)}:#{substitute(read_image_version)}"
end
end
|
#read_boot_options ⇒ Object
267
268
269
|
# File 'lib/dash/commands/proxy.rb', line 267
def read_boot_options
read_file(config.proxy_boot.options_file, default: config.proxy_boot.default_boot_options.join(" "))
end
|
#read_image ⇒ Object
271
272
273
|
# File 'lib/dash/commands/proxy.rb', line 271
def read_image
read_file(config.proxy_boot.image_file, default: config.proxy_boot.image_default)
end
|
#read_image_version ⇒ Object
#read_run_command ⇒ Object
279
280
281
|
# File 'lib/dash/commands/proxy.rb', line 279
def read_run_command
read_file(config.proxy_boot.run_command_file)
end
|
#remove_container ⇒ Object
Docker ANDs multiple --filter label= values, so matching both the current
and the pre-rename image title takes two commands rather than one filter
with two values. Without the legacy pass, dash proxy remove on a host that
has not yet been through the rename silently leaves the old container and
image behind. Stage 3d drops the legacy half.
#remove_legacy_container(timeout: nil) ⇒ Object
Stops and removes a pre-rename proxy container so the renamed one can claim
ports 80/443. No port-holder handoff spans two container names, which is
why this stage accepts a brief outage per host.
#remove_legacy_holder_container ⇒ Object
#remove_proxy_directory ⇒ Object
249
250
251
|
# File 'lib/dash/commands/proxy.rb', line 249
def remove_proxy_directory
remove_directory config.proxy_boot.host_directory
end
|
#remove_proxy_secrets_file ⇒ Object
Static path rather than proxy_run_config.secrets_path: the file must be
removable precisely when the run config (or its secrets) is gone.
#remove_stopped_container(name: container_name) ⇒ Object
193
194
195
|
# File 'lib/dash/commands/proxy.rb', line 193
def remove_stopped_container(name: container_name)
docker :container, :rm, name
end
|
#reset_boot_options ⇒ Object
283
284
285
|
# File 'lib/dash/commands/proxy.rb', line 283
def reset_boot_options
remove_file config.proxy_boot.options_file
end
|
#reset_image ⇒ Object
287
288
289
|
# File 'lib/dash/commands/proxy.rb', line 287
def reset_image
remove_file config.proxy_boot.image_file
end
|
#reset_image_version ⇒ Object
291
292
293
|
# File 'lib/dash/commands/proxy.rb', line 291
def reset_image_version
remove_file config.proxy_boot.image_version_file
end
|
#reset_run_command ⇒ Object
295
296
297
|
# File 'lib/dash/commands/proxy.rb', line 295
def reset_run_command
remove_file config.proxy_boot.run_command_file
end
|
#run(digest: nil, name: nil) ⇒ Object
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
# File 'lib/dash/commands/proxy.rb', line 23
def run(digest: nil, name: nil)
if proxy_run_config
docker \
:run,
"--name", name || container_name,
*proxy_run_config.network_args,
"--detach",
"--restart", "unless-stopped",
"--volume", "dash-proxy-config:/home/dash-proxy/.config/dash-proxy",
*config_digest_label_args(digest),
*proxy_run_config.docker_options_args,
*proxy_run_config.image,
*proxy_run_config.run_command
else
pipe boot_config, xargs(docker_run(digest: digest))
end
end
|
#run_holder ⇒ Object
159
160
161
162
163
164
165
166
167
168
169
|
# File 'lib/dash/commands/proxy.rb', line 159
def run_holder
docker \
:run,
"--name", proxy_run_config.holder_container_name,
"--network", "dash",
"--detach",
"--restart", "unless-stopped",
*proxy_run_config.holder_docker_args,
*proxy_run_config.image,
"dash-proxy", "hold"
end
|
#start ⇒ Object
93
94
95
|
# File 'lib/dash/commands/proxy.rb', line 93
def start
docker :container, :start, container_name
end
|
#start_holder_or_run ⇒ Object
171
172
173
|
# File 'lib/dash/commands/proxy.rb', line 171
def start_holder_or_run
combine docker(:container, :start, proxy_run_config.holder_container_name), run_holder, by: "||"
end
|
#start_or_run(digest: nil) ⇒ Object
101
102
103
|
# File 'lib/dash/commands/proxy.rb', line 101
def start_or_run(digest: nil)
combine start, run(digest: digest), by: "||"
end
|
#stop(name: container_name, timeout: nil) ⇒ Object
97
98
99
|
# File 'lib/dash/commands/proxy.rb', line 97
def stop(name: container_name, timeout: nil)
docker :container, :stop, *("--time #{timeout}" if timeout), name
end
|
#version ⇒ Object
109
110
111
112
113
|
# File 'lib/dash/commands/proxy.rb', line 109
def version
pipe \
docker(:inspect, container_name, "--format '{{.Config.Image}}'"),
[ :awk, "-F:", "'{print \$NF}'" ]
end
|
#wait_for_exit(name: container_name) ⇒ Object
189
190
191
|
# File 'lib/dash/commands/proxy.rb', line 189
def wait_for_exit(name: container_name)
docker :wait, name
end
|