Skip to content

feat: Add task to clean up containers with deleted roles #1524

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/kamal/cli/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,33 @@ def stale_containers
end
end

desc "deleted_roles", "Detect app deleted roles"
option :stop, aliases: "-s", type: :boolean, default: false, desc: "Stop the deleted roles containers"
def deleted_roles
stop = options[:stop]
role_regex = /#{Regexp.escape(KAMAL.config.service)}-(.+)-#{Regexp.escape(KAMAL.config.destination.to_s)}/

with_lock_if_stopping do
on(KAMAL.app_hosts) do |host|
valid_roles = KAMAL.roles_on(host).map(&:name)

app = KAMAL.app(host: host)
all_containers = capture_with_info(*app.all_containers, raise_on_non_zero_exit: false).split("\n")
all_containers.each do |container|
role = container.match(role_regex)[1]
next if valid_roles.include?(role)

if stop
puts_by_host host, "Stopping stale container for deleted role #{role}"
execute :docker, :stop, container
else
puts_by_host host, "Detected container for deleted role #{role} (use `kamal app deleted_roles --stop` to stop)"
end
end
end
end
end

desc "images", "Show app images on servers"
def images
on(KAMAL.app_hosts) { |host| puts_by_host host, capture_with_info(*KAMAL.app.list_images) }
Expand Down
1 change: 1 addition & 0 deletions lib/kamal/cli/main.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def deploy(boot_accessories: false)

say "Detect stale containers...", :magenta
invoke "kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true)
invoke "kamal:cli:app:deleted_roles", [], invoke_options.merge(stop: true)

invoke "kamal:cli:app:boot", [], invoke_options

Expand Down
4 changes: 4 additions & 0 deletions lib/kamal/commands/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ def ensure_env_directory
make_directory role.env_directory
end

def all_containers
docker :ps, *image_filter_args, "--format", '"{{.Names}}"'
end

private
def latest_image_id
docker :image, :ls, *argumentize("--filter", "reference=#{config.latest_image}"), "--format", "'{{.ID}}'"
Expand Down
22 changes: 22 additions & 0 deletions test/cli/app_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,28 @@ class CliAppTest < CliTestCase
end
end

test "deleted_roles" do
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :ps, "--filter", "label=service=app", "--format", "\"{{.Names}}\"", raise_on_non_zero_exit: false)
.returns("app-deleted-role--version\napp-deleted-role-2--version\n")


run_command("deleted_roles").tap do |output|
assert_match /Detected container for deleted role deleted-role/, output
end
end

test "stop deleted_roles" do
SSHKit::Backend::Abstract.any_instance.expects(:capture_with_info)
.with(:docker, :ps, "--filter", "label=service=app", "--format", "\"{{.Names}}\"", raise_on_non_zero_exit: false)
.returns("app-deleted-role--version\napp-deleted-role-2--version\n")


run_command("deleted_roles", "--stop").tap do |output|
assert_match /Stopping stale container for deleted role deleted-role/, output
end
end

test "details" do
run_command("details").tap do |output|
assert_match "docker ps --filter label=service=app --filter label=destination= --filter label=role=web", output
Expand Down
5 changes: 5 additions & 0 deletions test/cli/main_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class CliMainTest < CliTestCase
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:build:pull", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:proxy:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:deleted_roles", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:prune:all", [], invoke_options)

Expand All @@ -46,6 +47,7 @@ class CliMainTest < CliTestCase
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:build:deliver", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:proxy:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:deleted_roles", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:prune:all", [], invoke_options)

Expand All @@ -69,6 +71,7 @@ class CliMainTest < CliTestCase
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:build:pull", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:proxy:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:deleted_roles", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:prune:all", [], invoke_options)

Expand Down Expand Up @@ -167,6 +170,7 @@ class CliMainTest < CliTestCase
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:build:deliver", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:proxy:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:deleted_roles", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:prune:all", [], invoke_options)

Expand All @@ -181,6 +185,7 @@ class CliMainTest < CliTestCase
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:build:deliver", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:proxy:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:stale_containers", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:deleted_roles", [], invoke_options.merge(stop: true))
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:app:boot", [], invoke_options)
Kamal::Cli::Main.any_instance.expects(:invoke).with("kamal:cli:prune:all", [], invoke_options)

Expand Down