Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions .cursor/prompts/deploy-dcm.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions .cursor/prompts/tear-down.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/validate-scripts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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" \
Expand Down
8 changes: 7 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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. The E2E suite currently supports unauthenticated test runs only.

Run `./scripts/deploy-dcm.sh --help` for all flags and environment variable overrides.

## Local dev scripts
Expand Down Expand Up @@ -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 |
Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -78,8 +79,11 @@ 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
./scripts/deploy-dcm.sh --tear-down
# 9. Deploy with authentication enabled (Keycloak + JWT validation)
./scripts/deploy-dcm.sh --auth-enabled

# 10. Tear down when done
./scripts/deploy-dcm.sh --auth-enabled --tear-down
```

Run `./scripts/deploy-dcm.sh --help` for all flags and environment variable overrides.
Expand Down
137 changes: 137 additions & 0 deletions scripts/deploy-dcm.sh
Comment thread
chadcrum marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -605,6 +608,115 @@ 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
}

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"
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
local db_password

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

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" "${db_password}"
upsert_deploy_env_var "${deploy_dir}" "DB_USER" "$(env_or_default DB_USER admin)"
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)"
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"
Expand Down Expand Up @@ -645,6 +757,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() {
Expand Down Expand Up @@ -727,6 +840,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)
Expand Down Expand Up @@ -773,6 +888,23 @@ 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

# 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|\"false\"|'false')[[:space:]]*$" "${CONTROL_PLANE_TMP_DIR}/deploy/.env"; then
AUTH_ENABLED=true
fi

if [[ "${AUTH_ENABLED}" == true ]]; then
COMPOSE_PROFILES+=("--profile" "auth")
Comment thread
chadcrum marked this conversation as resolved.
fi

# --- Running versions (standalone) ----------------------------------------- #

if [[ "${RUNNING_VERSIONS}" == true ]]; then
Expand Down Expand Up @@ -1076,6 +1208,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
Expand All @@ -1090,6 +1224,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
Expand Down
4 changes: 2 additions & 2 deletions tests/run-e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down
Loading