Updates to compose_down command to match docker compose behavior

Signed-off-by: Yashodhan Pise <technoy@gmail.com>
This commit is contained in:
Yashodhan Pise 2025-03-08 00:35:26 +05:30
parent 2edd4f376c
commit 902454d73f

View File

@ -2869,13 +2869,32 @@ def get_volume_names(compose, cnt):
@cmd_run(podman_compose, "down", "tear down entire stack") @cmd_run(podman_compose, "down", "tear down entire stack")
async def compose_down(compose: PodmanCompose, args): async def compose_down(compose: PodmanCompose, args):
# get_excluded fails as no-deps is not a supported cli arg
excluded = get_excluded(compose, args) excluded = get_excluded(compose, args)
podman_args = [] podman_args = []
timeout_global = getattr(args, "timeout", None) timeout_global = getattr(args, "timeout", None)
containers = list(reversed(compose.containers)) containers = list(reversed(compose.containers))
down_tasks = [] # Get list of currently running containers
running_cnt_names = (
(
await compose.podman.output(
[],
"ps",
[
"--filter",
f"label=io.podman.compose.project={compose.project_name}",
"-a",
"--format",
"{{ .Names }}",
],
)
)
.decode("utf-8")
.splitlines()
)
stop_tasks = []
for cnt in containers: for cnt in containers:
if cnt["_service"] in excluded: if cnt["_service"] in excluded:
continue continue
@ -2886,38 +2905,58 @@ async def compose_down(compose: PodmanCompose, args):
timeout = str_to_seconds(timeout_str) timeout = str_to_seconds(timeout_str)
if timeout is not None: if timeout is not None:
podman_stop_args.extend(["-t", str(timeout)]) podman_stop_args.extend(["-t", str(timeout)])
down_tasks.append( stop_tasks.append(compose.podman.run([], "stop", [*podman_stop_args, cnt["name"]]))
asyncio.create_task( await asyncio.gather(*stop_tasks)
compose.podman.run([], "stop", [*podman_stop_args, cnt["name"]]), name=cnt["name"] stop_tasks.clear()
)
) rm_tasks = []
await asyncio.gather(*down_tasks)
for cnt in containers: for cnt in containers:
if cnt["_service"] in excluded: if cnt["_service"] in excluded:
continue continue
await compose.podman.run([], "rm", [cnt["name"]]) rm_tasks.append(compose.podman.run([], "rm", [cnt["name"]]))
if cnt["name"] in running_cnt_names:
running_cnt_names.remove(cnt["name"])
await asyncio.gather(*rm_tasks)
rm_tasks.clear()
# The logic is updated based on docker compose documentation:
# `--remove-orphans`: Remove containers for services not defined in the Compose file
# Ref: https://docs.docker.com/reference/cli/docker/compose/down/#options
orphan_cnt_names = []
for cnt in running_cnt_names:
if not any(f"{compose.project_name}_{service}_" in cnt for service in compose.all_services):
orphan_cnt_names.append(cnt)
running_cnt_names.remove(cnt)
# We list the containers and remove them from running container list
# However, we stop them only if provided with CLI arg `--remove-orphans`
if args.remove_orphans: if args.remove_orphans:
names = ( for name in orphan_cnt_names:
( stop_tasks.append(compose.podman.run([], "stop", [*podman_args, name]))
await compose.podman.output( await asyncio.gather(*stop_tasks)
[], stop_tasks.clear()
"ps",
[ for name in orphan_cnt_names:
"--filter", rm_tasks.append(compose.podman.run([], "rm", [name]))
f"label=io.podman.compose.project={compose.project_name}", await asyncio.gather(*rm_tasks)
"-a", rm_tasks.clear()
"--format",
"{{ .Names }}", for cnt in running_cnt_names:
], # This logic goes away if the containers list can be updated accordingly at source
) # Clear containers not formed out of the current compose file definitions
) # E.g.: By using CLI `up --scale <APP>=<NUM>` option
.decode("utf-8") podman_stop_args = [*podman_args]
.splitlines() if timeout_global is not None:
) podman_stop_args.extend(["-t", str(timeout_global)])
for name in names: stop_tasks.append(compose.podman.run([], "stop", [*podman_stop_args, cnt]))
await compose.podman.run([], "stop", [*podman_args, name]) await asyncio.gather(*stop_tasks)
for name in names: stop_tasks.clear()
await compose.podman.run([], "rm", [name])
for cnt in running_cnt_names:
rm_tasks.append(compose.podman.run([], "rm", [cnt]))
await asyncio.gather(*rm_tasks)
rm_tasks.clear()
if args.volumes: if args.volumes:
vol_names_to_keep = set() vol_names_to_keep = set()
for cnt in containers: for cnt in containers: