hack/run-kubernetes-oidc_test.bats
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env bats
# Unit coverage for the OIDC lifecycle folded into kubernetes-latest.
# Sourcing run-kubernetes.sh only defines functions; every kubectl interaction
# below is replaced with a shell stub, so this file needs no cluster.
load test_helper
@test "HelmRelease upgrade wait rejects stale Ready and stale observedGeneration" {
. hack/e2e-chainsaw/_lib/run-kubernetes.sh
state_file=$(mktemp)
printf '0\n' > "$state_file"
kubectl() {
count=$(sed -n '1p' "$state_file")
count=$(( count + 1 ))
printf '%s\n' "$count" > "$state_file"
case "$count" in
1) printf '7|7|True|' ;;
2) printf '8|7|True|' ;;
*) printf '8|8|True|' ;;
esac
}
sleep() { :; }
cozy_wait_helmrelease_upgrade tenant-test kubernetes-demo 7 5 >/dev/null
[ "$(sed -n '1p' "$state_file")" -eq 3 ] || {
echo "the wait accepted Ready before both generation gates advanced" >&2
exit 1
}
}
@test "HelmRelease upgrade wait fails immediately on Stalled" {
. hack/e2e-chainsaw/_lib/run-kubernetes.sh
calls=$(mktemp)
kubectl() {
printf '%s\n' "$*" >> "$calls"
case "$*" in
*" get helmrelease "*) printf '8|8|False|True' ;;
*" describe helmrelease "*) return 0 ;;
*) return 1 ;;
esac
}
sleep() { echo "sleep must not run after Stalled=True" >&2; return 1; }
if cozy_wait_helmrelease_upgrade tenant-test kubernetes-demo 7 600 >/dev/null 2>&1; then
echo "a Stalled HelmRelease was accepted" >&2
exit 1
fi
[ "$(wc -l < "$calls")" -eq 2 ] || {
echo "the Stalled path did not stop after status plus diagnostics" >&2
cat "$calls" >&2
exit 1
}
}
@test "OIDC binding query fails when kubectl cannot list the bindings" {
# Piped straight into sort, the query took its status from sort, and a
# failed kubectl read as an empty binding list. The status has to hold
# without errexit too, because callers capture it in a substitution.
. hack/e2e-chainsaw/_lib/run-kubernetes.sh
kubectl() { echo "the server is currently unable to handle the request" >&2; return 1; }
if cozy_oidc_bindings demo >/dev/null 2>&1; then
echo "a failed binding query was reported as success" >&2
return 1
fi
}
@test "System OIDC assertion proves rendered objects bootstrap kubeconfig and tenant RBAC" {
. hack/e2e-chainsaw/_lib/run-kubernetes.sh
kubectl() {
case "$*" in
*" wait job kubernetes-demo-oidc-bootstrap "*) return 0 ;;
*" get kamajicontrolplane kubernetes-demo "*)
printf '[--authentication-config=/etc/kubernetes/authentication-config/config.yaml --requestheader-uid-headers=X-Remote-Uid]'
;;
*" get secret kubernetes-demo-oidc-authn-config "*)
printf 'url: https://keycloak.example.test/realms/cozy\naudiences:\n- tenant-test-kubernetes-demo\nuid:\n claim: sub\n' | base64 | tr -d '\n'
;;
*" get keycloakclient.v1.edp.epam.com tenant-test-kubernetes-demo "*) printf 'true' ;;
*" get keycloakclientscope.v1.edp.epam.com tenant-test-kubernetes-demo-audience "*) printf 'oidc-audience-mapper' ;;
*" get secret kubernetes-demo-oidc-kubeconfig "*)
printf 'args:\n- oidc-login\n- --oidc-client-id=tenant-test-kubernetes-demo\n' | base64 | tr -d '\n'
;;
*) echo "unexpected kubectl call: $*" >&2; return 1 ;;
esac
}
cozy_oidc_bindings() {
printf 'e2e-admin@example.test\tcluster-admin\ne2e-viewer@example.test\tview\n'
}
cozy_assert_oidc_system demo
}
@test "System OIDC assertion fails when the aggregation-layer UID header is missing" {
# Same mock as the passing case, minus --requestheader-uid-headers, so that
# flag is the only thing left to fail on. Drop the grep for it from the helper
# and this case goes red, which is what stops it passing for the wrong reason.
. hack/e2e-chainsaw/_lib/run-kubernetes.sh
kubectl() {
case "$*" in
*" wait job kubernetes-demo-oidc-bootstrap "*) return 0 ;;
*" get kamajicontrolplane kubernetes-demo "*)
printf '[--authentication-config=/etc/kubernetes/authentication-config/config.yaml]'
;;
*" get secret kubernetes-demo-oidc-authn-config "*)
printf 'url: https://keycloak.example.test/realms/cozy\naudiences:\n- tenant-test-kubernetes-demo\nuid:\n claim: sub\n' | base64 | tr -d '\n'
;;
*" get keycloakclient.v1.edp.epam.com tenant-test-kubernetes-demo "*) printf 'true' ;;
*" get keycloakclientscope.v1.edp.epam.com tenant-test-kubernetes-demo-audience "*) printf 'oidc-audience-mapper' ;;
*" get secret kubernetes-demo-oidc-kubeconfig "*)
printf 'args:\n- oidc-login\n- --oidc-client-id=tenant-test-kubernetes-demo\n' | base64 | tr -d '\n'
;;
*) echo "unexpected kubectl call: $*" >&2; return 1 ;;
esac
}
cozy_oidc_bindings() {
printf 'e2e-admin@example.test\tcluster-admin\ne2e-viewer@example.test\tview\n'
}
# The subshell restores `set -e`, which the chainsaw step runs the helper
# under: without it a failing `grep -q` mid-function does not end the
# function, and the exit status comes from the last assertion instead.
#
# It has to stand as its own command, not as an `if` condition. Shells
# suppress errexit for a command whose status is being tested, and that
# suppression reaches inside the subshell too, which is why the plain
# `if ( set -e; ... )` spelling reports success here.
local rc=0
set +e
( set -e; cozy_assert_oidc_system demo )
rc=$?
set -e
if [ "${rc}" -eq 0 ]; then
echo "the System assertion passed without --requestheader-uid-headers" >&2
return 1
fi
}
@test "System OIDC assertion fails when the uid claim mapping is missing" {
# Same mock as the passing case, minus the uid mapping, so that mapping is
# the only thing left to fail on. Without it a System-mode user is resolved
# to an empty UID and the published header carries nothing across the
# aggregation layer.
. hack/e2e-chainsaw/_lib/run-kubernetes.sh
kubectl() {
case "$*" in
*" wait job kubernetes-demo-oidc-bootstrap "*) return 0 ;;
*" get kamajicontrolplane kubernetes-demo "*)
printf '[--authentication-config=/etc/kubernetes/authentication-config/config.yaml --requestheader-uid-headers=X-Remote-Uid]'
;;
*" get secret kubernetes-demo-oidc-authn-config "*)
printf 'url: https://keycloak.example.test/realms/cozy\naudiences:\n- tenant-test-kubernetes-demo\n' | base64 | tr -d '\n'
;;
*" get keycloakclient.v1.edp.epam.com tenant-test-kubernetes-demo "*) printf 'true' ;;
*" get keycloakclientscope.v1.edp.epam.com tenant-test-kubernetes-demo-audience "*) printf 'oidc-audience-mapper' ;;
*" get secret kubernetes-demo-oidc-kubeconfig "*)
printf 'args:\n- oidc-login\n- --oidc-client-id=tenant-test-kubernetes-demo\n' | base64 | tr -d '\n'
;;
*) echo "unexpected kubectl call: $*" >&2; return 1 ;;
esac
}
cozy_oidc_bindings() {
printf 'e2e-admin@example.test\tcluster-admin\ne2e-viewer@example.test\tview\n'
}
# Standalone subshell, not an `if` condition: errexit is suppressed for a
# command whose status is tested, and that reaches inside the subshell.
local rc=0
set +e
( set -e; cozy_assert_oidc_system demo )
rc=$?
set -e
if [ "${rc}" -eq 0 ]; then
echo "the System assertion passed without a uid claim mapping" >&2
return 1
fi
}
@test "CustomConfig transition fails when it cannot ask whether System leftovers are gone" {
# The teardown probes used `if kubectl get ... >/dev/null 2>&1`, which reads
# every non-zero exit as "the object is gone" -- including an RBAC denial, a
# timeout or a missing CRD -- so the suite reported a clean System teardown
# for never having observed one. --ignore-not-found separates the two.
#
# One probe is broken per iteration and the other two answer "absent", so
# each of the three is pinned individually: reverting any single one of them
# to the bare form lets that iteration pass and reddens this test. A test
# that merely broke all three would pass on any one surviving probe.
. hack/e2e-chainsaw/_lib/run-kubernetes.sh
cozy_wait_helmrelease_upgrade() { :; }
cozy_oidc_bindings() { printf 'byo-admin@example.test\tcluster-admin\n'; }
for unaskable in \
'keycloakclient.v1.edp.epam.com tenant-test-kubernetes-demo' \
'keycloakclientscope.v1.edp.epam.com tenant-test-kubernetes-demo-audience' \
'secret kubernetes-demo-oidc-kubeconfig'; do
COZY_TEST_UNASKABLE="$unaskable"
kubectl() {
case "$*" in
*" get helmrelease kubernetes-demo "*) printf '7' ;;
*" patch kuberneteses.apps.cozystack.io demo "*) : ;;
*" wait job kubernetes-demo-oidc-bootstrap "*) return 0 ;;
*" get kamajicontrolplane kubernetes-demo "*)
printf '[--authentication-config=/etc/kubernetes/authentication-config/config.yaml --requestheader-uid-headers=X-Remote-Uid]'
;;
*" get secret kubernetes-demo-oidc-authn-config "*)
printf 'url: https://idp.byo.example.test\naudiences:\n- cozystack-byo-demo\n' | base64 | tr -d '\n'
;;
*"${COZY_TEST_UNASKABLE}"*--ignore-not-found*)
# Cannot ask: an RBAC denial or an unreachable apiserver.
return 1
;;
# Absent, the way kubectl reports it with the flag: exit 0, no output.
*--ignore-not-found*) : ;;
*) echo "unexpected kubectl call: $*" >&2; return 1 ;;
esac
}
if cozy_switch_and_assert_oidc_custom_config demo; then
echo "the transition passed while the probe for ${unaskable} could not answer" >&2
return 1
fi
done
}
@test "CustomConfig transition fails when the aggregation-layer UID header is missing" {
# The System helper's twin. Same mock as the passing transition below, minus
# --requestheader-uid-headers, so that flag is the only thing left to fail on.
. hack/e2e-chainsaw/_lib/run-kubernetes.sh
kubectl() {
case "$*" in
*" get helmrelease kubernetes-demo "*) printf '7' ;;
*" patch kuberneteses.apps.cozystack.io demo "*) : ;;
*" wait job kubernetes-demo-oidc-bootstrap "*) return 0 ;;
*" get kamajicontrolplane kubernetes-demo "*)
printf '[--authentication-config=/etc/kubernetes/authentication-config/config.yaml]'
;;
*" get secret kubernetes-demo-oidc-authn-config "*)
printf 'url: https://idp.byo.example.test\naudiences:\n- cozystack-byo-demo\n' | base64 | tr -d '\n'
;;
*--ignore-not-found*) : ;;
*) echo "unexpected kubectl call: $*" >&2; return 1 ;;
esac
}
cozy_wait_helmrelease_upgrade() { :; }
cozy_oidc_bindings() { printf 'byo-admin@example.test\tcluster-admin\n'; }
# Standalone subshell, not an `if` condition: errexit is suppressed for a
# command whose status is tested, and that reaches inside the subshell.
local rc=0
set +e
( set -e; cozy_switch_and_assert_oidc_custom_config demo )
rc=$?
set -e
if [ "${rc}" -eq 0 ]; then
echo "the transition passed without --requestheader-uid-headers" >&2
return 1
fi
}
@test "CustomConfig transition patches the live cluster waits for the new generation and rejects System leftovers" {
. hack/e2e-chainsaw/_lib/run-kubernetes.sh
calls=$(mktemp)
wait_args=$(mktemp)
patch_body=$(mktemp)
kubectl() {
printf '%s\n' "$*" >> "$calls"
case "$*" in
*" get helmrelease kubernetes-demo "*) printf '7' ;;
*" patch kuberneteses.apps.cozystack.io demo "*)
previous=
for argument in "$@"; do
if [ "$previous" = --patch ]; then
printf '%s\n' "$argument" > "$patch_body"
break
fi
previous="$argument"
done
;;
*" wait job kubernetes-demo-oidc-bootstrap "*) return 0 ;;
*" get kamajicontrolplane kubernetes-demo "*)
printf '[--authentication-config=/etc/kubernetes/authentication-config/config.yaml --requestheader-uid-headers=X-Remote-Uid]'
;;
*" get secret kubernetes-demo-oidc-authn-config "*)
printf 'url: https://idp.byo.example.test\naudiences:\n- cozystack-byo-demo\n' | base64 | tr -d '\n'
;;
# Absence the way kubectl reports it under --ignore-not-found: exit 0
# and no output. The fail-closed half is pinned by the next test,
# not by this one -- here a bare probe would read the catch-all's
# non-zero exit as absence and pass.
*" get keycloakclient.v1.edp.epam.com tenant-test-kubernetes-demo --ignore-not-found"*) : ;;
*" get keycloakclientscope.v1.edp.epam.com tenant-test-kubernetes-demo-audience --ignore-not-found"*) : ;;
*" get secret kubernetes-demo-oidc-kubeconfig --ignore-not-found"*) : ;;
*) echo "unexpected kubectl call: $*" >&2; return 1 ;;
esac
}
cozy_wait_helmrelease_upgrade() { printf '%s\n' "$*" > "$wait_args"; }
cozy_oidc_bindings() { printf 'byo-admin@example.test\tcluster-admin\n'; }
cozy_switch_and_assert_oidc_custom_config demo
[ "$(sed -n '1p' "$wait_args")" = 'tenant-test kubernetes-demo 7 600' ] || {
echo "the transition did not wait from the pre-patch generation" >&2
cat "$wait_args" >&2
exit 1
}
jq -e '.spec.oidc.mode == "CustomConfig"' "$patch_body" >/dev/null
jq -e '.spec.oidc.users == [{"email":"byo-admin@example.test","role":"admin"}]' "$patch_body" >/dev/null
jq -er '.spec.oidc.customConfig.config' "$patch_body" | grep -q 'https://idp.byo.example.test'
jq -er '.spec.oidc.customConfig.config' "$patch_body" | grep -q 'cozystack-byo-demo'
}