This commit is contained in:
Donal McBreen
2023-08-28 09:39:14 +01:00
parent 989d09e027
commit 782b1979ab
29 changed files with 558 additions and 262 deletions

View File

@@ -5,6 +5,7 @@ class CliAppTest < CliTestCase
stub_running
run_command("boot").tap do |output|
assert_match "docker tag dhh/app:latest dhh/app:latest", output
assert_match /docker rename app-web-latest app-web-latest_replaced_[0-9a-f]{16}/, output
assert_match /docker run --detach --restart unless-stopped --name app-web-latest --hostname 1.1.1.1-[0-9a-f]{12} /, output
assert_match "docker container ls --all --filter name=^app-web-123$ --quiet | xargs docker stop", output
end
@@ -13,26 +14,12 @@ class CliAppTest < CliTestCase
test "boot will rename if same version is already running" do
run_command("details") # Preheat Kamal const
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :container, :ls, "--filter", "name=^app-web-latest$", "--quiet", raise_on_non_zero_exit: false)
.returns("12345678") # running version
SSHKit::Backend::Abstract.any_instance.expects(: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.expects(:capture_with_info)
.with(:docker, :ps, "--filter", "label=service=app", "--filter", "label=role=web", "--filter", "status=running", "--filter", "status=restarting", "--latest", "--format", "\"{{.Names}}\"", "|", "while read line; do echo ${line#app-web-}; done", raise_on_non_zero_exit: false)
.returns("123") # old version
stub_running
run_command("boot").tap do |output|
assert_match /Renaming container .* to .* as already deployed on 1.1.1.1/, output # Rename
assert_match /docker rename app-web-latest app-web-latest_replaced_[0-9a-f]{16}/, output
assert_match /docker run --detach --restart unless-stopped --name app-web-latest --hostname 1.1.1.1-[0-9a-f]{12} /, output
assert_match "docker container ls --all --filter name=^app-web-123$ --quiet | xargs docker stop", output
end
ensure
Thread.report_on_exception = true
end
test "boot uses group strategy when specified" do
@@ -45,9 +32,27 @@ class CliAppTest < CliTestCase
run_command("boot", config: :with_boot_strategy)
end
test "boot without traefik file provider raises exception" do
Thread.report_on_exception = false
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :inspect, "-f '{{index .Args 1 }}'", :traefik)
.returns("[--providers.docker --log.level=DEBUG --accesslog --accesslog.format=json]").at_least_once
assert_raises(SSHKit::Runner::ExecuteError, "Exception while executing on host 1.1.1.1: File provider not enabled, you'll need to run `kamal traefik reboot` to deploy") do
run_command("boot")
end
ensure
Thread.report_on_exception = true
end
test "boot errors leave lock in place" do
invoke_options = { "config_file" => "test/fixtures/deploy_simple.yml", "version" => "999" }
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :inspect, "-f '{{index .Args 1 }}'", :traefik)
.returns("[--providers.docker --providers.file.directory=/var/run/traefik-config --providers.file.watch --log.level=DEBUG --accesslog --accesslog.format=json]").at_least_once
Kamal::Cli::App.any_instance.expects(:using_version).raises(RuntimeError)
assert !KAMAL.holding_lock?
@@ -58,6 +63,13 @@ class CliAppTest < CliTestCase
end
test "start" do
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with { |*args| args == [ :docker, :inspect, "-f '{{index .Args 1 }}'", :traefik ] }
.returns("[--providers.docker --providers.file.directory=/var/run/traefik-config --providers.file.watch --log.level=DEBUG --accesslog --accesslog.format=json]").at_least_once
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info)
.with { |*args| args != [ :docker, :inspect, "-f '{{index .Args 1 }}'", :traefik ] }
.returns("").at_least_once
run_command("start").tap do |output|
assert_match "docker start app-web-999", output
end
@@ -124,7 +136,7 @@ class CliAppTest < CliTestCase
test "exec" do
run_command("exec", "ruby -v").tap do |output|
assert_match "docker run --rm dhh/app:latest ruby -v", output
assert_match "docker run --rm --env-file .kamal/env/roles/app-web.env dhh/app:latest ruby -v", output
end
end
@@ -180,10 +192,26 @@ class CliAppTest < CliTestCase
end
def stub_running
SecureRandom.stubs(:hex).with(16).returns("12345678901234567890123456789012")
SecureRandom.stubs(:hex).with(6).returns("123456789012")
SecureRandom.stubs(:hex).with(8).returns("1234567890123456")
SSHKit::Backend::Abstract.any_instance.stubs(:capture_with_info).returns("123") # old version
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :inspect, "-f '{{index .Args 1 }}'", :traefik)
.returns("[--providers.docker --providers.file.directory=/var/run/traefik-config --providers.file.watch --log.level=DEBUG --accesslog --accesslog.format=json]").at_least_once
SSHKit::Backend::Abstract.any_instance.expects(: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.expects(:capture_with_info)
.with(:docker, :inspect, "-f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}'", "app-web-latest")
.returns("172.17.0.3").at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :exec, :traefik, :wget, "-qSO", "/dev/null", "http://localhost:80/up", "2>&1", "|", :grep, "-i", "X-Kamal-Run-ID", "|", :cut, "-d ' ' -f 4")
.returns("12345678901234567890123456789012").at_least_once
end
end

View File

@@ -5,7 +5,7 @@ class CliHealthcheckTest < CliTestCase
# Prevent expected failures from outputting to terminal
Thread.report_on_exception = false
Kamal::Utils::HealthcheckPoller.stubs(:sleep) # No sleeping when retrying
Object.any_instance.stubs(:sleep) # No sleeping when retrying
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with(:docker, :container, :ls, "--all", "--filter", "name=^healthcheck-app-999$", "--quiet", "|", :xargs, :docker, :stop, raise_on_non_zero_exit: false)
@@ -28,13 +28,15 @@ class CliHealthcheckTest < CliTestCase
assert_match "container not ready (unhealthy), retrying in 2s (attempt 2/7)...", output
assert_match "Container is healthy!", output
end
ensure
Thread.report_on_exception = true
end
test "perform failing to become healthy" do
# Prevent expected failures from outputting to terminal
Thread.report_on_exception = false
Kamal::Utils::HealthcheckPoller.stubs(:sleep) # No sleeping when retrying
Object.any_instance.stubs(:sleep) # No sleeping when retrying
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with(:docker, :container, :ls, "--all", "--filter", "name=^healthcheck-app-999$", "--quiet", "|", :xargs, :docker, :stop, raise_on_non_zero_exit: false)
@@ -62,6 +64,8 @@ class CliHealthcheckTest < CliTestCase
run_command("perform")
end
assert_match "container not ready (unhealthy)", exception.message
ensure
Thread.report_on_exception = true
end
private

View File

@@ -66,7 +66,7 @@ class CliMainTest < CliTestCase
.with { |*args| args == [ :mkdir, "-p", ".kamal" ] }
SSHKit::Backend::Abstract.any_instance.stubs(:execute)
.with { |*arg| arg[0..1] == [:mkdir, ".kamal/lock-app"] }
.with { |*args| args[0..1] == [:mkdir, ".kamal/lock-app"] }
.raises(RuntimeError, "mkdir: cannot create directory kamal_lock-app: File exists")
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_debug)
@@ -75,6 +75,8 @@ class CliMainTest < CliTestCase
assert_raises(Kamal::Cli::LockError) do
run_command("deploy")
end
ensure
Thread.report_on_exception = true
end
test "deploy error when locking" do
@@ -90,6 +92,8 @@ class CliMainTest < CliTestCase
assert_raises(SSHKit::Runner::ExecuteError) do
run_command("deploy")
end
ensure
Thread.report_on_exception = true
end
test "deploy errors during outside section leave remove lock" do
@@ -173,9 +177,14 @@ class CliMainTest < CliTestCase
assert_match /docker container ls --all --filter name=\^app-web-nonsense\$ --quiet/, output
assert_match /The app version 'nonsense' is not available as a container/, output
end
ensure
Thread.report_on_exception = true
end
test "rollback good version" do
SecureRandom.stubs(:hex).with(16).returns("12345678901234567890123456789012")
SecureRandom.stubs(:hex).with(6).returns("123456789012")
[ "web", "workers" ].each do |role|
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :container, :ls, "--filter", "name=^app-#{role}-123$", "--quiet", raise_on_non_zero_exit: false)
@@ -191,6 +200,18 @@ class CliMainTest < CliTestCase
.returns("running").at_least_once # health check
end
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :inspect, "-f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}'", "app-web-123")
.returns("172.17.0.3").at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :inspect, "-f '{{index .Args 1 }}'", :traefik)
.returns("[--providers.docker --providers.file.directory=/var/run/traefik-config --providers.file.watch --log.level=DEBUG --accesslog --accesslog.format=json]").at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :exec, :traefik, :wget, "-qSO", "/dev/null", "http://localhost:80/up", "2>&1", "|", :grep, "-i", "X-Kamal-Run-ID", "|", :cut, "-d ' ' -f 4")
.returns("12345678901234567890123456789012").at_least_once
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
hook_variables = { version: 123, service_version: "app@123", hosts: "1.1.1.1,1.1.1.2,1.1.1.3,1.1.1.4", command: "rollback" }
@@ -207,8 +228,13 @@ class CliMainTest < CliTestCase
test "rollback without old version" do
Kamal::Cli::Main.any_instance.stubs(:container_available?).returns(true)
Kamal::Utils::HealthcheckPoller.stubs(:sleep)
Object.stubs(:sleep)
SecureRandom.stubs(:hex).with(16).returns("12345678901234567890123456789012")
SecureRandom.stubs(:hex).with(6).returns("123456789012")
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :inspect, "-f '{{index .Args 1 }}'", :traefik)
.returns("[--providers.docker --providers.file.directory=/var/run/traefik-config --providers.file.watch --log.level=DEBUG --accesslog --accesslog.format=json]").at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :container, :ls, "--filter", "name=^app-web-123$", "--quiet", raise_on_non_zero_exit: false)
.returns("").at_least_once
@@ -218,6 +244,13 @@ class CliMainTest < CliTestCase
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :container, :ls, "--all", "--filter", "name=^app-web-123$", "--quiet", "|", :xargs, :docker, :inspect, "--format", "'{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}'")
.returns("running").at_least_once # health check
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :inspect, "-f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}'", "app-web-123")
.returns("172.17.0.3").at_least_once
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :exec, :traefik, :wget, "-qSO", "/dev/null", "http://localhost:80/up", "2>&1", "|", :grep, "-i", "X-Kamal-Run-ID", "|", :cut, "-d ' ' -f 4")
.returns("12345678901234567890123456789012").at_least_once
run_command("rollback", "123").tap do |output|
assert_match "Start container with version 123", output

View File

@@ -4,7 +4,7 @@ class CliTraefikTest < CliTestCase
test "boot" do
run_command("boot").tap do |output|
assert_match "docker login", output
assert_match "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\" #{Kamal::Commands::Traefik::DEFAULT_IMAGE} --providers.docker --log.level=\"DEBUG\"", output
assert_match "docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/traefik-config:/var/run/traefik-config --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" #{Kamal::Configuration::Traefik::Static::DEFAULT_IMAGE} --providers.docker --providers.file.directory=\"/var/run/traefik-config\" --providers.file.watch --log.level=\"DEBUG\"", output
end
end
@@ -14,11 +14,13 @@ class CliTraefikTest < CliTestCase
run_command("reboot").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 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\" #{Kamal::Commands::Traefik::DEFAULT_IMAGE} --providers.docker --log.level=\"DEBUG\"", output
assert_match "docker run --name traefik --detach --restart unless-stopped --publish 80:80 --volume /var/run/docker.sock:/var/run/docker.sock --volume $(pwd)/.kamal/traefik-config:/var/run/traefik-config --env-file .kamal/env/traefik/traefik.env --log-opt max-size=\"10m\" #{Kamal::Configuration::Traefik::Static::DEFAULT_IMAGE} --providers.docker --providers.file.directory=\"/var/run/traefik-config\" --providers.file.watch --log.level=\"DEBUG\"", output
end
end
test "reboot --rolling" do
Object.any_instance.stubs(:sleep)
run_command("reboot", "--rolling").tap do |output|
assert_match "Running docker container prune --force --filter label=org.opencontainers.image.title=Traefik on 1.1.1.1", output
end