dash 3.2.0 → 4.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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/bin/dash +2 -2
- data/lib/{kamal → dash}/cli/accessory.rb +24 -24
- data/lib/dash/cli/alias/command.rb +10 -0
- data/lib/{kamal → dash}/cli/app/assets.rb +2 -2
- data/lib/{kamal → dash}/cli/app/boot.rb +16 -16
- data/lib/{kamal → dash}/cli/app/error_pages.rb +6 -6
- data/lib/{kamal → dash}/cli/app/rollout_boot.rb +6 -6
- data/lib/{kamal → dash}/cli/app/ssl_certificates.rb +3 -3
- data/lib/{kamal → dash}/cli/app.rb +87 -87
- data/lib/{kamal → dash}/cli/base.rb +72 -48
- data/lib/dash/cli/build/clone.rb +59 -0
- data/lib/{kamal → dash}/cli/build/port_forwarding.rb +1 -1
- data/lib/{kamal → dash}/cli/build.rb +45 -45
- data/lib/{kamal → dash}/cli/doctor/config_checks.rb +3 -3
- data/lib/{kamal → dash}/cli/doctor/endpoint_checks.rb +26 -8
- data/lib/{kamal → dash}/cli/doctor/host_checks.rb +22 -22
- data/lib/{kamal → dash}/cli/doctor.rb +9 -9
- data/lib/{kamal → dash}/cli/healthcheck/barrier.rb +2 -2
- data/lib/{kamal → dash}/cli/healthcheck/drift_error.rb +2 -2
- data/lib/dash/cli/healthcheck/error.rb +2 -0
- data/lib/{kamal → dash}/cli/healthcheck/poller.rb +7 -7
- data/lib/{kamal → dash}/cli/lock.rb +1 -1
- data/lib/dash/cli/main/migrate.rb +77 -0
- data/lib/{kamal → dash}/cli/main.rb +82 -71
- data/lib/{kamal → dash}/cli/proxy/drift.rb +3 -3
- data/lib/dash/cli/proxy/legacy_rename.rb +58 -0
- data/lib/{kamal → dash}/cli/proxy/loadbalancer_claim.rb +3 -3
- data/lib/{kamal → dash}/cli/proxy/loadbalancer_reboot.rb +23 -23
- data/lib/{kamal → dash}/cli/proxy/reboot.rb +21 -21
- data/lib/{kamal → dash}/cli/proxy.rb +178 -162
- data/lib/{kamal → dash}/cli/prune.rb +10 -10
- data/lib/{kamal → dash}/cli/registry.rb +11 -11
- data/lib/{kamal → dash}/cli/secrets.rb +3 -3
- data/lib/{kamal → dash}/cli/server.rb +14 -14
- data/lib/{kamal → dash}/cli/templates/deploy.yml +4 -4
- data/lib/dash/cli/templates/sample_hooks/docker-setup.sample +3 -0
- data/lib/dash/cli/templates/sample_hooks/post-app-boot.sample +3 -0
- data/lib/dash/cli/templates/sample_hooks/post-app-stop.sample +9 -0
- data/lib/dash/cli/templates/sample_hooks/post-deploy.sample +14 -0
- data/lib/dash/cli/templates/sample_hooks/post-proxy-deploy.sample +3 -0
- data/lib/dash/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
- data/lib/dash/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
- data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-app-stop.sample +2 -2
- data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-build.sample +8 -8
- data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-connect.sample +8 -8
- data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-deploy.sample +9 -9
- data/lib/dash/cli/templates/sample_hooks/pre-proxy-deploy.sample +3 -0
- data/lib/dash/cli/templates/sample_hooks/pre-proxy-reboot.sample +3 -0
- data/lib/{kamal → dash}/cli/templates/secrets +6 -6
- data/lib/{kamal → dash}/cli.rb +2 -2
- data/lib/{kamal → dash}/commander/specifics.rb +2 -2
- data/lib/{kamal → dash}/commander.rb +32 -24
- data/lib/{kamal → dash}/commands/accessory/proxy.rb +2 -2
- data/lib/{kamal → dash}/commands/accessory.rb +2 -2
- data/lib/{kamal → dash}/commands/app/assets.rb +1 -1
- data/lib/{kamal → dash}/commands/app/containers.rb +1 -1
- data/lib/{kamal → dash}/commands/app/error_pages.rb +2 -2
- data/lib/{kamal → dash}/commands/app/execution.rb +2 -2
- data/lib/{kamal → dash}/commands/app/images.rb +1 -1
- data/lib/{kamal → dash}/commands/app/logging.rb +1 -1
- data/lib/{kamal → dash}/commands/app/proxy.rb +2 -2
- data/lib/{kamal → dash}/commands/app.rb +2 -2
- data/lib/{kamal → dash}/commands/auditor.rb +3 -7
- data/lib/{kamal → dash}/commands/base.rb +39 -3
- data/lib/{kamal → dash}/commands/builder/base.rb +2 -2
- data/lib/{kamal → dash}/commands/builder/clone.rb +3 -3
- data/lib/{kamal → dash}/commands/builder/cloud.rb +1 -1
- data/lib/{kamal → dash}/commands/builder/hybrid.rb +1 -1
- data/lib/{kamal → dash}/commands/builder/local.rb +1 -1
- data/lib/{kamal → dash}/commands/builder/pack.rb +1 -1
- data/lib/{kamal → dash}/commands/builder/remote.rb +1 -1
- data/lib/{kamal → dash}/commands/builder.rb +7 -7
- data/lib/dash/commands/docker.rb +89 -0
- data/lib/{kamal → dash}/commands/hook.rb +1 -1
- data/lib/{kamal → dash}/commands/loadbalancer.rb +55 -24
- data/lib/{kamal → dash}/commands/lock.rb +3 -3
- data/lib/{kamal → dash}/commands/proxy/cert_transfer.rb +6 -6
- data/lib/dash/commands/proxy.rb +375 -0
- data/lib/{kamal → dash}/commands/prune.rb +2 -2
- data/lib/dash/commands/registry.rb +44 -0
- data/lib/{kamal → dash}/commands/server.rb +1 -5
- data/lib/dash/commands.rb +2 -0
- data/lib/{kamal → dash}/configuration/accessory.rb +13 -13
- data/lib/{kamal → dash}/configuration/alias.rb +3 -3
- data/lib/{kamal → dash}/configuration/boot.rb +2 -2
- data/lib/{kamal → dash}/configuration/builder.rb +7 -7
- data/lib/{kamal → dash}/configuration/docs/accessory.yml +3 -3
- data/lib/{kamal → dash}/configuration/docs/builder.yml +3 -3
- data/lib/{kamal → dash}/configuration/docs/configuration.yml +13 -6
- data/lib/{kamal → dash}/configuration/docs/env.yml +10 -10
- data/lib/{kamal → dash}/configuration/docs/proxy.yml +68 -45
- data/lib/{kamal → dash}/configuration/docs/registry.yml +5 -5
- data/lib/{kamal → dash}/configuration/env/tag.rb +2 -2
- data/lib/{kamal → dash}/configuration/env.rb +5 -5
- data/lib/{kamal → dash}/configuration/loadbalancer.rb +5 -5
- data/lib/{kamal → dash}/configuration/logging.rb +3 -3
- data/lib/{kamal → dash}/configuration/output.rb +4 -4
- data/lib/{kamal → dash}/configuration/proxy/acme.rb +9 -8
- data/lib/{kamal → dash}/configuration/proxy/boot.rb +9 -11
- data/lib/{kamal → dash}/configuration/proxy/run.rb +20 -20
- data/lib/{kamal → dash}/configuration/proxy.rb +51 -29
- data/lib/{kamal → dash}/configuration/registry.rb +3 -3
- data/lib/{kamal → dash}/configuration/role/healthcheck.rb +3 -3
- data/lib/{kamal → dash}/configuration/role.rb +14 -14
- data/lib/{kamal → dash}/configuration/servers.rb +4 -4
- data/lib/{kamal → dash}/configuration/ssh.rb +2 -2
- data/lib/{kamal → dash}/configuration/sshkit.rb +2 -2
- data/lib/{kamal → dash}/configuration/validation.rb +2 -2
- data/lib/{kamal → dash}/configuration/validator/accessory.rb +1 -1
- data/lib/{kamal → dash}/configuration/validator/alias.rb +2 -2
- data/lib/{kamal → dash}/configuration/validator/builder.rb +1 -1
- data/lib/dash/configuration/validator/configuration.rb +6 -0
- data/lib/{kamal → dash}/configuration/validator/env.rb +2 -2
- data/lib/{kamal → dash}/configuration/validator/proxy.rb +31 -31
- data/lib/{kamal → dash}/configuration/validator/registry.rb +1 -1
- data/lib/{kamal → dash}/configuration/validator/role.rb +1 -1
- data/lib/{kamal → dash}/configuration/validator/servers.rb +1 -1
- data/lib/{kamal → dash}/configuration/validator.rb +4 -4
- data/lib/{kamal → dash}/configuration/volume.rb +2 -2
- data/lib/{kamal → dash}/configuration.rb +47 -37
- data/lib/{kamal → dash}/docker.rb +1 -1
- data/lib/{kamal → dash}/env_file.rb +1 -1
- data/lib/{kamal → dash}/git.rb +1 -1
- data/lib/{kamal → dash}/otel_shipper.rb +3 -3
- data/lib/{kamal → dash}/output/base_logger.rb +1 -1
- data/lib/{kamal → dash}/output/file_logger.rb +1 -1
- data/lib/{kamal → dash}/output/formatter.rb +1 -1
- data/lib/{kamal → dash}/output/otel_logger.rb +3 -3
- data/lib/dash/project_directory.rb +28 -0
- data/lib/{kamal → dash}/secrets/adapters/aws_secrets_manager.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/base.rb +2 -2
- data/lib/{kamal → dash}/secrets/adapters/bitwarden.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/bitwarden_secrets_manager.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/doppler.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/enpass.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/gcp_secret_manager.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/last_pass.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/one_password.rb +2 -2
- data/lib/{kamal → dash}/secrets/adapters/passbolt.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters/test.rb +1 -1
- data/lib/{kamal → dash}/secrets/adapters.rb +2 -2
- data/lib/{kamal → dash}/secrets/dotenv/inline_command_substitution.rb +2 -2
- data/lib/{kamal → dash}/secrets.rb +6 -6
- data/lib/{kamal → dash}/tags.rb +8 -3
- data/lib/{kamal → dash}/utils/sensitive.rb +1 -1
- data/lib/{kamal → dash}/utils.rb +16 -6
- data/lib/dash/version.rb +3 -0
- data/lib/{kamal.rb → dash.rb} +4 -4
- metadata +157 -154
- data/lib/kamal/cli/alias/command.rb +0 -10
- data/lib/kamal/cli/build/clone.rb +0 -59
- data/lib/kamal/cli/healthcheck/error.rb +0 -2
- data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/post-app-stop.sample +0 -9
- data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +0 -14
- data/lib/kamal/cli/templates/sample_hooks/post-proxy-deploy.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/pre-proxy-deploy.sample +0 -3
- data/lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample +0 -3
- data/lib/kamal/commands/docker.rb +0 -55
- data/lib/kamal/commands/proxy.rb +0 -266
- data/lib/kamal/commands/registry.rb +0 -38
- data/lib/kamal/commands.rb +0 -2
- data/lib/kamal/configuration/validator/configuration.rb +0 -6
- data/lib/kamal/version.rb +0 -3
- /data/lib/{kamal → dash}/configuration/docs/alias.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/boot.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/logging.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/output.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/role.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/servers.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/ssh.yml +0 -0
- /data/lib/{kamal → dash}/configuration/docs/sshkit.yml +0 -0
- /data/lib/{kamal → dash}/sshkit_with_ext.rb +0 -0
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
class Dash::Commands::Proxy < Dash::Commands::Base
|
|
2
|
+
include Dash::Commands::Proxy::CertTransfer
|
|
3
|
+
|
|
4
|
+
delegate :argumentize, :optionize, to: Dash::Utils
|
|
5
|
+
attr_reader :proxy_run_config
|
|
6
|
+
|
|
7
|
+
CONFIG_DIGEST_LABEL = "org.dash.proxy-config-digest"
|
|
8
|
+
|
|
9
|
+
# Containers booted before the stage-3b rename carry the old key. New ones are
|
|
10
|
+
# labelled with CONFIG_DIGEST_LABEL only, but reads fall back to the legacy key
|
|
11
|
+
# so upgrading doesn't read as config drift and reboot every proxy for nothing.
|
|
12
|
+
# Both the legacy constant and the fallback go away in stage 3d.
|
|
13
|
+
LEGACY_CONFIG_DIGEST_LABEL = "org.kamal.proxy-config-digest"
|
|
14
|
+
|
|
15
|
+
CONFIG_DIGEST_FORMAT = "'{{ with index .Config.Labels \"#{CONFIG_DIGEST_LABEL}\" }}{{ . }}" \
|
|
16
|
+
"{{ else }}{{ index .Config.Labels \"#{LEGACY_CONFIG_DIGEST_LABEL}\" }}{{ end }}'"
|
|
17
|
+
|
|
18
|
+
def initialize(config, host:)
|
|
19
|
+
super(config)
|
|
20
|
+
@proxy_run_config = config.proxy_run(host)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def run(digest: nil, name: nil)
|
|
24
|
+
if proxy_run_config
|
|
25
|
+
docker \
|
|
26
|
+
:run,
|
|
27
|
+
"--name", name || container_name,
|
|
28
|
+
*proxy_run_config.network_args,
|
|
29
|
+
"--detach",
|
|
30
|
+
"--restart", "unless-stopped",
|
|
31
|
+
"--volume", "dash-proxy-config:/home/dash-proxy/.config/dash-proxy",
|
|
32
|
+
*config_digest_label_args(digest),
|
|
33
|
+
*proxy_run_config.docker_options_args,
|
|
34
|
+
*proxy_run_config.image,
|
|
35
|
+
*proxy_run_config.run_command
|
|
36
|
+
else
|
|
37
|
+
pipe boot_config, xargs(docker_run(digest: digest))
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Stage 3c migrations. All three are idempotent and guarded on the
|
|
42
|
+
# destination not existing, so a second deploy is a no-op. Stage 3d deletes
|
|
43
|
+
# them along with the legacy constants they read.
|
|
44
|
+
|
|
45
|
+
# Copies the pre-rename config volume into the new one, before anything
|
|
46
|
+
# starts. The volume holds the routing table and the ACME account and
|
|
47
|
+
# certificate cache; losing it means re-issuing every certificate and
|
|
48
|
+
# spending Let's Encrypt rate limits to get back where we were.
|
|
49
|
+
#
|
|
50
|
+
# Runs in the dash-proxy image itself — already pulled by this point in the
|
|
51
|
+
# boot sequence, and its ubuntu base has sh and cp. `--user root` because the
|
|
52
|
+
# image's own user cannot write the destination volume; `cp -a` preserves the
|
|
53
|
+
# uid, which the rename leaves at 1001.
|
|
54
|
+
# The guard is negated and leads the chain, with `|| true` last, because
|
|
55
|
+
# shell `&&` and `||` share precedence and associate left: written as
|
|
56
|
+
# `exists || legacy_exists && create && copy` it would parse as
|
|
57
|
+
# `((exists || legacy_exists) && create) && copy` and re-copy the legacy
|
|
58
|
+
# volume over live state on every deploy. Leading with `! exists` makes the
|
|
59
|
+
# whole chain a single left-associative AND, which short-circuits correctly.
|
|
60
|
+
def copy_legacy_config_volume(volume: Dash::Configuration::Proxy::CONFIG_VOLUME, legacy: Dash::Configuration::Proxy::LEGACY_CONFIG_VOLUME)
|
|
61
|
+
any \
|
|
62
|
+
combine(
|
|
63
|
+
negate(volume_exists(volume)),
|
|
64
|
+
volume_exists(legacy),
|
|
65
|
+
docker(:volume, :create, volume),
|
|
66
|
+
copy_between_volumes(legacy, volume)
|
|
67
|
+
),
|
|
68
|
+
[ :true ]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Stops and removes a pre-rename proxy container so the renamed one can claim
|
|
72
|
+
# ports 80/443. No port-holder handoff spans two container names, which is
|
|
73
|
+
# why this stage accepts a brief outage per host.
|
|
74
|
+
def remove_legacy_container(timeout: nil)
|
|
75
|
+
any \
|
|
76
|
+
combine(
|
|
77
|
+
container_exists(Dash::Configuration::Proxy::LEGACY_CONTAINER_NAME),
|
|
78
|
+
docker(:container, :stop, *("--time=#{timeout}" if timeout), Dash::Configuration::Proxy::LEGACY_CONTAINER_NAME),
|
|
79
|
+
docker(:container, :rm, Dash::Configuration::Proxy::LEGACY_CONTAINER_NAME)
|
|
80
|
+
),
|
|
81
|
+
[ :true ]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def remove_legacy_holder_container
|
|
85
|
+
any \
|
|
86
|
+
combine(
|
|
87
|
+
container_exists(Dash::Configuration::Proxy::LEGACY_HOLDER_CONTAINER_NAME),
|
|
88
|
+
docker(:container, :rm, "--force", Dash::Configuration::Proxy::LEGACY_HOLDER_CONTAINER_NAME)
|
|
89
|
+
),
|
|
90
|
+
[ :true ]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def start
|
|
94
|
+
docker :container, :start, container_name
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def stop(name: container_name, timeout: nil)
|
|
98
|
+
docker :container, :stop, *("--time #{timeout}" if timeout), name
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def start_or_run(digest: nil)
|
|
102
|
+
combine start, run(digest: digest), by: "||"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def info
|
|
106
|
+
docker :ps, "--filter", "'name=^#{container_name}$'"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def version
|
|
110
|
+
pipe \
|
|
111
|
+
docker(:inspect, container_name, "--format '{{.Config.Image}}'"),
|
|
112
|
+
[ :awk, "-F:", "'{print \$NF}'" ]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def config_digest
|
|
116
|
+
docker :inspect, container_name, "--format", CONFIG_DIGEST_FORMAT
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def container_id(only_running: false)
|
|
120
|
+
container_id_for(container_name: container_name, only_running: only_running)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def pull
|
|
124
|
+
if proxy_run_config
|
|
125
|
+
docker :pull, proxy_run_config.image
|
|
126
|
+
else
|
|
127
|
+
docker :pull, "#{substitute(read_image)}:#{substitute(read_image_version)}"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def list(name: container_name, json: false)
|
|
132
|
+
docker :exec, name, "dash-proxy", :list, *("--json" if json)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def cache_stats(count: false, json: false)
|
|
136
|
+
docker :exec, container_name, "dash-proxy", :cache, :stats, *optionize({ count: count || nil, json: json || nil }.compact)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def cache_purge(service, path_prefix: nil)
|
|
140
|
+
docker :exec, container_name, "dash-proxy", :cache, :purge, service, *optionize({ "path-prefix": path_prefix }.compact)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# One mount destination per line - what the running container was actually
|
|
144
|
+
# booted with, as opposed to what the current configuration would mount.
|
|
145
|
+
def mount_destinations
|
|
146
|
+
docker :inspect, container_name, "--format", "'{{range .Mounts}}{{println .Destination}}{{end}}'"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Zero-downtime handoff commands (proxy/run port_holder mode)
|
|
150
|
+
|
|
151
|
+
def port_holder?
|
|
152
|
+
proxy_run_config&.port_holder? || false
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def next_container_name
|
|
156
|
+
"#{container_name}-next"
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def run_holder
|
|
160
|
+
docker \
|
|
161
|
+
:run,
|
|
162
|
+
"--name", proxy_run_config.holder_container_name,
|
|
163
|
+
"--network", "dash",
|
|
164
|
+
"--detach",
|
|
165
|
+
"--restart", "unless-stopped",
|
|
166
|
+
*proxy_run_config.holder_docker_args,
|
|
167
|
+
*proxy_run_config.image,
|
|
168
|
+
"dash-proxy", "hold"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def start_holder_or_run
|
|
172
|
+
combine docker(:container, :start, proxy_run_config.holder_container_name), run_holder, by: "||"
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def holder_container_id
|
|
176
|
+
container_id_for(container_name: proxy_run_config.holder_container_name, only_running: true)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Cancel the restart policy before draining: drain makes the proxy exit on
|
|
180
|
+
# its own, which - unlike `docker stop` - an active restart policy would undo.
|
|
181
|
+
def disable_restart
|
|
182
|
+
docker :update, "--restart=no", container_name
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def drain(timeout: nil)
|
|
186
|
+
docker :exec, container_name, "dash-proxy", :drain, *("--drain-timeout=#{timeout}s" if timeout)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def wait_for_exit(name: container_name)
|
|
190
|
+
docker :wait, name
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def remove_stopped_container(name: container_name)
|
|
194
|
+
docker :container, :rm, name
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def promote_next_container
|
|
198
|
+
docker :container, :rename, next_container_name, container_name
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def logs(timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil)
|
|
202
|
+
pipe \
|
|
203
|
+
docker(:logs, container_name, ("--since #{since}" if since), ("--tail #{lines}" if lines), ("--timestamps" if timestamps), "2>&1"),
|
|
204
|
+
("grep '#{grep}'#{" #{grep_options}" if grep_options}" if grep)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def follow_logs(host:, timestamps: true, grep: nil, grep_options: nil)
|
|
208
|
+
run_over_ssh pipe(
|
|
209
|
+
docker(:logs, container_name, ("--timestamps" if timestamps), "--tail", "10", "--follow", "2>&1"),
|
|
210
|
+
(%(grep "#{grep}"#{" #{grep_options}" if grep_options}) if grep)
|
|
211
|
+
).join(" "), host: host
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# `retry` takes a host, or --all; the rest take no arguments.
|
|
215
|
+
def domains(subcommand, *args)
|
|
216
|
+
docker :exec, container_name, "dash-proxy", "domains", subcommand, *args
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Docker ANDs multiple `--filter label=` values, so matching both the current
|
|
220
|
+
# and the pre-rename image title takes two commands rather than one filter
|
|
221
|
+
# with two values. Without the legacy pass, `dash proxy remove` on a host that
|
|
222
|
+
# has not yet been through the rename silently leaves the old container and
|
|
223
|
+
# image behind. Stage 3d drops the legacy half.
|
|
224
|
+
def remove_container
|
|
225
|
+
combine \
|
|
226
|
+
prune_containers_titled(Dash::Configuration::Proxy::IMAGE_TITLE),
|
|
227
|
+
prune_containers_titled(Dash::Configuration::Proxy::LEGACY_IMAGE_TITLE)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def remove_image
|
|
231
|
+
combine \
|
|
232
|
+
prune_images_titled(Dash::Configuration::Proxy::IMAGE_TITLE),
|
|
233
|
+
prune_images_titled(Dash::Configuration::Proxy::LEGACY_IMAGE_TITLE)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def cleanup_traefik
|
|
237
|
+
chain \
|
|
238
|
+
docker(:container, :stop, "traefik"),
|
|
239
|
+
combine(
|
|
240
|
+
docker(:container, :prune, "--force", "--filter", "label=org.opencontainers.image.title=Traefik"),
|
|
241
|
+
docker(:image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=Traefik")
|
|
242
|
+
)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def ensure_proxy_directory
|
|
246
|
+
make_directory config.proxy_boot.host_directory
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def remove_proxy_directory
|
|
250
|
+
remove_directory config.proxy_boot.host_directory
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def ensure_apps_config_directory
|
|
254
|
+
make_directory config.proxy_boot.apps_directory
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Static path rather than proxy_run_config.secrets_path: the file must be
|
|
258
|
+
# removable precisely when the run config (or its secrets) is gone.
|
|
259
|
+
def remove_proxy_secrets_file
|
|
260
|
+
remove_file File.join(config.proxy_boot.host_directory, Dash::Configuration::Proxy::Run::SECRETS_FILENAME)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def boot_config
|
|
264
|
+
[ :echo, "#{substitute(read_boot_options)} #{substitute(read_image)}:#{substitute(read_image_version)} #{substitute(read_run_command)}" ]
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def read_boot_options
|
|
268
|
+
read_file(config.proxy_boot.options_file, default: config.proxy_boot.default_boot_options.join(" "))
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def read_image
|
|
272
|
+
read_file(config.proxy_boot.image_file, default: config.proxy_boot.image_default)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def read_image_version
|
|
276
|
+
read_file(config.proxy_boot.image_version_file, default: Dash::Configuration::Proxy::Run::MINIMUM_VERSION)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def read_run_command
|
|
280
|
+
read_file(config.proxy_boot.run_command_file)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def reset_boot_options
|
|
284
|
+
remove_file config.proxy_boot.options_file
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def reset_image
|
|
288
|
+
remove_file config.proxy_boot.image_file
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def reset_image_version
|
|
292
|
+
remove_file config.proxy_boot.image_version_file
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def reset_run_command
|
|
296
|
+
remove_file config.proxy_boot.run_command_file
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def loadbalancer
|
|
300
|
+
@loadbalancer ||= Dash::Commands::Loadbalancer.new(config, loadbalancer_config: DASH.loadbalancer_config)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
private
|
|
304
|
+
def container_name
|
|
305
|
+
config.proxy_boot.container_name
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def cert_store_volume_args
|
|
309
|
+
[ "--volume", "dash-proxy-config:/home/dash-proxy/.config/dash-proxy" ]
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Same fallback as #pull: without a run config the image comes from the
|
|
313
|
+
# legacy boot config files on the host.
|
|
314
|
+
def one_off_image
|
|
315
|
+
if proxy_run_config
|
|
316
|
+
[ proxy_run_config.image ]
|
|
317
|
+
else
|
|
318
|
+
[ "#{substitute(read_image)}:#{substitute(read_image_version)}" ]
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def config_digest_label_args(digest)
|
|
323
|
+
[ "--label", "#{CONFIG_DIGEST_LABEL}=#{digest}" ] if digest
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def negate(command)
|
|
327
|
+
[ "!", *command ]
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def volume_exists(name)
|
|
331
|
+
docker :volume, :inspect, name, ">", "/dev/null", "2>&1"
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def container_exists(name)
|
|
335
|
+
docker :container, :inspect, name, ">", "/dev/null", "2>&1"
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def copy_between_volumes(from, to)
|
|
339
|
+
docker \
|
|
340
|
+
:run, "--rm", "--user", "root", "--entrypoint", "sh",
|
|
341
|
+
"--volume", "#{from}:/from",
|
|
342
|
+
"--volume", "#{to}:/to",
|
|
343
|
+
proxy_image,
|
|
344
|
+
"-c", "'cp -a /from/. /to/'"
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# The image the volume copy borrows. The proxy this gem is pinned to is
|
|
348
|
+
# already pulled by the time the copy runs, and `rake release` gates on
|
|
349
|
+
# MINIMUM_VERSION being published, so this is always resolvable — unlike
|
|
350
|
+
# the legacy boot path's image, which is read from a file on the host.
|
|
351
|
+
def proxy_image
|
|
352
|
+
proxy_run_config&.image ||
|
|
353
|
+
"#{config.proxy_boot.image_default}:#{Dash::Configuration::Proxy::Run::MINIMUM_VERSION}"
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def prune_containers_titled(title)
|
|
357
|
+
docker :container, :prune, "--force", "--filter", "label=org.opencontainers.image.title=#{title}"
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def prune_images_titled(title)
|
|
361
|
+
docker :image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=#{title}"
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def docker_run(digest: nil)
|
|
365
|
+
docker \
|
|
366
|
+
:run,
|
|
367
|
+
"--name", container_name,
|
|
368
|
+
"--network", "dash",
|
|
369
|
+
"--detach",
|
|
370
|
+
"--restart", "unless-stopped",
|
|
371
|
+
"--volume", "dash-proxy-config:/home/dash-proxy/.config/dash-proxy",
|
|
372
|
+
*config_digest_label_args(digest),
|
|
373
|
+
*config.proxy_boot.apps_volume.docker_args
|
|
374
|
+
end
|
|
375
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require "active_support/duration"
|
|
2
2
|
require "active_support/core_ext/numeric/time"
|
|
3
3
|
|
|
4
|
-
class
|
|
4
|
+
class Dash::Commands::Prune < Dash::Commands::Base
|
|
5
5
|
def dangling_images
|
|
6
6
|
docker :image, :prune, "--force", "--filter", "label=service=#{config.service}"
|
|
7
7
|
end
|
|
@@ -15,7 +15,7 @@ class Kamal::Commands::Prune < Kamal::Commands::Base
|
|
|
15
15
|
|
|
16
16
|
# Scoped to one role so a busy sibling role cannot push another role's newest
|
|
17
17
|
# container past the retain window. That matters beyond disk hygiene: a
|
|
18
|
-
# container
|
|
18
|
+
# container dash-proxy has put to sleep is `exited`, so it is a removal
|
|
19
19
|
# candidate, and once it is gone every wake 404s. With `retain >= 1` a role's
|
|
20
20
|
# newest container always survives, and the slept one is always the newest —
|
|
21
21
|
# sleeping happens to the current release.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
class Dash::Commands::Registry < Dash::Commands::Base
|
|
2
|
+
LOCAL_CONTAINER_NAME = "dash-docker-registry"
|
|
3
|
+
LEGACY_LOCAL_CONTAINER_NAME = "kamal-docker-registry"
|
|
4
|
+
|
|
5
|
+
def login(registry_config: nil)
|
|
6
|
+
registry_config ||= config.registry
|
|
7
|
+
|
|
8
|
+
return if registry_config.local?
|
|
9
|
+
|
|
10
|
+
docker :login,
|
|
11
|
+
registry_config.server,
|
|
12
|
+
"-u", sensitive(Dash::Utils.escape_shell_value(registry_config.username)),
|
|
13
|
+
"-p", sensitive(Dash::Utils.escape_shell_value(registry_config.password))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def logout(registry_config: nil)
|
|
17
|
+
registry_config ||= config.registry
|
|
18
|
+
|
|
19
|
+
docker :logout, registry_config.server
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def setup(registry_config: nil)
|
|
23
|
+
registry_config ||= config.registry
|
|
24
|
+
|
|
25
|
+
combine \
|
|
26
|
+
docker(:start, LOCAL_CONTAINER_NAME),
|
|
27
|
+
docker(:run, "--detach", "-p", "127.0.0.1:#{registry_config.local_port}:5000", "--name", LOCAL_CONTAINER_NAME, "registry:3"),
|
|
28
|
+
by: "||"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The legacy container is torn down alongside the current one so an operator's
|
|
32
|
+
# laptop doesn't keep a stray kamal-docker-registry holding the local port
|
|
33
|
+
# after upgrading. Dash::Cli::Registry#remove already runs this with
|
|
34
|
+
# raise_on_non_zero_exit: false, so a missing container of either name is fine.
|
|
35
|
+
def remove
|
|
36
|
+
chain \
|
|
37
|
+
combine(docker(:stop, LOCAL_CONTAINER_NAME), docker(:rm, LOCAL_CONTAINER_NAME)),
|
|
38
|
+
combine(docker(:stop, LEGACY_LOCAL_CONTAINER_NAME), docker(:rm, LEGACY_LOCAL_CONTAINER_NAME))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def local?
|
|
42
|
+
config.registry.local?
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
class
|
|
2
|
-
include
|
|
1
|
+
class Dash::Configuration::Accessory
|
|
2
|
+
include Dash::Configuration::Validation
|
|
3
3
|
|
|
4
|
-
DEFAULT_NETWORK = "
|
|
4
|
+
DEFAULT_NETWORK = "dash"
|
|
5
5
|
|
|
6
|
-
delegate :argumentize, :optionize, to:
|
|
6
|
+
delegate :argumentize, :optionize, to: Dash::Utils
|
|
7
7
|
|
|
8
8
|
attr_reader :name, :env, :proxy, :registry
|
|
9
9
|
|
|
@@ -14,7 +14,7 @@ class Kamal::Configuration::Accessory
|
|
|
14
14
|
accessory_config,
|
|
15
15
|
example: validation_yml["accessories"]["mysql"],
|
|
16
16
|
context: "accessories/#{name}",
|
|
17
|
-
with:
|
|
17
|
+
with: Dash::Configuration::Validator::Accessory
|
|
18
18
|
|
|
19
19
|
ensure_valid_roles
|
|
20
20
|
|
|
@@ -121,17 +121,17 @@ class Kamal::Configuration::Accessory
|
|
|
121
121
|
attr_reader :config, :accessory_config
|
|
122
122
|
|
|
123
123
|
def initialize_env
|
|
124
|
-
|
|
124
|
+
Dash::Configuration::Env.new \
|
|
125
125
|
config: accessory_config.fetch("env", {}),
|
|
126
126
|
secrets: config.secrets,
|
|
127
127
|
context: "accessories/#{name}/env"
|
|
128
128
|
end
|
|
129
129
|
|
|
130
130
|
# The load balancer fans the app's own service out to role targets only
|
|
131
|
-
# (see
|
|
132
|
-
# and must keep the host/TLS it registers with
|
|
131
|
+
# (see Dash::Cli::Proxy#loadbalancer), so an accessory is never behind it
|
|
132
|
+
# and must keep the host/TLS it registers with dash-proxy directly.
|
|
133
133
|
def initialize_proxy
|
|
134
|
-
|
|
134
|
+
Dash::Configuration::Proxy.new \
|
|
135
135
|
config: config,
|
|
136
136
|
proxy_config: accessory_config["proxy"],
|
|
137
137
|
context: "accessories/#{name}/proxy",
|
|
@@ -140,7 +140,7 @@ class Kamal::Configuration::Accessory
|
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
def initialize_registry
|
|
143
|
-
|
|
143
|
+
Dash::Configuration::Registry.new \
|
|
144
144
|
config: accessory_config,
|
|
145
145
|
secrets: config.secrets,
|
|
146
146
|
context: "accessories/#{name}/registry"
|
|
@@ -187,7 +187,7 @@ class Kamal::Configuration::Accessory
|
|
|
187
187
|
|
|
188
188
|
def path_volumes(paths)
|
|
189
189
|
paths.map do |local, config|
|
|
190
|
-
|
|
190
|
+
Dash::Configuration::Volume.new \
|
|
191
191
|
host_path: config[:host_path],
|
|
192
192
|
container_path: config[:container_path],
|
|
193
193
|
options: config[:options]
|
|
@@ -273,9 +273,9 @@ class Kamal::Configuration::Accessory
|
|
|
273
273
|
|
|
274
274
|
def ensure_valid_roles
|
|
275
275
|
if accessory_config["roles"] && (missing_roles = accessory_config["roles"] - config.roles.map(&:name)).any?
|
|
276
|
-
raise
|
|
276
|
+
raise Dash::ConfigurationError, "accessories/#{name}: unknown roles #{missing_roles.join(", ")}"
|
|
277
277
|
elsif accessory_config["role"] && !config.role(accessory_config["role"])
|
|
278
|
-
raise
|
|
278
|
+
raise Dash::ConfigurationError, "accessories/#{name}: unknown role #{accessory_config["role"]}"
|
|
279
279
|
end
|
|
280
280
|
end
|
|
281
281
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
class
|
|
2
|
-
include
|
|
1
|
+
class Dash::Configuration::Alias
|
|
2
|
+
include Dash::Configuration::Validation
|
|
3
3
|
|
|
4
4
|
attr_reader :name, :command
|
|
5
5
|
|
|
@@ -10,6 +10,6 @@ class Kamal::Configuration::Alias
|
|
|
10
10
|
command,
|
|
11
11
|
example: validation_yml["aliases"]["uname"],
|
|
12
12
|
context: "aliases/#{name}",
|
|
13
|
-
with:
|
|
13
|
+
with: Dash::Configuration::Validator::Alias
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
class
|
|
2
|
-
include
|
|
1
|
+
class Dash::Configuration::Builder
|
|
2
|
+
include Dash::Configuration::Validation
|
|
3
3
|
|
|
4
4
|
attr_reader :config, :builder_config
|
|
5
5
|
delegate :image, :service, to: :config
|
|
@@ -12,7 +12,7 @@ class Kamal::Configuration::Builder
|
|
|
12
12
|
@server = config.registry.server
|
|
13
13
|
@service = config.service
|
|
14
14
|
|
|
15
|
-
validate! builder_config, with:
|
|
15
|
+
validate! builder_config, with: Dash::Configuration::Validator::Builder
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def to_h
|
|
@@ -31,7 +31,7 @@ class Kamal::Configuration::Builder
|
|
|
31
31
|
@local_arches ||= if local_disabled?
|
|
32
32
|
[]
|
|
33
33
|
elsif remote
|
|
34
|
-
arches & [
|
|
34
|
+
arches & [ Dash::Utils.docker_arch ]
|
|
35
35
|
else
|
|
36
36
|
arches
|
|
37
37
|
end
|
|
@@ -136,7 +136,7 @@ class Kamal::Configuration::Builder
|
|
|
136
136
|
end
|
|
137
137
|
|
|
138
138
|
def git_clone?
|
|
139
|
-
|
|
139
|
+
Dash::Git.used? && builder_config["context"].nil?
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
def clone_directory
|
|
@@ -201,11 +201,11 @@ class Kamal::Configuration::Builder
|
|
|
201
201
|
end
|
|
202
202
|
|
|
203
203
|
def repo_basename
|
|
204
|
-
File.basename(
|
|
204
|
+
File.basename(Dash::Git.root)
|
|
205
205
|
end
|
|
206
206
|
|
|
207
207
|
def repo_relative_pwd
|
|
208
|
-
Dir.pwd.delete_prefix(
|
|
208
|
+
Dir.pwd.delete_prefix(Dash::Git.root)
|
|
209
209
|
end
|
|
210
210
|
|
|
211
211
|
def pwd_sha
|
|
@@ -150,7 +150,7 @@ accessories:
|
|
|
150
150
|
#
|
|
151
151
|
# The network the accessory will be attached to.
|
|
152
152
|
#
|
|
153
|
-
# Defaults to
|
|
153
|
+
# Defaults to dash:
|
|
154
154
|
network: custom
|
|
155
155
|
|
|
156
156
|
# Proxy
|
|
@@ -160,12 +160,12 @@ accessories:
|
|
|
160
160
|
# app-level `proxy:` (see `dash docs proxy`), though root-only keys such as
|
|
161
161
|
# `loadbalancer` and `reboot_on_deploy` have no effect inside an accessory.
|
|
162
162
|
# Declaring a proxy here makes the accessory's hosts proxy hosts:
|
|
163
|
-
# `dash proxy boot` (and `dash deploy`) will run
|
|
163
|
+
# `dash proxy boot` (and `dash deploy`) will run dash-proxy on them
|
|
164
164
|
# automatically.
|
|
165
165
|
#
|
|
166
166
|
# Accessories are never load balanced. The loadbalancer fans the app's own
|
|
167
167
|
# service out to its role targets, so an accessory always registers its
|
|
168
|
-
# hostname and TLS with the
|
|
168
|
+
# hostname and TLS with the dash-proxy on its own host — whether or not the
|
|
169
169
|
# loadbalancer is active for the app. Point the accessory's DNS record at the
|
|
170
170
|
# accessory host, not at the loadbalancer.
|
|
171
171
|
#
|
|
@@ -19,13 +19,13 @@ builder:
|
|
|
19
19
|
|
|
20
20
|
# Remote
|
|
21
21
|
#
|
|
22
|
-
# The connection string for a remote builder. If supplied,
|
|
22
|
+
# The connection string for a remote builder. If supplied, dash will use this
|
|
23
23
|
# for builds that do not match the local architecture of the deployment host.
|
|
24
24
|
remote: ssh://docker@docker-builder
|
|
25
25
|
|
|
26
26
|
# Local
|
|
27
27
|
#
|
|
28
|
-
# If set to false,
|
|
28
|
+
# If set to false, dash will always use the remote builder even when building
|
|
29
29
|
# the local architecture.
|
|
30
30
|
#
|
|
31
31
|
# Defaults to true:
|
|
@@ -87,7 +87,7 @@ builder:
|
|
|
87
87
|
|
|
88
88
|
# Build secrets
|
|
89
89
|
#
|
|
90
|
-
# Values are read from `.
|
|
90
|
+
# Values are read from `.dash/secrets`:
|
|
91
91
|
secrets:
|
|
92
92
|
- SECRET1
|
|
93
93
|
- SECRET2
|