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 d1a27998e1f04..4187d2fbeaa3e 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: @@ -119,6 +130,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 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..eb75182d74702 --- /dev/null +++ b/ci-operator/step-registry/stackrox/opp-readiness/stackrox-opp-readiness-commands.sh @@ -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." 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..41ea4e4aa5e43 --- /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: 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. 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..4a15bf4ea48a7 --- /dev/null +++ b/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-commands.sh @@ -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 +} + +# --------------------------------------------------------------------------- +# 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}" 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..e3ea4dabbd240 --- /dev/null +++ b/ci-operator/step-registry/stackrox/opp-smoke/stackrox-opp-smoke-ref.yaml @@ -0,0 +1,23 @@ +ref: + as: stackrox-opp-smoke + commands: stackrox-opp-smoke-commands.sh + resources: + requests: + cpu: 2000m + memory: 6Gi + from: acs-smoke-runner + timeout: 1h0m0s + env: + - name: STACKROX_REF + default: "main" + documentation: Git ref (branch/tag) for stackrox/stackrox sparse clone + - name: SCANNER_REF + default: "main" + documentation: Git ref (branch/tag) for stackrox/scanner proto clone + 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.