From c6cef4eafff6a2a39db99e52307e98da19c2b4c8 Mon Sep 17 00:00:00 2001 From: Chad Crum Date: Mon, 31 Aug 2026 16:06:53 -0400 Subject: [PATCH 1/4] feat(deploy): bootstrap deploy/.env and add auth-enabled flag (FLPATH-4806) Align utilities E2E deploy with control-plane externalized compose credentials: bootstrap deploy/.env after clone, wire --auth-enabled and Jenkins AUTH_DISABLED env through compose auth profile, and forward auth flags from run-e2e.sh. Signed-off-by: Chad Crum Co-authored-by: Cursor --- .cursor/prompts/deploy-dcm.md | 29 +++++-- .github/workflows/validate-scripts.yaml | 7 ++ CLAUDE.md | 8 +- README.md | 12 ++- scripts/deploy-dcm.sh | 105 ++++++++++++++++++++++++ tests/run-e2e.sh | 16 ++++ 6 files changed, 167 insertions(+), 10 deletions(-) diff --git a/.cursor/prompts/deploy-dcm.md b/.cursor/prompts/deploy-dcm.md index 01e1961..a331b64 100644 --- a/.cursor/prompts/deploy-dcm.md +++ b/.cursor/prompts/deploy-dcm.md @@ -48,6 +48,19 @@ Deploy the full DCM stack for E2E testing using `scripts/deploy-dcm.sh`. ./scripts/deploy-dcm.sh --cleanup-on-failure ``` +### Deploy with Authentication Enabled +```bash +# Starts Keycloak (compose profile auth) and enables JWT validation +./scripts/deploy-dcm.sh --auth-enabled + +# Equivalent via environment (Jenkins uses this today) +AUTH_DISABLED=false AUTH_ISSUER_URL=http://keycloak:8080/realms/dcm AUTH_JWT_AUDIENCE=dcm-api \ + ./scripts/deploy-dcm.sh + +# Tear down auth-enabled stack with the same flag +./scripts/deploy-dcm.sh --auth-enabled --tear-down +``` + ### Deploy with k8s Container Service Provider ```bash # Auto-detects cluster from existing oc/kubectl session @@ -107,17 +120,23 @@ When any service provider is enabled, the script resolves cluster access in this | `OPENSHIFT_API` | `--cluster-api` | | `OPENSHIFT_USERNAME` | `--cluster-username` | | `OPENSHIFT_PASSWORD` | `--cluster-password` | +| `AUTH_DISABLED` | Set to `false` to enable auth (same as `--auth-enabled`) | Flags take precedence over environment variables. +## Compose Credentials + +After cloning control-plane, the script creates `deploy/.env` from `deploy/.env.example` if missing. Database and optional auth credentials are written there for compose `env_file: .env` services. Lab defaults match control-plane's `.env.example`; override via shell env vars before running the script. + ## What Happens 1. Clones control-plane (`deploy/compose.yaml`) -2. Runs `podman-compose up -d` -3. Verifies all containers are running -4. Polls `/api/v1alpha1/health` (90s timeout) -5. Resolves container images to git commit SHAs via Quay.io API -6. Writes `dcm-versions.json` +2. Bootstraps `deploy/.env` with DB credentials (and auth credentials when `--auth-enabled`) +3. Runs `podman-compose up -d` +4. Verifies all containers are running +5. Polls `/api/v1alpha1/health` (90s timeout) +6. Resolves container images to git commit SHAs via Quay.io API +7. Writes `dcm-versions.json` ## Output diff --git a/.github/workflows/validate-scripts.yaml b/.github/workflows/validate-scripts.yaml index f643718..2cbeccd 100644 --- a/.github/workflows/validate-scripts.yaml +++ b/.github/workflows/validate-scripts.yaml @@ -98,6 +98,7 @@ jobs: --expect-contains "--control-plane-branch" "control-plane-branch flag" \ --expect-contains "--kubeconfig" "kubeconfig flag" \ --expect-contains "--compose-file" "compose-file flag" \ + --expect-contains "--auth-enabled" "auth-enabled flag" \ --expect-contains "--cleanup-on-failure" "cleanup-on-failure flag" \ --expect-contains "--running-versions" "running-versions flag" \ --expect-contains "--tear-down" "tear-down flag" \ @@ -252,6 +253,12 @@ jobs: --expect-contains "DCM_ACM_CLUSTER_SP_URL" "ACM cluster SP URL env var" \ --expect-contains "DCM_NATS_URL" "NATS URL env var" + run_case "--help contains auth flags" \ + --expect-exit "--help" 0 \ + --expect-contains "--auth-enabled" "auth-enabled flag" \ + --expect-contains "--keycloak-url" "keycloak-url flag" \ + --expect-contains "DCM_KEYCLOAK_URL" "keycloak URL env var" + # --- Unknown flag handling -------------------------------------------- run_case "unknown flag is rejected" \ diff --git a/CLAUDE.md b/CLAUDE.md index 897314f..a90dd28 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,7 +28,9 @@ CI runs ShellCheck on changed `*.sh` files via `.github/workflows/lint.yaml` (on Deploys the full DCM stack for E2E testing by cloning control-plane (`deploy/compose.yaml`), running `podman-compose up`, and polling health endpoints until all services respond 2xx. -**Flow:** clone control-plane → `podman-compose up -d` → verify containers running → poll `/api/v1alpha1/health` → collect container versions from Quay.io API → write `dcm-versions.json`. +**Flow:** clone control-plane → bootstrap `deploy/.env` → `podman-compose up -d` → verify containers running → poll `/api/v1alpha1/health` → collect container versions from Quay.io API → write `dcm-versions.json`. + +**Compose credentials:** After clone, the script copies `deploy/.env.example` to `deploy/.env` when missing and upserts DB/auth keys (lab defaults unless overridden by shell env). Control-plane compose reads these via `env_file: .env`. Pass `--auth-enabled` or set `AUTH_DISABLED=false` to add the compose `auth` profile (Keycloak) and write auth credentials into `.env`. **Modes:** The script has three mutually exclusive modes: - **Deploy** (default): full clone + bring-up + health check. Pass `--cleanup-on-failure` to auto-teardown on error (default leaves partial state for debugging). @@ -48,6 +50,8 @@ When a non-main version is specified, `--control-plane-branch` is auto-derived t **Cluster authentication:** When any provider is enabled, the script resolves cluster access in priority order: explicit `--kubeconfig`, existing `oc`/`kubectl` session, or `oc login` via `--cluster-api` + `--cluster-password`. +**Control-plane authentication:** Pass `--auth-enabled` (or set `AUTH_DISABLED=false`) to start Keycloak and enable JWT validation. Use the same flag on `--tear-down` when tearing down an auth-enabled stack. `tests/run-e2e.sh` accepts `--auth-enabled` and `--keycloak-url` for Jenkins compatibility. + Run `./scripts/deploy-dcm.sh --help` for all flags and environment variable overrides. ## Local dev scripts @@ -107,6 +111,8 @@ The script is organized into sections separated by comment banners. Key function | `validate_k8s_container_provider` | Validates k8s container SP prerequisites | | `validate_k8s_storage_provider` | Validates k8s storage SP prerequisites | | `validate_acm_cluster_provider` | Validates ACM cluster SP prerequisites | +| `ensure_deploy_env` | Bootstraps `deploy/.env` from `.env.example` and upserts credentials | +| `upsert_deploy_env_var` | Idempotently sets a key in `deploy/.env` | | `resolve_provider_cli` | Resolves `oc`/`kubectl` per provider's `CLI_REQUIREMENT` | | `collect_provider_compose` | Collects compose profiles/overrides for an enabled provider | | `verify_health` | Confirms all compose services are running, then polls health endpoints with timeout | diff --git a/README.md b/README.md index d906598..0445cc4 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,10 @@ Both deploy mode and `--running-versions` produce a `dcm-versions.json` mapping `scripts/deploy-dcm.sh` automates the full DCM stack lifecycle for E2E testing: 1. Clones the [control-plane](https://github.com/dcm-project/control-plane) repo (`deploy/compose.yaml`) -2. Starts all services with `podman-compose up` -3. Polls health endpoints until every service responds 2xx -4. Resolves running container images to git commit SHAs via the Quay.io API +2. Bootstraps `deploy/.env` from `deploy/.env.example` (compose credentials; see control-plane `deploy/RUN.md`) +3. Starts all services with `podman-compose up` +4. Polls health endpoints until every service responds 2xx +5. Resolves running container images to git commit SHAs via the Quay.io API ### Prerequisites @@ -78,7 +79,10 @@ Both deploy mode and `--running-versions` produce a `dcm-versions.json` mapping # 8. Deploy ACM cluster provider (install ACM first if needed) ./scripts/deploy-dcm.sh --acm-cluster-service-provider --deploy-acm --kubeconfig ~/.kube/config -# 9. Tear down when done +# 9. Deploy with authentication enabled (Keycloak + JWT validation) +./scripts/deploy-dcm.sh --auth-enabled + +# 10. Tear down when done ./scripts/deploy-dcm.sh --tear-down ``` diff --git a/scripts/deploy-dcm.sh b/scripts/deploy-dcm.sh index fc943c1..8a9a8da 100755 --- a/scripts/deploy-dcm.sh +++ b/scripts/deploy-dcm.sh @@ -145,6 +145,7 @@ EOF --cluster-username USER Username for oc login (default: kubeadmin) --cluster-password PASS Password for oc login --compose-file PATH Additional compose file to merge (repeatable, e.g. port overrides) + --auth-enabled Enable authentication (Keycloak + JWT validation; adds compose profile auth) --cleanup-on-failure Tear down the stack automatically if deployment fails (default: leave for debugging) --running-versions Print versions of all running containers and write dcm-versions.json --tear-down Stop the stack, remove volumes, and clean the deploy directory @@ -165,6 +166,7 @@ Environment variables (flags take precedence): OPENSHIFT_API Same as --cluster-api OPENSHIFT_USERNAME Same as --cluster-username (default: kubeadmin) OPENSHIFT_PASSWORD Same as --cluster-password + AUTH_DISABLED Set to 'false' to enable auth (same effect as --auth-enabled) EOF # Provider namespace env vars (generated from registry) @@ -189,6 +191,7 @@ Examples: $(basename "$0") --k8s-storage-service-provider --kubeconfig ~/.kube/config $(basename "$0") --all-service-providers --cluster-api https://api.cluster.example.com --cluster-password secret $(basename "$0") --acm-cluster-service-provider --deploy-acm --kubeconfig ~/.kube/config + $(basename "$0") --auth-enabled $(basename "$0") --tear-down $(basename "$0") --running-versions EOF @@ -605,6 +608,89 @@ resolve_provider_cli() { esac } +# --- Compose credential bootstrap ------------------------------------------ # +# +# control-plane deploy/compose.yaml reads credentials from deploy/.env +# (see control-plane deploy/.env.example). Bootstrap that file after clone. + +env_or_default() { + local var_name="$1" + local default_value="$2" + if [[ -n "${!var_name:-}" ]]; then + echo "${!var_name}" + else + echo "${default_value}" + fi +} + +upsert_deploy_env_var() { + local deploy_dir="$1" + local key="$2" + local value="$3" + local env_file="${deploy_dir}/deploy/.env" + local tmp + + [[ -n "${value}" ]] || return 0 + + tmp="$(mktemp)" + if [[ -f "${env_file}" ]]; then + grep -v "^${key}=" "${env_file}" > "${tmp}" || true + fi + printf '%s=%s\n' "${key}" "${value}" >> "${tmp}" + mv "${tmp}" "${env_file}" +} + +ensure_deploy_env() { + local deploy_dir="$1" + local env_file="${deploy_dir}/deploy/.env" + local env_example="${deploy_dir}/deploy/.env.example" + local var + + if [[ ! -f "${env_file}" ]]; then + if [[ ! -f "${env_example}" ]]; then + err "Missing ${env_example} — cannot bootstrap deploy credentials" + return 1 + fi + cp "${env_example}" "${env_file}" + info "Created ${env_file} from .env.example" + fi + + upsert_deploy_env_var "${deploy_dir}" "POSTGRES_USER" "$(env_or_default POSTGRES_USER admin)" + upsert_deploy_env_var "${deploy_dir}" "POSTGRES_PASSWORD" "$(env_or_default POSTGRES_PASSWORD adminpass)" + upsert_deploy_env_var "${deploy_dir}" "DB_USER" "$(env_or_default DB_USER admin)" + upsert_deploy_env_var "${deploy_dir}" "DB_PASS" "$(env_or_default DB_PASS adminpass)" + upsert_deploy_env_var "${deploy_dir}" "DB_PASSWORD" "$(env_or_default DB_PASSWORD adminpass)" + + if [[ "${AUTH_ENABLED}" == true ]]; then + upsert_deploy_env_var "${deploy_dir}" "KEYCLOAK_ADMIN" "$(env_or_default KEYCLOAK_ADMIN admin)" + upsert_deploy_env_var "${deploy_dir}" "KEYCLOAK_ADMIN_PASSWORD" "$(env_or_default KEYCLOAK_ADMIN_PASSWORD admin)" + upsert_deploy_env_var "${deploy_dir}" "DCM_DEV_USER_PASSWORD" "$(env_or_default DCM_DEV_USER_PASSWORD admin)" + upsert_deploy_env_var "${deploy_dir}" "AUTH_PROXY_SECRET" "$(env_or_default AUTH_PROXY_SECRET dcm-dev-proxy-secret)" + upsert_deploy_env_var "${deploy_dir}" "AUTH_DISABLED" "false" + upsert_deploy_env_var "${deploy_dir}" "AUTH_ISSUER_URL" "$(env_or_default AUTH_ISSUER_URL http://keycloak:8080/realms/dcm)" + upsert_deploy_env_var "${deploy_dir}" "AUTH_JWT_AUDIENCE" "$(env_or_default AUTH_JWT_AUDIENCE dcm-api)" + upsert_deploy_env_var "${deploy_dir}" "DCM_ADMIN_SUBJECT" "$(env_or_default DCM_ADMIN_SUBJECT 56deb662-4820-5d83-b828-f4beb11a5fa7)" + else + upsert_deploy_env_var "${deploy_dir}" "AUTH_DISABLED" "true" + fi + + if [[ -n "${DCM_VERSION:-}" ]]; then + for var in "${VERSION_ENV_VARS[@]}"; do + upsert_deploy_env_var "${deploy_dir}" "${var}" "${DCM_VERSION}" + done + else + for var in "${VERSION_ENV_VARS[@]}"; do + if [[ -n "${!var:-}" ]]; then + upsert_deploy_env_var "${deploy_dir}" "${var}" "${!var}" + fi + done + fi + + if [[ -n "${ACM_CLUSTER_SP_PULL_SECRET:-}" ]]; then + upsert_deploy_env_var "${deploy_dir}" "ACM_CLUSTER_SP_PULL_SECRET" "${ACM_CLUSTER_SP_PULL_SECRET}" + fi +} + # Collect compose args (profiles and overrides) for an enabled provider. collect_provider_compose() { local i="$1" @@ -645,6 +731,7 @@ DCM_KUBECONFIG="${KUBECONFIG:-}" OPENSHIFT_API="${OPENSHIFT_API:-}" OPENSHIFT_USERNAME="${OPENSHIFT_USERNAME:-kubeadmin}" OPENSHIFT_PASSWORD="${OPENSHIFT_PASSWORD:-}" +AUTH_ENABLED_EXPLICIT=false COMPOSE_EXTRA_FILE_ARGS=() require_arg() { @@ -727,6 +814,8 @@ while [[ $# -gt 0 ]]; do require_arg "$1" "${2:-}" COMPOSE_EXTRA_FILE_ARGS+=("-f" "$(cd "$(dirname "${2:-}")" && pwd)/$(basename "${2:-}")") shift 2 ;; + --auth-enabled) + AUTH_ENABLED_EXPLICIT=true; shift ;; --cleanup-on-failure) CLEANUP_ON_FAILURE=true; shift ;; --running-versions) @@ -773,11 +862,22 @@ for i in $(seq 0 $((PROV_COUNT - 1))); do collect_provider_compose "${i}" done +AUTH_ENABLED=false +if [[ "${AUTH_ENABLED_EXPLICIT}" == true ]] || [[ "${AUTH_DISABLED:-}" == "false" ]]; then + AUTH_ENABLED=true +fi +if [[ "${AUTH_ENABLED}" == true ]]; then + COMPOSE_PROFILES+=("--profile" "auth") +fi + # --- Running versions (standalone) ----------------------------------------- # if [[ "${RUNNING_VERSIONS}" == true ]]; then check_required_tools podman podman-compose curl jq || exit 1 ensure_podman_running || exit 1 + if [[ -d "${CONTROL_PLANE_TMP_DIR}/deploy" ]]; then + ensure_deploy_env "${CONTROL_PLANE_TMP_DIR}" || exit 1 + fi get_running_versions "${CONTROL_PLANE_TMP_DIR}/deploy/compose.yaml" ${COMPOSE_EXTRA_FILE_ARGS[@]+"${COMPOSE_EXTRA_FILE_ARGS[@]}"} ${COMPOSE_PROFILES[@]+"${COMPOSE_PROFILES[@]}"} || exit 1 exit 0 fi @@ -1076,6 +1176,8 @@ fi log "Cloning control-plane (repo=${CONTROL_PLANE_REPO}, branch=${CONTROL_PLANE_BRANCH})" git clone --branch "${CONTROL_PLANE_BRANCH}" --single-branch --depth 1 "${CONTROL_PLANE_REPO}" "${CONTROL_PLANE_TMP_DIR}" +ensure_deploy_env "${CONTROL_PLANE_TMP_DIR}" || exit 1 + # --- Deploy ---------------------------------------------------------------- # if [[ "${CLEANUP_ON_FAILURE}" == true ]]; then @@ -1090,6 +1192,9 @@ done if [[ ${#ENABLED_LABELS[@]} -gt 0 ]]; then info "Enabled providers: ${ENABLED_LABELS[*]}" fi +if [[ "${AUTH_ENABLED}" == true ]]; then + info "Authentication enabled (compose profile: auth)" +fi podman-compose -f "${CONTROL_PLANE_TMP_DIR}/deploy/compose.yaml" ${COMPOSE_EXTRA_FILE_ARGS[@]+"${COMPOSE_EXTRA_FILE_ARGS[@]}"} ${COMPOSE_PROFILES[@]+"${COMPOSE_PROFILES[@]}"} up -d echo diff --git a/tests/run-e2e.sh b/tests/run-e2e.sh index 2bc4500..efcb93d 100755 --- a/tests/run-e2e.sh +++ b/tests/run-e2e.sh @@ -31,6 +31,8 @@ Options: --gateway-url URL Override DCM_GATEWAY_URL (default: http://localhost:8080/api/v1alpha1) --label-filter EXPR Ginkgo label filter (e.g. "smoke", "cli") --junit-report FILE Write JUnit XML report to FILE + --auth-enabled Enable authentication on deploy (forwarded to deploy-dcm.sh) + --keycloak-url URL Keycloak base URL for auth tests (default: http://localhost:8180) --help Show this help message Deploy passthrough flags (forwarded to deploy-dcm.sh): @@ -63,6 +65,7 @@ Environment variables: DCM_KUBEVIRT_SP_URL KubeVirt SP direct URL (default: http://localhost:8081/api/v1alpha1) DCM_NATS_URL NATS URL for event tests (default: nats://localhost:4222) DCM_GATEWAY_URL Control plane API URL (default: http://localhost:8080/api/v1alpha1) + DCM_KEYCLOAK_URL Keycloak URL when using --keycloak-url (default: http://localhost:8180) CLI binary resolution order: 1. --dcm-cli-path flag or DCM_CLI_PATH env var @@ -79,6 +82,7 @@ Examples: $(basename "$0") --control-plane-branch feature-x --skip-teardown $(basename "$0") --k8s-container-service-provider --cluster-api https://api.example.com:6443 $(basename "$0") --skip-deploy --label-filter "sp && container" + $(basename "$0") --auth-enabled --skip-teardown EOF } @@ -159,6 +163,7 @@ CLI_VERSION="${CLI_VERSION:-main}" GATEWAY_URL="" LABEL_FILTER="" JUNIT_REPORT="" +KEYCLOAK_URL="" DEPLOY_ARGS=() ENABLE_CONTAINER_SP=false ENABLE_ACM_CLUSTER_SP=false @@ -191,6 +196,12 @@ while [[ $# -gt 0 ]]; do --junit-report) JUNIT_REPORT="$2" shift 2 ;; + --auth-enabled) + DEPLOY_ARGS+=("$1") + shift ;; + --keycloak-url) + KEYCLOAK_URL="$2" + shift 2 ;; --control-plane-branch|--control-plane-dir|--control-plane-repo) DEPLOY_ARGS+=("$1" "$2") shift 2 ;; @@ -266,6 +277,11 @@ if [[ -n "${GATEWAY_URL}" ]]; then info "DCM_GATEWAY_URL=${GATEWAY_URL}" fi +if [[ -n "${KEYCLOAK_URL}" ]]; then + export DCM_KEYCLOAK_URL="${KEYCLOAK_URL}" + info "DCM_KEYCLOAK_URL=${KEYCLOAK_URL}" +fi + # Export SP URLs when providers are enabled. if [[ "${ENABLE_CONTAINER_SP}" == "true" ]] || [[ "${ENABLE_ACM_CLUSTER_SP}" == "true" ]]; then export DCM_NATS_URL="${DCM_NATS_URL:-nats://localhost:4222}" From 1e9821f521e94856e15d07ea6b63134c25a8958f Mon Sep 17 00:00:00 2001 From: Chad Crum Date: Wed, 16 Sep 2026 15:19:28 -0400 Subject: [PATCH 2/4] fix(deploy): address secret bootstrap review findings Signed-off-by: Chad Crum --- .cursor/prompts/tear-down.md | 7 ++++ .github/workflows/validate-scripts.yaml | 6 ---- CLAUDE.md | 2 +- scripts/deploy-dcm.sh | 44 +++++++++++++++++++++---- tests/run-e2e.sh | 16 --------- 5 files changed, 46 insertions(+), 29 deletions(-) diff --git a/.cursor/prompts/tear-down.md b/.cursor/prompts/tear-down.md index ad1d8a7..d106945 100644 --- a/.cursor/prompts/tear-down.md +++ b/.cursor/prompts/tear-down.md @@ -14,6 +14,13 @@ Stop and clean up a running DCM deployment. ./scripts/deploy-dcm.sh --control-plane-dir /path/to/deploy --tear-down ``` +### Authenticated stack +```bash +./scripts/deploy-dcm.sh --auth-enabled --tear-down +``` + +Use the same authentication setting used during deployment so the auth compose profile, including Keycloak, is included in teardown. + ## What Happens 1. Stops all containers via `podman-compose down -v` diff --git a/.github/workflows/validate-scripts.yaml b/.github/workflows/validate-scripts.yaml index 2cbeccd..46b82f8 100644 --- a/.github/workflows/validate-scripts.yaml +++ b/.github/workflows/validate-scripts.yaml @@ -253,12 +253,6 @@ jobs: --expect-contains "DCM_ACM_CLUSTER_SP_URL" "ACM cluster SP URL env var" \ --expect-contains "DCM_NATS_URL" "NATS URL env var" - run_case "--help contains auth flags" \ - --expect-exit "--help" 0 \ - --expect-contains "--auth-enabled" "auth-enabled flag" \ - --expect-contains "--keycloak-url" "keycloak-url flag" \ - --expect-contains "DCM_KEYCLOAK_URL" "keycloak URL env var" - # --- Unknown flag handling -------------------------------------------- run_case "unknown flag is rejected" \ diff --git a/CLAUDE.md b/CLAUDE.md index a90dd28..2aa55e3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -50,7 +50,7 @@ When a non-main version is specified, `--control-plane-branch` is auto-derived t **Cluster authentication:** When any provider is enabled, the script resolves cluster access in priority order: explicit `--kubeconfig`, existing `oc`/`kubectl` session, or `oc login` via `--cluster-api` + `--cluster-password`. -**Control-plane authentication:** Pass `--auth-enabled` (or set `AUTH_DISABLED=false`) to start Keycloak and enable JWT validation. Use the same flag on `--tear-down` when tearing down an auth-enabled stack. `tests/run-e2e.sh` accepts `--auth-enabled` and `--keycloak-url` for Jenkins compatibility. +**Control-plane authentication:** Pass `--auth-enabled` (or set `AUTH_DISABLED=false`) to start Keycloak and enable JWT validation. Use the same flag on `--tear-down` when tearing down an auth-enabled stack. The E2E suite currently supports unauthenticated test runs only. Run `./scripts/deploy-dcm.sh --help` for all flags and environment variable overrides. diff --git a/scripts/deploy-dcm.sh b/scripts/deploy-dcm.sh index 8a9a8da..784ab1e 100755 --- a/scripts/deploy-dcm.sh +++ b/scripts/deploy-dcm.sh @@ -623,6 +623,30 @@ env_or_default() { fi } +resolve_db_password() { + local password_var + local resolved_password="" + local explicit_password="" + + for password_var in POSTGRES_PASSWORD DB_PASS DB_PASSWORD; do + if [[ -n "${!password_var:-}" ]]; then + if [[ -z "${explicit_password}" ]]; then + explicit_password="${!password_var}" + resolved_password="${password_var}=${!password_var}" + elif [[ "${!password_var}" != "${explicit_password}" ]]; then + err "Conflicting database passwords supplied via ${resolved_password%%=*} and ${password_var}" + return 1 + fi + fi + done + + if [[ -z "${explicit_password}" ]]; then + explicit_password="adminpass" + fi + + printf '%s\n' "${explicit_password}" +} + upsert_deploy_env_var() { local deploy_dir="$1" local key="$2" @@ -645,6 +669,7 @@ ensure_deploy_env() { local env_file="${deploy_dir}/deploy/.env" local env_example="${deploy_dir}/deploy/.env.example" local var + local db_password if [[ ! -f "${env_file}" ]]; then if [[ ! -f "${env_example}" ]]; then @@ -655,11 +680,12 @@ ensure_deploy_env() { info "Created ${env_file} from .env.example" fi + db_password="$(resolve_db_password)" || return 1 upsert_deploy_env_var "${deploy_dir}" "POSTGRES_USER" "$(env_or_default POSTGRES_USER admin)" - upsert_deploy_env_var "${deploy_dir}" "POSTGRES_PASSWORD" "$(env_or_default POSTGRES_PASSWORD adminpass)" + upsert_deploy_env_var "${deploy_dir}" "POSTGRES_PASSWORD" "${db_password}" upsert_deploy_env_var "${deploy_dir}" "DB_USER" "$(env_or_default DB_USER admin)" - upsert_deploy_env_var "${deploy_dir}" "DB_PASS" "$(env_or_default DB_PASS adminpass)" - upsert_deploy_env_var "${deploy_dir}" "DB_PASSWORD" "$(env_or_default DB_PASSWORD adminpass)" + upsert_deploy_env_var "${deploy_dir}" "DB_PASS" "${db_password}" + upsert_deploy_env_var "${deploy_dir}" "DB_PASSWORD" "${db_password}" if [[ "${AUTH_ENABLED}" == true ]]; then upsert_deploy_env_var "${deploy_dir}" "KEYCLOAK_ADMIN" "$(env_or_default KEYCLOAK_ADMIN admin)" @@ -866,6 +892,15 @@ AUTH_ENABLED=false if [[ "${AUTH_ENABLED_EXPLICIT}" == true ]] || [[ "${AUTH_DISABLED:-}" == "false" ]]; then AUTH_ENABLED=true fi + +# Read the existing deployment configuration for standalone inspection and teardown. +# Do not source it: deploy/.env contains values that should not be executed as shell code. +if [[ "${RUNNING_VERSIONS}" == true || "${TEAR_DOWN}" == true ]] && + [[ -f "${CONTROL_PLANE_TMP_DIR}/deploy/.env" ]] && + grep -Eq '^AUTH_DISABLED[[:space:]]*=[[:space:]]*false[[:space:]]*$' "${CONTROL_PLANE_TMP_DIR}/deploy/.env"; then + AUTH_ENABLED=true +fi + if [[ "${AUTH_ENABLED}" == true ]]; then COMPOSE_PROFILES+=("--profile" "auth") fi @@ -875,9 +910,6 @@ fi if [[ "${RUNNING_VERSIONS}" == true ]]; then check_required_tools podman podman-compose curl jq || exit 1 ensure_podman_running || exit 1 - if [[ -d "${CONTROL_PLANE_TMP_DIR}/deploy" ]]; then - ensure_deploy_env "${CONTROL_PLANE_TMP_DIR}" || exit 1 - fi get_running_versions "${CONTROL_PLANE_TMP_DIR}/deploy/compose.yaml" ${COMPOSE_EXTRA_FILE_ARGS[@]+"${COMPOSE_EXTRA_FILE_ARGS[@]}"} ${COMPOSE_PROFILES[@]+"${COMPOSE_PROFILES[@]}"} || exit 1 exit 0 fi diff --git a/tests/run-e2e.sh b/tests/run-e2e.sh index efcb93d..2bc4500 100755 --- a/tests/run-e2e.sh +++ b/tests/run-e2e.sh @@ -31,8 +31,6 @@ Options: --gateway-url URL Override DCM_GATEWAY_URL (default: http://localhost:8080/api/v1alpha1) --label-filter EXPR Ginkgo label filter (e.g. "smoke", "cli") --junit-report FILE Write JUnit XML report to FILE - --auth-enabled Enable authentication on deploy (forwarded to deploy-dcm.sh) - --keycloak-url URL Keycloak base URL for auth tests (default: http://localhost:8180) --help Show this help message Deploy passthrough flags (forwarded to deploy-dcm.sh): @@ -65,7 +63,6 @@ Environment variables: DCM_KUBEVIRT_SP_URL KubeVirt SP direct URL (default: http://localhost:8081/api/v1alpha1) DCM_NATS_URL NATS URL for event tests (default: nats://localhost:4222) DCM_GATEWAY_URL Control plane API URL (default: http://localhost:8080/api/v1alpha1) - DCM_KEYCLOAK_URL Keycloak URL when using --keycloak-url (default: http://localhost:8180) CLI binary resolution order: 1. --dcm-cli-path flag or DCM_CLI_PATH env var @@ -82,7 +79,6 @@ Examples: $(basename "$0") --control-plane-branch feature-x --skip-teardown $(basename "$0") --k8s-container-service-provider --cluster-api https://api.example.com:6443 $(basename "$0") --skip-deploy --label-filter "sp && container" - $(basename "$0") --auth-enabled --skip-teardown EOF } @@ -163,7 +159,6 @@ CLI_VERSION="${CLI_VERSION:-main}" GATEWAY_URL="" LABEL_FILTER="" JUNIT_REPORT="" -KEYCLOAK_URL="" DEPLOY_ARGS=() ENABLE_CONTAINER_SP=false ENABLE_ACM_CLUSTER_SP=false @@ -196,12 +191,6 @@ while [[ $# -gt 0 ]]; do --junit-report) JUNIT_REPORT="$2" shift 2 ;; - --auth-enabled) - DEPLOY_ARGS+=("$1") - shift ;; - --keycloak-url) - KEYCLOAK_URL="$2" - shift 2 ;; --control-plane-branch|--control-plane-dir|--control-plane-repo) DEPLOY_ARGS+=("$1" "$2") shift 2 ;; @@ -277,11 +266,6 @@ if [[ -n "${GATEWAY_URL}" ]]; then info "DCM_GATEWAY_URL=${GATEWAY_URL}" fi -if [[ -n "${KEYCLOAK_URL}" ]]; then - export DCM_KEYCLOAK_URL="${KEYCLOAK_URL}" - info "DCM_KEYCLOAK_URL=${KEYCLOAK_URL}" -fi - # Export SP URLs when providers are enabled. if [[ "${ENABLE_CONTAINER_SP}" == "true" ]] || [[ "${ENABLE_ACM_CLUSTER_SP}" == "true" ]]; then export DCM_NATS_URL="${DCM_NATS_URL:-nats://localhost:4222}" From 631a0ac75e85ce6d0f9dd48a14ca40eacd0587ff Mon Sep 17 00:00:00 2001 From: Chad Crum Date: Wed, 16 Sep 2026 16:19:17 -0400 Subject: [PATCH 3/4] fix(e2e): preserve deploy args during teardown Signed-off-by: Chad Crum --- tests/run-e2e.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run-e2e.sh b/tests/run-e2e.sh index 2bc4500..57f7c06 100755 --- a/tests/run-e2e.sh +++ b/tests/run-e2e.sh @@ -313,9 +313,9 @@ fi # Teardown the stack. if [[ "${SKIP_TEARDOWN}" == "false" ]]; then log "Tearing down DCM stack" - if ! "${DEPLOY_SCRIPT}" --tear-down; then + if ! "${DEPLOY_SCRIPT}" --tear-down "${DEPLOY_ARGS[@]+"${DEPLOY_ARGS[@]}"}"; then err "Teardown failed (non-fatal) — containers may still be running" - err "Manual cleanup: ${DEPLOY_SCRIPT} --tear-down" + err "Manual cleanup: ${DEPLOY_SCRIPT} --tear-down ${DEPLOY_ARGS[*]}" fi else log "Skipping teardown (--skip-teardown)" From 4890c8dd0a4dc28b277f2d74e6e47d02ac0864dd Mon Sep 17 00:00:00 2001 From: Chad Crum Date: Thu, 17 Sep 2026 09:42:55 -0400 Subject: [PATCH 4/4] fix(deploy): accept quoted auth setting values Signed-off-by: Chad Crum --- README.md | 2 +- scripts/deploy-dcm.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0445cc4..c1bafc9 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Both deploy mode and `--running-versions` produce a `dcm-versions.json` mapping ./scripts/deploy-dcm.sh --auth-enabled # 10. Tear down when done -./scripts/deploy-dcm.sh --tear-down +./scripts/deploy-dcm.sh --auth-enabled --tear-down ``` Run `./scripts/deploy-dcm.sh --help` for all flags and environment variable overrides. diff --git a/scripts/deploy-dcm.sh b/scripts/deploy-dcm.sh index 784ab1e..ea6160c 100755 --- a/scripts/deploy-dcm.sh +++ b/scripts/deploy-dcm.sh @@ -897,7 +897,7 @@ fi # Do not source it: deploy/.env contains values that should not be executed as shell code. if [[ "${RUNNING_VERSIONS}" == true || "${TEAR_DOWN}" == true ]] && [[ -f "${CONTROL_PLANE_TMP_DIR}/deploy/.env" ]] && - grep -Eq '^AUTH_DISABLED[[:space:]]*=[[:space:]]*false[[:space:]]*$' "${CONTROL_PLANE_TMP_DIR}/deploy/.env"; then + grep -Eq "^AUTH_DISABLED[[:space:]]*=[[:space:]]*(false|\"false\"|'false')[[:space:]]*$" "${CONTROL_PLANE_TMP_DIR}/deploy/.env"; then AUTH_ENABLED=true fi