diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e1dee2c..20cacb23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - Fix: k8s scopes that have both a custom domain and additional ports now deploy, instead of failing with "Failed to build ingress template" +- Fix: k8s scopes now honor the scope-configurations provider's `security.image_pull_secrets_enabled` / `security.image_pull_secrets` over the `values.yaml` default, so non-EKS clusters (e.g. AKS pulling from ACR) no longer reference a non-existent `ecr-secret` and hit `ImagePullBackOff` ## [1.16.3] - 2026-09-08 - Add: remove resource level restriction for `elasticloadbalancing:Describe*` permissions as AWS does not support it. diff --git a/k8s/deployment/build_context b/k8s/deployment/build_context index c0340487..67d3f91a 100755 --- a/k8s/deployment/build_context +++ b/k8s/deployment/build_context @@ -85,6 +85,43 @@ validate_status() { esac } +# Resolves the image pull secrets for the pod spec into IMAGE_PULL_SECRETS. +# Priority (highest to lowest): +# 1. PULL_SECRETS env var (full JSON object, used as-is) +# 2. scope-configurations provider (security.image_pull_secrets_enabled / +# security.image_pull_secrets) — only when the provider explicitly sets the flag +# 3. IMAGE_PULL_SECRETS from values.yaml (the shipped default) +# 4. Disabled +# The flag is read with an explicit null-check instead of get_config_value because +# that helper evaluates ` // empty`, which turns an explicit `false` into "not set". +resolve_image_pull_secrets() { + if [[ -n "${PULL_SECRETS:-}" ]]; then + IMAGE_PULL_SECRETS=$PULL_SECRETS + return 0 + fi + + local provider_enabled + provider_enabled=$(echo "$CONTEXT" | jq -r \ + '.providers["scope-configurations"].security.image_pull_secrets_enabled + | if . == null then "null" else tostring end') + + if [ "$provider_enabled" != "null" ]; then + local provider_secrets + provider_secrets=$(get_config_value \ + --provider '.providers["scope-configurations"].security.image_pull_secrets | @json' \ + --default "[]" + ) + IMAGE_PULL_SECRETS=$(jq -n \ + --argjson enabled "$provider_enabled" \ + --argjson secrets "$provider_secrets" \ + '{ENABLED: $enabled, SECRETS: $secrets}') + elif [ -n "${IMAGE_PULL_SECRETS:-}" ]; then + IMAGE_PULL_SECRETS=$(echo "$IMAGE_PULL_SECRETS" | jq .) + else + IMAGE_PULL_SECRETS='{"ENABLED":false,"SECRETS":[]}' + fi +} + if ! validate_status "$SERVICE_ACTION" "$DEPLOYMENT_STATUS"; then log error "❌ Invalid deployment status '$DEPLOYMENT_STATUS' for action '$SERVICE_ACTION'" log error "💡 Possible causes:" @@ -115,27 +152,7 @@ if [ "$DEPLOY_STRATEGY" = "rolling" ] && [ "$DEPLOYMENT_STATUS" = "running" ]; t GREEN_REPLICAS=$(( MIN_REPLICAS > GREEN_REPLICAS ? MIN_REPLICAS : GREEN_REPLICAS )) fi -if [[ -n "$PULL_SECRETS" ]]; then - IMAGE_PULL_SECRETS=$PULL_SECRETS -else - if [ -n "${IMAGE_PULL_SECRETS:-}" ]; then - IMAGE_PULL_SECRETS=$(echo "$IMAGE_PULL_SECRETS" | jq .) - else - PULL_SECRETS_ENABLED=$(get_config_value \ - --provider '.providers["scope-configurations"].security.image_pull_secrets_enabled' \ - --default "false" - ) - PULL_SECRETS_LIST=$(get_config_value \ - --provider '.providers["scope-configurations"].security.image_pull_secrets | @json' \ - --default "[]" - ) - - IMAGE_PULL_SECRETS=$(jq -n \ - --argjson enabled "$PULL_SECRETS_ENABLED" \ - --argjson secrets "$PULL_SECRETS_LIST" \ - '{ENABLED: $enabled, SECRETS: $secrets}') - fi -fi +resolve_image_pull_secrets SCOPE_TRAFFIC_PROTOCOL=$(echo "$CONTEXT" | jq -r .scope.capabilities.protocol) diff --git a/k8s/deployment/tests/build_context.bats b/k8s/deployment/tests/build_context.bats index 72f95cc6..d59a1b85 100644 --- a/k8s/deployment/tests/build_context.bats +++ b/k8s/deployment/tests/build_context.bats @@ -19,12 +19,15 @@ setup() { # Extract validate_status function from build_context for isolated testing eval "$(sed -n '/^validate_status()/,/^}/p' "$PROJECT_ROOT/k8s/deployment/build_context")" + + # Extract resolve_image_pull_secrets from build_context for isolated testing + eval "$(sed -n '/^resolve_image_pull_secrets()/,/^}/p' "$PROJECT_ROOT/k8s/deployment/build_context")" } teardown() { - unset -f validate_status 2>/dev/null || true + unset -f validate_status resolve_image_pull_secrets 2>/dev/null || true unset CONTEXT DEPLOY_STRATEGY POD_DISRUPTION_BUDGET_ENABLED POD_DISRUPTION_BUDGET_MAX_UNAVAILABLE 2>/dev/null || true - unset TRAFFIC_CONTAINER_IMAGE TRAFFIC_MANAGER_CONFIG_MAP IMAGE_PULL_SECRETS IAM CONTAINER_MEMORY_IN_MEMORY CONTAINER_CPU_IN_MILLICORES MAIN_TRAFFIC_MANAGER_PORT 2>/dev/null || true + unset TRAFFIC_CONTAINER_IMAGE TRAFFIC_MANAGER_CONFIG_MAP IMAGE_PULL_SECRETS PULL_SECRETS IAM CONTAINER_MEMORY_IN_MEMORY CONTAINER_CPU_IN_MILLICORES MAIN_TRAFFIC_MANAGER_PORT 2>/dev/null || true } # ============================================================================= @@ -260,17 +263,56 @@ resolve_traffic_container_version() { } # ============================================================================= -# Image Pull Secrets Tests +# Image Pull Secrets Tests (resolve_image_pull_secrets) # ============================================================================= -@test "image pull secrets: PULL_SECRETS takes precedence over IMAGE_PULL_SECRETS" { - PULL_SECRETS='["secret1"]' - IMAGE_PULL_SECRETS="{}" +@test "resolve_image_pull_secrets: PULL_SECRETS env takes precedence over provider and default" { + export PULL_SECRETS='{"ENABLED":true,"SECRETS":["from-env"]}' + export IMAGE_PULL_SECRETS='{"ENABLED":true,"SECRETS":["ecr-secret"]}' + export CONTEXT=$(echo "$CONTEXT" | jq '.providers["scope-configurations"] = { + "security": {"image_pull_secrets_enabled": true, "image_pull_secrets": ["acr-secret"]} + }') - if [[ -n "$PULL_SECRETS" ]]; then - IMAGE_PULL_SECRETS=$PULL_SECRETS - fi + resolve_image_pull_secrets + + assert_json_equal "$IMAGE_PULL_SECRETS" '{"ENABLED":true,"SECRETS":["from-env"]}' +} + +@test "resolve_image_pull_secrets: provider wins over the values.yaml default" { + export IMAGE_PULL_SECRETS='{"ENABLED":true,"SECRETS":["ecr-secret"]}' + export CONTEXT=$(echo "$CONTEXT" | jq '.providers["scope-configurations"] = { + "security": {"image_pull_secrets_enabled": true, "image_pull_secrets": ["acr-secret"]} + }') + + resolve_image_pull_secrets + + assert_json_equal "$IMAGE_PULL_SECRETS" '{"ENABLED":true,"SECRETS":["acr-secret"]}' +} + +@test "resolve_image_pull_secrets: provider can explicitly disable pull secrets" { + export IMAGE_PULL_SECRETS='{"ENABLED":true,"SECRETS":["ecr-secret"]}' + export CONTEXT=$(echo "$CONTEXT" | jq '.providers["scope-configurations"] = { + "security": {"image_pull_secrets_enabled": false} + }') + + resolve_image_pull_secrets + + assert_json_equal "$IMAGE_PULL_SECRETS" '{"ENABLED":false,"SECRETS":[]}' +} + +@test "resolve_image_pull_secrets: falls back to the values.yaml default when the provider is silent" { + export IMAGE_PULL_SECRETS='{"ENABLED":true,"SECRETS":["ecr-secret"]}' + + resolve_image_pull_secrets + + assert_json_equal "$IMAGE_PULL_SECRETS" '{"ENABLED":true,"SECRETS":["ecr-secret"]}' +} + +@test "resolve_image_pull_secrets: disabled when neither provider nor default is set" { + unset IMAGE_PULL_SECRETS + + resolve_image_pull_secrets - assert_equal "$IMAGE_PULL_SECRETS" '["secret1"]' + assert_json_equal "$IMAGE_PULL_SECRETS" '{"ENABLED":false,"SECRETS":[]}' } # =============================================================================