diff --git a/k8s/deployment/build_context b/k8s/deployment/build_context index c0340487..ba548659 100755 --- a/k8s/deployment/build_context +++ b/k8s/deployment/build_context @@ -118,23 +118,28 @@ 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 + # Providers win over IMAGE_PULL_SECRETS, which is how values.yaml reaches + # this script (the workflow includes it), so an installation can turn the + # secrets off or replace them from scope-configurations like every other + # setting. Without a provider the included value still applies. + ENV_PULL_SECRETS=${IMAGE_PULL_SECRETS:-'{}'} + + PULL_SECRETS_ENABLED=$(get_config_value \ + --provider '.providers["scope-configurations"].security.image_pull_secrets_enabled' \ + --default "$(echo "$ENV_PULL_SECRETS" | jq -r '.ENABLED // false')" + ) + PULL_SECRETS_LIST=$(get_config_value \ + --provider '.providers["scope-configurations"].security.image_pull_secrets | @json' \ + --default "$(echo "$ENV_PULL_SECRETS" | jq -c '.SECRETS // []')" + ) + + # An empty list means nothing to pull with: rendering `imagePullSecrets` with + # no entries, or naming a secret the cluster does not have, only makes the + # kubelet warn on every pod. + IMAGE_PULL_SECRETS=$(jq -n \ + --argjson enabled "$PULL_SECRETS_ENABLED" \ + --argjson secrets "$PULL_SECRETS_LIST" \ + '{ENABLED: ($enabled and ($secrets | length > 0)), SECRETS: $secrets}') fi 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..cd97e0d0 100644 --- a/k8s/deployment/tests/build_context.bats +++ b/k8s/deployment/tests/build_context.bats @@ -273,6 +273,86 @@ resolve_traffic_container_version() { assert_equal "$IMAGE_PULL_SECRETS" '["secret1"]' } +# The resolution below mirrors build_context: the provider wins, then the +# IMAGE_PULL_SECRETS env (which is how values.yaml reaches the script), and an +# empty secret list always disables the block. +resolve_pull_secrets() { + local env_value=${IMAGE_PULL_SECRETS:-'{}'} + local enabled secrets + enabled=$(get_config_value \ + --provider '.providers["scope-configurations"].security.image_pull_secrets_enabled' \ + --default "$(echo "$env_value" | jq -r '.ENABLED // false')" + ) + secrets=$(get_config_value \ + --provider '.providers["scope-configurations"].security.image_pull_secrets | @json' \ + --default "$(echo "$env_value" | jq -c '.SECRETS // []')" + ) + jq -n --argjson enabled "$enabled" --argjson secrets "$secrets" \ + '{ENABLED: ($enabled and ($secrets | length > 0)), SECRETS: $secrets}' +} + +@test "image pull secrets: the values.yaml default leaves the block off" { + # What k8s/values.yaml now ships. + export IMAGE_PULL_SECRETS='{"ENABLED":false,"SECRETS":[]}' + + result=$(resolve_pull_secrets) + + assert_equal "$(echo "$result" | jq -r '.ENABLED')" "false" +} + +@test "image pull secrets: an included value with secrets still applies when no provider sets it" { + export IMAGE_PULL_SECRETS='{"ENABLED":true,"SECRETS":["ecr-secret"]}' + + result=$(resolve_pull_secrets) + + assert_equal "$(echo "$result" | jq -r '.ENABLED')" "true" + assert_contains "$result" "ecr-secret" +} + +@test "image pull secrets: the provider turns them on over an off default" { + export IMAGE_PULL_SECRETS='{"ENABLED":false,"SECRETS":[]}' + export CONTEXT=$(echo "$CONTEXT" | jq '.providers["scope-configurations"] = { + "security": { "image_pull_secrets_enabled": true, "image_pull_secrets": ["registry-creds"] } + }') + + result=$(resolve_pull_secrets) + + assert_equal "$(echo "$result" | jq -r '.ENABLED')" "true" + assert_contains "$result" "registry-creds" +} + +@test "image pull secrets: the provider overrides 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": ["registry-creds"] } + }') + + result=$(resolve_pull_secrets) + + assert_contains "$result" "registry-creds" + assert_equal "$(echo "$result" | jq -r '.SECRETS | index("ecr-secret") // "absent"')" "absent" +} + +@test "image pull secrets: an empty provider list turns the block off" { + export IMAGE_PULL_SECRETS='{"ENABLED":true,"SECRETS":["ecr-secret"]}' + export CONTEXT=$(echo "$CONTEXT" | jq '.providers["scope-configurations"] = { + "security": { "image_pull_secrets": [] } + }') + + result=$(resolve_pull_secrets) + + assert_equal "$(echo "$result" | jq -r '.ENABLED')" "false" + assert_equal "$(echo "$result" | jq -c '.SECRETS')" "[]" +} + +@test "image pull secrets: nothing configured anywhere leaves it off" { + unset IMAGE_PULL_SECRETS + + result=$(resolve_pull_secrets) + + assert_equal "$(echo "$result" | jq -r '.ENABLED')" "false" +} + # ============================================================================= # get_config_value Tests - DEPLOY_STRATEGY # ============================================================================= diff --git a/k8s/values.yaml b/k8s/values.yaml index 4f43e079..6fe7faf4 100644 --- a/k8s/values.yaml +++ b/k8s/values.yaml @@ -49,10 +49,13 @@ configuration: # port for pod-to-pod traffic BEFORE setting this — with VPC CNI custom # networking that is the security group in the ENIConfig CRD, not the node's. # MAIN_TRAFFIC_MANAGER_PORT: 10080 + # Off by default, like azure and azure-aro: the scope names pull secrets but + # never creates them, so a default pointing at one leaves every pod asking + # the kubelet for a secret the cluster does not have. Clusters that need one + # set security.image_pull_secrets in the scope-configurations provider. IMAGE_PULL_SECRETS: - ENABLED: true - SECRETS: - - ecr-secret + ENABLED: false + SECRETS: [] # VAULT_ADDR: "http://localhost:8200" # VAULT_TOKEN: "myroot" IAM: