Simplify messages and remove multiple execute error

This commit is contained in:
Donal McBreen
2024-05-21 10:40:01 +01:00
parent fa7e941648
commit 706b82baa1
6 changed files with 32 additions and 47 deletions

View File

@@ -7,15 +7,6 @@ require "kamal"
begin begin
Kamal::Cli::Main.start(ARGV) Kamal::Cli::Main.start(ARGV)
rescue SSHKit::Runner::MultipleExecuteError => e
e.execute_errors.each do |execute_error|
puts " \e[31mERROR (#{execute_error.cause.class}): #{execute_error.message}\e[0m"
end
if ENV["VERBOSE"]
puts "Backtrace for the first error:"
puts e.execute_errors.first.cause.backtrace
end
exit 1
rescue SSHKit::Runner::ExecuteError => e rescue SSHKit::Runner::ExecuteError => e
puts " \e[31mERROR (#{e.cause.class}): #{e.message}\e[0m" puts " \e[31mERROR (#{e.cause.class}): #{e.message}\e[0m"
puts e.cause.backtrace if ENV["VERBOSE"] puts e.cause.backtrace if ENV["VERBOSE"]

View File

@@ -14,7 +14,17 @@ class Kamal::Cli::App::Boot
def run def run
old_version = old_version_renamed_if_clashing old_version = old_version_renamed_if_clashing
start_new_version wait_at_barrier if queuer?
begin
start_new_version
rescue => e
close_barrier if gatekeeper?
stop_new_version
raise
end
release_barrier if gatekeeper?
if old_version if old_version
stop_old_version(old_version) stop_old_version(old_version)
@@ -34,22 +44,16 @@ class Kamal::Cli::App::Boot
end end
def start_new_version def start_new_version
wait_at_barrier if queuer?
audit "Booted app version #{version}" audit "Booted app version #{version}"
execute *app.tie_cord(role.cord_host_file) if uses_cord? execute *app.tie_cord(role.cord_host_file) if uses_cord?
hostname = "#{host.to_s[0...51].gsub(/\.+$/, '')}-#{SecureRandom.hex(6)}" hostname = "#{host.to_s[0...51].gsub(/\.+$/, '')}-#{SecureRandom.hex(6)}"
execute *app.run(hostname: hostname) execute *app.run(hostname: hostname)
Kamal::Cli::Healthcheck::Poller.wait_for_healthy(pause_after_ready: true) { capture_with_info(*app.status(version: version)) } Kamal::Cli::Healthcheck::Poller.wait_for_healthy(pause_after_ready: true) { capture_with_info(*app.status(version: version)) }
end
release_barrier if gatekeeper? def stop_new_version
rescue => e
close_barrier if gatekeeper?
execute *app.stop(version: version), raise_on_non_zero_exit: false execute *app.stop(version: version), raise_on_non_zero_exit: false
raise
end end
def stop_old_version(version) def stop_old_version(version)
@@ -68,22 +72,22 @@ class Kamal::Cli::App::Boot
def release_barrier def release_barrier
if barrier.open if barrier.open
info "First #{KAMAL.primary_role} container healthy, continuing other roles (#{host})" info "First #{KAMAL.primary_role} container is healthy on #{host}, booting other roles"
end end
end end
def wait_at_barrier def wait_at_barrier
info "Waiting for a healthy #{KAMAL.primary_role} container (#{host})..." info "Waiting for the first healthy #{KAMAL.primary_role} container before booting #{role} on #{host}..."
barrier.wait barrier.wait
info "First #{KAMAL.primary_role} container is healthy, continuing (#{host})" info "First #{KAMAL.primary_role} container is healthy, booting #{role} on #{host}..."
rescue Kamal::Cli::Healthcheck::Error rescue Kamal::Cli::Healthcheck::Error
info "First #{KAMAL.primary_role} container is unhealthy, stopping (#{host})" info "First #{KAMAL.primary_role} container is unhealthy, not booting #{role} on #{host}"
raise raise
end end
def close_barrier def close_barrier
if barrier.close if barrier.close
info "First #{KAMAL.primary_role} container unhealthy, stopping other roles (#{host})" info "First #{KAMAL.primary_role} container is unhealthy on #{host}, not booting other roles"
error capture_with_info(*app.logs(version: version)) error capture_with_info(*app.logs(version: version))
error capture_with_info(*app.container_health_log(version: version)) error capture_with_info(*app.container_health_log(version: version))
end end

View File

@@ -244,7 +244,7 @@ class Kamal::Cli::Main < Kamal::Cli::Base
raise "Container not found" unless container_id.present? raise "Container not found" unless container_id.present?
end end
end end
rescue SSHKit::Runner::ExecuteError => e rescue SSHKit::Runner::ExecuteError, SSHKit::Runner::MultipleExecuteError => e
if e.message =~ /Container not found/ if e.message =~ /Container not found/
say "Error looking for container version #{version}: #{e.message}" say "Error looking for container version #{version}: #{e.message}"
return false return false

View File

@@ -104,14 +104,6 @@ class SSHKit::Backend::Netssh
prepend LimitConcurrentStartsInstance prepend LimitConcurrentStartsInstance
end end
class SSHKit::Runner::MultipleExecuteError < SSHKit::StandardError
attr_reader :execute_errors
def initialize(execute_errors)
@execute_errors = execute_errors
end
end
class SSHKit::Runner::Parallel class SSHKit::Runner::Parallel
# SSHKit joins the threads in sequence and fails on the first error it encounters, which means that we wait threads # SSHKit joins the threads in sequence and fails on the first error it encounters, which means that we wait threads
# before the first failure to complete but not for ones after. # before the first failure to complete but not for ones after.
@@ -140,7 +132,7 @@ class SSHKit::Runner::Parallel
if exceptions.one? if exceptions.one?
raise exceptions.first raise exceptions.first
elsif exceptions.many? elsif exceptions.many?
raise SSHKit::Runner::MultipleExecuteError.new(exceptions) raise exceptions.first, [ "Exceptions on #{exceptions.count} hosts:", exceptions.map(&:message) ].join("\n")
end end
end end
end end

View File

@@ -136,11 +136,11 @@ class CliAppTest < CliTestCase
.returns("running").at_least_once # workers health check .returns("running").at_least_once # workers health check
run_command("boot", config: :with_roles, host: nil).tap do |output| run_command("boot", config: :with_roles, host: nil).tap do |output|
assert_match "Waiting for a healthy web container (1.1.1.3)...", output assert_match "Waiting for the first healthy web container before booting workers on 1.1.1.3...", output
assert_match "Waiting for a healthy web container (1.1.1.4)...", output assert_match "Waiting for the first healthy web container before booting workers on 1.1.1.4...", output
assert_match "First web container is healthy, continuing (1.1.1.3)", output assert_match "First web container is healthy, booting workers on 1.1.1.3", output
assert_match "First web container is healthy, continuing (1.1.1.4)", output assert_match "First web container is healthy, booting workers on 1.1.1.4", output
end end
end end
test "boot with web barrier closed" do test "boot with web barrier closed" do
@@ -156,14 +156,12 @@ class CliAppTest < CliTestCase
stderred do stderred do
run_command("boot", config: :with_roles, host: nil, allow_execute_error: true).tap do |output| run_command("boot", config: :with_roles, host: nil, allow_execute_error: true).tap do |output|
assert_match "Waiting for a healthy web container (1.1.1.3)...", output assert_match "Waiting for the first healthy web container before booting workers on 1.1.1.3...", output
assert_match "Waiting for a healthy web container (1.1.1.4)...", output assert_match "Waiting for the first healthy web container before booting workers on 1.1.1.4...", output
assert_match "First web container is unhealthy, stopping (1.1.1.3)", output assert_match "First web container is unhealthy, not booting workers on 1.1.1.3", output
assert_match "First web container is unhealthy, stopping (1.1.1.4)", output assert_match "First web container is unhealthy, not booting workers on 1.1.1.4", output
assert_match "Running docker container ls --all --filter name=^app-web-latest$ --quiet | xargs docker stop on 1.1.1.1", output assert_match "Running docker container ls --all --filter name=^app-web-latest$ --quiet | xargs docker stop on 1.1.1.1", output
assert_match "Running docker container ls --all --filter name=^app-web-latest$ --quiet | xargs docker stop on 1.1.1.2", output assert_match "Running docker container ls --all --filter name=^app-web-latest$ --quiet | xargs docker stop on 1.1.1.2", output
assert_match "Running docker container ls --all --filter name=^app-workers-latest$ --quiet | xargs docker stop on 1.1.1.3", output
assert_match "Running docker container ls --all --filter name=^app-workers-latest$ --quiet | xargs docker stop on 1.1.1.4", output
end end
end end
ensure ensure

View File

@@ -25,9 +25,9 @@ class BrokenDeployTest < IntegrationTest
private private
def assert_failed_deploy(output) def assert_failed_deploy(output)
assert_match "Waiting for a healthy web container (vm3)...", output assert_match "Waiting for the first healthy web container before booting workers on vm3...", output
assert_match /First #{KAMAL.primary_role} container is unhealthy, stopping \(vm[12]\)/, output assert_match /First web container is unhealthy on vm[12], not booting other roles/, output
assert_match "First #{KAMAL.primary_role} container unhealthy, stopping other roles (vm3)...", output assert_match "First web container is unhealthy, not booting workers on vm3", output
assert_match "nginx: [emerg] unexpected end of file, expecting \";\" or \"}\" in /etc/nginx/conf.d/default.conf:2", output assert_match "nginx: [emerg] unexpected end of file, expecting \";\" or \"}\" in /etc/nginx/conf.d/default.conf:2", output
assert_match 'ERROR {"Status":"unhealthy","FailingStreak":0,"Log":[]}', output assert_match 'ERROR {"Status":"unhealthy","FailingStreak":0,"Log":[]}', output
end end