From 42ac7613d9c6205b7e866d902d327995adf75a78 Mon Sep 17 00:00:00 2001 From: Michael Pruitt Date: Wed, 12 Aug 2026 16:04:19 -0500 Subject: [PATCH 1/4] INTEROP-9411: Replace Quay UI smoke tests with cross-product interop validation The existing quay-tests-quay-interop-test step runs isolated Cypress UI tests (login, org CRUD, repo CRUD) that don't validate cross-product integration. Replace with interop-tests-opp-quay-smoke that validates: 1. Push/pull image via Quay route (Quay + ODF storage serving) 2. ODF PVC backing verification (Quay + ODF integration) 3. ACS scan detection of pushed image (ACS registry watcher) This complements acm-opp-app (which tests build-triggered ACS scanning) by testing ACS registry watcher scanning of independently pushed images. Configs updated: ocp4.22, ocp5.0, ocp5.1 (AWS only; vSphere unchanged). --- ...stron-policy-collection-main__ocp4.22.yaml | 2 +- ...ostron-policy-collection-main__ocp5.0.yaml | 2 +- ...ostron-policy-collection-main__ocp5.1.yaml | 2 +- .../interop-tests/opp-quay-smoke/OWNERS | 4 + .../interop-tests-opp-quay-smoke-commands.sh | 325 ++++++++++++++++++ ...rop-tests-opp-quay-smoke-ref.metadata.json | 11 + .../interop-tests-opp-quay-smoke-ref.yaml | 22 ++ 7 files changed, 365 insertions(+), 3 deletions(-) create mode 100644 ci-operator/step-registry/interop-tests/opp-quay-smoke/OWNERS create mode 100755 ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh create mode 100644 ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-ref.metadata.json create mode 100644 ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-ref.yaml 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..4f1199716a1d2 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 @@ -126,7 +126,7 @@ tests: - ref: acm-fetch-managed-clusters - ref: acm-opp-app - ref: interop-tests-ocs-tests - - ref: quay-tests-quay-interop-test + - ref: interop-tests-opp-quay-smoke - ref: acm-tests-observability - ref: acm-tests-grc - ref: acm-tests-alc diff --git a/ci-operator/config/stolostron/policy-collection/stolostron-policy-collection-main__ocp5.0.yaml b/ci-operator/config/stolostron/policy-collection/stolostron-policy-collection-main__ocp5.0.yaml index 2efc0cc8ee622..a80e72ba1635f 100644 --- a/ci-operator/config/stolostron/policy-collection/stolostron-policy-collection-main__ocp5.0.yaml +++ b/ci-operator/config/stolostron/policy-collection/stolostron-policy-collection-main__ocp5.0.yaml @@ -124,7 +124,7 @@ tests: - ref: acm-fetch-managed-clusters - ref: acm-opp-app - ref: interop-tests-ocs-tests - - ref: quay-tests-quay-interop-test + - ref: interop-tests-opp-quay-smoke - ref: acm-tests-observability - ref: acm-tests-grc - ref: acm-tests-alc diff --git a/ci-operator/config/stolostron/policy-collection/stolostron-policy-collection-main__ocp5.1.yaml b/ci-operator/config/stolostron/policy-collection/stolostron-policy-collection-main__ocp5.1.yaml index 84e618d000c94..2f6e98ffabbd1 100644 --- a/ci-operator/config/stolostron/policy-collection/stolostron-policy-collection-main__ocp5.1.yaml +++ b/ci-operator/config/stolostron/policy-collection/stolostron-policy-collection-main__ocp5.1.yaml @@ -121,7 +121,7 @@ tests: - ref: acm-fetch-managed-clusters - ref: acm-opp-app - ref: interop-tests-ocs-tests - - ref: quay-tests-quay-interop-test + - ref: interop-tests-opp-quay-smoke - ref: acm-tests-observability - ref: acm-tests-grc - ref: acm-tests-alc diff --git a/ci-operator/step-registry/interop-tests/opp-quay-smoke/OWNERS b/ci-operator/step-registry/interop-tests/opp-quay-smoke/OWNERS new file mode 100644 index 0000000000000..0ce20c59fb95d --- /dev/null +++ b/ci-operator/step-registry/interop-tests/opp-quay-smoke/OWNERS @@ -0,0 +1,4 @@ +approvers: +- cspi-qe-ocp-lp +reviewers: +- cspi-qe-ocp-lp diff --git a/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh b/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh new file mode 100755 index 0000000000000..e7df2e73fb1ee --- /dev/null +++ b/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh @@ -0,0 +1,325 @@ +#!/bin/bash +set -euo pipefail +shopt -s inherit_errexit + +ARTIFACT_DIR="${ARTIFACT_DIR:=/tmp/artifacts}" +mkdir -p "${ARTIFACT_DIR}" +JUNIT_FILE="${ARTIFACT_DIR}/junit_quay_interop.xml" +IMAGE_TAG="${BUILD_ID:-$(date +%s)}" + +typeset -A testStatus +typeset -A testDuration +typeset -A testFailureMsg +typeset -a allTests=( + "[sig-interop][Jira:INTEROP][Feature:Quay] Push and pull image via Quay route" + "[sig-interop][Jira:INTEROP][Feature:Quay] Verify ODF PVC backing Quay storage" + "[sig-interop][Jira:INTEROP][Feature:Quay] ACS scan of pushed Quay image" +) + +for t in "${allTests[@]}"; do + testStatus["${t}"]="skipped" + testDuration["${t}"]=0 + testFailureMsg["${t}"]="Test did not run" +done + +typeset -i suiteStart=0 +suiteStart=$(date +%s) + +RecordResult() { + typeset name="${1}"; shift + typeset status="${1}"; shift + typeset msg="${1:-}"; shift || true + typeset dur="${1:-0}"; shift || true + testStatus["${name}"]="${status}" + testDuration["${name}"]="${dur}" + testFailureMsg["${name}"]="${msg}" +} + +# shellcheck disable=SC2329 +GenerateJunit() { + typeset -i total=${#allTests[@]} + typeset -i failures=0 skipped=0 + typeset -i elapsed=$(( $(date +%s) - suiteStart )) + + for t in "${allTests[@]}"; do + [[ "${testStatus[${t}]}" == "failed" ]] && failures=$((failures + 1)) + [[ "${testStatus[${t}]}" == "skipped" ]] && skipped=$((skipped + 1)) + done + + cat > "${JUNIT_FILE}" < + + +EOF + + for t in "${allTests[@]}"; do + typeset escaped_name + escaped_name=$(printf '%s' "${t}" | sed 's/&/\&/g; s//\>/g; s/"/\"/g') + typeset escaped_msg + escaped_msg=$(printf '%s' "${testFailureMsg[${t}]}" | sed 's/&/\&/g; s//\>/g; s/"/\"/g') + + if [[ "${testStatus[${t}]}" == "failed" ]]; then + echo " " >> "${JUNIT_FILE}" + elif [[ "${testStatus[${t}]}" == "skipped" ]]; then + echo " " >> "${JUNIT_FILE}" + else + echo " " >> "${JUNIT_FILE}" + fi + done + + cat >> "${JUNIT_FILE}" < + +EOF + cat "${JUNIT_FILE}" +} + +trap GenerateJunit EXIT + +DiscoverQuay() { + QUAY_NS=$(oc get quayregistry --all-namespaces -o jsonpath='{.items[0].metadata.namespace}') + QUAY_REGISTRY=$(oc get quayregistry -n "${QUAY_NS}" -o jsonpath='{.items[0].metadata.name}') + QUAY_HOST=$(oc get quayregistry -n "${QUAY_NS}" "${QUAY_REGISTRY}" -o jsonpath='{.status.registryEndpoint}') + QUAY_HOST="${QUAY_HOST#https://}" + export QUAY_NS QUAY_REGISTRY QUAY_HOST +} + +GetQuayAuth() { + typeset configSecret + configSecret=$(oc get quayregistry -n "${QUAY_NS}" "${QUAY_REGISTRY}" -o jsonpath='{.spec.configBundleSecret}') + if [[ -z "${configSecret}" ]]; then + configSecret="${QUAY_REGISTRY}-config-bundle" + fi + + QUAY_USER=$(oc get secret -n "${QUAY_NS}" "${configSecret}" -o jsonpath='{.data.SUPER_USER_EMAIL}' 2>/dev/null | base64 -d || echo "") + if [[ -z "${QUAY_USER}" ]]; then + QUAY_USER="quayadmin" + fi + QUAY_PASSWORD=$(oc get secret -n "${QUAY_NS}" "${configSecret}" -o jsonpath='{.data.SUPER_USER_PASSWORD}' 2>/dev/null | base64 -d || echo "") + + if [[ -z "${QUAY_PASSWORD}" ]]; then + typeset initSecret="${QUAY_REGISTRY}-init-config-bundle-secret" + QUAY_PASSWORD=$(oc get secret -n "${QUAY_NS}" "${initSecret}" -o jsonpath='{.data.superuser-password}' 2>/dev/null | base64 -d || echo "") + fi + + if [[ -z "${QUAY_PASSWORD}" ]]; then + for secret in $(oc get secrets -n "${QUAY_NS}" -o jsonpath='{.items[*].metadata.name}' | tr ' ' '\n' | grep -i "quay.*config"); do + QUAY_PASSWORD=$(oc get secret -n "${QUAY_NS}" "${secret}" -o go-template='{{index .data "config.yaml"}}' 2>/dev/null | base64 -d | grep -oP "(?<=SUPER_USER_PASSWORD: ).*" || echo "") + [[ -n "${QUAY_PASSWORD}" ]] && break + done + fi + + export QUAY_USER QUAY_PASSWORD +} + +PreflightCheck() { + if ! curl -sk --connect-timeout 15 "https://${QUAY_HOST}/api/v1/discovery" | grep -qi "quay"; then + echo "ERROR: Quay route not reachable at ${QUAY_HOST}" >&2 + return 1 + fi +} + +CreateTestOrg() { + typeset token + token=$(curl -sk -X POST "https://${QUAY_HOST}/api/v1/signin" \ + -H "Content-Type: application/json" \ + -d "{\"user\":\"${QUAY_USER}\",\"pass\":\"${QUAY_PASSWORD}\"}" | \ + python3 -c "import sys,json; print(json.load(sys.stdin).get('token',''))" 2>/dev/null || echo "") + + if [[ -z "${token}" ]]; then + token=$(curl -sk -H "Authorization: Basic $(echo -n "${QUAY_USER}:${QUAY_PASSWORD}" | base64)" \ + "https://${QUAY_HOST}/api/v1/user/" | \ + python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('token',''))" 2>/dev/null || echo "") + fi + + QUAY_TOKEN="${token}" + export QUAY_TOKEN + + curl -sk -X POST "https://${QUAY_HOST}/api/v1/organization/" \ + -H "Authorization: Bearer ${QUAY_TOKEN}" \ + -H "Content-Type: application/json" \ + -d '{"name":"interop-smoke-test","email":"interop-test@example.com"}' || true +} + +################################################################################ +# Test Case 1: Push and pull image via Quay route +################################################################################ +RunPushPull() { + typeset testName="[sig-interop][Jira:INTEROP][Feature:Quay] Push and pull image via Quay route" + typeset -i start elapsed + start=$(date +%s) + + DiscoverQuay + GetQuayAuth + PreflightCheck || { elapsed=$(( $(date +%s) - start )); RecordResult "${testName}" "failed" "Quay route not reachable" "${elapsed}"; return 1; } + CreateTestOrg + + typeset pushTarget="${QUAY_HOST}/interop-smoke-test/ubi-smoke:${IMAGE_TAG}" + typeset authFile="/tmp/quay-auth.json" + + cat > "${authFile}" <&1; then + elapsed=$(( $(date +%s) - start )) + RecordResult "${testName}" "failed" "skopeo push to Quay failed" "${elapsed}" + return 1 + fi + + if ! skopeo inspect --tls-verify=false \ + --authfile="${authFile}" \ + "docker://${pushTarget}" >/dev/null 2>&1; then + elapsed=$(( $(date +%s) - start )) + RecordResult "${testName}" "failed" "Image not pullable from Quay after push" "${elapsed}" + return 1 + fi + + elapsed=$(( $(date +%s) - start )) + RecordResult "${testName}" "passed" "" "${elapsed}" + return 0 +} + +################################################################################ +# Test Case 2: Verify ODF PVC backing Quay storage +################################################################################ +RunOdfPvcCheck() { + typeset testName="[sig-interop][Jira:INTEROP][Feature:Quay] Verify ODF PVC backing Quay storage" + typeset -i start elapsed + start=$(date +%s) + + typeset pvcCount + pvcCount=$(oc get pvc -n "${QUAY_NS}" -l app=quay -o json 2>/dev/null | python3 -c " +import sys, json +data = json.load(sys.stdin) +items = data.get('items', []) +print(len(items)) +" 2>/dev/null || echo "0") + + if [[ "${pvcCount}" == "0" ]]; then + pvcCount=$(oc get pvc -n "${QUAY_NS}" -o json | python3 -c " +import sys, json +data = json.load(sys.stdin) +items = [i for i in data.get('items', []) if 'quay' in i['metadata'].get('name','').lower()] +print(len(items)) +" 2>/dev/null || echo "0") + fi + + if [[ "${pvcCount}" == "0" ]]; then + elapsed=$(( $(date +%s) - start )) + RecordResult "${testName}" "failed" "No Quay-related PVCs found in ${QUAY_NS}" "${elapsed}" + return 1 + fi + + typeset unboundPvcs + unboundPvcs=$(oc get pvc -n "${QUAY_NS}" -o json | python3 -c " +import sys, json +data = json.load(sys.stdin) +items = [i for i in data.get('items', []) if 'quay' in i['metadata'].get('name','').lower()] +unbound = [i['metadata']['name'] for i in items if i['status'].get('phase') != 'Bound'] +print(' '.join(unbound)) +" 2>/dev/null || echo "") + + if [[ -n "${unboundPvcs}" ]]; then + elapsed=$(( $(date +%s) - start )) + RecordResult "${testName}" "failed" "Unbound PVCs: ${unboundPvcs}" "${elapsed}" + return 1 + fi + + typeset odfBacked + odfBacked=$(oc get pvc -n "${QUAY_NS}" -o json | python3 -c " +import sys, json +data = json.load(sys.stdin) +items = [i for i in data.get('items', []) if 'quay' in i['metadata'].get('name','').lower()] +sc_names = set(i['spec'].get('storageClassName','') for i in items) +odf = any('ocs' in s or 'ceph' in s or 'odf' in s for s in sc_names) +print('true' if odf else 'false') +" 2>/dev/null || echo "false") + + if [[ "${odfBacked}" != "true" ]]; then + elapsed=$(( $(date +%s) - start )) + RecordResult "${testName}" "failed" "Quay PVCs not using ODF/Ceph storage class" "${elapsed}" + return 1 + fi + + elapsed=$(( $(date +%s) - start )) + RecordResult "${testName}" "passed" "" "${elapsed}" + return 0 +} + +################################################################################ +# Test Case 3: ACS scan of pushed Quay image +################################################################################ +RunAcsScan() { + typeset testName="[sig-interop][Jira:INTEROP][Feature:Quay] ACS scan of pushed Quay image" + typeset -i start elapsed + start=$(date +%s) + + typeset acsHost acsPassword + acsHost=$(oc get route -n stackrox central -o jsonpath='{.spec.host}' 2>/dev/null || echo "") + if [[ -z "${acsHost}" ]]; then + elapsed=$(( $(date +%s) - start )) + RecordResult "${testName}" "failed" "ACS Central route not found" "${elapsed}" + return 1 + fi + + acsPassword=$(oc get secret -n stackrox central-htpasswd -o jsonpath='{.data.password}' 2>/dev/null | base64 -d || echo "") + if [[ -z "${acsPassword}" ]]; then + elapsed=$(( $(date +%s) - start )) + RecordResult "${testName}" "failed" "ACS admin password not found" "${elapsed}" + return 1 + fi + + typeset pushTarget="${QUAY_HOST}/interop-smoke-test/ubi-smoke:${IMAGE_TAG}" + typeset -i attempts=0 maxAttempts=20 + + while (( attempts < maxAttempts )); do + typeset scanResult + scanResult=$(curl -sk -u "admin:${acsPassword}" \ + "https://${acsHost}/v1/images?query=Image:${pushTarget}" 2>/dev/null || echo "") + + if echo "${scanResult}" | python3 -c " +import sys, json +data = json.load(sys.stdin) +images = data.get('images', []) +sys.exit(0 if len(images) > 0 else 1) +" 2>/dev/null; then + elapsed=$(( $(date +%s) - start )) + RecordResult "${testName}" "passed" "" "${elapsed}" + return 0 + fi + + attempts=$((attempts + 1)) + sleep 15 + done + + elapsed=$(( $(date +%s) - start )) + RecordResult "${testName}" "failed" "ACS did not detect pushed image within 5 minutes" "${elapsed}" + return 1 +} + +################################################################################ +# Main execution +################################################################################ + +RunPushPull || true +RunOdfPvcCheck || true +RunAcsScan || true + +if [[ "${MAP_TESTS}" == "true" ]]; then + eval "$( + typeset -a _fURL=() + type -t wget 1>/dev/null && _fURL=(wget --timeout=30 -qO-) || _fURL=(curl --connect-timeout 10 --max-time 30 -fsSL) + "${_fURL[@]}" \ + https://raw.githubusercontent.com/RedHatQE/OpenShift-LP-QE--Tools/refs/heads/main/libs/bash/ci-operator/interop/common/ExitTrap--PostProcessPrep.sh + )" || true + if type -t ExitTrap--PostProcessPrep 1>/dev/null; then + LP_IO__ET_PPP__NEW_TS_NAME="${DR__RP__CR_COMP_NAME}--%s" \ + ExitTrap--PostProcessPrep || true + fi +fi + +exit 0 diff --git a/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-ref.metadata.json b/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-ref.metadata.json new file mode 100644 index 0000000000000..d63af8c878633 --- /dev/null +++ b/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-ref.metadata.json @@ -0,0 +1,11 @@ +{ + "path": "interop-tests/opp-quay-smoke/interop-tests-opp-quay-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/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-ref.yaml b/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-ref.yaml new file mode 100644 index 0000000000000..eb2e58149f440 --- /dev/null +++ b/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-ref.yaml @@ -0,0 +1,22 @@ +ref: + as: interop-tests-opp-quay-smoke + from: cli + cli: latest + commands: interop-tests-opp-quay-smoke-commands.sh + timeout: 30m0s + grace_period: 5m0s + resources: + requests: + cpu: 100m + memory: 256Mi + documentation: |- + Validates Quay as a cross-product registry within the OPP bundle. + Tests image push/pull via the Quay route, verifies ODF-backed PVC storage, + and confirms ACS detects and scans the pushed image. + env: + - name: DR__RP__CR_COMP_NAME + default: "lp-interop--Quay" + documentation: Component Readiness component name for junit remapping + - name: MAP_TESTS + default: "false" + documentation: When true, remap junit test suite names for Component Readiness routing From e4618404795e85e67708cb766134472e1728c834 Mon Sep 17 00:00:00 2001 From: Michael Pruitt Date: Fri, 14 Aug 2026 07:29:38 -0500 Subject: [PATCH 2/4] Address review feedback: propagate validation failures Track validation results via status variable instead of discarding with || true. Exit with nonzero when any validation fails. --- .../interop-tests-opp-quay-smoke-commands.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh b/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh index e7df2e73fb1ee..ffde40ed149ba 100755 --- a/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh +++ b/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh @@ -305,9 +305,10 @@ sys.exit(0 if len(images) > 0 else 1) # Main execution ################################################################################ -RunPushPull || true -RunOdfPvcCheck || true -RunAcsScan || true +typeset -i status=0 +RunPushPull || status=1 +RunOdfPvcCheck || status=1 +RunAcsScan || status=1 if [[ "${MAP_TESTS}" == "true" ]]; then eval "$( @@ -322,4 +323,4 @@ if [[ "${MAP_TESTS}" == "true" ]]; then fi fi -exit 0 +exit "${status}" From 4eee7bc5b51ffe5a58bdfba4fca3824d7ec4ed89 Mon Sep 17 00:00:00 2001 From: Michael Pruitt Date: Mon, 17 Aug 2026 08:39:08 -0500 Subject: [PATCH 3/4] fix: Apply mpitt best practices --- .../interop-tests-opp-quay-smoke-commands.sh | 76 ++++++++++--------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh b/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh index ffde40ed149ba..0fb6bc579dd09 100755 --- a/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh +++ b/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh @@ -4,8 +4,8 @@ shopt -s inherit_errexit ARTIFACT_DIR="${ARTIFACT_DIR:=/tmp/artifacts}" mkdir -p "${ARTIFACT_DIR}" -JUNIT_FILE="${ARTIFACT_DIR}/junit_quay_interop.xml" -IMAGE_TAG="${BUILD_ID:-$(date +%s)}" +typeset junitFile="${ARTIFACT_DIR}/junit_quay_interop.xml" +typeset imageTag="${BUILD_ID:-$(date +%s)}" typeset -A testStatus typeset -A testDuration @@ -25,7 +25,7 @@ done typeset -i suiteStart=0 suiteStart=$(date +%s) -RecordResult() { +function RecordResult () { typeset name="${1}"; shift typeset status="${1}"; shift typeset msg="${1:-}"; shift || true @@ -36,7 +36,7 @@ RecordResult() { } # shellcheck disable=SC2329 -GenerateJunit() { +function GenerateJunit () { typeset -i total=${#allTests[@]} typeset -i failures=0 skipped=0 typeset -i elapsed=$(( $(date +%s) - suiteStart )) @@ -46,7 +46,7 @@ GenerateJunit() { [[ "${testStatus[${t}]}" == "skipped" ]] && skipped=$((skipped + 1)) done - cat > "${JUNIT_FILE}" < "${junitFile}" < @@ -59,24 +59,24 @@ EOF escaped_msg=$(printf '%s' "${testFailureMsg[${t}]}" | sed 's/&/\&/g; s//\>/g; s/"/\"/g') if [[ "${testStatus[${t}]}" == "failed" ]]; then - echo " " >> "${JUNIT_FILE}" + echo " " >> "${junitFile}" elif [[ "${testStatus[${t}]}" == "skipped" ]]; then - echo " " >> "${JUNIT_FILE}" + echo " " >> "${junitFile}" else - echo " " >> "${JUNIT_FILE}" + echo " " >> "${junitFile}" fi done - cat >> "${JUNIT_FILE}" <> "${junitFile}" < EOF - cat "${JUNIT_FILE}" + cat "${junitFile}" } trap GenerateJunit EXIT -DiscoverQuay() { +function DiscoverQuay () { QUAY_NS=$(oc get quayregistry --all-namespaces -o jsonpath='{.items[0].metadata.namespace}') QUAY_REGISTRY=$(oc get quayregistry -n "${QUAY_NS}" -o jsonpath='{.items[0].metadata.name}') QUAY_HOST=$(oc get quayregistry -n "${QUAY_NS}" "${QUAY_REGISTRY}" -o jsonpath='{.status.registryEndpoint}') @@ -84,7 +84,7 @@ DiscoverQuay() { export QUAY_NS QUAY_REGISTRY QUAY_HOST } -GetQuayAuth() { +function GetQuayAuth () { typeset configSecret configSecret=$(oc get quayregistry -n "${QUAY_NS}" "${QUAY_REGISTRY}" -o jsonpath='{.spec.configBundleSecret}') if [[ -z "${configSecret}" ]]; then @@ -112,14 +112,14 @@ GetQuayAuth() { export QUAY_USER QUAY_PASSWORD } -PreflightCheck() { +function PreflightCheck () { if ! curl -sk --connect-timeout 15 "https://${QUAY_HOST}/api/v1/discovery" | grep -qi "quay"; then echo "ERROR: Quay route not reachable at ${QUAY_HOST}" >&2 return 1 fi } -CreateTestOrg() { +function CreateTestOrg () { typeset token token=$(curl -sk -X POST "https://${QUAY_HOST}/api/v1/signin" \ -H "Content-Type: application/json" \ @@ -144,7 +144,7 @@ CreateTestOrg() { ################################################################################ # Test Case 1: Push and pull image via Quay route ################################################################################ -RunPushPull() { +function RunPushPull () { typeset testName="[sig-interop][Jira:INTEROP][Feature:Quay] Push and pull image via Quay route" typeset -i start elapsed start=$(date +%s) @@ -154,7 +154,7 @@ RunPushPull() { PreflightCheck || { elapsed=$(( $(date +%s) - start )); RecordResult "${testName}" "failed" "Quay route not reachable" "${elapsed}"; return 1; } CreateTestOrg - typeset pushTarget="${QUAY_HOST}/interop-smoke-test/ubi-smoke:${IMAGE_TAG}" + typeset pushTarget="${QUAY_HOST}/interop-smoke-test/ubi-smoke:${imageTag}" typeset authFile="/tmp/quay-auth.json" cat > "${authFile}" < 0 else 1) # Main execution ################################################################################ -typeset -i status=0 -RunPushPull || status=1 -RunOdfPvcCheck || status=1 -RunAcsScan || status=1 - -if [[ "${MAP_TESTS}" == "true" ]]; then - eval "$( - typeset -a _fURL=() - type -t wget 1>/dev/null && _fURL=(wget --timeout=30 -qO-) || _fURL=(curl --connect-timeout 10 --max-time 30 -fsSL) - "${_fURL[@]}" \ - https://raw.githubusercontent.com/RedHatQE/OpenShift-LP-QE--Tools/refs/heads/main/libs/bash/ci-operator/interop/common/ExitTrap--PostProcessPrep.sh - )" || true - if type -t ExitTrap--PostProcessPrep 1>/dev/null; then - LP_IO__ET_PPP__NEW_TS_NAME="${DR__RP__CR_COMP_NAME}--%s" \ - ExitTrap--PostProcessPrep || true +function Main () { + typeset -i status=0 + RunPushPull || status=1 + RunOdfPvcCheck || status=1 + RunAcsScan || status=1 + + if [[ "${MAP_TESTS}" == "true" ]]; then + eval "$( + typeset -a _fURL=() + type -t wget 1>/dev/null && _fURL=(wget --timeout=30 -qO-) || _fURL=(curl --connect-timeout 10 --max-time 30 -fsSL) + "${_fURL[@]}" \ + https://raw.githubusercontent.com/RedHatQE/OpenShift-LP-QE--Tools/refs/heads/main/libs/bash/ci-operator/interop/common/ExitTrap--PostProcessPrep.sh + )" || true + if type -t ExitTrap--PostProcessPrep 1>/dev/null; then + LP_IO__ET_PPP__NEW_TS_NAME="${DR__RP__CR_COMP_NAME}--%s" \ + ExitTrap--PostProcessPrep || true + fi fi -fi -exit "${status}" + exit "${status}" +} + +Main "$@" From 06f097efb519ef39cef08500da69f3e0f8ea35b0 Mon Sep 17 00:00:00 2001 From: Michael Pruitt Date: Mon, 17 Aug 2026 09:12:02 -0500 Subject: [PATCH 4/4] fix: Hoist Quay discovery to Main, safe JSON encoding, auth cleanup - Move DiscoverQuay/GetQuayAuth/PreflightCheck/CreateTestOrg from RunPushPull into Main so all tests access exported variables regardless of execution order - Use python3 json.dumps for sign-in payload to prevent credential injection via special characters in passwords - Clean up /tmp/quay-auth.json after test execution --- .../interop-tests-opp-quay-smoke-commands.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh b/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh index 0fb6bc579dd09..f726090036915 100755 --- a/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh +++ b/ci-operator/step-registry/interop-tests/opp-quay-smoke/interop-tests-opp-quay-smoke-commands.sh @@ -120,10 +120,12 @@ function PreflightCheck () { } function CreateTestOrg () { + typeset signinPayload + signinPayload=$(python3 -c "import json,sys; print(json.dumps({'user':sys.argv[1],'pass':sys.argv[2]}))" "${QUAY_USER}" "${QUAY_PASSWORD}") typeset token token=$(curl -sk -X POST "https://${QUAY_HOST}/api/v1/signin" \ -H "Content-Type: application/json" \ - -d "{\"user\":\"${QUAY_USER}\",\"pass\":\"${QUAY_PASSWORD}\"}" | \ + -d "${signinPayload}" | \ python3 -c "import sys,json; print(json.load(sys.stdin).get('token',''))" 2>/dev/null || echo "") if [[ -z "${token}" ]]; then @@ -149,11 +151,6 @@ function RunPushPull () { typeset -i start elapsed start=$(date +%s) - DiscoverQuay - GetQuayAuth - PreflightCheck || { elapsed=$(( $(date +%s) - start )); RecordResult "${testName}" "failed" "Quay route not reachable" "${elapsed}"; return 1; } - CreateTestOrg - typeset pushTarget="${QUAY_HOST}/interop-smoke-test/ubi-smoke:${imageTag}" typeset authFile="/tmp/quay-auth.json" @@ -306,11 +303,18 @@ sys.exit(0 if len(images) > 0 else 1) ################################################################################ function Main () { + DiscoverQuay + GetQuayAuth + PreflightCheck || { echo "FATAL: Quay not reachable; skipping all tests" >&2; exit 1; } + CreateTestOrg + typeset -i status=0 RunPushPull || status=1 RunOdfPvcCheck || status=1 RunAcsScan || status=1 + rm -f /tmp/quay-auth.json + if [[ "${MAP_TESTS}" == "true" ]]; then eval "$( typeset -a _fURL=()