diff --git a/stack_orchestrator/deploy/k8s/deploy_k8s.py b/stack_orchestrator/deploy/k8s/deploy_k8s.py index 3f073407..2d44fe23 100644 --- a/stack_orchestrator/deploy/k8s/deploy_k8s.py +++ b/stack_orchestrator/deploy/k8s/deploy_k8s.py @@ -29,6 +29,7 @@ from stack_orchestrator.deploy.k8s.helpers import ( from stack_orchestrator.deploy.k8s.helpers import ( install_ingress_for_kind, wait_for_ingress_in_kind, + is_ingress_running, ) from stack_orchestrator.deploy.k8s.helpers import ( pods_in_deployment, @@ -297,17 +298,30 @@ class K8sDeployer(Deployer): if actual_cluster != self.kind_cluster_name: # An existing cluster was found, use it instead self.kind_cluster_name = actual_cluster - # Ensure the referenced containers are copied into kind - load_images_into_kind( - self.kind_cluster_name, self.cluster_info.image_set + # Only load locally-built images into kind + # Registry images (docker.io, ghcr.io, etc.) will be pulled by k8s + local_containers = self.deployment_context.stack.obj.get( + "containers", [] ) + if local_containers: + # Filter image_set to only images matching local containers + local_images = { + img + for img in self.cluster_info.image_set + if any(c in img for c in local_containers) + } + if local_images: + load_images_into_kind(self.kind_cluster_name, local_images) + # Note: if no local containers defined, all images come from registries self.connect_api() if self.is_kind() and not self.skip_cluster_management: # Configure ingress controller (not installed by default in kind) - install_ingress_for_kind(self.cluster_info.spec.get_acme_email()) - # Wait for ingress to start - # (deployment provisioning will fail unless this is done) - wait_for_ingress_in_kind() + # Skip if already running + if not is_ingress_running(): + install_ingress_for_kind(self.cluster_info.spec.get_acme_email()) + # Wait for ingress to start + # (deployment provisioning will fail unless this is done) + wait_for_ingress_in_kind() # Create RuntimeClass if unlimited_memlock is enabled if self.cluster_info.spec.get_unlimited_memlock(): _create_runtime_class( diff --git a/stack_orchestrator/deploy/k8s/helpers.py b/stack_orchestrator/deploy/k8s/helpers.py index 5ee7e062..e88501e9 100644 --- a/stack_orchestrator/deploy/k8s/helpers.py +++ b/stack_orchestrator/deploy/k8s/helpers.py @@ -14,6 +14,7 @@ # along with this program. If not, see . from kubernetes import client, utils, watch +from kubernetes.client.exceptions import ApiException import os from pathlib import Path import subprocess @@ -295,6 +296,28 @@ def destroy_cluster(name: str): _run_command(f"kind delete cluster --name {name}") +def is_ingress_running() -> bool: + """Check if the Caddy ingress controller is already running in the cluster.""" + try: + core_v1 = client.CoreV1Api() + pods = core_v1.list_namespaced_pod( + namespace="caddy-system", + label_selector=( + "app.kubernetes.io/name=caddy-ingress-controller," + "app.kubernetes.io/component=controller" + ), + ) + for pod in pods.items: + if pod.status and pod.status.container_statuses: + if pod.status.container_statuses[0].ready is True: + if opts.o.debug: + print("Caddy ingress controller already running") + return True + return False + except ApiException: + return False + + def wait_for_ingress_in_kind(): core_v1 = client.CoreV1Api() for i in range(20):