Class: Dash::Commands::Proxy

Inherits:
Base
  • Object
show all
Includes:
CertTransfer
Defined in:
lib/dash/commands/proxy.rb

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 }}'"

Constants included from CertTransfer

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

Methods included from CertTransfer

#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_configObject (readonly)

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_configObject



202
203
204
# File 'lib/dash/commands/proxy.rb', line 202

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



87
88
89
# File 'lib/dash/commands/proxy.rb', line 87

def cache_purge(service, path_prefix: nil)
  docker :exec, container_name, "kamal-proxy", :cache, :purge, service, *optionize({ "path-prefix": path_prefix }.compact)
end

#cache_stats(count: false, json: false) ⇒ Object



83
84
85
# File 'lib/dash/commands/proxy.rb', line 83

def cache_stats(count: false, json: false)
  docker :exec, container_name, "kamal-proxy", :cache, :stats, *optionize({ count: count || nil, json: json || nil }.compact)
end

#cleanup_traefikObject



175
176
177
178
179
180
181
182
# File 'lib/dash/commands/proxy.rb', line 175

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_digestObject



63
64
65
# File 'lib/dash/commands/proxy.rb', line 63

def config_digest
  docker :inspect, container_name, "--format", CONFIG_DIGEST_FORMAT
end

#container_id(only_running: false) ⇒ Object



67
68
69
# File 'lib/dash/commands/proxy.rb', line 67

def container_id(only_running: false)
  container_id_for(container_name: container_name, only_running: only_running)
end

#disable_restartObject

Cancel the restart policy before draining: drain makes the proxy exit on its own, which - unlike docker stop - an active restart policy would undo.



129
130
131
# File 'lib/dash/commands/proxy.rb', line 129

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.



163
164
165
# File 'lib/dash/commands/proxy.rb', line 163

def domains(subcommand, *args)
  docker :exec, container_name, "kamal-proxy", "domains", subcommand, *args
end

#drain(timeout: nil) ⇒ Object



133
134
135
# File 'lib/dash/commands/proxy.rb', line 133

def drain(timeout: nil)
  docker :exec, container_name, "kamal-proxy", :drain, *("--drain-timeout=#{timeout}s" if timeout)
end

#ensure_apps_config_directoryObject



192
193
194
# File 'lib/dash/commands/proxy.rb', line 192

def ensure_apps_config_directory
  make_directory config.proxy_boot.apps_directory
end

#ensure_proxy_directoryObject



184
185
186
# File 'lib/dash/commands/proxy.rb', line 184

def ensure_proxy_directory
  make_directory config.proxy_boot.host_directory
end

#follow_logs(host:, timestamps: true, grep: nil, grep_options: nil) ⇒ Object



155
156
157
158
159
160
# File 'lib/dash/commands/proxy.rb', line 155

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_idObject



123
124
125
# File 'lib/dash/commands/proxy.rb', line 123

def holder_container_id
  container_id_for(container_name: proxy_run_config.holder_container_name, only_running: true)
end

#infoObject



53
54
55
# File 'lib/dash/commands/proxy.rb', line 53

def info
  docker :ps, "--filter", "'name=^#{container_name}$'"
end

#list(name: container_name, json: false) ⇒ Object



79
80
81
# File 'lib/dash/commands/proxy.rb', line 79

def list(name: container_name, json: false)
  docker :exec, name, "kamal-proxy", :list, *("--json" if json)
end

#loadbalancerObject



238
239
240
# File 'lib/dash/commands/proxy.rb', line 238

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



149
150
151
152
153
# File 'lib/dash/commands/proxy.rb', line 149

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_destinationsObject

One mount destination per line - what the running container was actually booted with, as opposed to what the current configuration would mount.



93
94
95
# File 'lib/dash/commands/proxy.rb', line 93

def mount_destinations
  docker :inspect, container_name, "--format", "'{{range .Mounts}}{{println .Destination}}{{end}}'"
end

#next_container_nameObject



103
104
105
# File 'lib/dash/commands/proxy.rb', line 103

def next_container_name
  "#{container_name}-next"
end

#port_holder?Boolean

Zero-downtime handoff commands (proxy/run port_holder mode)

Returns:

  • (Boolean)


99
100
101
# File 'lib/dash/commands/proxy.rb', line 99

def port_holder?
  proxy_run_config&.port_holder? || false
end

#promote_next_containerObject



145
146
147
# File 'lib/dash/commands/proxy.rb', line 145

def promote_next_container
  docker :container, :rename, next_container_name, container_name
end

#pullObject



71
72
73
74
75
76
77
# File 'lib/dash/commands/proxy.rb', line 71

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_optionsObject



206
207
208
# File 'lib/dash/commands/proxy.rb', line 206

def read_boot_options
  read_file(config.proxy_boot.options_file, default: config.proxy_boot.default_boot_options.join(" "))
end

#read_imageObject



210
211
212
# File 'lib/dash/commands/proxy.rb', line 210

def read_image
  read_file(config.proxy_boot.image_file, default: config.proxy_boot.image_default)
end

#read_image_versionObject



214
215
216
# File 'lib/dash/commands/proxy.rb', line 214

def read_image_version
  read_file(config.proxy_boot.image_version_file, default: Dash::Configuration::Proxy::Run::MINIMUM_VERSION)
end

#read_run_commandObject



218
219
220
# File 'lib/dash/commands/proxy.rb', line 218

def read_run_command
  read_file(config.proxy_boot.run_command_file)
end

#remove_containerObject



167
168
169
# File 'lib/dash/commands/proxy.rb', line 167

def remove_container
  docker :container, :prune, "--force", "--filter", "label=org.opencontainers.image.title=kamal-proxy"
end

#remove_imageObject



171
172
173
# File 'lib/dash/commands/proxy.rb', line 171

def remove_image
  docker :image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=kamal-proxy"
end

#remove_proxy_directoryObject



188
189
190
# File 'lib/dash/commands/proxy.rb', line 188

def remove_proxy_directory
  remove_directory config.proxy_boot.host_directory
end

#remove_proxy_secrets_fileObject

Static path rather than proxy_run_config.secrets_path: the file must be removable precisely when the run config (or its secrets) is gone.



198
199
200
# File 'lib/dash/commands/proxy.rb', line 198

def remove_proxy_secrets_file
  remove_file File.join(config.proxy_boot.host_directory, Dash::Configuration::Proxy::Run::SECRETS_FILENAME)
end

#remove_stopped_container(name: container_name) ⇒ Object



141
142
143
# File 'lib/dash/commands/proxy.rb', line 141

def remove_stopped_container(name: container_name)
  docker :container, :rm, name
end

#reset_boot_optionsObject



222
223
224
# File 'lib/dash/commands/proxy.rb', line 222

def reset_boot_options
  remove_file config.proxy_boot.options_file
end

#reset_imageObject



226
227
228
# File 'lib/dash/commands/proxy.rb', line 226

def reset_image
  remove_file config.proxy_boot.image_file
end

#reset_image_versionObject



230
231
232
# File 'lib/dash/commands/proxy.rb', line 230

def reset_image_version
  remove_file config.proxy_boot.image_version_file
end

#reset_run_commandObject



234
235
236
# File 'lib/dash/commands/proxy.rb', line 234

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", "kamal-proxy-config:/home/kamal-proxy/.config/kamal-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_holderObject



107
108
109
110
111
112
113
114
115
116
117
# File 'lib/dash/commands/proxy.rb', line 107

def run_holder
  docker \
    :run,
    "--name", proxy_run_config.holder_container_name,
    "--network", "kamal",
    "--detach",
    "--restart", "unless-stopped",
    *proxy_run_config.holder_docker_args,
    *proxy_run_config.image,
    "kamal-proxy", "hold"
end

#startObject



41
42
43
# File 'lib/dash/commands/proxy.rb', line 41

def start
  docker :container, :start, container_name
end

#start_holder_or_runObject



119
120
121
# File 'lib/dash/commands/proxy.rb', line 119

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



49
50
51
# File 'lib/dash/commands/proxy.rb', line 49

def start_or_run(digest: nil)
  combine start, run(digest: digest), by: "||"
end

#stop(name: container_name, timeout: nil) ⇒ Object



45
46
47
# File 'lib/dash/commands/proxy.rb', line 45

def stop(name: container_name, timeout: nil)
  docker :container, :stop, *("--time #{timeout}" if timeout), name
end

#versionObject



57
58
59
60
61
# File 'lib/dash/commands/proxy.rb', line 57

def version
  pipe \
    docker(:inspect, container_name, "--format '{{.Config.Image}}'"),
    [ :awk, "-F:", "'{print \$NF}'" ]
end

#wait_for_exit(name: container_name) ⇒ Object



137
138
139
# File 'lib/dash/commands/proxy.rb', line 137

def wait_for_exit(name: container_name)
  docker :wait, name
end