Skip existing containers on accessory boot

When booting an accessory, check for the container first and skip boot
if it exists. This allows us to rerun `kamal setup` on hosts with
accessories without raising an error.

Fixes: https://github.com/basecamp/kamal/issues/488
This commit is contained in:
Donal McBreen
2025-04-18 10:49:19 +01:00
parent 399f1526af
commit 03cac7ae3d
4 changed files with 17 additions and 3 deletions

View File

@@ -1,4 +1,5 @@
require "active_support/core_ext/array/conversions" require "active_support/core_ext/array/conversions"
require "concurrent/array"
class Kamal::Cli::Accessory < Kamal::Cli::Base class Kamal::Cli::Accessory < Kamal::Cli::Base
desc "boot [NAME]", "Boot new accessory service on host (use NAME=all to boot all accessories)" desc "boot [NAME]", "Boot new accessory service on host (use NAME=all to boot all accessories)"
@@ -10,6 +11,16 @@ class Kamal::Cli::Accessory < Kamal::Cli::Base
prepare(name) if prepare prepare(name) if prepare
with_accessory(name) do |accessory, hosts| with_accessory(name) do |accessory, hosts|
booted_hosts = Concurrent::Array.new
on(hosts) do |host|
booted_hosts << host.to_s if capture_with_info(*accessory.info(all: true, quiet: true)).strip.presence
end
if booted_hosts.any?
say "Skipping booting `#{name}` on #{booted_hosts.sort.join(", ")}, a container already exists", :yellow
hosts -= booted_hosts
end
directories(name) directories(name)
upload(name) upload(name)

View File

@@ -37,8 +37,8 @@ class Kamal::Commands::Accessory < Kamal::Commands::Base
docker :container, :stop, service_name docker :container, :stop, service_name
end end
def info def info(all: false, quiet: false)
docker :ps, *service_filter docker :ps, *("-a" if all), *("-q" if quiet), *service_filter
end end
def logs(timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil) def logs(timestamps: true, since: nil, lines: nil, grep: nil, grep_options: nil)

View File

@@ -17,6 +17,9 @@ class AccessoryTest < IntegrationTest
logs = kamal :accessory, :logs, :busybox, capture: true logs = kamal :accessory, :logs, :busybox, capture: true
assert_match /Starting busybox.../, logs assert_match /Starting busybox.../, logs
boot = kamal :accessory, :boot, :busybox, capture: true
assert_match /Skipping booting `busybox` on vm1, vm2, a container already exists/, boot
kamal :accessory, :remove, :busybox, "-y" kamal :accessory, :remove, :busybox, "-y"
assert_accessory_not_running :busybox assert_accessory_not_running :busybox
end end

View File

@@ -11,7 +11,7 @@ class IntegrationTest < ActiveSupport::TestCase
end end
teardown do teardown do
if !passed? && ENV["DEBUG"] if !passed? && ENV["DEBUG_CONTAINER_LOGS"]
[ :deployer, :vm1, :vm2, :shared, :load_balancer, :registry ].each do |container| [ :deployer, :vm1, :vm2, :shared, :load_balancer, :registry ].each do |container|
puts puts
puts "Logs for #{container}:" puts "Logs for #{container}:"