Add kamal proxy update
For easy updates from Traefik to kamal-proxy, add `kamal proxy update`. This stops and removes Traefik and kamal-proxy containers (just in case it is run after an update). Then it starts kamal-proxy and calls `kamal-proxy deploy` to route to any app containers that are already running. It can be run with a rolling option and calls the `pre-proxy-reboot` and `post-proxy-reboot` hooks for each host.
This commit is contained in:
@@ -60,6 +60,50 @@ class Kamal::Cli::Proxy < Kamal::Cli::Base
|
||||
end
|
||||
end
|
||||
|
||||
desc "update", "Update from Traefik to kamal-proxy, for when moving from Kamal v1 to Kamal v2"
|
||||
option :rolling, type: :boolean, default: false, desc: "Reboot proxy on hosts in sequence, rather than in parallel"
|
||||
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
|
||||
def update
|
||||
confirming "This will cause a brief outage on each host. Are you sure?" do
|
||||
with_lock do
|
||||
host_groups = options[:rolling] ? KAMAL.proxy_hosts : [ KAMAL.proxy_hosts ]
|
||||
host_groups.each do |hosts|
|
||||
host_list = Array(hosts).join(",")
|
||||
run_hook "pre-proxy-reboot", hosts: host_list
|
||||
on(hosts) do
|
||||
info "Updating proxy from Traefik to kamal-proxy on #{host}..."
|
||||
execute *KAMAL.auditor.record("Updated proxy from Traefik to kamal-proxy"), verbosity: :debug
|
||||
execute *KAMAL.registry.login
|
||||
|
||||
info "Stopping and removing Traefik on #{host}..."
|
||||
execute *KAMAL.proxy.stop(name: "traefik"), raise_on_non_zero_exit: false
|
||||
execute *KAMAL.proxy.remove_container(filter: "label=org.opencontainers.image.title=traefik")
|
||||
execute *KAMAL.proxy.remove_image(filter: "label=org.opencontainers.image.title=traefik")
|
||||
|
||||
info "Stopping and removing kamal-proxy on #{host}, if running..."
|
||||
execute *KAMAL.proxy.stop, raise_on_non_zero_exit: false
|
||||
execute *KAMAL.proxy.remove_container
|
||||
|
||||
info "Starting kamal-proxy on #{host}..."
|
||||
execute *KAMAL.proxy.run
|
||||
|
||||
KAMAL.roles_on(host).select(&:running_proxy?).each do |role|
|
||||
app = KAMAL.app(role: role, host: host)
|
||||
|
||||
version = capture_with_info(*app.current_running_version, raise_on_non_zero_exit: false).strip
|
||||
endpoint = capture_with_info(*app.container_endpoint(version: version)).strip
|
||||
raise Kamal::Cli::BootError, "Failed to get endpoint for #{role} on #{host}, is the app container running?" if endpoint.empty?
|
||||
|
||||
info "Deploying #{endpoint} for role `#{role}` on #{host}..."
|
||||
execute *KAMAL.proxy.deploy(role.container_prefix, target: endpoint)
|
||||
end
|
||||
end
|
||||
run_hook "post-proxy-reboot", hosts: host_list
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc "details", "Show details about proxy container from servers"
|
||||
def details
|
||||
on(KAMAL.proxy_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.proxy.info), type: "Proxy" }
|
||||
|
||||
@@ -26,8 +26,8 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base
|
||||
docker :container, :start, container_name
|
||||
end
|
||||
|
||||
def stop
|
||||
docker :container, :stop, container_name
|
||||
def stop(name: container_name)
|
||||
docker :container, :stop, name
|
||||
end
|
||||
|
||||
def start_or_run
|
||||
@@ -60,12 +60,12 @@ class Kamal::Commands::Proxy < Kamal::Commands::Base
|
||||
).join(" "), host: host
|
||||
end
|
||||
|
||||
def remove_container
|
||||
docker :container, :prune, "--force", "--filter", container_filter
|
||||
def remove_container(filter: container_filter)
|
||||
docker :container, :prune, "--force", "--filter", filter
|
||||
end
|
||||
|
||||
def remove_image
|
||||
docker :image, :prune, "--all", "--force", "--filter", image_filter
|
||||
def remove_image(filter: image_filter)
|
||||
docker :image, :prune, "--all", "--force", "--filter", filter
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -87,6 +87,28 @@ class CliProxyTest < CliTestCase
|
||||
end
|
||||
end
|
||||
|
||||
test "update" do
|
||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-123$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{.NetworkSettings.IPAddress}}{{range $k, $v := .NetworkSettings.Ports}}{{printf \":%s\" $k}}{{break}}{{end}}'", "|", :sed, "-e", "'s/\\/tcp$//'")
|
||||
.returns("172.1.0.2:80")
|
||||
.at_least_once
|
||||
|
||||
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
|
||||
.with { |*args| args[0..1] == [ :sh, "-c" ] }
|
||||
.returns("123")
|
||||
.at_least_once
|
||||
|
||||
run_command("update", "-y").tap do |output|
|
||||
assert_match "docker container stop traefik", output
|
||||
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=traefik", output
|
||||
assert_match "docker image prune --all --force --filter label=org.opencontainers.image.title=traefik", output
|
||||
assert_match "docker container stop kamal-proxy", output
|
||||
assert_match "docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy", output
|
||||
assert_match "docker run --name kamal-proxy --detach --restart unless-stopped --publish 80:80 --publish 443:443 --volume /var/run/docker.sock:/var/run/docker.sock --volume kamal-proxy:/root/.config/kamal-proxy --log-opt max-size=\"10m\" #{Kamal::Configuration::Proxy::DEFAULT_IMAGE}", output
|
||||
assert_match "docker exec kamal-proxy kamal-proxy deploy app-web --target \"172.1.0.2:80\"", output
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def run_command(*command)
|
||||
stdouted { Kamal::Cli::Proxy.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }
|
||||
|
||||
Reference in New Issue
Block a user