-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Add ACS SMOKE test pipeline for OPP interop #82838
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
50a7d04
dcd29b7
c20f375
27c76c8
12743d6
cdc0f79
f19cb22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| approvers: | ||
| - cspi-qe-ocp-lp | ||
| reviewers: | ||
| - cspi-qe-ocp-lp |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| #!/bin/bash | ||
| set -euo pipefail | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # ACS OPP Readiness Gate | ||
| # | ||
| # Verifies that ACS Central and SecuredCluster are operational before | ||
| # running SMOKE tests. Discovers namespaces dynamically via CRs. | ||
| # Writes credentials and connection details to $SHARED_DIR for | ||
| # downstream steps. | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| if [[ -f "${SHARED_DIR}/kubeconfig" ]]; then | ||
| export KUBECONFIG="${SHARED_DIR}/kubeconfig" | ||
| fi | ||
|
|
||
| POLL_INTERVAL=30 | ||
| TIMEOUT=300 # 5 minutes | ||
| ELAPSED=0 | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # wait_for - retry a check function with backoff until TIMEOUT | ||
| # --------------------------------------------------------------------------- | ||
| wait_for() { | ||
| local description="$1" | ||
| shift | ||
| local check_fn="$1" | ||
| shift | ||
|
|
||
| ELAPSED=0 | ||
| echo "[readiness] Waiting for: ${description}" | ||
| while true; do | ||
| if "${check_fn}" "$@"; then | ||
| echo "[readiness] OK: ${description}" | ||
| return 0 | ||
| fi | ||
| ELAPSED=$((ELAPSED + POLL_INTERVAL)) | ||
| if [[ ${ELAPSED} -ge ${TIMEOUT} ]]; then | ||
| echo "[readiness] TIMEOUT after ${TIMEOUT}s waiting for: ${description}" | ||
| return 1 | ||
| fi | ||
| echo "[readiness] ...retrying in ${POLL_INTERVAL}s (${ELAPSED}/${TIMEOUT}s)" | ||
| sleep "${POLL_INTERVAL}" | ||
| done | ||
| } | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Namespace discovery via CRs (never hardcode) | ||
| # --------------------------------------------------------------------------- | ||
| discover_central_ns() { | ||
| CENTRAL_NS="$(oc get centrals.platform.stackrox.io --all-namespaces \ | ||
| -o jsonpath='{.items[0].metadata.namespace}' 2>/dev/null)" \ | ||
| && [[ -n "${CENTRAL_NS}" ]] | ||
| } | ||
|
|
||
| discover_sc_ns() { | ||
| SC_NS="$(oc get securedclusters.platform.stackrox.io --all-namespaces \ | ||
| -o jsonpath='{.items[0].metadata.namespace}' 2>/dev/null)" \ | ||
| && [[ -n "${SC_NS}" ]] | ||
| } | ||
|
|
||
| CENTRAL_NS="" | ||
| SC_NS="" | ||
|
|
||
| wait_for "Central CR namespace discovery" discover_central_ns | ||
| echo "[readiness] Central namespace discovered" | ||
|
|
||
| wait_for "SecuredCluster CR namespace discovery" discover_sc_ns | ||
| echo "[readiness] SecuredCluster namespace discovered" | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Check 1: Central route exists | ||
| # --------------------------------------------------------------------------- | ||
| check_central_route() { | ||
| oc get route central -n "${CENTRAL_NS}" -o jsonpath='{.spec.host}' >/dev/null 2>&1 | ||
| } | ||
|
|
||
| wait_for "Central route" check_central_route | ||
|
|
||
| CENTRAL_URL="$(oc get route central -n "${CENTRAL_NS}" -o jsonpath='{.spec.host}')" | ||
| echo "[readiness] Central route discovered" | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Check 2: Central API health (v1/metadata returns 200) | ||
| # --------------------------------------------------------------------------- | ||
| check_central_api() { | ||
| local http_code | ||
| http_code="$(curl -sk -o /dev/null -w '%{http_code}' \ | ||
| "https://${CENTRAL_URL}/v1/metadata" --max-time 10)" || return 1 | ||
| [[ "${http_code}" == "200" ]] | ||
| } | ||
|
|
||
| wait_for "Central API health (v1/metadata)" check_central_api | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Check 3: At least 1 secured cluster connected | ||
| # --------------------------------------------------------------------------- | ||
| check_clusters_connected() { | ||
| # Disable xtrace to protect admin password in curl args | ||
| set +x | ||
| local cluster_count | ||
| cluster_count="$(curl -sk -u "admin:${ROX_ADMIN_PASSWORD}" \ | ||
| "https://${CENTRAL_URL}/v1/clusters" --max-time 10 \ | ||
| | jq '.clusters | length' 2>/dev/null)" || return 1 | ||
| [[ "${cluster_count}" -ge 1 ]] | ||
| } | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Check 6 (early): Extract ROX_ADMIN_PASSWORD before cluster check | ||
| # --------------------------------------------------------------------------- | ||
| echo "[readiness] Extracting ROX_ADMIN_PASSWORD..." | ||
| ROX_ADMIN_PASSWORD="" | ||
| set +x | ||
| ROX_ADMIN_PASSWORD="$(oc get secret -n "${CENTRAL_NS}" central-htpasswd \ | ||
| -o json | jq -r '.data.password' | base64 -d)" | ||
|
|
||
| if [[ -z "${ROX_ADMIN_PASSWORD}" ]]; then | ||
| echo "[readiness] FATAL: could not extract ROX_ADMIN_PASSWORD" | ||
| exit 1 | ||
| fi | ||
| echo "[readiness] ROX_ADMIN_PASSWORD extracted successfully" | ||
|
|
||
| wait_for "secured cluster connected (v1/clusters)" check_clusters_connected | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Check 4: Sensor pods Running (detect OOMKilled) | ||
| # --------------------------------------------------------------------------- | ||
| check_sensor_pods() { | ||
| local pod_json | ||
| pod_json="$(oc get pods -n "${SC_NS}" -l app=sensor -o json 2>/dev/null)" | ||
|
|
||
| local pod_count | ||
| pod_count="$(echo "${pod_json}" | jq '.items | length')" | ||
| if [[ "${pod_count}" -eq 0 ]]; then | ||
| echo "[readiness] no sensor pods found yet" | ||
| return 1 | ||
| fi | ||
|
|
||
| # Check for OOMKilled containers | ||
| local oom | ||
| oom="$(echo "${pod_json}" | jq -r ' | ||
| .items[].status.containerStatuses[]? | ||
| | select(.lastState.terminated.reason == "OOMKilled") | ||
| | .name | ||
| ')" | ||
| if [[ -n "${oom}" ]]; then | ||
| echo "[readiness] WARNING: OOMKilled detected in sensor containers: ${oom}" | ||
| fi | ||
|
|
||
| # All sensor pods must be Running | ||
| local not_running | ||
| not_running="$(echo "${pod_json}" | jq -r ' | ||
| .items[] | select(.status.phase != "Running") | ||
| | "\(.metadata.name):\(.status.phase)" | ||
| ')" | ||
| [[ -z "${not_running}" ]] | ||
| } | ||
|
|
||
| wait_for "sensor pods Running in ${SC_NS}" check_sensor_pods | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Check 5: Default policies loaded (count > 80) | ||
| # --------------------------------------------------------------------------- | ||
| check_policies_loaded() { | ||
| set +x | ||
| local policy_count | ||
| policy_count="$(curl -sk -u "admin:${ROX_ADMIN_PASSWORD}" \ | ||
| "https://${CENTRAL_URL}/v1/policies?query=" --max-time 10 \ | ||
| | jq '.policies | length' 2>/dev/null)" || return 1 | ||
| echo "[readiness] policy count: ${policy_count}" | ||
| [[ "${policy_count}" -gt 80 ]] | ||
| } | ||
|
|
||
| wait_for "default policies loaded (>80)" check_policies_loaded | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Write outputs to SHARED_DIR for downstream steps | ||
| # --------------------------------------------------------------------------- | ||
| echo "[readiness] Writing connection details to SHARED_DIR..." | ||
|
|
||
| set +x | ||
| echo "${ROX_ADMIN_PASSWORD}" > "${SHARED_DIR}/ROX_ADMIN_PASSWORD" | ||
|
|
||
| echo "${CENTRAL_URL}" > "${SHARED_DIR}/CENTRAL_URL" | ||
| echo "${CENTRAL_NS}" > "${SHARED_DIR}/CENTRAL_NS" | ||
| echo "${SC_NS}" > "${SHARED_DIR}/SC_NS" | ||
|
|
||
| echo "[readiness] All checks passed. ACS is ready for SMOKE tests." | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "path": "stackrox/opp-readiness/stackrox-opp-readiness-ref.yaml", | ||
| "owners": { | ||
| "approvers": [ | ||
| "cspi-qe-ocp-lp" | ||
| ], | ||
| "reviewers": [ | ||
| "cspi-qe-ocp-lp" | ||
| ] | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| ref: | ||
| as: stackrox-opp-readiness | ||
| commands: stackrox-opp-readiness-commands.sh | ||
| resources: | ||
| requests: | ||
| cpu: 100m | ||
| memory: 200Mi | ||
| from: cli | ||
| timeout: 40m0s | ||
| documentation: |- | ||
| Verify ACS Central and SecuredCluster are operational before running | ||
| SMOKE tests. Discovers namespaces dynamically via Central and | ||
| SecuredCluster CRs, then polls Central API health, secured-cluster | ||
| connectivity, sensor pod status, and default policy count. Writes | ||
| ROX_ADMIN_PASSWORD, CENTRAL_URL, CENTRAL_NS, and SC_NS to SHARED_DIR | ||
| for downstream steps. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| approvers: | ||
| - cspi-qe-ocp-lp | ||
| reviewers: | ||
| - cspi-qe-ocp-lp |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| #!/bin/bash | ||
| set -euo pipefail | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # ACS OPP SMOKE Test Runner | ||
| # | ||
| # Runs the stackrox qa-tests-backend testSMOKE suite against an ACS | ||
| # instance whose credentials were written to $SHARED_DIR by the | ||
| # stackrox-opp-readiness step. | ||
| # | ||
| # Image: acs-smoke-runner (UBI9 + OpenJDK 17 + git + oc) | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| if [[ -f "${SHARED_DIR}/kubeconfig" ]]; then | ||
| export KUBECONFIG="${SHARED_DIR}/kubeconfig" | ||
| fi | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Read credentials from SHARED_DIR (written by readiness gate) | ||
| # --------------------------------------------------------------------------- | ||
| echo "[smoke] Reading connection details from SHARED_DIR..." | ||
|
|
||
| CENTRAL_URL="$(cat "${SHARED_DIR}/CENTRAL_URL")" | ||
|
|
||
| set +x | ||
| ROX_ADMIN_PASSWORD="$(cat "${SHARED_DIR}/ROX_ADMIN_PASSWORD")" | ||
|
|
||
| echo "[smoke] Connection details loaded from SHARED_DIR" | ||
|
|
||
| # Allow pinning to a known-good ref for reproducibility | ||
| STACKROX_REF="${STACKROX_REF:-main}" | ||
| SCANNER_REF="${SCANNER_REF:-main}" | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Retry wrapper for network-dependent operations | ||
| # --------------------------------------------------------------------------- | ||
| retry_clone() { | ||
| local max_attempts=3 | ||
| local attempt=1 | ||
| while [[ $attempt -le $max_attempts ]]; do | ||
| if "$@"; then | ||
| return 0 | ||
| fi | ||
| echo "[smoke] Clone attempt $attempt/$max_attempts failed, retrying in 10s..." | ||
| sleep 10 | ||
| attempt=$((attempt + 1)) | ||
| done | ||
| echo "[smoke] ERROR: Clone failed after $max_attempts attempts" | ||
| return 1 | ||
|
Comment on lines
+37
to
+49
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win Remove failed clone directories before each retry. If Also applies to: 55-57, 66-66 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Sparse clone of stackrox/stackrox (qa-tests-backend + proto) | ||
| # --------------------------------------------------------------------------- | ||
| echo "[smoke] Sparse-cloning stackrox/stackrox..." | ||
| cd /tmp | ||
| retry_clone git clone --depth 1 --filter=blob:none --sparse --branch "${STACKROX_REF}" \ | ||
| https://github.com/stackrox/stackrox.git stackrox | ||
| cd stackrox | ||
| git sparse-checkout set qa-tests-backend/ proto/ | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Fetch scanner protos (no Go toolchain needed) | ||
| # --------------------------------------------------------------------------- | ||
| echo "[smoke] Fetching scanner protos..." | ||
| retry_clone git clone --depth 1 --filter=blob:none --sparse --branch "${SCANNER_REF}" \ | ||
| https://github.com/stackrox/scanner.git /tmp/scanner | ||
| cd /tmp/scanner | ||
| git sparse-checkout set proto/scanner | ||
| cp -r proto/scanner /tmp/stackrox/qa-tests-backend/src/main/proto/scanner | ||
| chmod -R u+w /tmp/stackrox/qa-tests-backend/src/main/proto/scanner | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Set environment for the Gradle test suite | ||
| # --------------------------------------------------------------------------- | ||
| export API_HOSTNAME="${CENTRAL_URL}" | ||
| export API_PORT="443" | ||
| export ROX_USERNAME="admin" | ||
| export ROX_ADMIN_PASSWORD | ||
| export CLUSTER="OPENSHIFT" | ||
| export CI="true" | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Run testSMOKE | ||
| # --------------------------------------------------------------------------- | ||
| echo "[smoke] Running testSMOKE..." | ||
| cd /tmp/stackrox/qa-tests-backend | ||
|
|
||
| TEST_EXIT=0 | ||
| ./gradlew testSMOKE -i --no-daemon -Dorg.gradle.jvmargs="-Xmx3g" || TEST_EXIT=$? | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Copy JUnit XML results to ARTIFACT_DIR | ||
| # --------------------------------------------------------------------------- | ||
| collect_artifacts() { | ||
| if [[ -d build/test-results/testSMOKE ]]; then | ||
| cp -v build/test-results/testSMOKE/*.xml "${ARTIFACT_DIR}/" 2>/dev/null || true | ||
| fi | ||
| if [[ -d build/reports/tests/testSMOKE ]]; then | ||
| mkdir -p "${ARTIFACT_DIR}/smoke-report" 2>/dev/null || true | ||
| cp -r build/reports/tests/testSMOKE/* "${ARTIFACT_DIR}/smoke-report/" 2>/dev/null || true | ||
| fi | ||
| } | ||
| collect_artifacts || true | ||
|
|
||
| echo "[smoke] Test run finished with exit code: ${TEST_EXIT}" | ||
| exit "${TEST_EXIT}" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "path": "stackrox/opp-smoke/stackrox-opp-smoke-ref.yaml", | ||
| "owners": { | ||
| "approvers": [ | ||
| "cspi-qe-ocp-lp" | ||
| ], | ||
| "reviewers": [ | ||
| "cspi-qe-ocp-lp" | ||
| ] | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.