@@ -2869,12 +2869,30 @@ def get_volume_names(compose, cnt):
2869
2869
2870
2870
@cmd_run (podman_compose , "down" , "tear down entire stack" )
2871
2871
async def compose_down (compose : PodmanCompose , args ):
2872
+ # get_excluded fails as no-deps is not a supported cli arg
2872
2873
excluded = get_excluded (compose , args )
2873
2874
podman_args = []
2874
2875
timeout_global = getattr (args , "timeout" , None )
2875
2876
containers = list (reversed (compose .containers ))
2876
2877
2877
- down_tasks = []
2878
+ # Get list of currently running containers
2879
+ running_cnt_names = (
2880
+ (
2881
+ await compose .podman .output (
2882
+ [],
2883
+ "ps" ,
2884
+ [
2885
+ "--filter" ,
2886
+ f"label=io.podman.compose.project={ compose .project_name } " ,
2887
+ "-a" ,
2888
+ "--format" ,
2889
+ "{{ .Names }}" ,
2890
+ ],
2891
+ )
2892
+ )
2893
+ .decode ("utf-8" )
2894
+ .splitlines ()
2895
+ )
2878
2896
2879
2897
for cnt in containers :
2880
2898
if cnt ["_service" ] in excluded :
@@ -2886,38 +2904,44 @@ async def compose_down(compose: PodmanCompose, args):
2886
2904
timeout = str_to_seconds (timeout_str )
2887
2905
if timeout is not None :
2888
2906
podman_stop_args .extend (["-t" , str (timeout )])
2889
- down_tasks .append (
2890
- asyncio .create_task (
2891
- compose .podman .run ([], "stop" , [* podman_stop_args , cnt ["name" ]]), name = cnt ["name" ]
2892
- )
2893
- )
2894
- await asyncio .gather (* down_tasks )
2907
+ await compose .podman .run ([], "stop" , [* podman_stop_args , cnt ["name" ]])
2908
+
2895
2909
for cnt in containers :
2896
2910
if cnt ["_service" ] in excluded :
2897
2911
continue
2898
2912
await compose .podman .run ([], "rm" , [cnt ["name" ]])
2913
+ if cnt ["name" ] in running_cnt_names :
2914
+ running_cnt_names .remove (cnt ["name" ])
2915
+
2916
+ # The logic is updated based on docker compose documentation:
2917
+ # `--remove-orphans`: Remove containers for services not defined in the Compose file
2918
+ # Ref: https://docs.docker.com/reference/cli/docker/compose/down/#options
2919
+ orphan_cnt_names = []
2920
+ for cnt in running_cnt_names :
2921
+ if not any (f"{ compose .project_name } _{ service } _" in cnt for service in compose .all_services ):
2922
+ orphan_cnt_names .append (cnt )
2923
+ running_cnt_names .remove (cnt )
2924
+
2925
+ # We list the containers and remove them from running container list
2926
+ # However, we stop them only if provided with CLI arg `--remove-orphans`
2899
2927
if args .remove_orphans :
2900
- names = (
2901
- (
2902
- await compose .podman .output (
2903
- [],
2904
- "ps" ,
2905
- [
2906
- "--filter" ,
2907
- f"label=io.podman.compose.project={ compose .project_name } " ,
2908
- "-a" ,
2909
- "--format" ,
2910
- "{{ .Names }}" ,
2911
- ],
2912
- )
2913
- )
2914
- .decode ("utf-8" )
2915
- .splitlines ()
2916
- )
2917
- for name in names :
2928
+ for name in orphan_cnt_names :
2918
2929
await compose .podman .run ([], "stop" , [* podman_args , name ])
2919
- for name in names :
2930
+ for name in orphan_cnt_names :
2920
2931
await compose .podman .run ([], "rm" , [name ])
2932
+
2933
+ for cnt in running_cnt_names :
2934
+ # This logic goes away if the containers list can be updated accordingly at source
2935
+ # Clear containers not formed out of the current compose file definitions
2936
+ # E.g.: By using CLI `up --scale <APP>=<NUM>` option
2937
+ podman_stop_args = [* podman_args ]
2938
+ if timeout_global is not None :
2939
+ podman_stop_args .extend (["-t" , str (timeout_global )])
2940
+ await compose .podman .run ([], "stop" , [* podman_stop_args , cnt ])
2941
+
2942
+ for cnt in running_cnt_names :
2943
+ await compose .podman .run ([], "rm" , [cnt ])
2944
+
2921
2945
if args .volumes :
2922
2946
vol_names_to_keep = set ()
2923
2947
for cnt in containers :
0 commit comments