dash 2.12.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +13 -0
- data/bin/dash +18 -0
- data/bin/kamal +18 -0
- data/lib/kamal/cli/accessory.rb +342 -0
- data/lib/kamal/cli/alias/command.rb +10 -0
- data/lib/kamal/cli/app/assets.rb +24 -0
- data/lib/kamal/cli/app/boot.rb +126 -0
- data/lib/kamal/cli/app/error_pages.rb +33 -0
- data/lib/kamal/cli/app/ssl_certificates.rb +28 -0
- data/lib/kamal/cli/app.rb +368 -0
- data/lib/kamal/cli/base.rb +324 -0
- data/lib/kamal/cli/build/clone.rb +59 -0
- data/lib/kamal/cli/build/port_forwarding.rb +66 -0
- data/lib/kamal/cli/build.rb +242 -0
- data/lib/kamal/cli/healthcheck/barrier.rb +33 -0
- data/lib/kamal/cli/healthcheck/error.rb +2 -0
- data/lib/kamal/cli/healthcheck/poller.rb +42 -0
- data/lib/kamal/cli/lock.rb +34 -0
- data/lib/kamal/cli/main.rb +299 -0
- data/lib/kamal/cli/proxy.rb +419 -0
- data/lib/kamal/cli/prune.rb +34 -0
- data/lib/kamal/cli/registry.rb +49 -0
- data/lib/kamal/cli/secrets.rb +50 -0
- data/lib/kamal/cli/server.rb +70 -0
- data/lib/kamal/cli/templates/deploy.yml +102 -0
- data/lib/kamal/cli/templates/sample_hooks/docker-setup.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/post-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/post-deploy.sample +14 -0
- data/lib/kamal/cli/templates/sample_hooks/post-proxy-reboot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-app-boot.sample +3 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-build.sample +51 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-connect.sample +47 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-deploy.sample +122 -0
- data/lib/kamal/cli/templates/sample_hooks/pre-proxy-reboot.sample +3 -0
- data/lib/kamal/cli/templates/secrets +22 -0
- data/lib/kamal/cli.rb +9 -0
- data/lib/kamal/commander/specifics.rb +62 -0
- data/lib/kamal/commander.rb +230 -0
- data/lib/kamal/commands/accessory/proxy.rb +16 -0
- data/lib/kamal/commands/accessory.rb +118 -0
- data/lib/kamal/commands/app/assets.rb +51 -0
- data/lib/kamal/commands/app/containers.rb +31 -0
- data/lib/kamal/commands/app/error_pages.rb +9 -0
- data/lib/kamal/commands/app/execution.rb +38 -0
- data/lib/kamal/commands/app/images.rb +13 -0
- data/lib/kamal/commands/app/logging.rb +28 -0
- data/lib/kamal/commands/app/proxy.rb +32 -0
- data/lib/kamal/commands/app.rb +125 -0
- data/lib/kamal/commands/auditor.rb +39 -0
- data/lib/kamal/commands/base.rb +147 -0
- data/lib/kamal/commands/builder/base.rb +143 -0
- data/lib/kamal/commands/builder/clone.rb +32 -0
- data/lib/kamal/commands/builder/cloud.rb +22 -0
- data/lib/kamal/commands/builder/hybrid.rb +21 -0
- data/lib/kamal/commands/builder/local.rb +20 -0
- data/lib/kamal/commands/builder/pack.rb +46 -0
- data/lib/kamal/commands/builder/remote.rb +75 -0
- data/lib/kamal/commands/builder.rb +54 -0
- data/lib/kamal/commands/docker.rb +50 -0
- data/lib/kamal/commands/hook.rb +20 -0
- data/lib/kamal/commands/loadbalancer.rb +130 -0
- data/lib/kamal/commands/lock.rb +70 -0
- data/lib/kamal/commands/proxy.rb +150 -0
- data/lib/kamal/commands/prune.rb +38 -0
- data/lib/kamal/commands/registry.rb +38 -0
- data/lib/kamal/commands/server.rb +15 -0
- data/lib/kamal/commands.rb +2 -0
- data/lib/kamal/configuration/accessory.rb +280 -0
- data/lib/kamal/configuration/alias.rb +15 -0
- data/lib/kamal/configuration/boot.rb +29 -0
- data/lib/kamal/configuration/builder.rb +218 -0
- data/lib/kamal/configuration/docs/accessory.yml +160 -0
- data/lib/kamal/configuration/docs/alias.yml +29 -0
- data/lib/kamal/configuration/docs/boot.yml +21 -0
- data/lib/kamal/configuration/docs/builder.yml +132 -0
- data/lib/kamal/configuration/docs/configuration.yml +228 -0
- data/lib/kamal/configuration/docs/env.yml +118 -0
- data/lib/kamal/configuration/docs/logging.yml +21 -0
- data/lib/kamal/configuration/docs/output.yml +25 -0
- data/lib/kamal/configuration/docs/proxy.yml +207 -0
- data/lib/kamal/configuration/docs/registry.yml +64 -0
- data/lib/kamal/configuration/docs/role.yml +54 -0
- data/lib/kamal/configuration/docs/servers.yml +27 -0
- data/lib/kamal/configuration/docs/ssh.yml +81 -0
- data/lib/kamal/configuration/docs/sshkit.yml +31 -0
- data/lib/kamal/configuration/env/tag.rb +13 -0
- data/lib/kamal/configuration/env.rb +42 -0
- data/lib/kamal/configuration/loadbalancer.rb +34 -0
- data/lib/kamal/configuration/logging.rb +33 -0
- data/lib/kamal/configuration/output.rb +34 -0
- data/lib/kamal/configuration/proxy/boot.rb +124 -0
- data/lib/kamal/configuration/proxy/run.rb +152 -0
- data/lib/kamal/configuration/proxy.rb +156 -0
- data/lib/kamal/configuration/registry.rb +40 -0
- data/lib/kamal/configuration/role.rb +247 -0
- data/lib/kamal/configuration/servers.rb +25 -0
- data/lib/kamal/configuration/ssh.rb +76 -0
- data/lib/kamal/configuration/sshkit.rb +26 -0
- data/lib/kamal/configuration/validation.rb +27 -0
- data/lib/kamal/configuration/validator/accessory.rb +13 -0
- data/lib/kamal/configuration/validator/alias.rb +15 -0
- data/lib/kamal/configuration/validator/builder.rb +15 -0
- data/lib/kamal/configuration/validator/configuration.rb +6 -0
- data/lib/kamal/configuration/validator/env.rb +54 -0
- data/lib/kamal/configuration/validator/proxy.rb +47 -0
- data/lib/kamal/configuration/validator/registry.rb +27 -0
- data/lib/kamal/configuration/validator/role.rb +13 -0
- data/lib/kamal/configuration/validator/servers.rb +7 -0
- data/lib/kamal/configuration/validator.rb +251 -0
- data/lib/kamal/configuration/volume.rb +29 -0
- data/lib/kamal/configuration.rb +465 -0
- data/lib/kamal/docker.rb +30 -0
- data/lib/kamal/env_file.rb +44 -0
- data/lib/kamal/git.rb +37 -0
- data/lib/kamal/otel_shipper.rb +176 -0
- data/lib/kamal/output/base_logger.rb +29 -0
- data/lib/kamal/output/file_logger.rb +51 -0
- data/lib/kamal/output/formatter.rb +36 -0
- data/lib/kamal/output/otel_logger.rb +70 -0
- data/lib/kamal/secrets/adapters/aws_secrets_manager.rb +59 -0
- data/lib/kamal/secrets/adapters/base.rb +33 -0
- data/lib/kamal/secrets/adapters/bitwarden.rb +81 -0
- data/lib/kamal/secrets/adapters/bitwarden_secrets_manager.rb +66 -0
- data/lib/kamal/secrets/adapters/doppler.rb +57 -0
- data/lib/kamal/secrets/adapters/enpass.rb +71 -0
- data/lib/kamal/secrets/adapters/gcp_secret_manager.rb +112 -0
- data/lib/kamal/secrets/adapters/last_pass.rb +40 -0
- data/lib/kamal/secrets/adapters/one_password.rb +104 -0
- data/lib/kamal/secrets/adapters/passbolt.rb +129 -0
- data/lib/kamal/secrets/adapters/test.rb +16 -0
- data/lib/kamal/secrets/adapters.rb +16 -0
- data/lib/kamal/secrets/dotenv/inline_command_substitution.rb +47 -0
- data/lib/kamal/secrets.rb +53 -0
- data/lib/kamal/sshkit_with_ext.rb +273 -0
- data/lib/kamal/tags.rb +40 -0
- data/lib/kamal/utils/sensitive.rb +20 -0
- data/lib/kamal/utils.rb +110 -0
- data/lib/kamal/version.rb +3 -0
- data/lib/kamal.rb +15 -0
- metadata +388 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
class Kamal::Cli::Proxy < Kamal::Cli::Base
|
|
2
|
+
desc "boot", "Boot proxy on servers"
|
|
3
|
+
def boot
|
|
4
|
+
modify(lock: true) do
|
|
5
|
+
on(KAMAL.hosts) do |host|
|
|
6
|
+
execute *KAMAL.docker.create_network
|
|
7
|
+
rescue SSHKit::Command::Failed => e
|
|
8
|
+
raise unless e.message.include?("already exists")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Skip proxy on loadbalancer host - the loadbalancer will handle it
|
|
12
|
+
proxy_hosts = KAMAL.proxy_hosts
|
|
13
|
+
if KAMAL.config.proxy.loadbalancer_on_proxy_host?
|
|
14
|
+
proxy_hosts = proxy_hosts - [ KAMAL.config.proxy.effective_loadbalancer ]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
on(proxy_hosts) do |host|
|
|
18
|
+
execute *KAMAL.registry.login
|
|
19
|
+
|
|
20
|
+
version = capture_with_info(*KAMAL.proxy(host).version).strip.presence
|
|
21
|
+
|
|
22
|
+
if version && Kamal::Utils.older_version?(version, Kamal::Configuration::Proxy::Run::MINIMUM_VERSION)
|
|
23
|
+
raise "kamal-proxy version #{version} is too old, run `kamal proxy reboot` in order to update to at least #{Kamal::Configuration::Proxy::Run::MINIMUM_VERSION}"
|
|
24
|
+
end
|
|
25
|
+
execute *KAMAL.proxy(host).ensure_apps_config_directory
|
|
26
|
+
execute *KAMAL.proxy(host).start_or_run
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if KAMAL.config.proxy.load_balancing?
|
|
30
|
+
on(KAMAL.config.proxy.effective_loadbalancer) do |host|
|
|
31
|
+
info "Starting loadbalancer on #{host}..."
|
|
32
|
+
execute *KAMAL.registry.login
|
|
33
|
+
execute *KAMAL.loadbalancer.ensure_apps_config_directory
|
|
34
|
+
execute *KAMAL.loadbalancer.start_or_run
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
desc "boot_config <set|get|reset>", "Manage kamal-proxy boot configuration"
|
|
41
|
+
option :publish, type: :boolean, default: true, desc: "Publish the proxy ports on the host"
|
|
42
|
+
option :publish_host_ip, type: :string, repeatable: true, default: nil, desc: "Host IP address to bind HTTP/HTTPS traffic to. Defaults to all interfaces"
|
|
43
|
+
option :http_port, type: :numeric, default: Kamal::Configuration::Proxy::Run::DEFAULT_HTTP_PORT, desc: "HTTP port to publish on the host"
|
|
44
|
+
option :https_port, type: :numeric, default: Kamal::Configuration::Proxy::Run::DEFAULT_HTTPS_PORT, desc: "HTTPS port to publish on the host"
|
|
45
|
+
option :log_max_size, type: :string, default: Kamal::Configuration::Proxy::Run::DEFAULT_LOG_MAX_SIZE, desc: "Max size of proxy logs"
|
|
46
|
+
option :registry, type: :string, default: nil, desc: "Registry to use for the proxy image"
|
|
47
|
+
option :repository, type: :string, default: nil, desc: "Repository for the proxy image"
|
|
48
|
+
option :image_version, type: :string, default: nil, desc: "Version of the proxy to run"
|
|
49
|
+
option :metrics_port, type: :numeric, default: nil, desc: "Port to report prometheus metrics on"
|
|
50
|
+
option :debug, type: :boolean, default: false, desc: "Whether to run the proxy in debug mode"
|
|
51
|
+
option :docker_options, type: :array, default: [], desc: "Docker options to pass to the proxy container", banner: "option=value option2=value2"
|
|
52
|
+
def boot_config(subcommand)
|
|
53
|
+
say "The proxy boot_config command is deprecated - set the config in the deploy YAML at proxy/run instead", :yellow
|
|
54
|
+
proxy_boot_config = KAMAL.config.proxy_boot
|
|
55
|
+
|
|
56
|
+
case subcommand
|
|
57
|
+
when "set"
|
|
58
|
+
boot_options = [
|
|
59
|
+
*(proxy_boot_config.publish_args(options[:http_port], options[:https_port], options[:publish_host_ip]) if options[:publish]),
|
|
60
|
+
*(proxy_boot_config.logging_args(options[:log_max_size])),
|
|
61
|
+
*("--expose=#{options[:metrics_port]}" if options[:metrics_port]),
|
|
62
|
+
*options[:docker_options].map { |option| "--#{option}" }
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
image = [
|
|
66
|
+
options[:registry].presence,
|
|
67
|
+
options[:repository].presence || proxy_boot_config.repository_name,
|
|
68
|
+
proxy_boot_config.image_name
|
|
69
|
+
].compact.join("/")
|
|
70
|
+
|
|
71
|
+
image_version = options[:image_version]
|
|
72
|
+
|
|
73
|
+
run_command_options = { debug: options[:debug] || nil, "metrics-port": options[:metrics_port] }.compact
|
|
74
|
+
run_command = "kamal-proxy run #{Kamal::Utils.optionize(run_command_options).join(" ")}" if run_command_options.any?
|
|
75
|
+
|
|
76
|
+
on(KAMAL.proxy_hosts) do |host|
|
|
77
|
+
proxy = KAMAL.proxy(host)
|
|
78
|
+
execute(*proxy.ensure_proxy_directory)
|
|
79
|
+
if boot_options != proxy_boot_config.default_boot_options
|
|
80
|
+
upload! StringIO.new(boot_options.join(" ")), proxy_boot_config.options_file
|
|
81
|
+
else
|
|
82
|
+
execute *proxy.reset_boot_options, raise_on_non_zero_exit: false
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if image != proxy_boot_config.image_default
|
|
86
|
+
upload! StringIO.new(image), proxy_boot_config.image_file
|
|
87
|
+
else
|
|
88
|
+
execute *proxy.reset_image, raise_on_non_zero_exit: false
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
if image_version
|
|
92
|
+
upload! StringIO.new(image_version), proxy_boot_config.image_version_file
|
|
93
|
+
else
|
|
94
|
+
execute *proxy.reset_image_version, raise_on_non_zero_exit: false
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
if run_command
|
|
98
|
+
upload! StringIO.new(run_command), proxy_boot_config.run_command_file
|
|
99
|
+
else
|
|
100
|
+
execute *proxy.reset_run_command, raise_on_non_zero_exit: false
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
when "get"
|
|
104
|
+
|
|
105
|
+
on(KAMAL.proxy_hosts) do |host|
|
|
106
|
+
puts "Host #{host}: #{capture_with_info(*KAMAL.proxy(host).boot_config)}"
|
|
107
|
+
end
|
|
108
|
+
when "reset"
|
|
109
|
+
on(KAMAL.proxy_hosts) do |host|
|
|
110
|
+
proxy = KAMAL.proxy(host)
|
|
111
|
+
execute *proxy.reset_boot_options, raise_on_non_zero_exit: false
|
|
112
|
+
execute *proxy.reset_image, raise_on_non_zero_exit: false
|
|
113
|
+
execute *proxy.reset_image_version, raise_on_non_zero_exit: false
|
|
114
|
+
execute *proxy.reset_run_command, raise_on_non_zero_exit: false
|
|
115
|
+
end
|
|
116
|
+
else
|
|
117
|
+
raise ArgumentError, "Unknown boot_config subcommand #{subcommand}"
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
desc "reboot", "Reboot proxy on servers (stop container, remove container, start new container)"
|
|
122
|
+
option :rolling, type: :boolean, default: false, desc: "Reboot proxy on hosts in sequence, rather than in parallel"
|
|
123
|
+
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
|
124
|
+
def reboot
|
|
125
|
+
confirming "This will cause a brief outage on each host. Are you sure?" do
|
|
126
|
+
modify(lock: true) do
|
|
127
|
+
# Skip proxy on loadbalancer host - it will be handled by loadbalancer reboot
|
|
128
|
+
proxy_hosts = KAMAL.proxy_hosts
|
|
129
|
+
if KAMAL.config.proxy.loadbalancer_on_proxy_host?
|
|
130
|
+
proxy_hosts = proxy_hosts - [ KAMAL.config.proxy.effective_loadbalancer ]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
host_groups = options[:rolling] ? proxy_hosts : [ proxy_hosts ]
|
|
134
|
+
host_groups.each do |hosts|
|
|
135
|
+
next if Array(hosts).empty?
|
|
136
|
+
|
|
137
|
+
host_list = Array(hosts).join(",")
|
|
138
|
+
run_hook "pre-proxy-reboot", hosts: host_list
|
|
139
|
+
on(hosts) do |host|
|
|
140
|
+
proxy = KAMAL.proxy(host)
|
|
141
|
+
execute *KAMAL.auditor.record("Rebooted proxy"), verbosity: :debug
|
|
142
|
+
execute *KAMAL.registry.login
|
|
143
|
+
|
|
144
|
+
info "Stopping and removing kamal-proxy on #{host}, if running..."
|
|
145
|
+
execute *proxy.stop, raise_on_non_zero_exit: false
|
|
146
|
+
execute *proxy.remove_container
|
|
147
|
+
execute *proxy.ensure_apps_config_directory
|
|
148
|
+
|
|
149
|
+
execute *proxy.run
|
|
150
|
+
end
|
|
151
|
+
run_hook "post-proxy-reboot", hosts: host_list
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
if KAMAL.config.proxy.load_balancing?
|
|
155
|
+
lb_host = KAMAL.config.proxy.effective_loadbalancer
|
|
156
|
+
run_hook "pre-loadbalancer-reboot", hosts: lb_host
|
|
157
|
+
|
|
158
|
+
on(lb_host) do |host|
|
|
159
|
+
execute *KAMAL.auditor.record("Rebooted loadbalancer"), verbosity: :debug
|
|
160
|
+
execute *KAMAL.registry.login
|
|
161
|
+
|
|
162
|
+
info "Stopping and removing #{KAMAL.loadbalancer.container_name} on #{host}, if running..."
|
|
163
|
+
execute *KAMAL.loadbalancer.stop, raise_on_non_zero_exit: false
|
|
164
|
+
execute *KAMAL.loadbalancer.remove_container
|
|
165
|
+
execute *KAMAL.loadbalancer.ensure_apps_config_directory
|
|
166
|
+
|
|
167
|
+
execute *KAMAL.loadbalancer.run
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
run_hook "post-loadbalancer-reboot", hosts: lb_host
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
desc "upgrade", "Upgrade to kamal-proxy on servers (stop container, remove container, start new container, reboot app)", hide: true
|
|
177
|
+
option :rolling, type: :boolean, default: false, desc: "Reboot proxy on hosts in sequence, rather than in parallel"
|
|
178
|
+
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
|
179
|
+
def upgrade
|
|
180
|
+
invoke_options = { "version" => KAMAL.config.latest_tag }.merge(options)
|
|
181
|
+
|
|
182
|
+
confirming "This will cause a brief outage on each host. Are you sure?" do
|
|
183
|
+
host_groups = options[:rolling] ? KAMAL.hosts : [ KAMAL.hosts ]
|
|
184
|
+
host_groups.each do |hosts|
|
|
185
|
+
host_list = Array(hosts).join(",")
|
|
186
|
+
say "Upgrading proxy on #{host_list}...", :magenta
|
|
187
|
+
run_hook "pre-proxy-reboot", hosts: host_list
|
|
188
|
+
on(hosts) do |host|
|
|
189
|
+
proxy = KAMAL.proxy(host)
|
|
190
|
+
execute *KAMAL.auditor.record("Rebooted proxy"), verbosity: :debug
|
|
191
|
+
execute *KAMAL.registry.login
|
|
192
|
+
|
|
193
|
+
info "Stopping and removing Traefik on #{host}, if running..."
|
|
194
|
+
execute *proxy.cleanup_traefik
|
|
195
|
+
|
|
196
|
+
info "Stopping and removing kamal-proxy on #{host}, if running..."
|
|
197
|
+
execute *proxy.stop, raise_on_non_zero_exit: false
|
|
198
|
+
execute *proxy.remove_container
|
|
199
|
+
execute *proxy.remove_image
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
KAMAL.with_specific_hosts(hosts) do
|
|
203
|
+
invoke "kamal:cli:proxy:boot", [], invoke_options
|
|
204
|
+
reset_invocation(Kamal::Cli::Proxy)
|
|
205
|
+
invoke "kamal:cli:app:boot", [], invoke_options
|
|
206
|
+
reset_invocation(Kamal::Cli::App)
|
|
207
|
+
invoke "kamal:cli:prune:all", [], invoke_options
|
|
208
|
+
reset_invocation(Kamal::Cli::Prune)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
run_hook "post-proxy-reboot", hosts: host_list
|
|
212
|
+
say "Upgraded proxy on #{host_list}", :magenta
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
desc "start", "Start existing proxy container on servers"
|
|
218
|
+
def start
|
|
219
|
+
modify(lock: true) do
|
|
220
|
+
on(KAMAL.proxy_hosts) do |host|
|
|
221
|
+
execute *KAMAL.auditor.record("Started proxy"), verbosity: :debug
|
|
222
|
+
execute *KAMAL.proxy(host).start
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
desc "stop", "Stop existing proxy container on servers"
|
|
228
|
+
def stop
|
|
229
|
+
modify(lock: true) do
|
|
230
|
+
on(KAMAL.proxy_hosts) do |host|
|
|
231
|
+
execute *KAMAL.auditor.record("Stopped proxy"), verbosity: :debug
|
|
232
|
+
execute *KAMAL.proxy(host).stop, raise_on_non_zero_exit: false
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
desc "restart", "Restart existing proxy container on servers"
|
|
238
|
+
def restart
|
|
239
|
+
modify(lock: true) do
|
|
240
|
+
stop
|
|
241
|
+
start
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
desc "details", "Show details about proxy container from servers"
|
|
246
|
+
def details
|
|
247
|
+
quiet = options[:quiet]
|
|
248
|
+
on(KAMAL.proxy_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.proxy(host).info), type: "Proxy", quiet: quiet }
|
|
249
|
+
|
|
250
|
+
if KAMAL.config.proxy.load_balancing?
|
|
251
|
+
on(KAMAL.config.proxy.effective_loadbalancer) do |host|
|
|
252
|
+
puts_by_host host, capture_with_info(*KAMAL.loadbalancer.info), type: "Loadbalancer"
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
desc "logs", "Show log lines from proxy on servers"
|
|
258
|
+
option :since, aliases: "-s", desc: "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)"
|
|
259
|
+
option :lines, type: :numeric, aliases: "-n", desc: "Number of log lines to pull from each server"
|
|
260
|
+
option :grep, aliases: "-g", desc: "Show lines with grep match only (use this to fetch specific requests by id)"
|
|
261
|
+
option :follow, aliases: "-f", desc: "Follow logs on primary server (or specific host set by --hosts)"
|
|
262
|
+
option :skip_timestamps, type: :boolean, aliases: "-T", desc: "Skip appending timestamps to logging output"
|
|
263
|
+
def logs
|
|
264
|
+
grep = options[:grep]
|
|
265
|
+
timestamps = !options[:skip_timestamps]
|
|
266
|
+
|
|
267
|
+
if options[:follow]
|
|
268
|
+
run_locally do
|
|
269
|
+
proxy = KAMAL.proxy(KAMAL.primary_host)
|
|
270
|
+
info "Following logs on #{KAMAL.primary_host}..."
|
|
271
|
+
info proxy.follow_logs(host: KAMAL.primary_host, timestamps: timestamps, grep: grep)
|
|
272
|
+
exec proxy.follow_logs(host: KAMAL.primary_host, timestamps: timestamps, grep: grep)
|
|
273
|
+
end
|
|
274
|
+
else
|
|
275
|
+
since = options[:since]
|
|
276
|
+
lines = options[:lines].presence || ((since || grep) ? nil : 100) # Default to 100 lines if since or grep isn't set
|
|
277
|
+
|
|
278
|
+
on(KAMAL.proxy_hosts) do |host|
|
|
279
|
+
puts_by_host host, capture(*KAMAL.proxy(host).logs(timestamps: timestamps, since: since, lines: lines, grep: grep)), type: "Proxy"
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
desc "remove", "Remove proxy container and image from servers"
|
|
285
|
+
option :force, type: :boolean, default: false, desc: "Force removing proxy when apps are still installed"
|
|
286
|
+
def remove
|
|
287
|
+
modify(lock: true) do
|
|
288
|
+
if removal_allowed?(options[:force])
|
|
289
|
+
stop
|
|
290
|
+
remove_container
|
|
291
|
+
remove_image
|
|
292
|
+
remove_proxy_directory
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
desc "loadbalancer STATUS", "Manage the load balancer"
|
|
298
|
+
def loadbalancer(status)
|
|
299
|
+
case status
|
|
300
|
+
when "info"
|
|
301
|
+
if KAMAL.config.proxy.load_balancing?
|
|
302
|
+
on(KAMAL.config.proxy.effective_loadbalancer) do |host|
|
|
303
|
+
puts "Loadbalancer status on #{host}:"
|
|
304
|
+
puts capture_with_info(*KAMAL.loadbalancer.info)
|
|
305
|
+
end
|
|
306
|
+
else
|
|
307
|
+
puts "Load balancing is not configured"
|
|
308
|
+
end
|
|
309
|
+
when "start"
|
|
310
|
+
if KAMAL.config.proxy.load_balancing?
|
|
311
|
+
on(KAMAL.config.proxy.effective_loadbalancer) do |host|
|
|
312
|
+
execute *KAMAL.registry.login
|
|
313
|
+
execute *KAMAL.loadbalancer.start_or_run
|
|
314
|
+
end
|
|
315
|
+
else
|
|
316
|
+
puts "Load balancing is not configured"
|
|
317
|
+
end
|
|
318
|
+
when "stop"
|
|
319
|
+
if KAMAL.config.proxy.load_balancing?
|
|
320
|
+
on(KAMAL.config.proxy.effective_loadbalancer) do |host|
|
|
321
|
+
execute *KAMAL.loadbalancer.stop, raise_on_non_zero_exit: false
|
|
322
|
+
end
|
|
323
|
+
else
|
|
324
|
+
puts "Load balancing is not configured"
|
|
325
|
+
end
|
|
326
|
+
when "logs"
|
|
327
|
+
if KAMAL.config.proxy.load_balancing?
|
|
328
|
+
on(KAMAL.config.proxy.effective_loadbalancer) do |host|
|
|
329
|
+
puts_by_host host, capture(*KAMAL.loadbalancer.logs(timestamps: true)), type: "Loadbalancer"
|
|
330
|
+
end
|
|
331
|
+
else
|
|
332
|
+
puts "Load balancing is not configured"
|
|
333
|
+
end
|
|
334
|
+
when "deploy"
|
|
335
|
+
if KAMAL.config.proxy.load_balancing?
|
|
336
|
+
targets = []
|
|
337
|
+
KAMAL.config.roles.each do |role|
|
|
338
|
+
next unless role.running_proxy?
|
|
339
|
+
|
|
340
|
+
role.hosts.each do |host|
|
|
341
|
+
targets << host
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
on(KAMAL.config.proxy.effective_loadbalancer) do |host|
|
|
346
|
+
info "Deploying to loadbalancer on #{host} with targets: #{targets.join(', ')}"
|
|
347
|
+
execute *KAMAL.loadbalancer.deploy(targets: targets)
|
|
348
|
+
end
|
|
349
|
+
else
|
|
350
|
+
puts "Load balancing is not configured"
|
|
351
|
+
end
|
|
352
|
+
else
|
|
353
|
+
puts "Unknown loadbalancer subcommand: #{status}. Available: info, start, stop, logs, deploy"
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
desc "remove_container", "Remove proxy container from servers", hide: true
|
|
358
|
+
def remove_container
|
|
359
|
+
modify(lock: true) do
|
|
360
|
+
on(KAMAL.proxy_hosts) do
|
|
361
|
+
execute *KAMAL.auditor.record("Removed proxy container"), verbosity: :debug
|
|
362
|
+
execute *KAMAL.proxy(host).remove_container
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
if KAMAL.config.proxy.load_balancing?
|
|
366
|
+
on(KAMAL.config.proxy.effective_loadbalancer) do
|
|
367
|
+
execute *KAMAL.auditor.record("Removed loadbalancer container"), verbosity: :debug
|
|
368
|
+
execute *KAMAL.loadbalancer.remove_container
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
desc "remove_image", "Remove proxy image from servers", hide: true
|
|
375
|
+
def remove_image
|
|
376
|
+
modify(lock: true) do
|
|
377
|
+
on(KAMAL.proxy_hosts) do
|
|
378
|
+
execute *KAMAL.auditor.record("Removed proxy image"), verbosity: :debug
|
|
379
|
+
execute *KAMAL.proxy(host).remove_image
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
if KAMAL.config.proxy.load_balancing?
|
|
383
|
+
on(KAMAL.config.proxy.effective_loadbalancer) do
|
|
384
|
+
execute *KAMAL.auditor.record("Removed loadbalancer image"), verbosity: :debug
|
|
385
|
+
execute *KAMAL.loadbalancer.remove_image
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
desc "remove_proxy_directory", "Remove the proxy directory from servers", hide: true
|
|
392
|
+
def remove_proxy_directory
|
|
393
|
+
modify(lock: true) do
|
|
394
|
+
on(KAMAL.proxy_hosts) do
|
|
395
|
+
execute *KAMAL.proxy(host).remove_proxy_directory, raise_on_non_zero_exit: false
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
private
|
|
401
|
+
def removal_allowed?(force)
|
|
402
|
+
on(KAMAL.proxy_hosts) do |host|
|
|
403
|
+
app_count = capture_with_info(*KAMAL.server.app_directory_count).chomp.to_i
|
|
404
|
+
raise "The are other applications installed on #{host}" if app_count > 0
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
true
|
|
408
|
+
rescue SSHKit::Runner::ExecuteError => e
|
|
409
|
+
raise unless e.message.include?("The are other applications installed on")
|
|
410
|
+
|
|
411
|
+
if force
|
|
412
|
+
say "Forcing, so removing the proxy, even though other apps are installed", :magenta
|
|
413
|
+
else
|
|
414
|
+
say "Not removing the proxy, as other apps are installed, ignore this check with kamal proxy remove --force", :magenta
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
force
|
|
418
|
+
end
|
|
419
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
class Kamal::Cli::Prune < Kamal::Cli::Base
|
|
2
|
+
desc "all", "Prune unused images and stopped containers"
|
|
3
|
+
def all
|
|
4
|
+
modify(lock: true) do
|
|
5
|
+
containers
|
|
6
|
+
images
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
desc "images", "Prune unused images"
|
|
11
|
+
def images
|
|
12
|
+
modify(lock: true) do
|
|
13
|
+
on(KAMAL.hosts) do
|
|
14
|
+
execute *KAMAL.auditor.record("Pruned images"), verbosity: :debug
|
|
15
|
+
execute *KAMAL.prune.dangling_images
|
|
16
|
+
execute *KAMAL.prune.tagged_images
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
desc "containers", "Prune all stopped containers, except the last n (default 5)"
|
|
22
|
+
option :retain, type: :numeric, default: nil, desc: "Number of containers to retain"
|
|
23
|
+
def containers
|
|
24
|
+
retain = options.fetch(:retain, KAMAL.config.retain_containers)
|
|
25
|
+
raise "retain must be at least 1" if retain < 1
|
|
26
|
+
|
|
27
|
+
modify(lock: true) do
|
|
28
|
+
on(KAMAL.hosts) do
|
|
29
|
+
execute *KAMAL.auditor.record("Pruned containers"), verbosity: :debug
|
|
30
|
+
execute *KAMAL.prune.app_containers(retain: retain)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
class Kamal::Cli::Registry < Kamal::Cli::Base
|
|
2
|
+
desc "setup", "Setup local registry or log in to remote registry locally and remotely"
|
|
3
|
+
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
|
4
|
+
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
|
5
|
+
def setup
|
|
6
|
+
ensure_docker_installed unless options[:skip_local]
|
|
7
|
+
|
|
8
|
+
if KAMAL.registry.local?
|
|
9
|
+
run_locally { execute *KAMAL.registry.setup } unless options[:skip_local]
|
|
10
|
+
else
|
|
11
|
+
run_locally { execute *KAMAL.registry.login } unless options[:skip_local]
|
|
12
|
+
on(KAMAL.hosts) { execute *KAMAL.registry.login } unless options[:skip_remote]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
desc "remove", "Remove local registry or log out of remote registry locally and remotely"
|
|
17
|
+
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
|
18
|
+
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
|
19
|
+
def remove
|
|
20
|
+
if KAMAL.registry.local?
|
|
21
|
+
run_locally { execute *KAMAL.registry.remove, raise_on_non_zero_exit: false } unless options[:skip_local]
|
|
22
|
+
else
|
|
23
|
+
run_locally { execute *KAMAL.registry.logout } unless options[:skip_local]
|
|
24
|
+
on(KAMAL.hosts) { execute *KAMAL.registry.logout } unless options[:skip_remote]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc "login", "Log in to remote registry locally and remotely"
|
|
29
|
+
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
|
30
|
+
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
|
31
|
+
def login
|
|
32
|
+
if KAMAL.registry.local?
|
|
33
|
+
raise "Cannot use login command with a local registry. Use `kamal registry setup` instead."
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
setup
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
desc "logout", "Log out of remote registry locally and remotely"
|
|
40
|
+
option :skip_local, aliases: "-L", type: :boolean, default: false, desc: "Skip local login"
|
|
41
|
+
option :skip_remote, aliases: "-R", type: :boolean, default: false, desc: "Skip remote login"
|
|
42
|
+
def logout
|
|
43
|
+
if KAMAL.registry.local?
|
|
44
|
+
raise "Cannot use logout command with a local registry. Use `kamal registry remove` instead."
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
remove
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
class Kamal::Cli::Secrets < Kamal::Cli::Base
|
|
2
|
+
desc "fetch [SECRETS...]", "Fetch secrets from a vault"
|
|
3
|
+
option :adapter, type: :string, aliases: "-a", required: true, desc: "Which vault adapter to use"
|
|
4
|
+
option :account, type: :string, required: false, desc: "The account identifier or username"
|
|
5
|
+
option :from, type: :string, required: false, desc: "A vault or folder to fetch the secrets from"
|
|
6
|
+
option :inline, type: :boolean, required: false, hidden: true
|
|
7
|
+
def fetch(*secrets)
|
|
8
|
+
adapter = initialize_adapter(options[:adapter])
|
|
9
|
+
|
|
10
|
+
if adapter.requires_account? && options[:account].blank?
|
|
11
|
+
return puts "No value provided for required options '--account'"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
results = adapter.fetch(secrets, **options.slice(:account, :from).symbolize_keys)
|
|
15
|
+
json = JSON.dump(results)
|
|
16
|
+
|
|
17
|
+
return_or_puts options[:inline] ? json.shellescape : json, inline: options[:inline]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
desc "extract", "Extract a single secret from the results of a fetch call"
|
|
21
|
+
option :inline, type: :boolean, required: false, hidden: true
|
|
22
|
+
def extract(name, secrets)
|
|
23
|
+
parsed_secrets = JSON.parse(secrets)
|
|
24
|
+
value = parsed_secrets[name] || parsed_secrets.find { |k, v| k.end_with?("/#{name}") }&.last
|
|
25
|
+
|
|
26
|
+
raise "Could not find secret #{name}" if value.nil?
|
|
27
|
+
|
|
28
|
+
return_or_puts value, inline: options[:inline]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
desc "print", "Print the secrets (for debugging)"
|
|
32
|
+
def print
|
|
33
|
+
KAMAL.config.secrets.to_h.each do |key, value|
|
|
34
|
+
puts "#{key}=#{value}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
def initialize_adapter(adapter)
|
|
40
|
+
Kamal::Secrets::Adapters.lookup(adapter)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def return_or_puts(value, inline: nil)
|
|
44
|
+
if inline
|
|
45
|
+
value
|
|
46
|
+
else
|
|
47
|
+
puts value
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
class Kamal::Cli::Server < Kamal::Cli::Base
|
|
2
|
+
desc "exec", "Run a custom command on the server (use --help to show options)"
|
|
3
|
+
option :interactive, type: :boolean, aliases: "-i", default: false, desc: "Run the command interactively (use for console/bash)"
|
|
4
|
+
option :raw, type: :boolean, default: false, desc: "Output raw, unmodified stdout"
|
|
5
|
+
def exec(*cmd)
|
|
6
|
+
raw = options[:raw]
|
|
7
|
+
|
|
8
|
+
if raw && options[:interactive]
|
|
9
|
+
raise ArgumentError, "Raw is not compatible with interactive"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
with_raw_output(raw) do
|
|
13
|
+
pre_connect_if_required
|
|
14
|
+
|
|
15
|
+
cmd = Kamal::Utils.join_commands(cmd)
|
|
16
|
+
hosts = KAMAL.hosts
|
|
17
|
+
quiet = options[:quiet]
|
|
18
|
+
|
|
19
|
+
case
|
|
20
|
+
when options[:interactive]
|
|
21
|
+
host = KAMAL.primary_host
|
|
22
|
+
|
|
23
|
+
say "Running '#{cmd}' on #{host} interactively...", :magenta
|
|
24
|
+
|
|
25
|
+
run_locally { exec KAMAL.server.run_over_ssh(cmd, host: host) }
|
|
26
|
+
else
|
|
27
|
+
say "Running '#{cmd}' on #{hosts.join(', ')}...", :magenta
|
|
28
|
+
|
|
29
|
+
on(hosts) do |host|
|
|
30
|
+
execute *KAMAL.auditor.record("Executed cmd '#{cmd}' on #{host}"), verbosity: :debug
|
|
31
|
+
puts_by_host host, capture_with_info(cmd, strip: !raw), quiet: quiet, raw: raw
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
desc "bootstrap", "Set up Docker to run Kamal apps"
|
|
38
|
+
def bootstrap
|
|
39
|
+
modify(lock: true) do
|
|
40
|
+
missing = []
|
|
41
|
+
|
|
42
|
+
on(KAMAL.hosts) do |host|
|
|
43
|
+
unless execute(*KAMAL.docker.installed?, raise_on_non_zero_exit: false)
|
|
44
|
+
if execute(*KAMAL.docker.superuser?, raise_on_non_zero_exit: false)
|
|
45
|
+
info "Missing Docker on #{host}. Installing…"
|
|
46
|
+
execute *KAMAL.docker.install
|
|
47
|
+
|
|
48
|
+
unless execute(*KAMAL.docker.root?, raise_on_non_zero_exit: false) ||
|
|
49
|
+
execute(*KAMAL.docker.in_docker_group?, raise_on_non_zero_exit: false)
|
|
50
|
+
execute *KAMAL.docker.add_to_docker_group
|
|
51
|
+
begin
|
|
52
|
+
execute *KAMAL.docker.refresh_session
|
|
53
|
+
rescue IOError
|
|
54
|
+
info "Session refreshed due to group change."
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
missing << host
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
if missing.any?
|
|
64
|
+
raise "Docker is not installed on #{missing.join(", ")} and can't be automatically installed without having root access and either `wget` or `curl`. Install Docker manually: https://docs.docker.com/engine/install/"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
run_hook "docker-setup"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|