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.
Files changed (178) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/bin/dash +2 -2
  4. data/lib/{kamal → dash}/cli/accessory.rb +24 -24
  5. data/lib/dash/cli/alias/command.rb +10 -0
  6. data/lib/{kamal → dash}/cli/app/assets.rb +2 -2
  7. data/lib/{kamal → dash}/cli/app/boot.rb +16 -16
  8. data/lib/{kamal → dash}/cli/app/error_pages.rb +6 -6
  9. data/lib/{kamal → dash}/cli/app/rollout_boot.rb +6 -6
  10. data/lib/{kamal → dash}/cli/app/ssl_certificates.rb +3 -3
  11. data/lib/{kamal → dash}/cli/app.rb +87 -87
  12. data/lib/{kamal → dash}/cli/base.rb +72 -48
  13. data/lib/dash/cli/build/clone.rb +59 -0
  14. data/lib/{kamal → dash}/cli/build/port_forwarding.rb +1 -1
  15. data/lib/{kamal → dash}/cli/build.rb +45 -45
  16. data/lib/{kamal → dash}/cli/doctor/config_checks.rb +3 -3
  17. data/lib/{kamal → dash}/cli/doctor/endpoint_checks.rb +26 -8
  18. data/lib/{kamal → dash}/cli/doctor/host_checks.rb +22 -22
  19. data/lib/{kamal → dash}/cli/doctor.rb +9 -9
  20. data/lib/{kamal → dash}/cli/healthcheck/barrier.rb +2 -2
  21. data/lib/{kamal → dash}/cli/healthcheck/drift_error.rb +2 -2
  22. data/lib/dash/cli/healthcheck/error.rb +2 -0
  23. data/lib/{kamal → dash}/cli/healthcheck/poller.rb +7 -7
  24. data/lib/{kamal → dash}/cli/lock.rb +1 -1
  25. data/lib/dash/cli/main/migrate.rb +77 -0
  26. data/lib/{kamal → dash}/cli/main.rb +82 -71
  27. data/lib/{kamal → dash}/cli/proxy/drift.rb +3 -3
  28. data/lib/dash/cli/proxy/legacy_rename.rb +58 -0
  29. data/lib/{kamal → dash}/cli/proxy/loadbalancer_claim.rb +3 -3
  30. data/lib/{kamal → dash}/cli/proxy/loadbalancer_reboot.rb +23 -23
  31. data/lib/{kamal → dash}/cli/proxy/reboot.rb +21 -21
  32. data/lib/{kamal → dash}/cli/proxy.rb +178 -162
  33. data/lib/{kamal → dash}/cli/prune.rb +10 -10
  34. data/lib/{kamal → dash}/cli/registry.rb +11 -11
  35. data/lib/{kamal → dash}/cli/secrets.rb +3 -3
  36. data/lib/{kamal → dash}/cli/server.rb +14 -14
  37. data/lib/{kamal → dash}/cli/templates/deploy.yml +4 -4
  38. data/lib/dash/cli/templates/sample_hooks/docker-setup.sample +3 -0
  39. data/lib/dash/cli/templates/sample_hooks/post-app-boot.sample +3 -0
  40. data/lib/dash/cli/templates/sample_hooks/post-app-stop.sample +9 -0
  41. data/lib/dash/cli/templates/sample_hooks/post-deploy.sample +14 -0
  42. data/lib/dash/cli/templates/sample_hooks/post-proxy-deploy.sample +3 -0
  43. data/lib/dash/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
  44. data/lib/dash/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
  45. data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-app-stop.sample +2 -2
  46. data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-build.sample +8 -8
  47. data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-connect.sample +8 -8
  48. data/lib/{kamal → dash}/cli/templates/sample_hooks/pre-deploy.sample +9 -9
  49. data/lib/dash/cli/templates/sample_hooks/pre-proxy-deploy.sample +3 -0
  50. data/lib/dash/cli/templates/sample_hooks/pre-proxy-reboot.sample +3 -0
  51. data/lib/{kamal → dash}/cli/templates/secrets +6 -6
  52. data/lib/{kamal → dash}/cli.rb +2 -2
  53. data/lib/{kamal → dash}/commander/specifics.rb +2 -2
  54. data/lib/{kamal → dash}/commander.rb +32 -24
  55. data/lib/{kamal → dash}/commands/accessory/proxy.rb +2 -2
  56. data/lib/{kamal → dash}/commands/accessory.rb +2 -2
  57. data/lib/{kamal → dash}/commands/app/assets.rb +1 -1
  58. data/lib/{kamal → dash}/commands/app/containers.rb +1 -1
  59. data/lib/{kamal → dash}/commands/app/error_pages.rb +2 -2
  60. data/lib/{kamal → dash}/commands/app/execution.rb +2 -2
  61. data/lib/{kamal → dash}/commands/app/images.rb +1 -1
  62. data/lib/{kamal → dash}/commands/app/logging.rb +1 -1
  63. data/lib/{kamal → dash}/commands/app/proxy.rb +2 -2
  64. data/lib/{kamal → dash}/commands/app.rb +2 -2
  65. data/lib/{kamal → dash}/commands/auditor.rb +3 -7
  66. data/lib/{kamal → dash}/commands/base.rb +39 -3
  67. data/lib/{kamal → dash}/commands/builder/base.rb +2 -2
  68. data/lib/{kamal → dash}/commands/builder/clone.rb +3 -3
  69. data/lib/{kamal → dash}/commands/builder/cloud.rb +1 -1
  70. data/lib/{kamal → dash}/commands/builder/hybrid.rb +1 -1
  71. data/lib/{kamal → dash}/commands/builder/local.rb +1 -1
  72. data/lib/{kamal → dash}/commands/builder/pack.rb +1 -1
  73. data/lib/{kamal → dash}/commands/builder/remote.rb +1 -1
  74. data/lib/{kamal → dash}/commands/builder.rb +7 -7
  75. data/lib/dash/commands/docker.rb +89 -0
  76. data/lib/{kamal → dash}/commands/hook.rb +1 -1
  77. data/lib/{kamal → dash}/commands/loadbalancer.rb +55 -24
  78. data/lib/{kamal → dash}/commands/lock.rb +3 -3
  79. data/lib/{kamal → dash}/commands/proxy/cert_transfer.rb +6 -6
  80. data/lib/dash/commands/proxy.rb +375 -0
  81. data/lib/{kamal → dash}/commands/prune.rb +2 -2
  82. data/lib/dash/commands/registry.rb +44 -0
  83. data/lib/{kamal → dash}/commands/server.rb +1 -5
  84. data/lib/dash/commands.rb +2 -0
  85. data/lib/{kamal → dash}/configuration/accessory.rb +13 -13
  86. data/lib/{kamal → dash}/configuration/alias.rb +3 -3
  87. data/lib/{kamal → dash}/configuration/boot.rb +2 -2
  88. data/lib/{kamal → dash}/configuration/builder.rb +7 -7
  89. data/lib/{kamal → dash}/configuration/docs/accessory.yml +3 -3
  90. data/lib/{kamal → dash}/configuration/docs/builder.yml +3 -3
  91. data/lib/{kamal → dash}/configuration/docs/configuration.yml +13 -6
  92. data/lib/{kamal → dash}/configuration/docs/env.yml +10 -10
  93. data/lib/{kamal → dash}/configuration/docs/proxy.yml +68 -45
  94. data/lib/{kamal → dash}/configuration/docs/registry.yml +5 -5
  95. data/lib/{kamal → dash}/configuration/env/tag.rb +2 -2
  96. data/lib/{kamal → dash}/configuration/env.rb +5 -5
  97. data/lib/{kamal → dash}/configuration/loadbalancer.rb +5 -5
  98. data/lib/{kamal → dash}/configuration/logging.rb +3 -3
  99. data/lib/{kamal → dash}/configuration/output.rb +4 -4
  100. data/lib/{kamal → dash}/configuration/proxy/acme.rb +9 -8
  101. data/lib/{kamal → dash}/configuration/proxy/boot.rb +9 -11
  102. data/lib/{kamal → dash}/configuration/proxy/run.rb +20 -20
  103. data/lib/{kamal → dash}/configuration/proxy.rb +51 -29
  104. data/lib/{kamal → dash}/configuration/registry.rb +3 -3
  105. data/lib/{kamal → dash}/configuration/role/healthcheck.rb +3 -3
  106. data/lib/{kamal → dash}/configuration/role.rb +14 -14
  107. data/lib/{kamal → dash}/configuration/servers.rb +4 -4
  108. data/lib/{kamal → dash}/configuration/ssh.rb +2 -2
  109. data/lib/{kamal → dash}/configuration/sshkit.rb +2 -2
  110. data/lib/{kamal → dash}/configuration/validation.rb +2 -2
  111. data/lib/{kamal → dash}/configuration/validator/accessory.rb +1 -1
  112. data/lib/{kamal → dash}/configuration/validator/alias.rb +2 -2
  113. data/lib/{kamal → dash}/configuration/validator/builder.rb +1 -1
  114. data/lib/dash/configuration/validator/configuration.rb +6 -0
  115. data/lib/{kamal → dash}/configuration/validator/env.rb +2 -2
  116. data/lib/{kamal → dash}/configuration/validator/proxy.rb +31 -31
  117. data/lib/{kamal → dash}/configuration/validator/registry.rb +1 -1
  118. data/lib/{kamal → dash}/configuration/validator/role.rb +1 -1
  119. data/lib/{kamal → dash}/configuration/validator/servers.rb +1 -1
  120. data/lib/{kamal → dash}/configuration/validator.rb +4 -4
  121. data/lib/{kamal → dash}/configuration/volume.rb +2 -2
  122. data/lib/{kamal → dash}/configuration.rb +47 -37
  123. data/lib/{kamal → dash}/docker.rb +1 -1
  124. data/lib/{kamal → dash}/env_file.rb +1 -1
  125. data/lib/{kamal → dash}/git.rb +1 -1
  126. data/lib/{kamal → dash}/otel_shipper.rb +3 -3
  127. data/lib/{kamal → dash}/output/base_logger.rb +1 -1
  128. data/lib/{kamal → dash}/output/file_logger.rb +1 -1
  129. data/lib/{kamal → dash}/output/formatter.rb +1 -1
  130. data/lib/{kamal → dash}/output/otel_logger.rb +3 -3
  131. data/lib/dash/project_directory.rb +28 -0
  132. data/lib/{kamal → dash}/secrets/adapters/aws_secrets_manager.rb +1 -1
  133. data/lib/{kamal → dash}/secrets/adapters/base.rb +2 -2
  134. data/lib/{kamal → dash}/secrets/adapters/bitwarden.rb +1 -1
  135. data/lib/{kamal → dash}/secrets/adapters/bitwarden_secrets_manager.rb +1 -1
  136. data/lib/{kamal → dash}/secrets/adapters/doppler.rb +1 -1
  137. data/lib/{kamal → dash}/secrets/adapters/enpass.rb +1 -1
  138. data/lib/{kamal → dash}/secrets/adapters/gcp_secret_manager.rb +1 -1
  139. data/lib/{kamal → dash}/secrets/adapters/last_pass.rb +1 -1
  140. data/lib/{kamal → dash}/secrets/adapters/one_password.rb +2 -2
  141. data/lib/{kamal → dash}/secrets/adapters/passbolt.rb +1 -1
  142. data/lib/{kamal → dash}/secrets/adapters/test.rb +1 -1
  143. data/lib/{kamal → dash}/secrets/adapters.rb +2 -2
  144. data/lib/{kamal → dash}/secrets/dotenv/inline_command_substitution.rb +2 -2
  145. data/lib/{kamal → dash}/secrets.rb +6 -6
  146. data/lib/{kamal → dash}/tags.rb +8 -3
  147. data/lib/{kamal → dash}/utils/sensitive.rb +1 -1
  148. data/lib/{kamal → dash}/utils.rb +16 -6
  149. data/lib/dash/version.rb +3 -0
  150. data/lib/{kamal.rb → dash.rb} +4 -4
  151. metadata +157 -154
  152. data/lib/kamal/cli/alias/command.rb +0 -10
  153. data/lib/kamal/cli/build/clone.rb +0 -59
  154. data/lib/kamal/cli/healthcheck/error.rb +0 -2
  155. data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +0 -3
  156. data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +0 -3
  157. data/lib/kamal/cli/templates/sample_hooks/post-app-stop.sample +0 -9
  158. data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +0 -14
  159. data/lib/kamal/cli/templates/sample_hooks/post-proxy-deploy.sample +0 -3
  160. data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +0 -3
  161. data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +0 -3
  162. data/lib/kamal/cli/templates/sample_hooks/pre-proxy-deploy.sample +0 -3
  163. data/lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample +0 -3
  164. data/lib/kamal/commands/docker.rb +0 -55
  165. data/lib/kamal/commands/proxy.rb +0 -266
  166. data/lib/kamal/commands/registry.rb +0 -38
  167. data/lib/kamal/commands.rb +0 -2
  168. data/lib/kamal/configuration/validator/configuration.rb +0 -6
  169. data/lib/kamal/version.rb +0 -3
  170. /data/lib/{kamal → dash}/configuration/docs/alias.yml +0 -0
  171. /data/lib/{kamal → dash}/configuration/docs/boot.yml +0 -0
  172. /data/lib/{kamal → dash}/configuration/docs/logging.yml +0 -0
  173. /data/lib/{kamal → dash}/configuration/docs/output.yml +0 -0
  174. /data/lib/{kamal → dash}/configuration/docs/role.yml +0 -0
  175. /data/lib/{kamal → dash}/configuration/docs/servers.yml +0 -0
  176. /data/lib/{kamal → dash}/configuration/docs/ssh.yml +0 -0
  177. /data/lib/{kamal → dash}/configuration/docs/sshkit.yml +0 -0
  178. /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 Kamal::Commands::Prune < Kamal::Commands::Base
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 kamal-proxy has put to sleep is `exited`, so it is a removal
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,8 +1,4 @@
1
- class Kamal::Commands::Server < Kamal::Commands::Base
2
- def ensure_run_directory
3
- make_directory config.run_directory
4
- end
5
-
1
+ class Dash::Commands::Server < Dash::Commands::Base
6
2
  def remove_app_directory
7
3
  remove_directory config.app_directory
8
4
  end
@@ -0,0 +1,2 @@
1
+ module Dash::Commands
2
+ end
@@ -1,9 +1,9 @@
1
- class Kamal::Configuration::Accessory
2
- include Kamal::Configuration::Validation
1
+ class Dash::Configuration::Accessory
2
+ include Dash::Configuration::Validation
3
3
 
4
- DEFAULT_NETWORK = "kamal"
4
+ DEFAULT_NETWORK = "dash"
5
5
 
6
- delegate :argumentize, :optionize, to: Kamal::Utils
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: Kamal::Configuration::Validator::Accessory
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
- Kamal::Configuration::Env.new \
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 Kamal::Cli::Proxy#loadbalancer), so an accessory is never behind it
132
- # and must keep the host/TLS it registers with kamal-proxy directly.
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
- Kamal::Configuration::Proxy.new \
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
- Kamal::Configuration::Registry.new \
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
- Kamal::Configuration::Volume.new \
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 Kamal::ConfigurationError, "accessories/#{name}: unknown roles #{missing_roles.join(", ")}"
276
+ raise Dash::ConfigurationError, "accessories/#{name}: unknown roles #{missing_roles.join(", ")}"
277
277
  elsif accessory_config["role"] && !config.role(accessory_config["role"])
278
- raise Kamal::ConfigurationError, "accessories/#{name}: unknown role #{accessory_config["role"]}"
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 Kamal::Configuration::Alias
2
- include Kamal::Configuration::Validation
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: Kamal::Configuration::Validator::Alias
13
+ with: Dash::Configuration::Validator::Alias
14
14
  end
15
15
  end
@@ -1,5 +1,5 @@
1
- class Kamal::Configuration::Boot
2
- include Kamal::Configuration::Validation
1
+ class Dash::Configuration::Boot
2
+ include Dash::Configuration::Validation
3
3
 
4
4
  attr_reader :boot_config
5
5
 
@@ -1,5 +1,5 @@
1
- class Kamal::Configuration::Builder
2
- include Kamal::Configuration::Validation
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: Kamal::Configuration::Validator::Builder
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 & [ Kamal::Utils.docker_arch ]
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
- Kamal::Git.used? && builder_config["context"].nil?
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(Kamal::Git.root)
204
+ File.basename(Dash::Git.root)
205
205
  end
206
206
 
207
207
  def repo_relative_pwd
208
- Dir.pwd.delete_prefix(Kamal::Git.root)
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 kamal:
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 kamal-proxy on them
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 kamal-proxy on its own host — whether or not 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, Kamal will use this
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, Kamal will always use the remote builder even when building
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 `.kamal/secrets`:
90
+ # Values are read from `.dash/secrets`:
91
91
  secrets:
92
92
  - SECRET1
93
93
  - SECRET2