From 3022367c77b317d0de0e02525b758483d6ea549f Mon Sep 17 00:00:00 2001 From: Michael Pruitt Date: Thu, 13 Aug 2026 09:13:22 -0500 Subject: [PATCH 1/5] INTEROP-9236: add stackrox-opp-readiness step Readiness gate for ACS Central and SecuredCluster before running SMOKE tests. Discovers namespaces dynamically via CRs, polls Central API health, secured-cluster connectivity, sensor pod readiness (with OOMKilled detection), and default policy count. Writes credentials and connection details to SHARED_DIR. --- .../stackrox/opp-readiness/OWNERS | 4 + .../stackrox-opp-readiness-commands.sh | 206 ++++++++++++++++++ .../stackrox-opp-readiness-ref.metadata.json | 11 + .../stackrox-opp-readiness-ref.yaml | 16 ++ 4 files changed, 237 insertions(+) create mode 100644 ci-operator/step-registry/stackrox/opp-readiness/OWNERS create mode 100755 ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-commands.sh create mode 100644 ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-ref.metadata.json create mode 100644 ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-ref.yaml diff --git a/ci-operator/step-registry/stackrox/opp-readiness/OWNERS b/ci-operator/step-registry/stackrox/opp-readiness/OWNERS new file mode 100644 index 0000000000000..0ce20c59fb95d --- /dev/null +++ b/ci-operator/step-registry/stackrox/opp-readiness/OWNERS @@ -0,0 +1,4 @@ +approvers: +- cspi-qe-ocp-lp +reviewers: +- cspi-qe-ocp-lp diff --git a/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-commands.sh b/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-commands.sh new file mode 100755 index 0000000000000..9a1b827c4b80f --- /dev/null +++ b/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-commands.sh @@ -0,0 +1,206 @@ +#!/bin/bash +set -eux -o 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. +# +# Dependencies: oc, curl, python3 (all present in the `cli` image). +# --------------------------------------------------------------------------- + +if [[ -f "${SHARED_DIR}/kubeconfig" ]]; then + export KUBECONFIG="${SHARED_DIR}/kubeconfig" +fi + +POLL_INTERVAL=30 +TIMEOUT=600 +ELAPSED=0 + +function WaitFor () { + typeset description="$1" + shift + typeset checkFn="$1" + shift + + ELAPSED=0 + echo "[readiness] Waiting for: ${description}" + while true; do + if "${checkFn}" "$@"; 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 + true +} + +function JsonLength () { + python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d.get('$1',[])))" +} + +# --------------------------------------------------------------------------- +# Namespace discovery via CRs (never hardcode) +# --------------------------------------------------------------------------- +function DiscoverCentralNs () { + CENTRAL_NS="$(oc get centrals.platform.stackrox.io --all-namespaces \ + -o jsonpath='{.items[0].metadata.namespace}' 2>/dev/null)" \ + && [[ -n "${CENTRAL_NS}" ]] +} + +function DiscoverScNs () { + 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="" + +WaitFor "Central CR namespace discovery" DiscoverCentralNs +echo "[readiness] Central namespace discovered" + +WaitFor "SecuredCluster CR namespace discovery" DiscoverScNs +echo "[readiness] SecuredCluster namespace discovered" + +# --------------------------------------------------------------------------- +# Check 1: Central route exists +# --------------------------------------------------------------------------- +function CheckCentralRoute () { + oc get route central -n "${CENTRAL_NS}" -o jsonpath='{.spec.host}' 2>/dev/null +} + +WaitFor "Central route" CheckCentralRoute + +set +x +CENTRAL_URL="$(oc get route central -n "${CENTRAL_NS}" -o jsonpath='{.spec.host}')" +set -x +echo "[readiness] Central route discovered" + +# --------------------------------------------------------------------------- +# Extract ROX_ADMIN_PASSWORD before API checks +# --------------------------------------------------------------------------- +echo "[readiness] Extracting ROX_ADMIN_PASSWORD..." +ROX_ADMIN_PASSWORD="" +set +x +ROX_ADMIN_PASSWORD="$(oc get secret -n "${CENTRAL_NS}" central-htpasswd \ + -o jsonpath='{.data.password}' | base64 -d)" +set -x + +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" + +# --------------------------------------------------------------------------- +# Check 2: Central API health (authenticated v1/metadata) +# --------------------------------------------------------------------------- +function CheckCentralApi () { + set +x + typeset httpCode="" + httpCode="$(curl -sk -o /dev/null -w '%{http_code}' \ + -u "admin:${ROX_ADMIN_PASSWORD}" \ + "https://${CENTRAL_URL}/v1/metadata" --max-time 10)" || { set -x; return 1; } + set -x + [[ "${httpCode}" == "200" ]] +} + +WaitFor "Central API health (v1/metadata)" CheckCentralApi + +# --------------------------------------------------------------------------- +# Check 3: At least 1 secured cluster connected +# --------------------------------------------------------------------------- +function CheckClustersConnected () { + set +x + typeset clusterCount="" + clusterCount="$(curl -sk -u "admin:${ROX_ADMIN_PASSWORD}" \ + "https://${CENTRAL_URL}/v1/clusters" --max-time 10 \ + | JsonLength clusters)" || { set -x; return 1; } + set -x + [[ "${clusterCount}" -ge 1 ]] +} + +WaitFor "secured cluster connected (v1/clusters)" CheckClustersConnected + +# --------------------------------------------------------------------------- +# Check 4: Sensor pods Running (detect OOMKilled) +# --------------------------------------------------------------------------- +function CheckSensorPods () { + typeset podCount="" + podCount="$(oc get pods -n "${SC_NS}" -l app=sensor \ + -o json 2>/dev/null | JsonLength items)" || return 1 + if [[ "${podCount}" -eq 0 ]]; then + echo "[readiness] no sensor pods found yet" + return 1 + fi + + typeset sensorJson="" + sensorJson="$(oc get pods -n "${SC_NS}" -l app=sensor -o json 2>/dev/null)" || return 1 + typeset oomContainers="" + if [[ -n "${sensorJson}" ]]; then + oomContainers="$(echo "${sensorJson}" | python3 -c " +import json,sys +d=json.load(sys.stdin) +for pod in d.get('items',[]): + for cs in pod.get('status',{}).get('containerStatuses',[]): + ls=cs.get('lastState',{}).get('terminated',{}) + if ls.get('reason')=='OOMKilled': + print(cs['name']) +")" + fi + if [[ -n "${oomContainers}" ]]; then + echo "[readiness] WARNING: OOMKilled detected in sensor containers: ${oomContainers}" + fi + + typeset podConditions="" + podConditions="$(oc get pods -n "${SC_NS}" -l app=sensor \ + -o jsonpath='{range .items[*]}{.metadata.name}{" "}{range .status.conditions[*]}{.type}={.status}{" "}{end}{"\n"}{end}' 2>/dev/null)" || return 1 + typeset notReady="" + notReady="$(echo "${podConditions}" | while IFS= read -r line; do + [[ -z "${line}" ]] && continue + if ! echo "${line}" | grep -q 'Ready=True'; then + echo "${line%% *}:NotReady" + fi + done)" + [[ -z "${notReady}" ]] +} + +WaitFor "sensor pods Running in ${SC_NS}" CheckSensorPods + +# --------------------------------------------------------------------------- +# Check 5: Default policies loaded (count > 80) +# --------------------------------------------------------------------------- +function CheckPoliciesLoaded () { + set +x + typeset policyCount="" + policyCount="$(curl -sk -u "admin:${ROX_ADMIN_PASSWORD}" \ + "https://${CENTRAL_URL}/v1/policies?query=" --max-time 10 \ + | JsonLength policies)" || { set -x; return 1; } + set -x + echo "[readiness] policy count: ${policyCount}" + [[ "${policyCount}" -gt 80 ]] +} + +WaitFor "default policies loaded (>80)" CheckPoliciesLoaded + +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" +set -x + +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." diff --git a/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-ref.metadata.json b/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-ref.metadata.json new file mode 100644 index 0000000000000..cb61d5b78e49a --- /dev/null +++ b/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-ref.metadata.json @@ -0,0 +1,11 @@ +{ + "path": "stackrox/opp-readiness/stackrox-opp-readiness-ref.yaml", + "owners": { + "approvers": [ + "cspi-qe-ocp-lp" + ], + "reviewers": [ + "cspi-qe-ocp-lp" + ] + } +} \ No newline at end of file diff --git a/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-ref.yaml b/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-ref.yaml new file mode 100644 index 0000000000000..08a3b381984ad --- /dev/null +++ b/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-ref.yaml @@ -0,0 +1,16 @@ +ref: + as: stackrox-opp-readiness + commands: stackrox-opp-readiness-commands.sh + resources: + requests: + cpu: 100m + memory: 200Mi + from: cli + timeout: 1h15m0s + 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. From e5db999132e777371a586f072929dee63312aaa6 Mon Sep 17 00:00:00 2001 From: Michael Pruitt Date: Thu, 13 Aug 2026 09:13:30 -0500 Subject: [PATCH 2/5] INTEROP-9236: add stackrox-opp-smoke step Runs the ACS qa-tests-backend SMOKE suite against a live ACS instance. Sparse-clones stackrox/stackrox and stackrox/scanner, materializes proto symlinks, injects Gradle init script for Gradle 9 task dependency, patches DEFAULT_CLUSTER_NAME for OPP cluster naming, and exports required credentials from Vault. JUnit XML results are copied to ARTIFACT_DIR. --- .../step-registry/stackrox/opp-smoke/OWNERS | 4 + .../opp-smoke/stackrox-opp-smoke-commands.sh | 105 ++++++++++++++++++ .../stackrox-opp-smoke-ref.metadata.json | 11 ++ .../opp-smoke/stackrox-opp-smoke-ref.yaml | 20 ++++ 4 files changed, 140 insertions(+) create mode 100644 ci-operator/step-registry/stackrox/opp-smoke/OWNERS create mode 100755 ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh create mode 100644 ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-ref.metadata.json create mode 100644 ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-ref.yaml diff --git a/ci-operator/step-registry/stackrox/opp-smoke/OWNERS b/ci-operator/step-registry/stackrox/opp-smoke/OWNERS new file mode 100644 index 0000000000000..0ce20c59fb95d --- /dev/null +++ b/ci-operator/step-registry/stackrox/opp-smoke/OWNERS @@ -0,0 +1,4 @@ +approvers: +- cspi-qe-ocp-lp +reviewers: +- cspi-qe-ocp-lp diff --git a/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh b/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh new file mode 100755 index 0000000000000..7e6852ff407b6 --- /dev/null +++ b/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh @@ -0,0 +1,105 @@ +#!/bin/bash +set -eux -o pipefail + +if [[ -f "${SHARED_DIR}/kubeconfig" ]]; then + export KUBECONFIG="${SHARED_DIR}/kubeconfig" +fi + +echo "[smoke] Reading connection details from SHARED_DIR..." + +set +x +CENTRAL_URL="$(cat "${SHARED_DIR}/CENTRAL_URL")" +ROX_ADMIN_PASSWORD="$(cat "${SHARED_DIR}/ROX_ADMIN_PASSWORD")" +set -x + +echo "[smoke] Connection details loaded from SHARED_DIR" + +STACKROX_REF="${STACKROX_REF:-master}" +SCANNER_REF="${SCANNER_REF:-master}" + +echo "[smoke] Sparse-cloning stackrox/stackrox..." +cd /tmp +rm -rf stackrox scanner +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/ + +echo "[smoke] Fetching scanner protos..." +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 + +echo "[smoke] Materializing proto sources (replace symlinks with copies)..." +cd /tmp/stackrox/qa-tests-backend/src/main/proto +for link in api internalapi storage test tools; do + if [[ -L "${link}" ]]; then + target="$(readlink -f "${link}")" + rm "${link}" + cp -r "${target}" "${link}" + fi +done + +echo "[smoke] Patching DEFAULT_CLUSTER_NAME to 'local-cluster'..." +sed -i 's/DEFAULT_CLUSTER_NAME = "remote"/DEFAULT_CLUSTER_NAME = "local-cluster"/' \ + /tmp/stackrox/qa-tests-backend/src/main/groovy/services/ClusterService.groovy +grep -q 'DEFAULT_CLUSTER_NAME = "local-cluster"' \ + /tmp/stackrox/qa-tests-backend/src/main/groovy/services/ClusterService.groovy \ + || { echo "[smoke] FATAL: DEFAULT_CLUSTER_NAME patch failed"; exit 1; } + +set +x +export API_HOSTNAME="${CENTRAL_URL}" +export API_PORT="443" +export ROX_USERNAME="admin" +export ROX_ADMIN_PASSWORD +export CLUSTER="OPENSHIFT" +export CI="true" +export POD_SECURITY_POLICIES="false" +export TEST_TARGET="smoke-test" +REGISTRY_USERNAME="$(cat /tmp/vault/stackrox-stackrox-e2e-tests/QUAY_RHACS_ENG_RO_USERNAME)" +export REGISTRY_USERNAME +REGISTRY_PASSWORD="$(cat /tmp/vault/stackrox-stackrox-e2e-tests/QUAY_RHACS_ENG_RO_PASSWORD)" +export REGISTRY_PASSWORD +if [[ -f /tmp/vault/stackrox-stackrox-e2e-tests/GOOGLE_CREDENTIALS_GCR_SCANNER_V2 ]]; then + GOOGLE_CREDENTIALS_GCR_SCANNER_V2="$(cat /tmp/vault/stackrox-stackrox-e2e-tests/GOOGLE_CREDENTIALS_GCR_SCANNER_V2)" + export GOOGLE_CREDENTIALS_GCR_SCANNER_V2 +fi +if [[ -f /tmp/vault/stackrox-stackrox-e2e-tests/GOOGLE_ARTIFACT_REGISTRY_SERVICE_ACCOUNT_V2 ]]; then + GOOGLE_ARTIFACT_REGISTRY_SERVICE_ACCOUNT_V2="$(cat /tmp/vault/stackrox-stackrox-e2e-tests/GOOGLE_ARTIFACT_REGISTRY_SERVICE_ACCOUNT_V2)" + export GOOGLE_ARTIFACT_REGISTRY_SERVICE_ACCOUNT_V2 +fi +set -x + +cd /tmp/stackrox/qa-tests-backend + +cat > /tmp/fix-proto-deps.gradle <<'INIT' +allprojects { + afterEvaluate { + tasks.matching { it.name == 'compileGroovy' }.configureEach { + dependsOn tasks.matching { it.name == 'generateProto' } + } + } +} +INIT + +echo "[smoke] Running testSMOKE..." +TEST_EXIT=0 +./gradlew testSMOKE --no-daemon --init-script /tmp/fix-proto-deps.gradle \ + -Dorg.gradle.jvmargs="-Xmx2g" || TEST_EXIT=$? + +echo "[smoke] Copying JUnit results to ARTIFACT_DIR..." +if [[ -d build/test-results/testSMOKE ]]; then + find build/test-results/testSMOKE -name '*.xml' -exec cp -v {} "${ARTIFACT_DIR}/" \; +fi + +if [[ -d build/reports/tests/testSMOKE ]]; then + mkdir -p "${ARTIFACT_DIR}/smoke-report" + find build/reports/tests/testSMOKE -mindepth 1 -maxdepth 1 \ + -exec cp -r {} "${ARTIFACT_DIR}/smoke-report/" \; +fi + +echo "[smoke] Test run finished with exit code: ${TEST_EXIT}" +exit "${TEST_EXIT}" diff --git a/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-ref.metadata.json b/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-ref.metadata.json new file mode 100644 index 0000000000000..0b518035376be --- /dev/null +++ b/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-ref.metadata.json @@ -0,0 +1,11 @@ +{ + "path": "stackrox/opp-smoke/stackrox-opp-smoke-ref.yaml", + "owners": { + "approvers": [ + "cspi-qe-ocp-lp" + ], + "reviewers": [ + "cspi-qe-ocp-lp" + ] + } +} \ No newline at end of file diff --git a/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-ref.yaml b/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-ref.yaml new file mode 100644 index 0000000000000..74d11f0815aa9 --- /dev/null +++ b/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-ref.yaml @@ -0,0 +1,20 @@ +ref: + as: stackrox-opp-smoke + commands: stackrox-opp-smoke-commands.sh + credentials: + - mount_path: /tmp/vault/stackrox-stackrox-e2e-tests + name: stackrox-stackrox-e2e-tests + namespace: test-credentials + resources: + requests: + cpu: 2000m + memory: 4Gi + from: acs-smoke-runner + timeout: 1h0m0s + documentation: |- + Run the ACS qa-tests-backend SMOKE suite against a live ACS + instance. Reads connection credentials from SHARED_DIR + (written by stackrox-opp-readiness). Sparse-clones the + stackrox/stackrox and stackrox/scanner repos, then executes + ./gradlew testSMOKE. JUnit XML results are copied to + ARTIFACT_DIR for Prow / Sippy consumption. From a603e286e84a1d757152c8ea0f278f2957510247 Mon Sep 17 00:00:00 2001 From: Michael Pruitt Date: Thu, 13 Aug 2026 09:13:36 -0500 Subject: [PATCH 3/5] INTEROP-9236: wire ACS steps into OPP 4.22 interop config Add acs-smoke-runner image (UBI9/openjdk-17 + git + oc) and insert stackrox-opp-readiness and stackrox-opp-smoke refs into the interop-opp-aws test chain. --- .../stolostron-policy-collection-main__ocp4.22.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ci-operator/config/stolostron/policy-collection/stolostron-policy-collection-main__ocp4.22.yaml b/ci-operator/config/stolostron/policy-collection/stolostron-policy-collection-main__ocp4.22.yaml index 7c648df07abcf..bab1bf951aa08 100644 --- a/ci-operator/config/stolostron/policy-collection/stolostron-policy-collection-main__ocp4.22.yaml +++ b/ci-operator/config/stolostron/policy-collection/stolostron-policy-collection-main__ocp4.22.yaml @@ -48,6 +48,17 @@ images: from: cli optional: true to: cli-with-git + - dockerfile_literal: | + FROM registry.access.redhat.com/ubi9/openjdk-17:1.21 + USER root + RUN microdnf install -y git && microdnf clean all + RUN cd /tmp \ + && curl -sLO https://mirror.openshift.com/pub/openshift-v4/clients/ocp/stable/openshift-client-linux.tar.gz \ + && curl -sL https://mirror.openshift.com/pub/openshift-v4/clients/ocp/stable/sha256sum.txt | grep openshift-client-linux.tar.gz | sha256sum -c - \ + && tar xzf openshift-client-linux.tar.gz -C /usr/local/bin oc kubectl \ + && rm -f openshift-client-linux.tar.gz + USER 1001 + to: acs-smoke-runner releases: latest: candidate: @@ -122,6 +133,8 @@ tests: - ref: acm-policies-openshift-plus-setup - ref: acm-policies-openshift-plus - chain: cucushift-installer-check-cluster-health + - ref: stackrox-opp-readiness + - ref: stackrox-opp-smoke - ref: acm-tests-clc-create - ref: acm-fetch-managed-clusters - ref: acm-opp-app From 198c8a76dafd14c7431a323b393974caa16ac31c Mon Sep 17 00:00:00 2001 From: Michael Pruitt Date: Thu, 13 Aug 2026 14:15:39 -0500 Subject: [PATCH 4/5] INTEROP-9236: treat test failures as informational when results exist The SMOKE step now exits 0 if tests actually ran and JUnit results were captured, even if some tests failed. Individual test failures (e.g. scanner timing issues in fresh clusters) are reported via JUnit XML artifacts for Sippy/Firewatch consumption rather than hard-failing the entire interop pipeline. --- .../stackrox/opp-smoke/stackrox-opp-smoke-commands.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh b/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh index 7e6852ff407b6..32a9a272402bc 100755 --- a/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh +++ b/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh @@ -102,4 +102,12 @@ if [[ -d build/reports/tests/testSMOKE ]]; then fi echo "[smoke] Test run finished with exit code: ${TEST_EXIT}" +if [[ "${TEST_EXIT}" -ne 0 ]] && [[ -d build/test-results/testSMOKE ]]; then + TOTAL="$(find build/test-results/testSMOKE -name '*.xml' -exec grep -l 'testcase' {} \; | wc -l)" + if [[ "${TOTAL}" -gt 0 ]]; then + echo "[smoke] Tests executed and results captured; treating as informational (exit 0)." + echo "[smoke] Review JUnit XML in ARTIFACT_DIR for individual test failures." + exit 0 + fi +fi exit "${TEST_EXIT}" From 670b5ba6cc86a33f82395e61105a9572636a6c0f Mon Sep 17 00:00:00 2001 From: Michael Pruitt Date: Mon, 17 Aug 2026 08:39:08 -0500 Subject: [PATCH 5/5] fix: Apply mpitt best practices --- .../stackrox-opp-readiness-commands.sh | 81 ++++++++++--------- .../opp-smoke/stackrox-opp-smoke-commands.sh | 16 ++-- 2 files changed, 51 insertions(+), 46 deletions(-) diff --git a/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-commands.sh b/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-commands.sh index 9a1b827c4b80f..5a78660adc402 100755 --- a/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-commands.sh +++ b/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-commands.sh @@ -1,5 +1,6 @@ #!/bin/bash set -eux -o pipefail +shopt -s inherit_errexit # --------------------------------------------------------------------------- # ACS OPP Readiness Gate @@ -16,9 +17,9 @@ if [[ -f "${SHARED_DIR}/kubeconfig" ]]; then export KUBECONFIG="${SHARED_DIR}/kubeconfig" fi -POLL_INTERVAL=30 -TIMEOUT=600 -ELAPSED=0 +typeset -i pollInterval=30 +typeset -i timeout=600 +typeset -i elapsed=0 function WaitFor () { typeset description="$1" @@ -26,20 +27,20 @@ function WaitFor () { typeset checkFn="$1" shift - ELAPSED=0 + elapsed=0 echo "[readiness] Waiting for: ${description}" while true; do if "${checkFn}" "$@"; 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}" + elapsed=$((elapsed + pollInterval)) + 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}" + echo "[readiness] ...retrying in ${pollInterval}s (${elapsed}/${timeout}s)" + sleep "${pollInterval}" done true } @@ -52,55 +53,57 @@ function JsonLength () { # Namespace discovery via CRs (never hardcode) # --------------------------------------------------------------------------- function DiscoverCentralNs () { - CENTRAL_NS="$(oc get centrals.platform.stackrox.io --all-namespaces \ + centralNs="$(oc get centrals.platform.stackrox.io --all-namespaces \ -o jsonpath='{.items[0].metadata.namespace}' 2>/dev/null)" \ - && [[ -n "${CENTRAL_NS}" ]] + && [[ -n "${centralNs}" ]] } function DiscoverScNs () { - SC_NS="$(oc get securedclusters.platform.stackrox.io --all-namespaces \ + scNs="$(oc get securedclusters.platform.stackrox.io --all-namespaces \ -o jsonpath='{.items[0].metadata.namespace}' 2>/dev/null)" \ - && [[ -n "${SC_NS}" ]] + && [[ -n "${scNs}" ]] } -CENTRAL_NS="" -SC_NS="" +typeset centralNs="" +typeset scNs="" WaitFor "Central CR namespace discovery" DiscoverCentralNs -echo "[readiness] Central namespace discovered" +echo "[readiness] Central namespace: ${centralNs}" WaitFor "SecuredCluster CR namespace discovery" DiscoverScNs -echo "[readiness] SecuredCluster namespace discovered" +echo "[readiness] SecuredCluster namespace: ${scNs}" # --------------------------------------------------------------------------- # Check 1: Central route exists # --------------------------------------------------------------------------- +typeset centralUrl="" + function CheckCentralRoute () { - oc get route central -n "${CENTRAL_NS}" -o jsonpath='{.spec.host}' 2>/dev/null + oc get route central -n "${centralNs}" -o jsonpath='{.spec.host}' 2>/dev/null } WaitFor "Central route" CheckCentralRoute set +x -CENTRAL_URL="$(oc get route central -n "${CENTRAL_NS}" -o jsonpath='{.spec.host}')" +centralUrl="$(oc get route central -n "${centralNs}" -o jsonpath='{.spec.host}')" set -x echo "[readiness] Central route discovered" # --------------------------------------------------------------------------- # Extract ROX_ADMIN_PASSWORD before API checks # --------------------------------------------------------------------------- -echo "[readiness] Extracting ROX_ADMIN_PASSWORD..." -ROX_ADMIN_PASSWORD="" +typeset roxAdminPassword="" +echo "[readiness] Extracting roxAdminPassword..." set +x -ROX_ADMIN_PASSWORD="$(oc get secret -n "${CENTRAL_NS}" central-htpasswd \ +roxAdminPassword="$(oc get secret -n "${centralNs}" central-htpasswd \ -o jsonpath='{.data.password}' | base64 -d)" set -x -if [[ -z "${ROX_ADMIN_PASSWORD}" ]]; then - echo "[readiness] FATAL: could not extract ROX_ADMIN_PASSWORD" +if [[ -z "${roxAdminPassword}" ]]; then + echo "[readiness] FATAL: could not extract roxAdminPassword" exit 1 fi -echo "[readiness] ROX_ADMIN_PASSWORD extracted successfully" +echo "[readiness] roxAdminPassword extracted successfully" # --------------------------------------------------------------------------- # Check 2: Central API health (authenticated v1/metadata) @@ -109,8 +112,8 @@ function CheckCentralApi () { set +x typeset httpCode="" httpCode="$(curl -sk -o /dev/null -w '%{http_code}' \ - -u "admin:${ROX_ADMIN_PASSWORD}" \ - "https://${CENTRAL_URL}/v1/metadata" --max-time 10)" || { set -x; return 1; } + -u "admin:${roxAdminPassword}" \ + "https://${centralUrl}/v1/metadata" --max-time 10)" || { set -x; return 1; } set -x [[ "${httpCode}" == "200" ]] } @@ -123,8 +126,8 @@ WaitFor "Central API health (v1/metadata)" CheckCentralApi function CheckClustersConnected () { set +x typeset clusterCount="" - clusterCount="$(curl -sk -u "admin:${ROX_ADMIN_PASSWORD}" \ - "https://${CENTRAL_URL}/v1/clusters" --max-time 10 \ + clusterCount="$(curl -sk -u "admin:${roxAdminPassword}" \ + "https://${centralUrl}/v1/clusters" --max-time 10 \ | JsonLength clusters)" || { set -x; return 1; } set -x [[ "${clusterCount}" -ge 1 ]] @@ -137,7 +140,7 @@ WaitFor "secured cluster connected (v1/clusters)" CheckClustersConnected # --------------------------------------------------------------------------- function CheckSensorPods () { typeset podCount="" - podCount="$(oc get pods -n "${SC_NS}" -l app=sensor \ + podCount="$(oc get pods -n "${scNs}" -l app=sensor \ -o json 2>/dev/null | JsonLength items)" || return 1 if [[ "${podCount}" -eq 0 ]]; then echo "[readiness] no sensor pods found yet" @@ -145,7 +148,7 @@ function CheckSensorPods () { fi typeset sensorJson="" - sensorJson="$(oc get pods -n "${SC_NS}" -l app=sensor -o json 2>/dev/null)" || return 1 + sensorJson="$(oc get pods -n "${scNs}" -l app=sensor -o json 2>/dev/null)" || return 1 typeset oomContainers="" if [[ -n "${sensorJson}" ]]; then oomContainers="$(echo "${sensorJson}" | python3 -c " @@ -163,7 +166,7 @@ for pod in d.get('items',[]): fi typeset podConditions="" - podConditions="$(oc get pods -n "${SC_NS}" -l app=sensor \ + podConditions="$(oc get pods -n "${scNs}" -l app=sensor \ -o jsonpath='{range .items[*]}{.metadata.name}{" "}{range .status.conditions[*]}{.type}={.status}{" "}{end}{"\n"}{end}' 2>/dev/null)" || return 1 typeset notReady="" notReady="$(echo "${podConditions}" | while IFS= read -r line; do @@ -175,7 +178,7 @@ for pod in d.get('items',[]): [[ -z "${notReady}" ]] } -WaitFor "sensor pods Running in ${SC_NS}" CheckSensorPods +WaitFor "sensor pods Running in ${scNs}" CheckSensorPods # --------------------------------------------------------------------------- # Check 5: Default policies loaded (count > 80) @@ -183,8 +186,8 @@ WaitFor "sensor pods Running in ${SC_NS}" CheckSensorPods function CheckPoliciesLoaded () { set +x typeset policyCount="" - policyCount="$(curl -sk -u "admin:${ROX_ADMIN_PASSWORD}" \ - "https://${CENTRAL_URL}/v1/policies?query=" --max-time 10 \ + policyCount="$(curl -sk -u "admin:${roxAdminPassword}" \ + "https://${centralUrl}/v1/policies?query=" --max-time 10 \ | JsonLength policies)" || { set -x; return 1; } set -x echo "[readiness] policy count: ${policyCount}" @@ -196,11 +199,11 @@ WaitFor "default policies loaded (>80)" CheckPoliciesLoaded 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 "${roxAdminPassword}" > "${SHARED_DIR}/ROX_ADMIN_PASSWORD" +echo "${centralUrl}" > "${SHARED_DIR}/CENTRAL_URL" set -x -echo "${CENTRAL_NS}" > "${SHARED_DIR}/CENTRAL_NS" -echo "${SC_NS}" > "${SHARED_DIR}/SC_NS" +echo "${centralNs}" > "${SHARED_DIR}/CENTRAL_NS" +echo "${scNs}" > "${SHARED_DIR}/SC_NS" echo "[readiness] All checks passed. ACS is ready for SMOKE tests." diff --git a/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh b/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh index 32a9a272402bc..3907a7d723dab 100755 --- a/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh +++ b/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh @@ -1,5 +1,6 @@ #!/bin/bash set -eux -o pipefail +shopt -s inherit_errexit if [[ -f "${SHARED_DIR}/kubeconfig" ]]; then export KUBECONFIG="${SHARED_DIR}/kubeconfig" @@ -86,9 +87,9 @@ allprojects { INIT echo "[smoke] Running testSMOKE..." -TEST_EXIT=0 +typeset -i testExit=0 ./gradlew testSMOKE --no-daemon --init-script /tmp/fix-proto-deps.gradle \ - -Dorg.gradle.jvmargs="-Xmx2g" || TEST_EXIT=$? + -Dorg.gradle.jvmargs="-Xmx2g" || testExit=$? echo "[smoke] Copying JUnit results to ARTIFACT_DIR..." if [[ -d build/test-results/testSMOKE ]]; then @@ -101,13 +102,14 @@ if [[ -d build/reports/tests/testSMOKE ]]; then -exec cp -r {} "${ARTIFACT_DIR}/smoke-report/" \; fi -echo "[smoke] Test run finished with exit code: ${TEST_EXIT}" -if [[ "${TEST_EXIT}" -ne 0 ]] && [[ -d build/test-results/testSMOKE ]]; then - TOTAL="$(find build/test-results/testSMOKE -name '*.xml' -exec grep -l 'testcase' {} \; | wc -l)" - if [[ "${TOTAL}" -gt 0 ]]; then +echo "[smoke] Test run finished with exit code: ${testExit}" +if [[ "${testExit}" -ne 0 ]] && [[ -d build/test-results/testSMOKE ]]; then + typeset total="" + total="$(find build/test-results/testSMOKE -name '*.xml' -exec grep -l 'testcase' {} \; | wc -l)" + if [[ "${total}" -gt 0 ]]; then echo "[smoke] Tests executed and results captured; treating as informational (exit 0)." echo "[smoke] Review JUnit XML in ARTIFACT_DIR for individual test failures." exit 0 fi fi -exit "${TEST_EXIT}" +exit "${testExit}"