so-l2l: add --delete-namespace flag to stop/down for full teardown

Plumb --delete-namespace through the CLI (stop, down), the
down_operation, the Deployer abstract method, and both k8s /
compose implementations. When set, k8s down() calls the existing
_delete_namespace() + _wait_for_namespace_gone() after label-
based resource deletion, restoring the old behavior for the
teardown case. Compose mode ignores the flag.

Default remains False, so normal stop/restart still keep the
namespace Active (Part B behavior).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pull/743/head
Prathamesh Musale 2026-04-16 04:21:40 +00:00
parent 258045190c
commit cf2269ebdc
5 changed files with 38 additions and 8 deletions

View File

@ -55,7 +55,8 @@ class DockerDeployer(Deployer):
except DockerException as e: except DockerException as e:
raise DeployerException(e) raise DeployerException(e)
def down(self, timeout, volumes, skip_cluster_management): def down(self, timeout, volumes, skip_cluster_management, delete_namespace=False):
# delete_namespace is k8s-only; ignored in compose mode.
if not opts.o.dry_run: if not opts.o.dry_run:
try: try:
return self.docker.compose.down(timeout=timeout, volumes=volumes) return self.docker.compose.down(timeout=timeout, volumes=volumes)

View File

@ -172,7 +172,13 @@ def up_operation(
) )
def down_operation(ctx, delete_volumes, extra_args_list, skip_cluster_management=False): def down_operation(
ctx,
delete_volumes,
extra_args_list,
skip_cluster_management=False,
delete_namespace=False,
):
timeout_arg = None timeout_arg = None
if extra_args_list: if extra_args_list:
timeout_arg = extra_args_list[0] timeout_arg = extra_args_list[0]
@ -182,6 +188,7 @@ def down_operation(ctx, delete_volumes, extra_args_list, skip_cluster_management
timeout=timeout_arg, timeout=timeout_arg,
volumes=delete_volumes, volumes=delete_volumes,
skip_cluster_management=skip_cluster_management, skip_cluster_management=skip_cluster_management,
delete_namespace=delete_namespace,
) )

View File

@ -24,7 +24,7 @@ class Deployer(ABC):
pass pass
@abstractmethod @abstractmethod
def down(self, timeout, volumes, skip_cluster_management): def down(self, timeout, volumes, skip_cluster_management, delete_namespace=False):
pass pass
@abstractmethod @abstractmethod

View File

@ -157,13 +157,21 @@ def prepare(ctx, skip_cluster_management):
default=True, default=True,
help="Skip cluster initialization/tear-down (only for kind-k8s deployments)", help="Skip cluster initialization/tear-down (only for kind-k8s deployments)",
) )
@click.option(
"--delete-namespace",
is_flag=True,
default=False,
help="Also delete the k8s namespace (full teardown)",
)
@click.argument("extra_args", nargs=-1) # help: command: down <service1> <service2> @click.argument("extra_args", nargs=-1) # help: command: down <service1> <service2>
@click.pass_context @click.pass_context
def down(ctx, delete_volumes, skip_cluster_management, extra_args): def down(ctx, delete_volumes, skip_cluster_management, delete_namespace, extra_args):
# Get the stack config file name # Get the stack config file name
# TODO: add cluster name and env file here # TODO: add cluster name and env file here
ctx.obj = make_deploy_context(ctx) ctx.obj = make_deploy_context(ctx)
down_operation(ctx, delete_volumes, extra_args, skip_cluster_management) down_operation(
ctx, delete_volumes, extra_args, skip_cluster_management, delete_namespace
)
# stop is the preferred alias for down # stop is the preferred alias for down
@ -176,12 +184,20 @@ def down(ctx, delete_volumes, skip_cluster_management, extra_args):
default=True, default=True,
help="Skip cluster initialization/tear-down (only for kind-k8s deployments)", help="Skip cluster initialization/tear-down (only for kind-k8s deployments)",
) )
@click.option(
"--delete-namespace",
is_flag=True,
default=False,
help="Also delete the k8s namespace (full teardown)",
)
@click.argument("extra_args", nargs=-1) # help: command: down <service1> <service2> @click.argument("extra_args", nargs=-1) # help: command: down <service1> <service2>
@click.pass_context @click.pass_context
def stop(ctx, delete_volumes, skip_cluster_management, extra_args): def stop(ctx, delete_volumes, skip_cluster_management, delete_namespace, extra_args):
# TODO: add cluster name and env file here # TODO: add cluster name and env file here
ctx.obj = make_deploy_context(ctx) ctx.obj = make_deploy_context(ctx)
down_operation(ctx, delete_volumes, extra_args, skip_cluster_management) down_operation(
ctx, delete_volumes, extra_args, skip_cluster_management, delete_namespace
)
@command.command() @command.command()

View File

@ -912,7 +912,7 @@ class K8sDeployer(Deployer):
call_stack_deploy_start(self.deployment_context) call_stack_deploy_start(self.deployment_context)
def down(self, timeout, volumes, skip_cluster_management): def down(self, timeout, volumes, skip_cluster_management, delete_namespace=False):
self.skip_cluster_management = skip_cluster_management self.skip_cluster_management = skip_cluster_management
self.connect_api() self.connect_api()
@ -940,6 +940,12 @@ class K8sDeployer(Deployer):
self._delete_labeled_resources(ns, label_selector, delete_volumes=volumes) self._delete_labeled_resources(ns, label_selector, delete_volumes=volumes)
# Full teardown: nuke the namespace and wait for termination so that a
# subsequent up() can recreate it cleanly.
if delete_namespace:
self._delete_namespace()
self._wait_for_namespace_gone()
if self.is_kind() and not self.skip_cluster_management: if self.is_kind() and not self.skip_cluster_management:
destroy_cluster(self.kind_cluster_name) destroy_cluster(self.kind_cluster_name)