Compare commits

..

14 Commits

Author SHA1 Message Date
Donal McBreen
21d7d6d79c Bump version for 1.9.2 2024-10-06 14:06:39 -04:00
Donal McBreen
f1b3c4a4fb Merge pull request #1063 from basecamp/safe-directory-fix-1.9
Safe directory fix 1.9
2024-10-06 18:55:56 +01:00
Ivan Velichko
fd9564f0c8 Relax the safe.directory requirement
Co-authored-by: Jeremy Daer <jeremydaer@gmail.com>
2024-10-06 13:44:23 -04:00
Ivan Velichko
d2338251a9 Fix git --add safe.directory command in Dockerfile
Upgrading kamal from `v1.8.3` to `v1.9.0` broke my [kamal playground](https://labs.iximiuz.com/playgrounds/kamal):

```
laborant@dev-machine:~/svc-a$ kamal setup
  INFO [34d0def6] Running /usr/bin/env mkdir -p .kamal on 172.16.0.3
  INFO [c34cf833] Running /usr/bin/env mkdir -p .kamal on 172.16.0.4
  INFO [34d0def6] Finished in 0.147 seconds with exit status 0 (successful).
  INFO [c34cf833] Finished in 0.204 seconds with exit status 0 (successful).
Acquiring the deploy lock...
Ensure Docker is installed...
  INFO [413ee426] Running docker -v on 172.16.0.4
  INFO [f1acacba] Running docker -v on 172.16.0.3
  INFO [413ee426] Finished in 0.036 seconds with exit status 0 (successful).
  INFO [f1acacba] Finished in 0.076 seconds with exit status 0 (successful).
Log into image registry...
  INFO [94cff492] Running docker login registry.iximiuz.com -u [REDACTED] -p [REDACTED] on localhost
  INFO [94cff492] Finished in 0.077 seconds with exit status 0 (successful).
  INFO [605c535f] Running docker login registry.iximiuz.com -u [REDACTED] -p [REDACTED] on 172.16.0.4
  INFO [6002b598] Running docker login registry.iximiuz.com -u [REDACTED] -p [REDACTED] on 172.16.0.3
  INFO [605c535f] Finished in 0.083 seconds with exit status 0 (successful).
  INFO [6002b598] Finished in 0.083 seconds with exit status 0 (successful).
Build and push app image...
  INFO [9d172b1e] Running docker --version && docker buildx version on localhost
  INFO [9d172b1e] Finished in 0.059 seconds with exit status 0 (successful).
  INFO Cloning repo into build directory `/tmp/kamal-clones/svc-a-2f65914456263/workdir/`...
  INFO [26fb1bd3] Running /usr/bin/env git -C /tmp/kamal-clones/svc-a-2f65914456263 clone /workdir --recurse-submodules on localhost
 ERROR Error preparing clone: Failed to clone repo: git exit status: 32768
git stdout: Nothing written
git stderr: Cloning into 'workdir'...
fatal: detected dubious ownership in repository at '/workdir/.git'
To add an exception for this directory, call:

        git config --global --add safe.directory /workdir/.git
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
, deleting and retrying...
  INFO Cloning repo into build directory `/tmp/kamal-clones/svc-a-2f65914456263/workdir/`...
  INFO [fd4aac0c] Running /usr/bin/env git -C /tmp/kamal-clones/svc-a-2f65914456263 clone /workdir --recurse-submodules on localhost
  Finished all in 0.3 seconds
Releasing the deploy lock...
  Finished all in 0.6 seconds
  ERROR (SSHKit::Command::Failed): git exit status: 32768
git stdout: Nothing written
git stderr: Cloning into 'workdir'...
fatal: detected dubious ownership in repository at '/workdir/.git'
To add an exception for this directory, call:

        git config --global --add safe.directory /workdir/.git
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

laborant@dev-machine:~/svc-a$ kamal version
2.0.0
```

I checked the [v1.8.3...v1.9.0](https://github.com/basecamp/kamal/compare/v1.8.3...v1.9.0) diff, and couldn't find anything even remotely related to the above error.

Then I checked the `git` versions in kamal `v1.8.3` and `v1.9.0` images:

```
docker run -it --rm --entrypoint sh ghcr.io/basecamp/kamal:v1.8.3 
/workdir # git --version
git version 2.38.5
```

vs.

```
docker run -it --rm --entrypoint sh ghcr.io/basecamp/kamal:v2.0.0 
/workdir # git --version
git version 2.39.5
```

Apparently, something changed in between `2.38.5` and `2.39.5` git releases (likely yet another CVE fix), and the `git config --global --add safe.directory /workdir` stopped working.

Here is the mitigation I currently use, but it's a bit awkward to do it:

```
docker build -t ghcr.io/basecamp/kamal:v2.0.0 - <<EOF
FROM ghcr.io/basecamp/kamal:v2.0.0

RUN git config --global --add safe.directory /workdir/.git
EOF
```

Hence, this PR.

To repro, you can start a [kamal playground](https://labs.iximiuz.com/playgrounds/kamal), then `docker pull ghcr.io/basecamp/kamal:v2.0.0` to override my patched image, and `cd svc-a && kamal setup`.
2024-10-06 13:44:12 -04:00
Donal McBreen
b00a4ec3e2 Merge pull request #1030 from basecamp/docker-not-latest
Do not tag 1.9.x Docker images as latest
2024-10-02 11:15:44 +01:00
Donal McBreen
4b09375ccd Exclude invalid Rails 8/Ruby 3.1 combination 2024-10-02 10:11:46 +01:00
Donal McBreen
3e0302230e Do not tag 1.9.x Docker images as latest
Only 2.x images should be set as latest.
2024-10-02 09:59:41 +01:00
Donal McBreen
bce2d35e9f Test 1-9-stable on push 2024-09-30 08:51:02 +01:00
Donal McBreen
46ea88a056 Bump version for 1.9.1 2024-09-30 08:49:47 +01:00
Donal McBreen
fa05270cac Merge pull request #997 from basecamp/traefik-2.11
Traefik 2.11 default to address CVE-2024-45410
2024-09-30 03:14:08 -04:00
Jeremy Daer
b058c45973 Traefik 2.11 default to address CVE-2024-45410
Fixes #968
2024-09-28 11:28:50 -04:00
Donal McBreen
9db1403721 Bump version for 1.9.0 2024-09-26 15:30:08 -04:00
Donal McBreen
bf4add9e72 Merge pull request #946 from basecamp/kamal-2.0-downgrade
Downgrade from Kamal 2 to 1.9
2024-09-18 10:27:40 +01:00
Donal McBreen
7c7785c1eb Downgrade from Kamal 2 to 1.9
Add a downgrade command, so you can reverse the upgrade process and go
back to Kamal 1.9. This replaces kamal-proxy and reboots all the
accessories.

This gives an upgrade and downgrade path:

Upgrade:
1. Upgrade config to be Kamal 2 compatible + use kamal 2.0
2. Run `kamal upgrade`

Downgrade:
1. Switch back to previous config + use kamal 1.9
2. Run `kamal downgrade`

You can set `--rolling` to downgrade one host at a time.
2024-09-18 10:11:32 +01:00
22 changed files with 258 additions and 16 deletions

View File

@@ -3,7 +3,7 @@ on:
push: push:
branches: branches:
- main - main
- 1-8-stable - 1-9-stable
pull_request: pull_request:
jobs: jobs:
rubocop: rubocop:
@@ -31,6 +31,9 @@ jobs:
gemfile: gemfile:
- Gemfile - Gemfile
- gemfiles/rails_edge.gemfile - gemfiles/rails_edge.gemfile
exclude:
- ruby-version: "3.1"
gemfile: gemfiles/rails_edge.gemfile
name: ${{ format('Tests (Ruby {0})', matrix.ruby-version) }} name: ${{ format('Tests (Ruby {0})', matrix.ruby-version) }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
continue-on-error: true continue-on-error: true

View File

@@ -51,5 +51,4 @@ jobs:
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: | tags: |
ghcr.io/basecamp/kamal:latest
ghcr.io/basecamp/kamal:${{ steps.version-tag.outputs.value }} ghcr.io/basecamp/kamal:${{ steps.version-tag.outputs.value }}

View File

@@ -33,7 +33,7 @@ WORKDIR /workdir
# Tell git it's safe to access /workdir/.git even if # Tell git it's safe to access /workdir/.git even if
# the directory is owned by a different user # the directory is owned by a different user
RUN git config --global --add safe.directory /workdir RUN git config --global --add safe.directory '*'
# Set the entrypoint to run the installed binary in /workdir # Set the entrypoint to run the installed binary in /workdir
# Example: docker run -it -v "$PWD:/workdir" kamal init # Example: docker run -it -v "$PWD:/workdir" kamal init

View File

@@ -1,7 +1,7 @@
PATH PATH
remote: . remote: .
specs: specs:
kamal (1.8.3) kamal (1.9.2)
activesupport (>= 7.0) activesupport (>= 7.0)
base64 (~> 0.2) base64 (~> 0.2)
bcrypt_pbkdf (~> 1.0) bcrypt_pbkdf (~> 1.0)

View File

@@ -222,6 +222,25 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
end end
end end
desc "downgrade", "Downgrade accessories from Kamal 2 to 1.9"
option :rolling, type: :boolean, default: false, desc: "Upgrade one host at a time"
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
def downgrade(name)
confirming "This will restart all accessories" do
with_lock do
host_groups = options[:rolling] ? KAMAL.accessory_hosts : [ KAMAL.accessory_hosts ]
host_groups.each do |hosts|
host_list = Array(hosts).join(",")
KAMAL.with_specific_hosts(hosts) do
say "Downgrading #{name} accessories on #{host_list}...", :magenta
reboot name
say "Downgraded #{name} accessories on #{host_list}...", :magenta
end
end
end
end
end
private private
def with_accessory(name) def with_accessory(name)
if KAMAL.config.accessory(name) if KAMAL.config.accessory(name)

View File

@@ -206,6 +206,10 @@ module Kamal::Cli
instance_variable_get("@_invocations").first instance_variable_get("@_invocations").first
end end
def reset_invocation(cli_class)
instance_variable_get("@_invocations")[cli_class].pop
end
def ensure_run_and_locks_directory def ensure_run_and_locks_directory
on(KAMAL.hosts) do on(KAMAL.hosts) do
execute(*KAMAL.server.ensure_run_directory) execute(*KAMAL.server.ensure_run_directory)

View File

@@ -217,6 +217,37 @@ class Kamal::Cli::Main < Kamal::Cli::Base
end end
end end
desc "downgrade", "Downgrade from Kamal 2 to 1.9"
option :confirmed, aliases: "-y", type: :boolean, default: false, desc: "Proceed without confirmation question"
option :rolling, type: :boolean, default: false, desc: "Downgrade one host at a time"
def downgrade
confirming "This will replace Traefik with kamal-proxy and restart all accessories" do
with_lock do
if options[:rolling]
(KAMAL.hosts | KAMAL.accessory_hosts).each do |host|
KAMAL.with_specific_hosts(host) do
say "Downgrading #{host}...", :magenta
if KAMAL.hosts.include?(host)
invoke "kamal:cli:traefik:downgrade", [], options.merge(confirmed: true, rolling: false)
reset_invocation(Kamal::Cli::Traefik)
end
if KAMAL.accessory_hosts.include?(host)
invoke "kamal:cli:accessory:downgrade", [ "all" ], options.merge(confirmed: true, rolling: false)
reset_invocation(Kamal::Cli::Accessory)
end
say "Downgraded #{host}", :magenta
end
end
else
say "Downgrading all hosts...", :magenta
invoke "kamal:cli:traefik:downgrade", [], options.merge(confirmed: true)
invoke "kamal:cli:accessory:downgrade", [ "all" ], options.merge(confirmed: true)
say "Downgraded all hosts", :magenta
end
end
end
end
desc "version", "Show Kamal version" desc "version", "Show Kamal version"
def version def version
puts Kamal::VERSION puts Kamal::VERSION

View File

@@ -119,4 +119,44 @@ class Kamal::Cli::Traefik < Kamal::Cli::Base
end end
end end
end end
desc "downgrade", "Downgrade to Traefik on servers (stop container, remove container, start new container, reboot app)"
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 downgrade
invoke_options = { "version" => KAMAL.config.latest_tag }.merge(options)
confirming "This will cause a brief outage on each host. Are you sure?" do
host_groups = options[:rolling] ? KAMAL.hosts : [ KAMAL.hosts ]
host_groups.each do |hosts|
host_list = Array(hosts).join(",")
say "Downgrading to Traefik on #{host_list}...", :magenta
run_hook "pre-traefik-reboot", hosts: host_list
on(hosts) do |host|
execute *KAMAL.auditor.record("Rebooted Traefik"), verbosity: :debug
execute *KAMAL.registry.login
"Stopping and removing kamal-proxy on #{host}, if running..."
execute *KAMAL.traefik.cleanup_kamal_proxy
"Stopping and removing Traefik on #{host}, if running..."
execute *KAMAL.traefik.stop, raise_on_non_zero_exit: false
execute *KAMAL.traefik.remove_container
execute *KAMAL.traefik.remove_image
end
KAMAL.with_specific_hosts(hosts) do
invoke "kamal:cli:traefik:boot", [], invoke_options
reset_invocation(Kamal::Cli::Traefik)
invoke "kamal:cli:app:boot", [], invoke_options
reset_invocation(Kamal::Cli::App)
invoke "kamal:cli:prune:all", [], invoke_options
reset_invocation(Kamal::Cli::Prune)
end
run_hook "post-traefik-reboot", hosts: host_list
say "Downgraded to Traefik on #{host_list}", :magenta
end
end
end
end end

View File

@@ -56,6 +56,13 @@ class Kamal::Commander
end end
end end
def with_specific_hosts(hosts)
original_hosts, self.specific_hosts = specific_hosts, hosts
yield
ensure
self.specific_hosts = original_hosts
end
def accessory_names def accessory_names
config.accessories&.collect(&:name) || [] config.accessories&.collect(&:name) || []
end end

View File

@@ -23,7 +23,7 @@ class Kamal::Commander::Specifics
end end
def accessory_hosts def accessory_hosts
specific_hosts || config.accessories.flat_map(&:hosts) config.accessories.flat_map(&:hosts) & specified_hosts
end end
private private

View File

@@ -62,6 +62,15 @@ class Kamal::Commands::Traefik < Kamal::Commands::Base
[ :rm, "-f", env.secrets_file ] [ :rm, "-f", env.secrets_file ]
end end
def cleanup_kamal_proxy
chain \
docker(:container, :stop, "kamal-proxy"),
combine(
docker(:container, :prune, "--force", "--filter", "label=org.opencontainers.image.title=kamal-proxy"),
docker(:image, :prune, "--all", "--force", "--filter", "label=org.opencontainers.image.title=kamal-proxy")
)
end
private private
def publish_args def publish_args
argumentize "--publish", port if publish? argumentize "--publish", port if publish?

View File

@@ -17,8 +17,8 @@ traefik:
# Image # Image
# #
# The Traefik image to use, defaults to `traefik:v2.10` # The Traefik image to use, defaults to `traefik:v2.11`
image: traefik:v2.9 image: traefik:v2.11
# Host port # Host port
# #

View File

@@ -1,5 +1,5 @@
class Kamal::Configuration::Traefik class Kamal::Configuration::Traefik
DEFAULT_IMAGE = "traefik:v2.10" DEFAULT_IMAGE = "traefik:v2.11"
CONTAINER_PORT = 80 CONTAINER_PORT = 80
DEFAULT_ARGS = { DEFAULT_ARGS = {
"log.level" => "DEBUG" "log.level" => "DEBUG"

View File

@@ -1,3 +1,3 @@
module Kamal module Kamal
VERSION = "1.8.3" VERSION = "1.9.2"
end end

View File

@@ -209,6 +209,24 @@ class CliAccessoryTest < CliTestCase
end end
end end
test "downgrade" do
run_command("downgrade", "-y", "all").tap do |output|
assert_match "Downgrading all accessories on 1.1.1.3,1.1.1.1,1.1.1.2...", output
assert_match "docker container stop app-mysql on 1.1.1.3", output
assert_match "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 3306:3306 --env-file .kamal/env/accessories/app-mysql.env --env MYSQL_ROOT_HOST=\"%\" --volume $PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf --volume $PWD/app-mysql/data:/var/lib/mysql --label service=\"app-mysql\" mysql:5.7 on 1.1.1.3", output
assert_match "Downgraded all accessories on 1.1.1.3,1.1.1.1,1.1.1.2", output
end
end
test "downgrade rolling" do
run_command("downgrade", "--rolling", "-y", "all").tap do |output|
assert_match "Downgrading all accessories on 1.1.1.3...", output
assert_match "docker container stop app-mysql on 1.1.1.3", output
assert_match "docker run --name app-mysql --detach --restart unless-stopped --log-opt max-size=\"10m\" --publish 3306:3306 --env-file .kamal/env/accessories/app-mysql.env --env MYSQL_ROOT_HOST=\"%\" --volume $PWD/app-mysql/etc/mysql/my.cnf:/etc/mysql/my.cnf --volume $PWD/app-mysql/data:/var/lib/mysql --label service=\"app-mysql\" mysql:5.7 on 1.1.1.3", output
assert_match "Downgraded all accessories on 1.1.1.3", output
end
end
private private
def run_command(*command) def run_command(*command)
stdouted { Kamal::Cli::Accessory.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) } stdouted { Kamal::Cli::Accessory.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }

View File

@@ -537,6 +537,34 @@ class CliMainTest < CliTestCase
assert_equal Kamal::VERSION, version assert_equal Kamal::VERSION, version
end end
test "downgrade" do
invoke_options = { "config_file" => "test/fixtures/deploy_with_accessories.yml", "skip_hooks" => false, "confirmed" => true, "rolling" => false }
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:traefik:downgrade", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:downgrade", [ "all" ], invoke_options)
run_command("downgrade", "-y", config_file: "deploy_with_accessories").tap do |output|
assert_match "Downgrading all hosts...", output
assert_match "Downgraded all hosts", output
end
end
test "downgrade rolling" do
invoke_options = { "config_file" => "test/fixtures/deploy_with_accessories.yml", "skip_hooks" => false, "confirmed" => true, "rolling" => false }
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:traefik:downgrade", [], invoke_options).times(4)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:accessory:downgrade", [ "all" ], invoke_options).times(3)
run_command("downgrade", "--rolling", "-y", config_file: "deploy_with_accessories").tap do |output|
assert_match "Downgrading 1.1.1.1...", output
assert_match "Downgraded 1.1.1.1", output
assert_match "Downgrading 1.1.1.2...", output
assert_match "Downgraded 1.1.1.2", output
assert_match "Downgrading 1.1.1.3...", output
assert_match "Downgraded 1.1.1.3", output
assert_match "Downgrading 1.1.1.4...", output
assert_match "Downgraded 1.1.1.4", output
end
end
private private
def run_command(*command, config_file: "deploy_simple") def run_command(*command, config_file: "deploy_simple")
stdouted { Kamal::Cli::Main.start([ *command, "-c", "test/fixtures/#{config_file}.yml" ]) } stdouted { Kamal::Cli::Main.start([ *command, "-c", "test/fixtures/#{config_file}.yml" ]) }

View File

@@ -103,6 +103,90 @@ class CliTraefikTest < CliTestCase
end end
end end
test "downgrade" do
Object.any_instance.stubs(:sleep)
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-latest$", "--quiet", raise_on_non_zero_exit: false)
.returns("12345678")
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-workers-latest$", "--quiet", raise_on_non_zero_exit: false)
.returns("12345678")
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with { |*args| args[0..1] == [ :sh, "-c" ] }
.returns("123") # old version
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-latest$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
.returns("running") # health check
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-workers-latest$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
.returns("running").at_least_once # workers health check
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\\n\" .Source .Destination}}{{ end }}'", "app-web-123", "|", :awk, "'$2 == \"/tmp/kamal-cord\" {print $1}'", raise_on_non_zero_exit: false)
.returns("") # old version
run_command("downgrade", "-y").tap do |output|
assert_match "Downgrading to Traefik on 1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4...", output
assert_match "docker login -u [REDACTED] -p [REDACTED]", output
assert_match "docker container stop kamal-proxy ; docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy && docker image prune --all --force --filter label=org.opencontainers.image.title=kamal-proxy", 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 "/usr/bin/env mkdir -p .kamal", output
assert_match "docker login -u [REDACTED] -p [REDACTED]", output
assert_match "docker container start traefik || docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" --label traefik.http.routers.catchall.entryPoints=\"http\" --label traefik.http.routers.catchall.rule=\"PathPrefix(\\`/\\`)\" --label traefik.http.routers.catchall.service=\"unavailable\" --label traefik.http.routers.catchall.priority=\"1\" --label traefik.http.services.unavailable.loadbalancer.server.port=\"0\" traefik:v2.11 --providers.docker --log.level=\"DEBUG\"", output
assert_match "/usr/bin/env mkdir -p .kamal", output
assert_match %r{docker rename app-web-latest app-web-latest_replaced_.*}, output
assert_match %r{docker run --detach --restart unless-stopped --name app-web-latest --hostname 1.1.1.1-.* -e KAMAL_CONTAINER_NAME="app-web-latest" -e KAMAL_VERSION="latest" --env-file .kamal/env/roles/app-web.env --health-cmd}, output
assert_match "docker tag dhh/app:latest dhh/app:latest", output
assert_match "/usr/bin/env mkdir -p .kamal", output
assert_match "docker ps -q -a --filter label=service=app --filter status=created --filter status=exited --filter status=dead | tail -n +6 | while read container_id; do docker rm $container_id; done", output
assert_match "docker image prune --force --filter label=service=app", output
assert_match "Downgraded to Traefik on 1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", output
end
end
test "downgrade rolling" do
Object.any_instance.stubs(:sleep)
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-latest$", "--quiet", raise_on_non_zero_exit: false)
.returns("12345678")
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-workers-latest$", "--quiet", raise_on_non_zero_exit: false)
.returns("12345678")
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with { |*args| args[0..1] == [ :sh, "-c" ] }
.returns("123") # old version
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-latest$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
.returns("running") # health check
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-workers-latest$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
.returns("running").at_least_once # workers health check
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with(:docker, :inspect, "-f '{{ range .Mounts }}{{printf \"%s %s\\n\" .Source .Destination}}{{ end }}'", "app-web-123", "|", :awk, "'$2 == \"/tmp/kamal-cord\" {print $1}'", raise_on_non_zero_exit: false)
.returns("") # old version
run_command("downgrade", "--rolling", "-y",).tap do |output|
%w[1.1.1.1 1.1.1.2 1.1.1.3 1.1.1.4].each do |host|
assert_match "Downgrading to Traefik on #{host}...", output
assert_match "docker container stop kamal-proxy ; docker container prune --force --filter label=org.opencontainers.image.title=kamal-proxy && docker image prune --all --force --filter label=org.opencontainers.image.title=kamal-proxy", output
assert_match "Downgraded to Traefik on #{host}", output
end
end
end
private private
def run_command(*command) def run_command(*command)
stdouted { Kamal::Cli::Traefik.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) } stdouted { Kamal::Cli::Traefik.start([ *command, "-c", "test/fixtures/deploy_with_accessories.yml" ]) }

View File

@@ -33,7 +33,7 @@ traefik:
args: args:
accesslog: true accesslog: true
accesslog.format: json accesslog.format: json
image: registry:4443/traefik:v2.10 image: registry:4443/traefik:v2.11
accessories: accessories:
busybox: busybox:
service: custom-busybox service: custom-busybox

View File

@@ -27,7 +27,7 @@ traefik:
args: args:
accesslog: true accesslog: true
accesslog.format: json accesslog.format: json
image: registry:4443/traefik:v2.10 image: registry:4443/traefik:v2.11
accessories: accessories:
busybox: busybox:
service: custom-busybox service: custom-busybox

View File

@@ -19,7 +19,7 @@ push_image_to_registry_4443() {
install_kamal install_kamal
push_image_to_registry_4443 nginx 1-alpine-slim push_image_to_registry_4443 nginx 1-alpine-slim
push_image_to_registry_4443 traefik v2.10 push_image_to_registry_4443 traefik v2.11
push_image_to_registry_4443 busybox 1.36.0 push_image_to_registry_4443 busybox 1.36.0
# .ssh is on a shared volume that persists between runs. Clean it up as the # .ssh is on a shared volume that persists between runs. Clean it up as the

View File

@@ -32,7 +32,7 @@ class MainTest < IntegrationTest
assert_match /Traefik Host: vm2/, details assert_match /Traefik Host: vm2/, details
assert_match /App Host: vm1/, details assert_match /App Host: vm1/, details
assert_match /App Host: vm2/, details assert_match /App Host: vm2/, details
assert_match /traefik:v2.10/, details assert_match /traefik:v2.11/, details
assert_match /registry:4443\/app:#{first_version}/, details assert_match /registry:4443\/app:#{first_version}/, details
audit = kamal :audit, capture: true audit = kamal :audit, capture: true

View File

@@ -52,11 +52,11 @@ class TraefikTest < IntegrationTest
private private
def assert_traefik_running def assert_traefik_running
assert_match /traefik:v2.10 "\/entrypoint.sh/, traefik_details assert_match /traefik:v2.11 "\/entrypoint.sh/, traefik_details
end end
def assert_traefik_not_running def assert_traefik_not_running
assert_no_match /traefik:v2.10 "\/entrypoint.sh/, traefik_details assert_no_match /traefik:v2.11 "\/entrypoint.sh/, traefik_details
end end
def traefik_details def traefik_details