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