From ef827d8120655e9dee0c96834a86c520a956f651 Mon Sep 17 00:00:00 2001 From: David Boreham Date: Wed, 8 Apr 2026 17:36:03 -0600 Subject: [PATCH] Use namespace for delpoyment to k8s --- src/stack/deploy/k8s/cluster_info.py | 11 ++-- src/stack/deploy/k8s/deploy_k8s.py | 84 +++++++++++++++++----------- src/stack/deploy/k8s/helpers.py | 16 +++--- 3 files changed, 64 insertions(+), 47 deletions(-) diff --git a/src/stack/deploy/k8s/cluster_info.py b/src/stack/deploy/k8s/cluster_info.py index 2776d21..372e1e4 100644 --- a/src/stack/deploy/k8s/cluster_info.py +++ b/src/stack/deploy/k8s/cluster_info.py @@ -92,6 +92,7 @@ def int(self, pod_files: List[str], compose_env_file, deployment_name, spec: Spe self.image_set = images_for_deployment(pod_files) self.environment_variables = DeployEnvVars({}) self.app_name = deployment_name + self.k8s_namespace = deployment_name self.spec = spec # Set the dynamic service ENV @@ -125,7 +126,7 @@ def get_ingress(self, use_tls=False, certificate=None, def_cluster_issuer="letse [ client.V1IngressTLS( hosts=certificate["spec"]["dnsNames"] if certificate else [host_name], - secret_name=certificate["spec"]["secretName"] if certificate else f"{self.app_name}-tls", + secret_name=certificate["spec"]["secretName"] if certificate else "tls", ) ] if use_tls @@ -170,7 +171,7 @@ def get_ingress(self, use_tls=False, certificate=None, def_cluster_issuer="letse ) ingress = client.V1Ingress( - metadata=client.V1ObjectMeta(name=f"{self.app_name}-ingress", annotations=ingress_annotations), + metadata=client.V1ObjectMeta(name="ingress", annotations=ingress_annotations), spec=spec, ) return ingress @@ -235,7 +236,7 @@ def get_pvcs(self): volume_name=k8s_volume_name, ) pvc = client.V1PersistentVolumeClaim( - metadata=client.V1ObjectMeta(name=f"{self.app_name}-{volume_name}", labels=labels), + metadata=client.V1ObjectMeta(name=volume_name, labels=labels), spec=spec, ) result.append(pvc) @@ -262,7 +263,7 @@ def get_configmaps(self): spec = client.V1ConfigMap( metadata=client.V1ObjectMeta( - name=f"{self.app_name}-{cfg_map_name}", + name=cfg_map_name, labels={"configmap-label": cfg_map_name}, ), binary_data=data, @@ -473,7 +474,7 @@ def get_deployments(self, image_pull_policy: str = None): deployment = client.V1Deployment( api_version="apps/v1", kind="Deployment", - metadata=client.V1ObjectMeta(name=f"{self.app_name}-deploy-{service_name}"), + metadata=client.V1ObjectMeta(name=f"deploy-{service_name}"), spec=spec, ) diff --git a/src/stack/deploy/k8s/deploy_k8s.py b/src/stack/deploy/k8s/deploy_k8s.py index 5e576ad..7254000 100644 --- a/src/stack/deploy/k8s/deploy_k8s.py +++ b/src/stack/deploy/k8s/deploy_k8s.py @@ -36,7 +36,7 @@ containers_in_pod, log_stream_from_string, ) -from stack.deploy.k8s.helpers import generate_kind_config, DEFAULT_K8S_NAMESPACE +from stack.deploy.k8s.helpers import generate_kind_config from stack.deploy.k8s.cluster_info import ClusterInfo from stack.opts import opts from stack.deploy.deployment_context import DeploymentContext @@ -63,7 +63,7 @@ class K8sDeployer(Deployer): core_api: client.CoreV1Api apps_api: client.AppsV1Api networking_api: client.NetworkingV1Api - k8s_namespace: str = DEFAULT_K8S_NAMESPACE + k8s_namespace: str kind_cluster_name: str skip_cluster_management: bool cluster_info: ClusterInfo @@ -86,6 +86,7 @@ def __init__( self.deployment_dir = deployment_context.deployment_dir self.deployment_context = deployment_context self.kind_cluster_name = compose_project_name + self.k8s_namespace = compose_project_name self.cluster_info = ClusterInfo() self.cluster_info.int( compose_files, @@ -237,6 +238,13 @@ def up(self, detach, skip_cluster_management, services): else: log_info("Dry run mode enabled, skipping k8s API connect") + if not opts.o.dry_run: + namespace = client.V1Namespace( + metadata=client.V1ObjectMeta(name=self.k8s_namespace) + ) + self.core_api.create_namespace(body=namespace) + log_debug(f"Namespace {self.k8s_namespace} created") + self._create_volume_data() self._create_deployments() @@ -331,6 +339,12 @@ def down(self, timeout, volumes, skip_cluster_management): # noqa: C901 else: log_debug("No ingress to delete") + try: + self.core_api.delete_namespace(name=self.k8s_namespace) + log_debug(f"Namespace {self.k8s_namespace} deleted") + except client.exceptions.ApiException as e: + _check_delete_exception(e) + if self.is_kind() and not self.skip_cluster_management: # Destroy the kind cluster destroy_cluster(self.kind_cluster_name) @@ -341,13 +355,12 @@ def down(self, timeout, volumes, skip_cluster_management): # noqa: C901 def status(self): self.connect_api() # Call whatever API we need to get the running container list - all_pods = self.core_api.list_pod_for_all_namespaces(watch=False) - pods = [] - - if all_pods.items: - for p in all_pods.items: - if f"{self.cluster_info.app_name}-deploy" in p.metadata.name: - pods.append(p) + pod_response = self.core_api.list_namespaced_pod( + namespace=self.k8s_namespace, + label_selector=f"app={self.cluster_info.app_name}", + watch=False, + ) + pods = pod_response.items if pod_response.items else [] if not pods: return @@ -394,29 +407,32 @@ def status(self): def ps(self): self.connect_api() - pods = self.core_api.list_pod_for_all_namespaces(watch=False) + pod_response = self.core_api.list_namespaced_pod( + namespace=self.k8s_namespace, + label_selector=f"app={self.cluster_info.app_name}", + watch=False, + ) ret = [] - for p in pods.items: - if f"{self.cluster_info.app_name}-deploy" in p.metadata.name: - pod_ip = p.status.pod_ip - ports = AttrDict() - for c in p.spec.containers: - if c.ports: - for prt in c.ports: - ports[str(prt.container_port)] = [AttrDict({"HostIp": pod_ip, "HostPort": prt.container_port})] - - ret.append( - AttrDict( - { - "id": f"{p.metadata.namespace}/{p.metadata.name}", - "name": p.metadata.name, - "namespace": p.metadata.namespace, - "network_settings": AttrDict({"ports": ports}), - } - ) + for p in pod_response.items: + pod_ip = p.status.pod_ip + ports = AttrDict() + for c in p.spec.containers: + if c.ports: + for prt in c.ports: + ports[str(prt.container_port)] = [AttrDict({"HostIp": pod_ip, "HostPort": prt.container_port})] + + ret.append( + AttrDict( + { + "id": f"{p.metadata.namespace}/{p.metadata.name}", + "name": p.metadata.name, + "namespace": p.metadata.namespace, + "network_settings": AttrDict({"ports": ports}), + } ) + ) return ret @@ -427,10 +443,10 @@ def port(self, service, private_port): def execute(self, service_name, command, tty, envs): self.connect_api() - pods = pods_in_deployment(self.core_api, self.cluster_info.app_name) + pods = pods_in_deployment(self.core_api, self.cluster_info.app_name, self.k8s_namespace) k8s_pod_name = None for pod in pods: - if f"{self.cluster_info.app_name}-deploy-{service_name}" in pod: + if f"deploy-{service_name}" in pod: k8s_pod_name = pod break @@ -459,7 +475,7 @@ def execute(self, service_name, command, tty, envs): def logs(self, services, tail, follow, stream): self.connect_api() - pods = pods_in_deployment(self.core_api, self.cluster_info.app_name) + pods = pods_in_deployment(self.core_api, self.cluster_info.app_name, self.k8s_namespace) if len(pods) == 0: log_data = "******* Pods not running ********\n" @@ -467,7 +483,7 @@ def logs(self, services, tail, follow, stream): matched_pods = [] for svc in services: for pod in pods: - if f"{self.cluster_info.app_name}-deploy-{svc}" in pod: + if f"deploy-{svc}" in pod: matched_pods.append(pod) pods = matched_pods @@ -486,7 +502,7 @@ def log_follower(pod_name, container): threads = [] for k8s_pod_name in pods: - containers = containers_in_pod(self.core_api, k8s_pod_name) + containers = containers_in_pod(self.core_api, k8s_pod_name, self.k8s_namespace) for container in containers: t = Thread(target=log_follower, args=(k8s_pod_name, container), daemon=True) t.start() @@ -498,7 +514,7 @@ def log_follower(pod_name, container): else: all_logs = [] for k8s_pod_name in pods: - containers = containers_in_pod(self.core_api, k8s_pod_name) + containers = containers_in_pod(self.core_api, k8s_pod_name, self.k8s_namespace) # If the pod is not yet started, the logs request below will throw an exception try: log_data = "" diff --git a/src/stack/deploy/k8s/helpers.py b/src/stack/deploy/k8s/helpers.py index afd335c..d39ce10 100644 --- a/src/stack/deploy/k8s/helpers.py +++ b/src/stack/deploy/k8s/helpers.py @@ -88,9 +88,9 @@ def load_images_into_kind(kind_cluster_name: str, image_set: Set[str]): raise DeployerException(f"kind create cluster failed: {result}") -def pods_in_deployment(core_api: client.CoreV1Api, deployment_name: str): +def pods_in_deployment(core_api: client.CoreV1Api, deployment_name: str, namespace: str = DEFAULT_K8S_NAMESPACE): pods = [] - pod_response = core_api.list_namespaced_pod(namespace=DEFAULT_K8S_NAMESPACE, label_selector=f"app={deployment_name}") + pod_response = core_api.list_namespaced_pod(namespace=namespace, label_selector=f"app={deployment_name}") log_debug(f"pod_response: {pod_response}") for pod_info in pod_response.items: pod_name = pod_info.metadata.name @@ -98,9 +98,9 @@ def pods_in_deployment(core_api: client.CoreV1Api, deployment_name: str): return pods -def containers_in_pod(core_api: client.CoreV1Api, pod_name: str): +def containers_in_pod(core_api: client.CoreV1Api, pod_name: str, namespace: str = DEFAULT_K8S_NAMESPACE): containers = [] - pod_response = core_api.read_namespaced_pod(pod_name, namespace=DEFAULT_K8S_NAMESPACE) + pod_response = core_api.read_namespaced_pod(pod_name, namespace=namespace) log_debug(f"pod_response: {pod_response}") pod_containers = pod_response.spec.containers for pod_container in pod_containers: @@ -189,11 +189,11 @@ def volumes_for_service(parsed_pod_files, service, spec, app_name): for mount_string in volumes: volume_name = mount_string.split(":")[0] if volume_name in spec.get_configmaps(): - config_map = client.V1ConfigMapVolumeSource(name=f"{app_name}-{volume_name}") + config_map = client.V1ConfigMapVolumeSource(name=volume_name) volume = client.V1Volume(name=volume_name, config_map=config_map) result.append(volume) else: - claim = client.V1PersistentVolumeClaimVolumeSource(claim_name=f"{app_name}-{volume_name}") + claim = client.V1PersistentVolumeClaimVolumeSource(claim_name=volume_name) volume = client.V1Volume(name=volume_name, persistent_volume_claim=claim) result.append(volume) return result @@ -207,11 +207,11 @@ def volumes_for_pod_files(parsed_pod_files, spec, app_name): volumes = parsed_pod_file["volumes"] for volume_name in volumes.keys(): if volume_name in spec.get_configmaps(): - config_map = client.V1ConfigMapVolumeSource(name=f"{app_name}-{volume_name}") + config_map = client.V1ConfigMapVolumeSource(name=volume_name) volume = client.V1Volume(name=volume_name, config_map=config_map) result.append(volume) else: - claim = client.V1PersistentVolumeClaimVolumeSource(claim_name=f"{app_name}-{volume_name}") + claim = client.V1PersistentVolumeClaimVolumeSource(claim_name=volume_name) volume = client.V1Volume(name=volume_name, persistent_volume_claim=claim) result.append(volume) return result