diff --git a/stack_orchestrator/deploy/k8s/cluster_info.py b/stack_orchestrator/deploy/k8s/cluster_info.py index 165ca4cd..1656e301 100644 --- a/stack_orchestrator/deploy/k8s/cluster_info.py +++ b/stack_orchestrator/deploy/k8s/cluster_info.py @@ -107,6 +107,8 @@ class ClusterInfo: def init_jobs(self, job_files: List[str]): """Initialize parsed job YAML map from job compose files.""" self.parsed_job_yaml_map = parsed_pod_files_map_from_file_names(job_files) + # Add job images to the image set so they're loaded into kind + self.image_set.update(images_for_deployment(job_files)) if opts.o.debug: print(f"Parsed job yaml map: {self.parsed_job_yaml_map}") diff --git a/stack_orchestrator/deploy/k8s/deploy_k8s.py b/stack_orchestrator/deploy/k8s/deploy_k8s.py index 4693e8e1..3afff76d 100644 --- a/stack_orchestrator/deploy/k8s/deploy_k8s.py +++ b/stack_orchestrator/deploy/k8s/deploy_k8s.py @@ -563,9 +563,10 @@ class K8sDeployer(Deployer): print("No pods defined, skipping Deployment creation") return # Process compose files into Deployments (one per pod file) - # image-pull-policy from spec, default Always (production). - # Testing specs use IfNotPresent so kind-loaded local images are used. - pull_policy = self.cluster_info.spec.get("image-pull-policy", "Always") + # image-pull-policy from spec. Default IfNotPresent for kind (local + # images are loaded via `kind load`), Always for production k8s. + default_policy = "IfNotPresent" if self.is_kind() else "Always" + pull_policy = self.cluster_info.spec.get("image-pull-policy", default_policy) deployments = self.cluster_info.get_deployments(image_pull_policy=pull_policy) for deployment in deployments: # Apply image overrides if provided @@ -664,7 +665,8 @@ class K8sDeployer(Deployer): def _create_jobs(self): # Process job compose files into k8s Jobs - jobs = self.cluster_info.get_jobs(image_pull_policy="Always") + job_pull_policy = "IfNotPresent" if self.is_kind() else "Always" + jobs = self.cluster_info.get_jobs(image_pull_policy=job_pull_policy) for job in jobs: if opts.o.debug: print(f"Sending this job: {job}") @@ -844,7 +846,6 @@ class K8sDeployer(Deployer): create_registry_secret( self.cluster_info.spec, self.cluster_info.app_name, self.k8s_namespace ) - create_registry_secret(self.cluster_info.spec, self.cluster_info.app_name, self.k8s_namespace) self._create_volume_data() self._create_external_services() @@ -1126,7 +1127,8 @@ class K8sDeployer(Deployer): else: # Non-Helm path: create job from ClusterInfo self.connect_api() - jobs = self.cluster_info.get_jobs(image_pull_policy="Always") + job_pull_policy = "IfNotPresent" if self.is_kind() else "Always" + jobs = self.cluster_info.get_jobs(image_pull_policy=job_pull_policy) # Find the matching job by name target_name = f"{self.cluster_info.app_name}-job-{job_name}" matched_job = None diff --git a/tests/k8s-deploy/run-deploy-test.sh b/tests/k8s-deploy/run-deploy-test.sh index 9cd28692..0cd095ef 100755 --- a/tests/k8s-deploy/run-deploy-test.sh +++ b/tests/k8s-deploy/run-deploy-test.sh @@ -111,8 +111,10 @@ echo "dbfc7a4d-44a7-416d-b5f3-29842cc47650" > $test_deployment_dir/configmaps/te deployment_spec_file=${test_deployment_dir}/spec.yml sed -i 's/^secrets: {}$/secrets:\n test-secret:\n - TEST_SECRET_KEY/' ${deployment_spec_file} -# Get the deployment ID for kubectl queries +# Get the deployment ID and namespace for kubectl queries deployment_id=$(cat ${test_deployment_dir}/deployment.yml | cut -d ' ' -f 2) +# Namespace is derived from stack name: laconic-{stack_name} +deployment_ns="laconic-test" echo "deploy create output file test: passed" # Try to start the deployment (--perform-cluster-management needed on first start @@ -179,17 +181,17 @@ fi # --- New feature tests: namespace, labels, jobs, secrets --- # Check that the pod is in the deployment-specific namespace (not default) -ns_pod_count=$(kubectl get pods -n laconic-${deployment_id} -l app=${deployment_id} --no-headers 2>/dev/null | wc -l) +ns_pod_count=$(kubectl get pods -n ${deployment_ns} -l app=${deployment_id} --no-headers 2>/dev/null | wc -l) if [ "$ns_pod_count" -gt 0 ]; then echo "namespace isolation test: passed" else echo "namespace isolation test: FAILED" - echo "Expected pod in namespace laconic-${deployment_id}" + echo "Expected pod in namespace ${deployment_ns}" delete_cluster_exit fi # Check that the stack label is set on the pod -stack_label_count=$(kubectl get pods -n laconic-${deployment_id} -l app.kubernetes.io/stack=test --no-headers 2>/dev/null | wc -l) +stack_label_count=$(kubectl get pods -n ${deployment_ns} -l app.kubernetes.io/stack=test --no-headers 2>/dev/null | wc -l) if [ "$stack_label_count" -gt 0 ]; then echo "stack label test: passed" else @@ -199,7 +201,7 @@ fi # Check that the job completed successfully for i in {1..30}; do - job_status=$(kubectl get job ${deployment_id}-job-test-job -n laconic-${deployment_id} -o jsonpath='{.status.succeeded}' 2>/dev/null || true) + job_status=$(kubectl get job ${deployment_id}-job-test-job -n ${deployment_ns} -o jsonpath='{.status.succeeded}' 2>/dev/null || true) if [ "$job_status" == "1" ]; then break fi @@ -214,7 +216,7 @@ else fi # Check that the secrets spec results in an envFrom secretRef on the pod -secret_ref=$(kubectl get pod -n laconic-${deployment_id} -l app=${deployment_id} \ +secret_ref=$(kubectl get pod -n ${deployment_ns} -l app=${deployment_id} \ -o jsonpath='{.items[0].spec.containers[0].envFrom[?(@.secretRef.name=="test-secret")].secretRef.name}' 2>/dev/null || true) if [ "$secret_ref" == "test-secret" ]; then echo "secrets envFrom test: passed" @@ -235,7 +237,7 @@ $TEST_TARGET_SO deployment --dir $test_deployment_dir stop --delete-volumes --sk # Without this, 'start' fails with 403 Forbidden because the namespace # is still in Terminating state. for i in {1..60}; do - if ! kubectl get namespace laconic-${deployment_id} 2>/dev/null | grep -q .; then + if ! kubectl get namespace ${deployment_ns} 2>/dev/null | grep -q .; then break fi sleep 2