Add compatibility matrix fixture execution
Run package test jobs across every selected provider combination with isolated lane state, released-image canaries, controlled OIDC DNS, and deterministic cleanup.
This commit is contained in:
parent
957d170150
commit
20ea9557fd
1 changed files with 989 additions and 53 deletions
|
|
@ -180,6 +180,15 @@ render_manifests() {
|
|||
local coredns_image="$9"
|
||||
local curl_image="${10}"
|
||||
local aws_cli_image="${11}"
|
||||
local postgres_replicas="${12}"
|
||||
local redis_replicas="${13}"
|
||||
local dragonfly_replicas="${14}"
|
||||
local vault_replicas="${15}"
|
||||
local keycloak_replicas="${16}"
|
||||
local zitadel_replicas="${17}"
|
||||
local garage_replicas="${18}"
|
||||
local versitygw_replicas="${19}"
|
||||
local coredns_replicas="${20}"
|
||||
sed \
|
||||
-e "s|@@POSTGRES_IMAGE@@|$postgres_image|g" \
|
||||
-e "s|@@REDIS_IMAGE@@|$redis_image|g" \
|
||||
|
|
@ -192,6 +201,15 @@ render_manifests() {
|
|||
-e "s|@@COREDNS_IMAGE@@|$coredns_image|g" \
|
||||
-e "s|@@CURL_IMAGE@@|$curl_image|g" \
|
||||
-e "s|@@AWS_CLI_IMAGE@@|$aws_cli_image|g" \
|
||||
-e "s|@@POSTGRES_REPLICAS@@|$postgres_replicas|g" \
|
||||
-e "s|@@REDIS_REPLICAS@@|$redis_replicas|g" \
|
||||
-e "s|@@DRAGONFLY_REPLICAS@@|$dragonfly_replicas|g" \
|
||||
-e "s|@@VAULT_REPLICAS@@|$vault_replicas|g" \
|
||||
-e "s|@@KEYCLOAK_REPLICAS@@|$keycloak_replicas|g" \
|
||||
-e "s|@@ZITADEL_REPLICAS@@|$zitadel_replicas|g" \
|
||||
-e "s|@@GARAGE_REPLICAS@@|$garage_replicas|g" \
|
||||
-e "s|@@VERSITYGW_REPLICAS@@|$versitygw_replicas|g" \
|
||||
-e "s|@@COREDNS_REPLICAS@@|$coredns_replicas|g" \
|
||||
"$TESTBED/dependencies.yaml.in" > "$RUNTIME/dependencies.yaml"
|
||||
sed \
|
||||
-e "s|@@POSTGRES_IMAGE@@|$postgres_image|g" \
|
||||
|
|
@ -215,7 +233,69 @@ render_manifests() {
|
|||
} > "$RUNTIME/fixture.env"
|
||||
}
|
||||
|
||||
select_fixture_families() {
|
||||
local requested=("$@")
|
||||
local family
|
||||
local candidate
|
||||
local found
|
||||
local use_all=false
|
||||
FIXTURE_FAMILIES=()
|
||||
if [[ "${#requested[@]}" = 0 ]]; then
|
||||
requested=(all)
|
||||
fi
|
||||
for family in "${requested[@]}"; do
|
||||
case "$family" in
|
||||
all)
|
||||
use_all=true
|
||||
;;
|
||||
durable | coordination | oidc | target | secret) ;;
|
||||
*)
|
||||
printf 'unknown fixture family: %s\n' "$family" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [[ "$use_all" = true ]]; then
|
||||
requested=(durable coordination oidc target secret)
|
||||
fi
|
||||
for candidate in durable coordination oidc target secret; do
|
||||
found=false
|
||||
for family in "${requested[@]}"; do
|
||||
if [[ "$family" = "$candidate" ]]; then
|
||||
found=true
|
||||
fi
|
||||
done
|
||||
if [[ "$found" = true ]]; then
|
||||
FIXTURE_FAMILIES+=("$candidate")
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
fixture_has_family() {
|
||||
local expected="$1"
|
||||
local family
|
||||
for family in "${FIXTURE_FAMILIES[@]}"; do
|
||||
if [[ "$family" = "$expected" ]]; then
|
||||
return
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
load_selected_image() {
|
||||
local family="$1"
|
||||
local variable="$2"
|
||||
local name="$3"
|
||||
local source="$4"
|
||||
local image
|
||||
if fixture_has_family "$family"; then
|
||||
image="$(load_external_image "$name" "$source")" || return
|
||||
printf -v "$variable" '%s' "$image"
|
||||
fi
|
||||
}
|
||||
|
||||
setup_fixture() {
|
||||
select_fixture_families "$@" || return
|
||||
mkdir -p "$RUNTIME"
|
||||
rm -rf "$RUNTIME/image-archives"
|
||||
mkdir -p "$RUNTIME/image-archives"
|
||||
|
|
@ -229,17 +309,47 @@ setup_fixture() {
|
|||
--kubeconfig "$KUBECONFIG_PATH" \
|
||||
--wait 180s
|
||||
|
||||
POSTGRES_LOCAL_IMAGE="$(load_external_image postgres "$POSTGRES_IMAGE")"
|
||||
REDIS_LOCAL_IMAGE="$(load_external_image redis "$REDIS_IMAGE")"
|
||||
DRAGONFLY_LOCAL_IMAGE="$(load_external_image dragonfly "$DRAGONFLY_IMAGE")"
|
||||
VAULT_LOCAL_IMAGE="$(load_external_image vault "$VAULT_IMAGE")"
|
||||
KEYCLOAK_LOCAL_IMAGE="$(load_external_image keycloak "$KEYCLOAK_IMAGE")"
|
||||
ZITADEL_LOCAL_IMAGE="$(load_external_image zitadel "$ZITADEL_IMAGE")"
|
||||
GARAGE_LOCAL_IMAGE="$(load_external_image garage "$GARAGE_IMAGE")"
|
||||
VERSITYGW_LOCAL_IMAGE="$(load_external_image versitygw "$VERSITYGW_IMAGE")"
|
||||
COREDNS_LOCAL_IMAGE="$(load_external_image coredns "$COREDNS_IMAGE")"
|
||||
CURL_LOCAL_IMAGE="$(load_external_image curl "$CURL_IMAGE")"
|
||||
AWS_CLI_LOCAL_IMAGE="$(load_external_image aws-cli "$AWS_CLI_IMAGE")"
|
||||
POSTGRES_LOCAL_IMAGE=remora-testbed/unused:postgres
|
||||
REDIS_LOCAL_IMAGE=remora-testbed/unused:redis
|
||||
DRAGONFLY_LOCAL_IMAGE=remora-testbed/unused:dragonfly
|
||||
VAULT_LOCAL_IMAGE=remora-testbed/unused:vault
|
||||
KEYCLOAK_LOCAL_IMAGE=remora-testbed/unused:keycloak
|
||||
ZITADEL_LOCAL_IMAGE=remora-testbed/unused:zitadel
|
||||
GARAGE_LOCAL_IMAGE=remora-testbed/unused:garage
|
||||
VERSITYGW_LOCAL_IMAGE=remora-testbed/unused:versitygw
|
||||
COREDNS_LOCAL_IMAGE=remora-testbed/unused:coredns
|
||||
CURL_LOCAL_IMAGE=remora-testbed/unused:curl
|
||||
AWS_CLI_LOCAL_IMAGE=remora-testbed/unused:aws-cli
|
||||
if fixture_has_family durable || fixture_has_family oidc; then
|
||||
POSTGRES_LOCAL_IMAGE="$(load_external_image postgres "$POSTGRES_IMAGE")"
|
||||
fi
|
||||
load_selected_image coordination REDIS_LOCAL_IMAGE redis "$REDIS_IMAGE"
|
||||
load_selected_image coordination DRAGONFLY_LOCAL_IMAGE dragonfly "$DRAGONFLY_IMAGE"
|
||||
load_selected_image secret VAULT_LOCAL_IMAGE vault "$VAULT_IMAGE"
|
||||
load_selected_image oidc KEYCLOAK_LOCAL_IMAGE keycloak "$KEYCLOAK_IMAGE"
|
||||
load_selected_image oidc ZITADEL_LOCAL_IMAGE zitadel "$ZITADEL_IMAGE"
|
||||
load_selected_image target GARAGE_LOCAL_IMAGE garage "$GARAGE_IMAGE"
|
||||
load_selected_image target VERSITYGW_LOCAL_IMAGE versitygw "$VERSITYGW_IMAGE"
|
||||
load_selected_image oidc COREDNS_LOCAL_IMAGE coredns "$COREDNS_IMAGE"
|
||||
if fixture_has_family oidc || fixture_has_family target; then
|
||||
CURL_LOCAL_IMAGE="$(load_external_image curl "$CURL_IMAGE")"
|
||||
fi
|
||||
load_selected_image target AWS_CLI_LOCAL_IMAGE aws-cli "$AWS_CLI_IMAGE"
|
||||
|
||||
POSTGRES_REPLICAS=0
|
||||
REDIS_REPLICAS=0
|
||||
DRAGONFLY_REPLICAS=0
|
||||
VAULT_REPLICAS=0
|
||||
KEYCLOAK_REPLICAS=0
|
||||
ZITADEL_REPLICAS=0
|
||||
GARAGE_REPLICAS=0
|
||||
VERSITYGW_REPLICAS=0
|
||||
COREDNS_REPLICAS=0
|
||||
if fixture_has_family durable || fixture_has_family oidc; then POSTGRES_REPLICAS=1; fi
|
||||
if fixture_has_family coordination; then REDIS_REPLICAS=1; DRAGONFLY_REPLICAS=1; fi
|
||||
if fixture_has_family secret; then VAULT_REPLICAS=1; fi
|
||||
if fixture_has_family oidc; then KEYCLOAK_REPLICAS=1; ZITADEL_REPLICAS=1; COREDNS_REPLICAS=1; fi
|
||||
if fixture_has_family target; then GARAGE_REPLICAS=1; VERSITYGW_REPLICAS=1; fi
|
||||
render_manifests \
|
||||
"$POSTGRES_LOCAL_IMAGE" \
|
||||
"$REDIS_LOCAL_IMAGE" \
|
||||
|
|
@ -251,15 +361,570 @@ setup_fixture() {
|
|||
"$VERSITYGW_LOCAL_IMAGE" \
|
||||
"$COREDNS_LOCAL_IMAGE" \
|
||||
"$CURL_LOCAL_IMAGE" \
|
||||
"$AWS_CLI_LOCAL_IMAGE"
|
||||
"$AWS_CLI_LOCAL_IMAGE" \
|
||||
"$POSTGRES_REPLICAS" \
|
||||
"$REDIS_REPLICAS" \
|
||||
"$DRAGONFLY_REPLICAS" \
|
||||
"$VAULT_REPLICAS" \
|
||||
"$KEYCLOAK_REPLICAS" \
|
||||
"$ZITADEL_REPLICAS" \
|
||||
"$GARAGE_REPLICAS" \
|
||||
"$VERSITYGW_REPLICAS" \
|
||||
"$COREDNS_REPLICAS"
|
||||
|
||||
kube apply --filename "$RUNTIME/dependencies.yaml"
|
||||
for deployment in postgres redis dragonfly vault keycloak zitadel garage versitygw controlled-dns; do
|
||||
kube --namespace "$FIXTURE_NAMESPACE" rollout status "deployment/$deployment" --timeout 240s
|
||||
apply_fixture_component common
|
||||
if fixture_has_family durable || fixture_has_family oidc; then apply_fixture_component postgres; fi
|
||||
if fixture_has_family coordination; then apply_fixture_component redis; apply_fixture_component dragonfly; fi
|
||||
if fixture_has_family secret; then apply_fixture_component vault; fi
|
||||
if fixture_has_family oidc; then apply_fixture_component keycloak; apply_fixture_component zitadel; apply_fixture_component controlled-dns; fi
|
||||
if fixture_has_family target; then apply_fixture_component garage; apply_fixture_component versitygw; fi
|
||||
if fixture_has_family durable || fixture_has_family oidc; then wait_for_deployment postgres; fi
|
||||
if fixture_has_family coordination; then wait_for_deployment redis; wait_for_deployment dragonfly; fi
|
||||
if fixture_has_family secret; then wait_for_deployment vault; fi
|
||||
if fixture_has_family oidc; then wait_for_deployment keycloak; wait_for_deployment zitadel; wait_for_deployment controlled-dns; fi
|
||||
if fixture_has_family target; then wait_for_deployment garage; wait_for_deployment versitygw; fi
|
||||
}
|
||||
|
||||
apply_fixture_component() {
|
||||
kube apply \
|
||||
--filename "$RUNTIME/dependencies.yaml" \
|
||||
--selector "fixture.remora.dev/component=$1"
|
||||
}
|
||||
|
||||
wait_for_deployment() {
|
||||
kube --namespace "$FIXTURE_NAMESPACE" rollout status "deployment/$1" --timeout 240s
|
||||
}
|
||||
|
||||
expand_fixture_matrix() {
|
||||
local coordination_providers=(-)
|
||||
local oidc_providers=(-)
|
||||
local target_providers=(-)
|
||||
local coordination
|
||||
local oidc
|
||||
local target
|
||||
local lane=0
|
||||
select_fixture_families "$@" || return
|
||||
if fixture_has_family coordination; then coordination_providers=(redis dragonfly); fi
|
||||
if fixture_has_family oidc; then oidc_providers=(keycloak zitadel); fi
|
||||
if fixture_has_family target; then target_providers=(garage versitygw); fi
|
||||
for coordination in "${coordination_providers[@]}"; do
|
||||
for oidc in "${oidc_providers[@]}"; do
|
||||
for target in "${target_providers[@]}"; do
|
||||
lane=$((lane + 1))
|
||||
printf '%03d\t%s\t%s\t%s\n' "$lane" "$coordination" "$oidc" "$target"
|
||||
done
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
reset_lane_durable_store() {
|
||||
local lane="$1"
|
||||
local database="remora_lane_$lane"
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec deployment/postgres -- \
|
||||
env PGPASSWORD=synthetic-postgres psql \
|
||||
--username remora \
|
||||
--dbname postgres \
|
||||
--set ON_ERROR_STOP=1 \
|
||||
--command "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$database'" \
|
||||
--command "DROP DATABASE IF EXISTS $database" \
|
||||
--command "CREATE DATABASE $database" >&2 || return
|
||||
printf '%s\n' "$database"
|
||||
}
|
||||
|
||||
reset_lane_target_store() {
|
||||
local provider="$1"
|
||||
local bucket="$2"
|
||||
local deployment
|
||||
local endpoint
|
||||
if [[ "$provider" = garage ]]; then
|
||||
deployment=garage
|
||||
endpoint=http://127.0.0.1:3900
|
||||
else
|
||||
deployment=versitygw
|
||||
endpoint=http://127.0.0.1:10000
|
||||
fi
|
||||
if kube --namespace "$FIXTURE_NAMESPACE" exec "deployment/$deployment" --container s3-readiness -- \
|
||||
aws --endpoint-url "$endpoint" s3api head-bucket --bucket "$bucket" >/dev/null 2>&1; then
|
||||
cleanup_lane_target_store "$provider" "$bucket" || return
|
||||
fi
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec "deployment/$deployment" --container s3-readiness -- \
|
||||
aws --endpoint-url "$endpoint" s3api create-bucket --bucket "$bucket" >&2 || return
|
||||
}
|
||||
|
||||
reset_keycloak_lane() {
|
||||
local lane="$1"
|
||||
local realm="remora-lane-$lane"
|
||||
local username="remora-user-$lane"
|
||||
local client_id="remora-client-$lane"
|
||||
if kube --namespace "$FIXTURE_NAMESPACE" exec deployment/keycloak -- \
|
||||
/opt/keycloak/bin/kcadm.sh get "realms/$realm" \
|
||||
--no-config \
|
||||
--server http://127.0.0.1:8080 \
|
||||
--realm master \
|
||||
--user remora-admin \
|
||||
--password synthetic-keycloak >/dev/null 2>&1; then
|
||||
cleanup_keycloak_lane "$lane" || return
|
||||
fi
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec deployment/keycloak -- \
|
||||
/opt/keycloak/bin/kcadm.sh create realms \
|
||||
--no-config \
|
||||
--server http://127.0.0.1:8080 \
|
||||
--realm master \
|
||||
--user remora-admin \
|
||||
--password synthetic-keycloak \
|
||||
--set "realm=$realm" \
|
||||
--set enabled=true >&2 || return
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec deployment/keycloak -- \
|
||||
/opt/keycloak/bin/kcadm.sh create clients \
|
||||
--no-config \
|
||||
--server http://127.0.0.1:8080 \
|
||||
--realm master \
|
||||
--user remora-admin \
|
||||
--password synthetic-keycloak \
|
||||
-r "$realm" \
|
||||
--set "clientId=$client_id" \
|
||||
--set publicClient=true \
|
||||
--set standardFlowEnabled=true \
|
||||
--set 'redirectUris=["http://localhost/callback"]' >&2 || return
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec deployment/keycloak -- \
|
||||
/opt/keycloak/bin/kcadm.sh create roles \
|
||||
--no-config \
|
||||
--server http://127.0.0.1:8080 \
|
||||
--realm master \
|
||||
--user remora-admin \
|
||||
--password synthetic-keycloak \
|
||||
-r "$realm" \
|
||||
--set name=test-user >&2 || return
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec deployment/keycloak -- \
|
||||
/opt/keycloak/bin/kcadm.sh create users \
|
||||
--no-config \
|
||||
--server http://127.0.0.1:8080 \
|
||||
--realm master \
|
||||
--user remora-admin \
|
||||
--password synthetic-keycloak \
|
||||
-r "$realm" \
|
||||
--set "username=$username" \
|
||||
--set enabled=true \
|
||||
--set emailVerified=true \
|
||||
--set "email=$username@remora.test" >&2 || return
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec deployment/keycloak -- \
|
||||
/opt/keycloak/bin/kcadm.sh set-password \
|
||||
--no-config \
|
||||
--server http://127.0.0.1:8080 \
|
||||
--realm master \
|
||||
--user remora-admin \
|
||||
--password synthetic-keycloak \
|
||||
-r "$realm" \
|
||||
--username "$username" \
|
||||
--new-password Synthetic-OIDC-1! >&2 || return
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec deployment/keycloak -- \
|
||||
/opt/keycloak/bin/kcadm.sh add-roles \
|
||||
--no-config \
|
||||
--server http://127.0.0.1:8080 \
|
||||
--realm master \
|
||||
--user remora-admin \
|
||||
--password synthetic-keycloak \
|
||||
-r "$realm" \
|
||||
--uusername "$username" \
|
||||
--rolename test-user >&2 || return
|
||||
printf '%s\t%s\t%s\t%s\n' \
|
||||
"http://keycloak.remora.test:8080/realms/$realm" \
|
||||
"$realm" \
|
||||
"$client_id" \
|
||||
"$username"
|
||||
}
|
||||
|
||||
zitadel_request() {
|
||||
local endpoint="$1"
|
||||
local body="$2"
|
||||
local organization="${3:-}"
|
||||
local method="${4:-POST}"
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec deployment/zitadel --container provisioner -- \
|
||||
/bin/sh -ec '
|
||||
endpoint="$1"
|
||||
body="$2"
|
||||
organization="$3"
|
||||
method="$4"
|
||||
token="$(cat /bootstrap/admin.pat)"
|
||||
set -- \
|
||||
--fail \
|
||||
--silent \
|
||||
--show-error \
|
||||
--request "$method" \
|
||||
--header "Host: zitadel.remora.test" \
|
||||
--header "Authorization: Bearer $token" \
|
||||
--header "Content-Type: application/json"
|
||||
if test -n "$organization"; then
|
||||
set -- "$@" --header "x-zitadel-orgid: $organization"
|
||||
fi
|
||||
curl "$@" --data "$body" "http://127.0.0.1:8080$endpoint"
|
||||
' remora-zitadel-request "$endpoint" "$body" "$organization" "$method"
|
||||
}
|
||||
|
||||
json_string_field() {
|
||||
local document="$1"
|
||||
local field="$2"
|
||||
local value
|
||||
value="$(sed -nE 's/.*"'"$field"'"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/p' <<< "$document")"
|
||||
if [[ -z "$value" ]]; then
|
||||
printf 'ZITADEL response omitted %s\n' "$field" >&2
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "$value"
|
||||
}
|
||||
|
||||
reset_zitadel_lane() {
|
||||
local lane="$1"
|
||||
local tenant="remora-lane-$lane"
|
||||
local username="remora-user-$lane"
|
||||
local response
|
||||
local organization_id
|
||||
local project_id
|
||||
local user_id
|
||||
local client_id
|
||||
cleanup_zitadel_lane "$lane" || return
|
||||
response="$(zitadel_request /management/v1/orgs "{\"name\":\"$tenant\"}")" || return
|
||||
organization_id="$(json_string_field "$response" id)" || return
|
||||
response="$(zitadel_request /management/v1/projects "{\"name\":\"$tenant\",\"projectRoleAssertion\":true,\"projectRoleCheck\":true}" "$organization_id")" || return
|
||||
project_id="$(json_string_field "$response" id)" || return
|
||||
zitadel_request \
|
||||
"/management/v1/projects/$project_id/roles" \
|
||||
'{"roleKey":"test-user","displayName":"Test User","group":"fixture"}' \
|
||||
"$organization_id" >/dev/null || return
|
||||
response="$(zitadel_request \
|
||||
"/management/v1/projects/$project_id/apps/oidc" \
|
||||
"{\"name\":\"$tenant\",\"redirectUris\":[\"http://localhost/callback\"],\"postLogoutRedirectUris\":[\"http://localhost/logout\"],\"responseTypes\":[\"OIDC_RESPONSE_TYPE_CODE\"],\"grantTypes\":[\"OIDC_GRANT_TYPE_AUTHORIZATION_CODE\",\"OIDC_GRANT_TYPE_REFRESH_TOKEN\"],\"appType\":\"OIDC_APP_TYPE_USER_AGENT\",\"authMethodType\":\"OIDC_AUTH_METHOD_TYPE_NONE\",\"version\":\"OIDC_VERSION_1_0\",\"devMode\":true,\"accessTokenType\":\"OIDC_TOKEN_TYPE_JWT\",\"accessTokenRoleAssertion\":true}" \
|
||||
"$organization_id")" || return
|
||||
client_id="$(json_string_field "$response" clientId)" || return
|
||||
response="$(zitadel_request \
|
||||
/management/v1/users/human \
|
||||
"{\"userName\":\"$username\",\"profile\":{\"firstName\":\"Remora\",\"lastName\":\"Lane $lane\",\"displayName\":\"Remora Lane $lane\",\"preferredLanguage\":\"en\",\"gender\":\"GENDER_UNSPECIFIED\"},\"email\":{\"email\":\"$username@remora.test\",\"isEmailVerified\":true},\"password\":\"Synthetic-OIDC-1!\",\"passwordChangeRequired\":false}" \
|
||||
"$organization_id")" || return
|
||||
user_id="$(json_string_field "$response" userId)" || return
|
||||
zitadel_request \
|
||||
"/management/v1/users/$user_id/grants" \
|
||||
"{\"projectId\":\"$project_id\",\"roleKeys\":[\"test-user\"]}" \
|
||||
"$organization_id" >/dev/null || return
|
||||
printf '%s\t%s\t%s\t%s\n' \
|
||||
http://zitadel.remora.test:8080 \
|
||||
"$organization_id" \
|
||||
"$client_id" \
|
||||
"$username"
|
||||
}
|
||||
|
||||
reset_lane_oidc_provider() {
|
||||
local provider="$1"
|
||||
local lane="$2"
|
||||
if [[ "$provider" = keycloak ]]; then
|
||||
reset_keycloak_lane "$lane"
|
||||
else
|
||||
reset_zitadel_lane "$lane"
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_lane_durable_store() {
|
||||
local database="remora_lane_$1"
|
||||
local remaining
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec deployment/postgres -- \
|
||||
env PGPASSWORD=synthetic-postgres psql \
|
||||
--username remora \
|
||||
--dbname postgres \
|
||||
--set ON_ERROR_STOP=1 \
|
||||
--command "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$database'" \
|
||||
--command "DROP DATABASE IF EXISTS $database" >&2 || return
|
||||
remaining="$(kube --namespace "$FIXTURE_NAMESPACE" exec deployment/postgres -- \
|
||||
env PGPASSWORD=synthetic-postgres psql \
|
||||
--username remora \
|
||||
--dbname postgres \
|
||||
--tuples-only \
|
||||
--no-align \
|
||||
--command "SELECT count(*) FROM pg_database WHERE datname = '$database'")" || return
|
||||
[[ "$remaining" = 0 ]]
|
||||
}
|
||||
|
||||
cleanup_lane_coordination_store() {
|
||||
local provider="$1"
|
||||
local namespace="$2"
|
||||
local keys
|
||||
local key
|
||||
while true; do
|
||||
keys="$(kube --namespace "$FIXTURE_NAMESPACE" exec "deployment/$provider" -- \
|
||||
redis-cli --scan --pattern "$namespace*")" || return
|
||||
[[ -z "$keys" ]] && return
|
||||
while IFS= read -r key; do
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec "deployment/$provider" -- \
|
||||
redis-cli DEL "$key" >/dev/null || return
|
||||
done <<< "$keys"
|
||||
done
|
||||
}
|
||||
|
||||
cleanup_lane_target_store() {
|
||||
local provider="$1"
|
||||
local bucket="$2"
|
||||
local deployment
|
||||
local endpoint
|
||||
if [[ "$provider" = garage ]]; then
|
||||
deployment=garage
|
||||
endpoint=http://127.0.0.1:3900
|
||||
else
|
||||
deployment=versitygw
|
||||
endpoint=http://127.0.0.1:10000
|
||||
fi
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec "deployment/$deployment" --container s3-readiness -- \
|
||||
aws --endpoint-url "$endpoint" s3 rm "s3://$bucket" --recursive >&2 || return
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec "deployment/$deployment" --container s3-readiness -- \
|
||||
aws --endpoint-url "$endpoint" s3api delete-bucket --bucket "$bucket" >&2 || return
|
||||
if kube --namespace "$FIXTURE_NAMESPACE" exec "deployment/$deployment" --container s3-readiness -- \
|
||||
aws --endpoint-url "$endpoint" s3api head-bucket --bucket "$bucket" >/dev/null 2>&1; then
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_keycloak_lane() {
|
||||
local realm="remora-lane-$1"
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec deployment/keycloak -- \
|
||||
/opt/keycloak/bin/kcadm.sh delete "realms/$realm" \
|
||||
--no-config \
|
||||
--server http://127.0.0.1:8080 \
|
||||
--realm master \
|
||||
--user remora-admin \
|
||||
--password synthetic-keycloak >&2 || return
|
||||
}
|
||||
|
||||
find_zitadel_lane_organizations() {
|
||||
local lane="$1"
|
||||
local tenant="remora-lane-$lane"
|
||||
local response
|
||||
response="$(zitadel_request /v2/organizations/_search '{}')" || return
|
||||
jq --raw-output --arg tenant "$tenant" \
|
||||
'.result[]? | select(.name == $tenant) | .id' <<< "$response"
|
||||
}
|
||||
|
||||
cleanup_zitadel_lane() {
|
||||
local lane="$1"
|
||||
local organization_ids
|
||||
local organization_id
|
||||
local status=0
|
||||
organization_ids="$(find_zitadel_lane_organizations "$lane")" || return
|
||||
while IFS= read -r organization_id; do
|
||||
[[ -z "$organization_id" ]] && continue
|
||||
zitadel_request "/v2/organizations/$organization_id" '' '' DELETE >/dev/null || status=1
|
||||
done <<< "$organization_ids"
|
||||
return "$status"
|
||||
}
|
||||
|
||||
cleanup_lane_secret_store() {
|
||||
local path="secret/remora/lane-$1"
|
||||
kube --namespace "$FIXTURE_NAMESPACE" exec deployment/vault -- \
|
||||
env VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN=synthetic-vault-token \
|
||||
vault kv metadata delete "$path" >/dev/null || return
|
||||
}
|
||||
|
||||
cleanup_lane_state() {
|
||||
local lane="$1"
|
||||
local coordination="$2"
|
||||
local oidc="$3"
|
||||
local target="$4"
|
||||
local status=0
|
||||
if fixture_has_family durable && ! cleanup_lane_durable_store "$lane"; then
|
||||
printf 'durable store cleanup failed for lane %s\n' "$lane" >&2
|
||||
status=1
|
||||
fi
|
||||
if [[ "$coordination" != - ]] && ! cleanup_lane_coordination_store "$coordination" "remora:lane:$lane:"; then
|
||||
printf 'coordination store cleanup failed for lane %s\n' "$lane" >&2
|
||||
status=1
|
||||
fi
|
||||
if [[ "$oidc" = keycloak ]] && ! cleanup_keycloak_lane "$lane"; then
|
||||
printf 'Keycloak cleanup failed for lane %s\n' "$lane" >&2
|
||||
status=1
|
||||
fi
|
||||
if [[ "$oidc" = zitadel ]] && ! cleanup_zitadel_lane "$lane"; then
|
||||
printf 'ZITADEL cleanup failed for lane %s\n' "$lane" >&2
|
||||
status=1
|
||||
fi
|
||||
if [[ "$target" != - ]] && ! cleanup_lane_target_store "$target" "remora-lane-$lane"; then
|
||||
printf 'target store cleanup failed for lane %s\n' "$lane" >&2
|
||||
status=1
|
||||
fi
|
||||
if fixture_has_family secret && ! cleanup_lane_secret_store "$lane"; then
|
||||
printf 'secret store cleanup failed for lane %s\n' "$lane" >&2
|
||||
status=1
|
||||
fi
|
||||
return "$status"
|
||||
}
|
||||
|
||||
write_lane_profile() {
|
||||
local lane="$1"
|
||||
local coordination="$2"
|
||||
local oidc="$3"
|
||||
local target="$4"
|
||||
local profile_name="remora-fixture-lane-$lane"
|
||||
local database=
|
||||
local database_url=
|
||||
local coordination_url=
|
||||
local coordination_namespace=
|
||||
local oidc_issuer=
|
||||
local oidc_tenant=
|
||||
local oidc_client_id=
|
||||
local oidc_username=
|
||||
local oidc_profile
|
||||
local target_endpoint=
|
||||
local target_bucket=
|
||||
local target_prefix=
|
||||
local target_access_key=
|
||||
local target_secret_key=
|
||||
local target_region=
|
||||
local vault_path=
|
||||
local profile
|
||||
if fixture_has_family durable; then
|
||||
database="$(reset_lane_durable_store "$lane")" || return
|
||||
database_url="postgres://remora:synthetic-postgres@postgres:5432/$database?sslmode=disable"
|
||||
fi
|
||||
if [[ "$coordination" != - ]]; then
|
||||
coordination_url="redis://$coordination:6379"
|
||||
coordination_namespace="remora:lane:$lane:"
|
||||
cleanup_lane_coordination_store "$coordination" "$coordination_namespace" || return
|
||||
fi
|
||||
if [[ "$oidc" != - ]]; then
|
||||
oidc_profile="$(reset_lane_oidc_provider "$oidc" "$lane")" || return
|
||||
IFS=$'\t' read -r oidc_issuer oidc_tenant oidc_client_id oidc_username <<< "$oidc_profile"
|
||||
fi
|
||||
if [[ "$target" != - ]]; then
|
||||
target_bucket="remora-lane-$lane"
|
||||
target_prefix="lane-$lane/"
|
||||
reset_lane_target_store "$target" "$target_bucket" || return
|
||||
if [[ "$target" = garage ]]; then
|
||||
target_endpoint=http://garage:3900
|
||||
target_access_key=GK00000000000000000000000000000000
|
||||
target_secret_key=0000000000000000000000000000000000000000000000000000000000000000
|
||||
target_region=garage
|
||||
else
|
||||
target_endpoint=http://versitygw:10000
|
||||
target_access_key=synthetic-s3-access
|
||||
target_secret_key=synthetic-s3-secret
|
||||
target_region=us-east-1
|
||||
fi
|
||||
fi
|
||||
if fixture_has_family secret; then
|
||||
vault_path="secret/remora/lane-$lane"
|
||||
cleanup_lane_secret_store "$lane" || return
|
||||
fi
|
||||
printf -v profile '%s\n' \
|
||||
"REMORA_FIXTURE_LANE=$lane" \
|
||||
"REMORA_COORDINATION_PROVIDER=$coordination" \
|
||||
"REMORA_COORDINATION_URL=$coordination_url" \
|
||||
"REMORA_COORDINATION_NAMESPACE=$coordination_namespace" \
|
||||
"REMORA_OIDC_PROVIDER=$oidc" \
|
||||
"REMORA_OIDC_ISSUER=$oidc_issuer" \
|
||||
"REMORA_OIDC_TENANT=$oidc_tenant" \
|
||||
"REMORA_OIDC_CLIENT_ID=$oidc_client_id" \
|
||||
"REMORA_OIDC_USERNAME=$oidc_username" \
|
||||
"REMORA_OIDC_PASSWORD=Synthetic-OIDC-1!" \
|
||||
"REMORA_TARGET_PROVIDER=$target" \
|
||||
"REMORA_TARGET_ENDPOINT=$target_endpoint" \
|
||||
"REMORA_TARGET_BUCKET=$target_bucket" \
|
||||
"REMORA_TARGET_PREFIX=$target_prefix" \
|
||||
"REMORA_TARGET_ACCESS_KEY=$target_access_key" \
|
||||
"REMORA_TARGET_SECRET_KEY=$target_secret_key" \
|
||||
"REMORA_TARGET_REGION=$target_region" \
|
||||
"REMORA_VAULT_ADDR=http://vault:8200" \
|
||||
"REMORA_VAULT_TOKEN=synthetic-vault-token" \
|
||||
"REMORA_VAULT_PATH=$vault_path" \
|
||||
"DATABASE_URL=$database_url"
|
||||
kube --namespace "$FIXTURE_NAMESPACE" delete \
|
||||
"configmap/$profile_name" \
|
||||
"configmap/$profile_name-file" \
|
||||
--ignore-not-found \
|
||||
--wait=true >&2 || return
|
||||
kube --namespace "$FIXTURE_NAMESPACE" create configmap "$profile_name" \
|
||||
--from-literal="REMORA_FIXTURE_LANE=$lane" \
|
||||
--from-literal="REMORA_COORDINATION_PROVIDER=$coordination" \
|
||||
--from-literal="REMORA_COORDINATION_URL=$coordination_url" \
|
||||
--from-literal="REMORA_COORDINATION_NAMESPACE=$coordination_namespace" \
|
||||
--from-literal="REMORA_OIDC_PROVIDER=$oidc" \
|
||||
--from-literal="REMORA_OIDC_ISSUER=$oidc_issuer" \
|
||||
--from-literal="REMORA_OIDC_TENANT=$oidc_tenant" \
|
||||
--from-literal="REMORA_OIDC_CLIENT_ID=$oidc_client_id" \
|
||||
--from-literal="REMORA_OIDC_USERNAME=$oidc_username" \
|
||||
--from-literal=REMORA_OIDC_PASSWORD=Synthetic-OIDC-1! \
|
||||
--from-literal="REMORA_TARGET_PROVIDER=$target" \
|
||||
--from-literal="REMORA_TARGET_ENDPOINT=$target_endpoint" \
|
||||
--from-literal="REMORA_TARGET_BUCKET=$target_bucket" \
|
||||
--from-literal="REMORA_TARGET_PREFIX=$target_prefix" \
|
||||
--from-literal="REMORA_TARGET_ACCESS_KEY=$target_access_key" \
|
||||
--from-literal="REMORA_TARGET_SECRET_KEY=$target_secret_key" \
|
||||
--from-literal="REMORA_TARGET_REGION=$target_region" \
|
||||
--from-literal=REMORA_VAULT_ADDR=http://vault:8200 \
|
||||
--from-literal=REMORA_VAULT_TOKEN=synthetic-vault-token \
|
||||
--from-literal="REMORA_VAULT_PATH=$vault_path" \
|
||||
--from-literal="DATABASE_URL=$database_url" >&2 || return
|
||||
kube --namespace "$FIXTURE_NAMESPACE" create configmap "$profile_name-file" \
|
||||
--from-literal="profile.env=$profile" >&2 || return
|
||||
printf '%s\n' "$profile_name"
|
||||
}
|
||||
|
||||
render_lane_job() {
|
||||
local template="$1"
|
||||
local output="$2"
|
||||
local job="$3"
|
||||
local profile="$4"
|
||||
local image="$5"
|
||||
local postgres_image="${POSTGRES_LOCAL_IMAGE:-remora-testbed/unused:postgres}"
|
||||
local redis_image="${REDIS_LOCAL_IMAGE:-remora-testbed/unused:redis}"
|
||||
local vault_image="${VAULT_LOCAL_IMAGE:-remora-testbed/unused:vault}"
|
||||
local curl_image="${CURL_LOCAL_IMAGE:-remora-testbed/unused:curl}"
|
||||
local aws_cli_image="${AWS_CLI_LOCAL_IMAGE:-remora-testbed/unused:aws-cli}"
|
||||
sed \
|
||||
-e "s|@@JOB_NAME@@|$job|g" \
|
||||
-e "s|@@PROFILE_NAME@@|$profile|g" \
|
||||
-e "s|@@TEST_IMAGE@@|$image|g" \
|
||||
-e "s|@@POSTGRES_IMAGE@@|$postgres_image|g" \
|
||||
-e "s|@@REDIS_IMAGE@@|$redis_image|g" \
|
||||
-e "s|@@VAULT_IMAGE@@|$vault_image|g" \
|
||||
-e "s|@@CURL_IMAGE@@|$curl_image|g" \
|
||||
-e "s|@@AWS_CLI_IMAGE@@|$aws_cli_image|g" \
|
||||
"$template" > "$output"
|
||||
}
|
||||
|
||||
run_job_matrix() {
|
||||
local job_base="$1"
|
||||
local template="$2"
|
||||
local image="$3"
|
||||
shift 3
|
||||
local families=("$@")
|
||||
local lane
|
||||
local coordination
|
||||
local oidc
|
||||
local target
|
||||
local job
|
||||
local profile
|
||||
local manifest
|
||||
select_fixture_families "${families[@]}" || return
|
||||
while IFS=$'\t' read -r lane coordination oidc target; do
|
||||
job="$job_base-$lane"
|
||||
profile="$(write_lane_profile "$lane" "$coordination" "$oidc" "$target")" || return
|
||||
manifest="$RUNTIME/$job.yaml"
|
||||
render_lane_job "$template" "$manifest" "$job" "$profile" "$image" || return
|
||||
printf 'Running fixture lane %s: coordination=%s oidc=%s target=%s\n' "$lane" "$coordination" "$oidc" "$target"
|
||||
run_job_manifest "$FIXTURE_NAMESPACE" "$job" "$manifest" || return
|
||||
cleanup_lane_state "$lane" "$coordination" "$oidc" "$target" || return
|
||||
kube --namespace "$FIXTURE_NAMESPACE" delete \
|
||||
"job/$job" \
|
||||
"configmap/$profile" \
|
||||
"configmap/$profile-file" \
|
||||
--wait=true || return
|
||||
rm -f "$manifest" || return
|
||||
done < <(expand_fixture_matrix "${families[@]}")
|
||||
}
|
||||
|
||||
verify_dependencies() {
|
||||
local family
|
||||
for family in durable coordination oidc target secret; do
|
||||
if ! fixture_has_family "$family"; then
|
||||
printf 'released-image dependency probes require the all fixture profile\n' >&2
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
kube apply --filename "$RUNTIME/probe.yaml"
|
||||
for job in state-probe oidc-probe garage-probe versitygw-probe; do
|
||||
wait_for_job "$FIXTURE_NAMESPACE" "$job"
|
||||
|
|
@ -278,6 +943,7 @@ destroy_fixture() {
|
|||
"$RUNTIME/probe.yaml" \
|
||||
"$RUNTIME/sample-job.yaml" \
|
||||
"$RUNTIME/durable-job.yaml"
|
||||
find "$RUNTIME" -maxdepth 1 -type f -name '*-[0-9][0-9][0-9].yaml' -delete
|
||||
}
|
||||
|
||||
kube() {
|
||||
|
|
@ -296,26 +962,45 @@ wait_for_job() {
|
|||
while ((SECONDS < deadline)); do
|
||||
complete="$(kube --namespace "$namespace" get "job/$job" --output jsonpath='{.status.conditions[?(@.type=="Complete")].status}')"
|
||||
if [[ "$complete" = True ]]; then
|
||||
kube --namespace "$namespace" logs "job/$job" --all-containers
|
||||
print_job_logs "$namespace" "$job"
|
||||
return
|
||||
fi
|
||||
failed="$(kube --namespace "$namespace" get "job/$job" --output jsonpath='{.status.conditions[?(@.type=="Failed")].status}')"
|
||||
if [[ "$failed" = True ]]; then
|
||||
kube --namespace "$namespace" logs "job/$job" --all-containers >&2 || true
|
||||
print_job_logs "$namespace" "$job" >&2 || true
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
kube --namespace "$namespace" logs "job/$job" --all-containers >&2 || true
|
||||
print_job_logs "$namespace" "$job" >&2 || true
|
||||
printf 'job/%s did not complete within 300 seconds\n' "$job" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
print_job_logs() {
|
||||
local namespace="$1"
|
||||
local job="$2"
|
||||
local pod
|
||||
local containers
|
||||
local container
|
||||
local status=0
|
||||
pod="$(kube --namespace "$namespace" get pods \
|
||||
--selector "job-name=$job" \
|
||||
--output jsonpath='{.items[0].metadata.name}')" || return
|
||||
containers="$(kube --namespace "$namespace" get "pod/$pod" \
|
||||
--output jsonpath='{range .spec.initContainers[*]}{.name}{"\n"}{end}{range .spec.containers[*]}{.name}{"\n"}{end}')" || return
|
||||
while IFS= read -r container; do
|
||||
[[ -z "$container" ]] && continue
|
||||
kube --namespace "$namespace" logs "pod/$pod" --container "$container" || status=1
|
||||
done <<< "$containers"
|
||||
return "$status"
|
||||
}
|
||||
|
||||
run_job_manifest() {
|
||||
local namespace="$1"
|
||||
local job="$2"
|
||||
local manifest="$3"
|
||||
kube --namespace "$namespace" delete "job/$job" --ignore-not-found --wait=true
|
||||
kube --namespace "$namespace" apply --filename "$manifest"
|
||||
wait_for_job "$namespace" "$job"
|
||||
kube --namespace "$namespace" delete "job/$job" --ignore-not-found --wait=true || return
|
||||
kube --namespace "$namespace" apply --filename "$manifest" || return
|
||||
wait_for_job "$namespace" "$job" || return
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue