From b01a80073b475f11f388af7dac9a1d7a5fb69899 Mon Sep 17 00:00:00 2001 From: Chai Bot Date: Mon, 21 Sep 2026 00:51:43 +0000 Subject: [PATCH 1/4] INTEROP-9511: MCO readiness poll loop + verbose log cleanup for OPP interop steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P2 — MCO Readiness Poll Loop: Replace the single-shot CheckMcoReady() check with a retry poll loop (24 attempts × 30s = ~720s budget, matching upstream ~700s). Each iteration queries the MCO CR conditions via jsonpath and logs structured progress. Bump the observability-odf step timeout from 10m to 15m to accommodate the poll. P3 — Verbose Log Cleanup: Gate `set -x` behind a DEBUG environment variable (default: "false") across all 14 OPP interop step scripts. When DEBUG is not "true", scripts run without shell tracing, producing clean CI logs. Existing credential-masking patterns (_wasTracing, xtraceOn/xtraceOff) are preserved. Unconditional `set -x` restore lines after credential handling are also gated on DEBUG. Add structured `echo ">>> PHASE: ..."` markers at major transitions in all modified scripts for log navigation without tracing. Add the DEBUG env var (default: "false") to all 14 corresponding ref YAMLs. Co-Authored-By: Claude Opus 4.6 --- .../interop-tests-deploy-odf-commands.sh | 1 + .../interop-tests-deploy-odf-ref.yaml | 5 +- .../interop-tests-ocs-tests-commands.sh | 5 +- .../interop-tests-ocs-tests-ref.yaml | 3 + .../interop-tests-opp-quay-smoke-commands.sh | 3 +- .../interop-tests-opp-quay-smoke-ref.yaml | 3 + .../opp/backup/interop-opp-backup-commands.sh | 15 +-- .../opp/backup/interop-opp-backup-ref.yaml | 3 + .../interop-opp-observability-odf-commands.sh | 93 ++++++++++++------- .../interop-opp-observability-odf-ref.yaml | 5 +- .../interop-opp-odf-health-commands.sh | 29 +++--- .../interop-opp-odf-health-ref.yaml | 3 + .../interop-opp-preflight-commands.sh | 17 ++-- .../preflight/interop-opp-preflight-ref.yaml | 3 + ...nterop-opp-product-upgrade-acm-commands.sh | 3 +- .../interop-opp-product-upgrade-acm-ref.yaml | 3 + ...nterop-opp-product-upgrade-acs-commands.sh | 3 +- .../interop-opp-product-upgrade-acs-ref.yaml | 3 + ...nterop-opp-product-upgrade-odf-commands.sh | 3 +- .../interop-opp-product-upgrade-odf-ref.yaml | 3 + ...terop-opp-product-upgrade-quay-commands.sh | 3 +- .../interop-opp-product-upgrade-quay-ref.yaml | 3 + .../opp/smoke/interop-opp-smoke-commands.sh | 15 +-- .../opp/smoke/interop-opp-smoke-ref.yaml | 4 + .../upgrade/interop-opp-upgrade-commands.sh | 14 ++- .../opp/upgrade/interop-opp-upgrade-ref.yaml | 3 + .../wait-mcp/interop-opp-wait-mcp-commands.sh | 3 +- .../wait-mcp/interop-opp-wait-mcp-ref.yaml | 3 + 28 files changed, 174 insertions(+), 80 deletions(-) diff --git a/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-commands.sh b/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-commands.sh index 82a36317e492c..90e8d035f8783 100644 --- a/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-commands.sh +++ b/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-commands.sh @@ -3,6 +3,7 @@ set -o nounset set -o errexit set -o pipefail +[[ "${DEBUG:-false}" == "true" ]] && set -x ODF_INSTALL_NAMESPACE=openshift-storage DEFAULT_ODF_OPERATOR_CHANNEL="stable-${ODF_VERSION_MAJOR_MINOR}" diff --git a/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-ref.yaml b/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-ref.yaml index 7885f4c3504ca..f4f74b7597de6 100644 --- a/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-ref.yaml +++ b/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-ref.yaml @@ -31,4 +31,7 @@ ref: default: "ocs-storagecluster" - name: ODF_VOLUME_SIZE documentation: The size of the ODF volume in Gi - default: "50" \ No newline at end of file + default: "50" + - name: DEBUG + default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging \ No newline at end of file diff --git a/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-commands.sh b/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-commands.sh index 98e05e4fceb76..3e7e477bfc705 100644 --- a/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-commands.sh +++ b/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-commands.sh @@ -1,7 +1,8 @@ #!/bin/bash -set -euxo pipefail +set -euo pipefail shopt -s inherit_errexit +[[ "${DEBUG:-false}" == "true" ]] && set -x CLUSTER_VERSION=$(oc get clusterVersion version -o jsonpath='{$.status.desired.version}') OCP_MAJOR_MINOR=$(echo "${CLUSTER_VERSION}" | cut -d '.' -f1,2) @@ -113,7 +114,7 @@ if [[ -f "${SHARED_DIR}/vsphere_context.sh" ]]; then set +x source "${SHARED_DIR}/vsphere_context.sh" source "${SHARED_DIR}/govc.sh" - set -x + [[ "${DEBUG:-false}" == "true" ]] && set -x cat >> "${LOGS_CONFIG}" << __APPENDED_ENV_DATA__ ENV_DATA: diff --git a/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-ref.yaml b/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-ref.yaml index 60148ddb444bc..275ff4ef76b4b 100644 --- a/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-ref.yaml +++ b/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-ref.yaml @@ -24,3 +24,6 @@ ref: - name: DISABLE_ENVIRONMENT_CHECKER default: "false" documentation: If true, skip the OCS environment checker during teardown + - name: DEBUG + default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging 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 46e61e1e64c23..0a853ead43e7c 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 @@ -1,5 +1,6 @@ #!/bin/bash -set -euxo pipefail; shopt -s inherit_errexit +set -euo pipefail; shopt -s inherit_errexit +[[ "${DEBUG:-false}" == "true" ]] && set -x ARTIFACT_DIR="${ARTIFACT_DIR:=/tmp/artifacts}" mkdir -p "${ARTIFACT_DIR}" 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 index 354fca9368380..3a55d8a77d0ab 100644 --- 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 @@ -22,3 +22,6 @@ ref: - name: MAP_TESTS default: "false" documentation: When true, remap junit test suite names for Component Readiness routing + - name: DEBUG + default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging diff --git a/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-commands.sh b/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-commands.sh index a5ea8129d32f3..27194b4cebf47 100644 --- a/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-commands.sh +++ b/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-commands.sh @@ -1,5 +1,6 @@ #!/bin/bash -set -euxo pipefail +set -euo pipefail +[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit # NOTE: BACKUP_TIMEOUT and OPP_OPERATORS are set via step config YAML (naming deviates from OPP__ convention) @@ -46,12 +47,12 @@ typeset timeoutPid=$! trap 'kill ${timeoutPid} || true' EXIT trap 'kill ${timeoutPid} || true; exit 124' TERM -: "=== Pre-Upgrade Cluster Backup ===" +echo ">>> PHASE: Pre-Upgrade Cluster Backup" : "Start time: $(date '+%F %T')" : "Backup timeout: ${BACKUP_TIMEOUT}s" # --- Etcd snapshot --- -: "--- Etcd Snapshot ---" +echo ">>> PHASE: Etcd Snapshot" typeset controlPlaneNode="" controlPlaneNode=$(oc get nodes -l node-role.kubernetes.io/master="" -o jsonpath='{.items[0].metadata.name}') || true if [[ -n "${controlPlaneNode}" ]]; then @@ -104,7 +105,7 @@ else fi # --- Control plane resource state --- -: "--- Control Plane State ---" +echo ">>> PHASE: Control Plane State" Capture "ClusterVersion" "${backupDir}/clusterversion.yaml" \ oc get clusterversion version -o yaml @@ -118,7 +119,7 @@ Capture "MachineConfigPools" "${backupDir}/machineconfigpools.yaml" \ oc get machineconfigpools -o yaml # --- OPP operator state --- -: "--- OPP Operator State ---" +echo ">>> PHASE: OPP Operator State" Capture "CSVs" "${backupDir}/csvs.yaml" \ oc get csv -A -o yaml @@ -129,7 +130,7 @@ Capture "InstallPlans" "${backupDir}/installplans.yaml" \ oc get installplans -A -o yaml # --- Backup manifest --- -: "--- Generating Backup Manifest ---" +echo ">>> PHASE: Generating Backup Manifest" typeset clusterVersion="" clusterVersion=$(oc get clusterversion version -o jsonpath='{.status.desired.version}') || true @@ -160,7 +161,7 @@ EOF : "OK: backup-manifest.json" # --- Summary --- -: "=== Backup Summary ===" +echo ">>> PHASE: Backup Summary" : "End time: $(date '+%F %T')" : "Cluster version: ${clusterVersion:-unknown}" : "Node count: ${nodeCount}" diff --git a/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-ref.yaml b/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-ref.yaml index ee690cd22c00e..7a9884039282b 100644 --- a/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-ref.yaml +++ b/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-ref.yaml @@ -14,6 +14,9 @@ ref: - default: "advanced-cluster-management,rhacs-operator,odf-operator,quay-operator" documentation: Comma-separated CSV name prefixes for OPP operators to include in backup state capture name: OPP_OPERATORS + - default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging + name: DEBUG from: cli grace_period: 1m resources: diff --git a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh index 10f0f11e5e3cb..39110acb4c88b 100755 --- a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh +++ b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh @@ -1,5 +1,6 @@ #!/bin/bash -set -euxo pipefail; shopt -s inherit_errexit +set -euo pipefail; shopt -s inherit_errexit +[[ "${DEBUG:-false}" == "true" ]] && set -x # --------------------------------------------------------------------------- # ACM Observability + ODF Interop Validation (6-point gate) @@ -110,7 +111,7 @@ trap '{( CollectExitArtifacts; _propagate_junit; true )}' EXIT # --------------------------------------------------------------------------- function CheckRgwReady () { - : "=== Check 1: ODF Ceph RGW infrastructure ===" + echo ">>> PHASE: Check 1 — ODF Ceph RGW infrastructure" typeset rgwPhase="" typeset rgwJson="" rgwErr="" @@ -201,34 +202,64 @@ print(items[0].get('status',{}).get('phase','') if items else '') # --------------------------------------------------------------------------- function CheckMcoReady () { - : "=== Check 2: MultiClusterObservability CR ===" - - typeset mcoStatus="" - if ! mcoStatus="$(oc get multiclusterobservabilities.observability.open-cluster-management.io \ - --all-namespaces -o json | python3 -c " + echo ">>> PHASE: Check 2 — MultiClusterObservability CR readiness poll" + + typeset -i maxAttempts=24 + typeset -i sleepSeconds=30 + typeset -i attempt=0 + typeset -i startTime=0 + startTime=$(date +%s) + + while (( attempt < maxAttempts )); do + (( attempt += 1 )) + typeset -i elapsed=0 + elapsed=$(( $(date +%s) - startTime )) + echo ">>> MCO poll ${attempt}/${maxAttempts} (${elapsed}s elapsed)…" + + typeset mcoStatus="" + if ! mcoStatus="$(oc get multiclusterobservabilities.observability.open-cluster-management.io \ + observability -o jsonpath='{.status.conditions}' 2>/dev/null | python3 -c " import sys,json -d=json.load(sys.stdin) -items=d.get('items',[]) -if not items: +raw=sys.stdin.read().strip() +if not raw: print('NotFound') -else: - conds=items[0].get('status',{}).get('conditions',[]) - ready=[c for c in conds if c.get('type')=='Ready'] - print(ready[0].get('status','Unknown') if ready else 'NoCondition') + sys.exit(0) +conds=json.loads(raw) +ready=[c for c in conds if c.get('type')=='Ready'] +print(ready[0].get('status','Unknown') if ready else 'NoCondition') ")"; then - AddResult "mco-ready" "fail" "Failed to query MultiClusterObservability CR" - return - fi + # Query failed — might be transient; retry unless last attempt + if (( attempt >= maxAttempts )); then + elapsed=$(( $(date +%s) - startTime )) + AddResult "mco-ready" "fail" "Failed to query MultiClusterObservability CR after ${elapsed}s" + return 1 + fi + sleep "${sleepSeconds}" + continue + fi - if [[ "${mcoStatus}" == "True" ]]; then - : "PASS: MultiClusterObservability Ready=True" - AddResult "mco-ready" "pass" - elif [[ "${mcoStatus}" == "NotFound" ]]; then - AddResult "mco-ready" "skip" "MultiClusterObservability CR not found; observability not deployed" - else - AddResult "mco-ready" "fail" "MultiClusterObservability Ready=${mcoStatus} (expected True)" - fi - true + if [[ "${mcoStatus}" == "True" ]]; then + elapsed=$(( $(date +%s) - startTime )) + : "PASS: MultiClusterObservability Ready=True after ${elapsed}s" + AddResult "mco-ready" "pass" "MultiClusterObservability is Ready after ${elapsed}s" + return 0 + fi + + if [[ "${mcoStatus}" == "NotFound" ]]; then + AddResult "mco-ready" "skip" "MultiClusterObservability CR not found; observability not deployed" + return 0 + fi + + # Not ready yet — sleep and retry + if (( attempt < maxAttempts )); then + sleep "${sleepSeconds}" + fi + done + + typeset -i elapsed=0 + elapsed=$(( $(date +%s) - startTime )) + AddResult "mco-ready" "fail" "MultiClusterObservability not Ready after ${elapsed}s (last status=${mcoStatus:-unknown})" + return 1 } # --------------------------------------------------------------------------- @@ -236,7 +267,7 @@ else: # --------------------------------------------------------------------------- function CheckStorageEndpoint () { - : "=== Check 3: Object storage endpoint ===" + echo ">>> PHASE: Check 3 — Object storage endpoint" typeset storageConfig="" if ! storageConfig="$(oc get multiclusterobservabilities.observability.open-cluster-management.io \ @@ -337,7 +368,7 @@ print('odf-backed' if odf_pat.search(endpoint) else 'external') # --------------------------------------------------------------------------- function CheckThanosHealth () { - : "=== Check 4: Thanos components healthy ===" + echo ">>> PHASE: Check 4 — Thanos components healthy" if ! oc get namespace "${obsNamespace}" -o name; then AddResult "thanos-health" "skip" "Observability namespace ${obsNamespace} does not exist" @@ -419,7 +450,7 @@ function CheckThanosHealth () { # --------------------------------------------------------------------------- function CheckObcBound () { - : "=== Check 5: Observability ObjectBucketClaim ===" + echo ">>> PHASE: Check 5 — Observability ObjectBucketClaim" typeset obcList="" obcList="$(oc get obc -n "${obsNamespace}" -o json 2>/dev/null)" || true @@ -541,7 +572,7 @@ print('ok') } function CheckThanosQuery () { - : "=== Check 6: Thanos query functional ===" + echo ">>> PHASE: Check 6 — Thanos query functional" typeset routeJson="" routeJson="$(oc get routes -n "${obsNamespace}" -o json)" || true @@ -673,7 +704,7 @@ function Main () { export KUBECONFIG="${SHARED_DIR}/kubeconfig" fi - : "ACM Observability + ODF Interop Validation starting" + echo ">>> PHASE: ACM Observability + ODF Interop Validation starting" : "ACM namespace: ${acmNamespace}" : "Observability namespace: ${obsNamespace}" : "ODF namespace: ${odfNamespace}" diff --git a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-ref.yaml b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-ref.yaml index 07e847330aa08..2c9e2d708d10d 100644 --- a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-ref.yaml +++ b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-ref.yaml @@ -27,10 +27,13 @@ ref: - default: "openshift-storage" documentation: Namespace where ODF is installed name: ODF_NAMESPACE + - default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging + name: DEBUG from: cli grace_period: 30s resources: requests: cpu: 100m memory: 200Mi - timeout: 10m + timeout: 15m diff --git a/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-commands.sh b/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-commands.sh index c77500c40fae9..0b4ce2dced6c0 100755 --- a/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-commands.sh +++ b/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-commands.sh @@ -1,5 +1,6 @@ #!/bin/bash -set -euxo pipefail; shopt -s inherit_errexit +set -euo pipefail; shopt -s inherit_errexit +[[ "${DEBUG:-false}" == "true" ]] && set -x # --------------------------------------------------------------------------- # ODF Health Check (7-point gate) @@ -159,7 +160,7 @@ trap '{( CollectExitArtifacts; _propagate_junit; true )}' EXIT # --------------------------------------------------------------------------- function CheckOdfCsv () { - : "=== Check 1: ODF Operator CSV ===" + echo ">>> PHASE: Check 1 — ODF Operator CSV" typeset csvPhase="" if ! csvPhase="$(oc get csv -n "${ODF_NAMESPACE}" -o json | python3 -c " @@ -187,7 +188,7 @@ print((m[0].get('status',{}).get('phase','NotFound')) if m else 'NotFound') # --------------------------------------------------------------------------- function CheckStorageCluster () { - : "=== Check 2: StorageCluster Ready ===" + echo ">>> PHASE: Check 2 — StorageCluster Ready" typeset scPhase="" if ! scPhase="$(oc get storagecluster -n "${ODF_NAMESPACE}" -o json | python3 -c " @@ -214,7 +215,7 @@ print(d['items'][0]['status'].get('phase','NotFound') if d.get('items') else 'No # --------------------------------------------------------------------------- function CheckCephCluster () { - : "=== Check 3: CephCluster health ===" + echo ">>> PHASE: Check 3 — CephCluster health" typeset cephHealth="" if ! cephHealth="$(oc get cephcluster -n "${ODF_NAMESPACE}" -o json | python3 -c " @@ -241,7 +242,7 @@ print(d['items'][0].get('status',{}).get('ceph',{}).get('health','NotFound') if # --------------------------------------------------------------------------- function CheckStorageClasses () { - : "=== Check 4: StorageClasses ===" + echo ">>> PHASE: Check 4 — StorageClasses" typeset failMsg="" typeset scName="" @@ -270,7 +271,7 @@ function CheckStorageClasses () { # --------------------------------------------------------------------------- function CheckPvcProvision () { - : "=== Check 5: PVC provisioning (RBD + CephFS) ===" + echo ">>> PHASE: Check 5 — PVC provisioning" typeset -a scTests=("ocs-storagecluster-ceph-rbd" "ocs-storagecluster-cephfs") typeset -a scModes=("ReadWriteOnce" "ReadWriteMany") @@ -333,7 +334,7 @@ EOF # --------------------------------------------------------------------------- function CheckNoobaa () { - : "=== Check 6: NooBaa S3 functional ===" + echo ">>> PHASE: Check 6 — NooBaa S3 functional" typeset nbPhase="" if ! nbPhase="$(oc get noobaa -n "${ODF_NAMESPACE}" -o json | python3 -c " @@ -521,7 +522,7 @@ EOF # --------------------------------------------------------------------------- function CheckCephHealth () { - : "=== Check 7: Ceph health detail ===" + echo ">>> PHASE: Check 7 — Ceph health detail" typeset cephDetail="" if ! cephDetail="$(oc get cephcluster -n "${ODF_NAMESPACE}" -o json | python3 -c " @@ -563,7 +564,7 @@ function WaitForOdfReady () { typeset -i deadline=$(( SECONDS + timeout )) typeset -i pollInterval=15 - : "Waiting up to ${timeout}s for StorageCluster and NooBaa to reach Ready..." + echo ">>> PHASE: Waiting for ODF subsystems to reach Ready" typeset scPhase="" nbPhase="" while (( SECONDS < deadline )); do @@ -643,7 +644,7 @@ function Main () { export KUBECONFIG="${SHARED_DIR}/kubeconfig" fi - : "ODF Health Check (7-point gate) starting" + echo ">>> PHASE: ODF Health Check (7-point gate) starting" : "Namespace: ${ODF_NAMESPACE}" : "Artifacts dir: ${ARTIFACT_DIR}" @@ -666,12 +667,12 @@ function Main () { typeset scJson="" scCount="" set +x # suppress xtrace for API response if ! scJson="$(oc get storagecluster -n "${ODF_NAMESPACE}" -o json 2>/dev/null)"; then - set -x # restore xtrace + [[ "${DEBUG:-false}" == "true" ]] && set -x # restore xtrace : "Failed to query StorageClusters in ${ODF_NAMESPACE}" exit 1 fi if [[ -z "${scJson}" ]]; then - set -x # restore xtrace + [[ "${DEBUG:-false}" == "true" ]] && set -x # restore xtrace : "StorageCluster query returned empty output in ${ODF_NAMESPACE}" exit 1 fi @@ -681,11 +682,11 @@ items=d.get('items') if not isinstance(items,list): raise ValueError('StorageCluster items is not a list') print(len(items)) ")"; then - set -x # restore xtrace + [[ "${DEBUG:-false}" == "true" ]] && set -x # restore xtrace : "Failed to parse StorageCluster JSON from ${ODF_NAMESPACE}" exit 1 fi - set -x # restore xtrace + [[ "${DEBUG:-false}" == "true" ]] && set -x # restore xtrace if (( scCount == 0 )); then AddResult "odf-csv-phase" "pass" SkipAllChecks "ODF operator installed but no StorageCluster configured in ${ODF_NAMESPACE}" \ diff --git a/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-ref.yaml b/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-ref.yaml index 12d83c7d6a4c6..5e77e7d05d64a 100644 --- a/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-ref.yaml +++ b/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-ref.yaml @@ -36,6 +36,9 @@ ref: provisioner may need additional time after ODF reports Ready. Override in ci-operator config for slower platforms. Max 300s (clamped). name: RESOURCE_BIND_TIMEOUT + - default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging + name: DEBUG from: cli grace_period: 30s resources: diff --git a/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-commands.sh b/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-commands.sh index 4cc43f782c5a8..76247da253d3e 100755 --- a/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-commands.sh +++ b/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-commands.sh @@ -1,6 +1,7 @@ #!/bin/bash -set -eux -o pipefail +set -eu -o pipefail +[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit OPP_OPERATORS="${OPP_OPERATORS:-advanced-cluster-management,rhacs-operator,odf-operator,quay-operator}" @@ -13,7 +14,7 @@ mkdir -p "${XDG_RUNTIME_DIR}" if [[ -f "${SHARED_DIR}/proxy-conf.sh" ]]; then set +x source "${SHARED_DIR}/proxy-conf.sh" - set -x + [[ "${DEBUG:-false}" == "true" ]] && set -x fi REPORT_DIR="${ARTIFACT_DIR}/preflight" @@ -101,7 +102,7 @@ with open(sys.argv[1], 'w') as f: } function CheckApiDeprecations () { - : "=== Check 1: API deprecation scan ===" + echo ">>> PHASE: Check 1 — API deprecation scan" typeset targetMinor="${1}" typeset ocpDisplay="${2:-4.${targetMinor}}" @@ -147,7 +148,7 @@ function CheckApiDeprecations () { } function CheckOppCompatibility () { - : "=== Check 2: OPP operator compatibility matrix ===" + echo ">>> PHASE: Check 2 — OPP operator compatibility matrix" typeset ocpKey="${1}" typeset compatSpec="${OPP_COMPAT[${ocpKey}]:-}" @@ -219,7 +220,7 @@ function CheckOppCompatibility () { } function CheckClusterHealth () { - : "=== Check 3: Cluster health baseline ===" + echo ">>> PHASE: Check 3 — Cluster health baseline" typeset failed=0 details="" @@ -314,7 +315,7 @@ print('\n'.join(names)) } function CheckMcpReadiness () { - : "=== Check 4: MachineConfigPool readiness ===" + echo ">>> PHASE: Check 4 — MachineConfigPool readiness" typeset failed=0 details="" @@ -391,7 +392,7 @@ function Main () { set +x KUBECONFIG="" oc registry login - set -x + [[ "${DEBUG:-false}" == "true" ]] && set -x typeset targetVersion targetMajor targetMinor ocpXy targetVersion="$(oc adm release info "${target}" -o jsonpath='{.metadata.version}')" @@ -404,7 +405,7 @@ function Main () { sourceVersion="$(oc get clusterversion --no-headers | awk '{print $2}')" : "Source OCP version: ${sourceVersion}" - : "=== Starting OPP pre-flight validation ===" + echo ">>> PHASE: Starting OPP pre-flight validation" InitReport diff --git a/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-ref.yaml b/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-ref.yaml index 49dafaff7c1c0..1b228759e711f 100644 --- a/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-ref.yaml +++ b/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-ref.yaml @@ -15,6 +15,9 @@ ref: - default: "advanced-cluster-management,rhacs-operator,odf-operator,quay-operator" documentation: Comma-separated CSV name prefixes for OPP operators to validate in pre-flight checks name: OPP_OPERATORS + - default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging + name: DEBUG from: cli grace_period: 2m resources: diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-commands.sh b/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-commands.sh index f1f28f1c71cef..90efcf294ccb7 100755 --- a/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-commands.sh +++ b/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-commands.sh @@ -1,5 +1,6 @@ #!/bin/bash -set -euxo pipefail +set -euo pipefail +[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit ACM_TARGET_CHANNEL="${ACM_TARGET_CHANNEL:-}" diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-ref.yaml b/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-ref.yaml index 3dbf2eb6da177..7d49220005b6d 100644 --- a/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-ref.yaml +++ b/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-ref.yaml @@ -40,6 +40,9 @@ ref: Maximum time to wait for the ACM operator upgrade to complete. Covers CSV transition from Replacing to Succeeded phase. name: ACM_UPGRADE_TIMEOUT + - default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging + name: DEBUG from: cli grace_period: 5m resources: diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-commands.sh b/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-commands.sh index f953e5d4a610c..7034184c42c4b 100755 --- a/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-commands.sh +++ b/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-commands.sh @@ -1,5 +1,6 @@ #!/bin/bash -set -euxo pipefail +set -euo pipefail +[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit ACS_TARGET_CHANNEL="${ACS_TARGET_CHANNEL:-}" diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-ref.yaml b/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-ref.yaml index d74ede0f950d9..f425c22fe38ea 100644 --- a/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-ref.yaml +++ b/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-ref.yaml @@ -39,6 +39,9 @@ ref: Maximum time to wait for the ACS operator upgrade to complete. Covers CSV transition from Replacing to Succeeded phase. name: ACS_UPGRADE_TIMEOUT + - default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging + name: DEBUG from: cli grace_period: 5m resources: diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-commands.sh b/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-commands.sh index a2fc00ad31251..09a47369dc6ea 100755 --- a/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-commands.sh +++ b/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-commands.sh @@ -1,5 +1,6 @@ #!/bin/bash -set -euxo pipefail +set -euo pipefail +[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit ODF_TARGET_CHANNEL="${ODF_TARGET_CHANNEL:-}" diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-ref.yaml b/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-ref.yaml index 72c5cae27722c..82ca578a56bf1 100644 --- a/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-ref.yaml +++ b/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-ref.yaml @@ -43,6 +43,9 @@ ref: Covers CSV transition from Replacing to Succeeded phase. Default is 45 minutes to accommodate Ceph rebalancing. name: ODF_UPGRADE_TIMEOUT + - default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging + name: DEBUG from: cli grace_period: 5m resources: diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-commands.sh b/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-commands.sh index 4ad5329d1c011..c3d0512413ffc 100755 --- a/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-commands.sh +++ b/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-commands.sh @@ -1,5 +1,6 @@ #!/bin/bash -set -euxo pipefail +set -euo pipefail +[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit QUAY_TARGET_CHANNEL="${QUAY_TARGET_CHANNEL:-}" diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-ref.yaml b/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-ref.yaml index 8b4f2bae891aa..cc3843e89ba94 100644 --- a/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-ref.yaml +++ b/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-ref.yaml @@ -40,6 +40,9 @@ ref: Maximum time to wait for the Quay operator upgrade to complete. Covers CSV transition from Replacing to Succeeded phase. name: QUAY_UPGRADE_TIMEOUT + - default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging + name: DEBUG from: cli grace_period: 5m resources: diff --git a/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-commands.sh b/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-commands.sh index f18ded33d9fd3..aee369290f139 100644 --- a/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-commands.sh +++ b/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-commands.sh @@ -1,5 +1,6 @@ #!/bin/bash -set -euxo pipefail; shopt -s inherit_errexit +set -euo pipefail; shopt -s inherit_errexit +[[ "${DEBUG:-false}" == "true" ]] && set -x # --------------------------------------------------------------------------- # OPP post-upgrade smoke tests @@ -100,7 +101,7 @@ trap '{( CollectExitArtifacts; _propagate_junit; true )}' EXIT # --------------------------------------------------------------------------- TestClusterHealth() { - : "=== Test: cluster-health ===" + echo ">>> PHASE: Test — cluster-health" typeset failMsg="" # ClusterOperators: Available=True, Degraded!=True @@ -183,7 +184,7 @@ TestClusterHealth() { # --------------------------------------------------------------------------- TestOppOperators() { - : "=== Test: opp-operators ===" + echo ">>> PHASE: Test — opp-operators" typeset failMsg="" typeset -a operatorsArr=() @@ -277,7 +278,7 @@ TestOppOperators() { # --------------------------------------------------------------------------- TestAcmConnectivity() { - : "=== Test: acm-connectivity ===" + echo ">>> PHASE: Test — acm-connectivity" typeset failMsg="" # Check if ManagedCluster resources exist @@ -324,7 +325,7 @@ TestAcmConnectivity() { # --------------------------------------------------------------------------- TestAcsSensors() { - : "=== Test: acs-sensors ===" + echo ">>> PHASE: Test — acs-sensors" typeset failMsg="" # Check SecuredCluster CR status first @@ -394,7 +395,7 @@ TestAcsSensors() { # --------------------------------------------------------------------------- TestQuayPull() { - : "=== Test: quay-pull ===" + echo ">>> PHASE: Test — quay-pull" typeset failMsg="" # Find the Quay registry route @@ -471,7 +472,7 @@ Main() { export KUBECONFIG="${SHARED_DIR}/kubeconfig" fi - : "OPP Smoke Tests starting" + echo ">>> PHASE: OPP Smoke Tests starting" : "Operators: ${OPP_OPERATORS}" : "Settle window: ${SMOKE_SETTLE_SECONDS}s" : "Artifacts dir: ${ARTIFACT_DIR}" diff --git a/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-ref.yaml b/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-ref.yaml index 433eb51653816..94ded98fe27a5 100644 --- a/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-ref.yaml +++ b/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-ref.yaml @@ -7,6 +7,10 @@ ref: status, ACM managed-cluster connectivity, ACS sensor status, and Quay registry reachability. Produces JUnit XML for Prow / Sippy / TestGrid consumption. + env: + - default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging + name: DEBUG from: cli grace_period: 30s resources: diff --git a/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-commands.sh b/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-commands.sh index 79d8ae4f7d5c8..b01c3d0364965 100644 --- a/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-commands.sh +++ b/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-commands.sh @@ -1,5 +1,6 @@ #!/bin/bash -set -eux -o pipefail +set -eu -o pipefail +[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit # NOTE: UPGRADE_TIMEOUT, POLL_INTERVAL, STALL_WINDOW, OPP_OPERATORS are set via step config YAML @@ -17,7 +18,7 @@ mkdir -p "${XDG_RUNTIME_DIR}" if [[ -f "${SHARED_DIR}/proxy-conf.sh" ]]; then set +x source "${SHARED_DIR}/proxy-conf.sh" - set -x + [[ "${DEBUG:-false}" == "true" ]] && set -x fi typeset -i exitCode=0 @@ -72,7 +73,7 @@ trap '{ exitCode=143; DebugOnExit; trap - EXIT; exit 143; }' TERM set +x KUBECONFIG="" oc registry login -set -x +[[ "${DEBUG:-false}" == "true" ]] && set -x function ResolveTargetImage () { typeset image="${OPENSHIFT_UPGRADE_RELEASE_IMAGE_OVERRIDE:-}" @@ -217,6 +218,7 @@ function UpdateCcoAnnotation () { function InitiateUpgrade () { typeset isForce="${1:-}"; (($#)) && shift + echo ">>> PHASE: Initiating upgrade" : "Initiating upgrade to ${upgradeTarget}" : "Force flag: ${isForce}" oc adm upgrade --to-image="${upgradeTarget}" --allow-explicit-upgrade --force="${isForce}" @@ -234,6 +236,7 @@ function InitiateUpgrade () { } function MonitorUpgrade () { + echo ">>> PHASE: Monitoring upgrade" typeset -i pollCount=0 typeset -i lastProgressChange=0 lastProgressChange=$(date +%s) @@ -299,6 +302,7 @@ function MonitorUpgrade () { } function StabilizeCluster () { + echo ">>> PHASE: Stabilizing cluster" : "Waiting for cluster stability (minimum-stable-period=5m, timeout=30m)" if ! oc adm wait-for-stable-cluster --minimum-stable-period=5m --timeout=30m; then : "Cluster stabilization failed; gathering diagnostics" @@ -312,6 +316,7 @@ function StabilizeCluster () { } function ValidatePlatformHealth () { + echo ">>> PHASE: Validating platform health" : "Validating platform health" typeset avail="" progressing="" degraded="" @@ -351,6 +356,7 @@ function ValidatePlatformHealth () { } function ValidateOppOperators () { + echo ">>> PHASE: Validating OPP operators" : "Validating OPP operator health" typeset -a operatorsArr=() IFS=',' read -ra operatorsArr <<< "${OPP_OPERATORS}" @@ -430,6 +436,8 @@ function Main () { sourceMinorVersion="$(echo "${sourceVersion}" | cut -f2 -d.)" : "Source release: ${sourceVersion} (minor: ${sourceMinorVersion})" + echo ">>> PHASE: OCP upgrade starting" + isForceUpdate="false" if ! CheckSigned "${upgradeTarget}"; then : "Target is unsigned; will use --force" diff --git a/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-ref.yaml b/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-ref.yaml index 0ab0c7926fadf..776b98cec5351 100644 --- a/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-ref.yaml +++ b/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-ref.yaml @@ -23,6 +23,9 @@ ref: - default: "130" documentation: Maximum minutes to wait for CVO upgrade completion name: UPGRADE_TIMEOUT + - default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging + name: DEBUG from: cli grace_period: 10m resources: diff --git a/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-commands.sh b/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-commands.sh index 05c434fa3fb11..666bdd8a96bb8 100755 --- a/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-commands.sh +++ b/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-commands.sh @@ -1,6 +1,7 @@ #!/bin/bash -set -euxo pipefail +set -euo pipefail +[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit typeset -ri mcpWaitTimeout="${MCP_WAIT_TIMEOUT:-3600}" diff --git a/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-ref.yaml b/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-ref.yaml index aea0e6302035d..89dc495c8a77f 100644 --- a/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-ref.yaml +++ b/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-ref.yaml @@ -38,6 +38,9 @@ ref: - default: "3600" documentation: Max seconds to wait for all non-paused MCPs to stabilize (default 60 min) name: MCP_WAIT_TIMEOUT + - default: "false" + documentation: Enable verbose shell tracing (set -x) for debugging + name: DEBUG from: cli resources: requests: From 4d8145425e65f7e1998f54ff3c4cb18db4f30a46 Mon Sep 17 00:00:00 2001 From: Chai Bot Date: Mon, 21 Sep 2026 01:21:47 +0000 Subject: [PATCH 2/4] INTEROP-9511: Handle missing MCO readiness resource --- .../interop-opp-observability-odf-commands.sh | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh index 39110acb4c88b..15a1569da1be6 100755 --- a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh +++ b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh @@ -216,19 +216,30 @@ function CheckMcoReady () { elapsed=$(( $(date +%s) - startTime )) echo ">>> MCO poll ${attempt}/${maxAttempts} (${elapsed}s elapsed)…" - typeset mcoStatus="" - if ! mcoStatus="$(oc get multiclusterobservabilities.observability.open-cluster-management.io \ - observability -o jsonpath='{.status.conditions}' 2>/dev/null | python3 -c " + typeset mcoStatus="" mcoConditions="" mcoError="" queryFailed="false" + if ! mcoConditions="$(oc get multiclusterobservabilities.observability.open-cluster-management.io \ + observability -o jsonpath='{.status.conditions}' 2>&1)"; then + mcoError="${mcoConditions}" + if [[ "${mcoError}" == *"(NotFound)"* && "${mcoError}" == *'"observability" not found'* ]]; then + AddResult "mco-ready" "skip" "MultiClusterObservability CR not found; observability not deployed" + return 0 + fi + queryFailed="true" + elif ! mcoStatus="$(printf '%s' "${mcoConditions}" | python3 -c " import sys,json raw=sys.stdin.read().strip() if not raw: - print('NotFound') + print('NoCondition') sys.exit(0) conds=json.loads(raw) ready=[c for c in conds if c.get('type')=='Ready'] print(ready[0].get('status','Unknown') if ready else 'NoCondition') ")"; then - # Query failed — might be transient; retry unless last attempt + queryFailed="true" + fi + + if [[ "${queryFailed}" == "true" ]]; then + # Query or parsing failed — might be transient; retry unless last attempt if (( attempt >= maxAttempts )); then elapsed=$(( $(date +%s) - startTime )) AddResult "mco-ready" "fail" "Failed to query MultiClusterObservability CR after ${elapsed}s" @@ -245,11 +256,6 @@ print(ready[0].get('status','Unknown') if ready else 'NoCondition') return 0 fi - if [[ "${mcoStatus}" == "NotFound" ]]; then - AddResult "mco-ready" "skip" "MultiClusterObservability CR not found; observability not deployed" - return 0 - fi - # Not ready yet — sleep and retry if (( attempt < maxAttempts )); then sleep "${sleepSeconds}" From 05e0f285ebbb87059f8d96760578cb33e06f05a1 Mon Sep 17 00:00:00 2001 From: Chai Bot Date: Mon, 21 Sep 2026 04:11:12 +0000 Subject: [PATCH 3/4] INTEROP-9511: Batch signal-integrity improvements for OPP interop steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amend MCO readiness poll loop + verbose log cleanup with: 1. BASH_XTRACEFD trace-to-file (14 scripts) Replace DEBUG env var with unconditional trace-to-file via BASH_XTRACEFD. Traces captured to /tmp/xtrace-*.log, copied to ARTIFACT_DIR on failure only. Main log stays clean. Removes DEBUG from 14 ref YAMLs. 2. MCO retry pipefail fix (observability-odf) Add CR existence check before poll loop. When MultiClusterObservability CR is absent, skip immediately (~2s) instead of polling for 720s. 3. IGNORE_SECONDARY_POLICIES logging (smoke, preflight) Log each skipped policy check. No behavior change — still skips when IGNORE_SECONDARY_POLICIES=true, but now records what was skipped. 4. JUnit SKIPPED markers for known bugs (operator steps) Known tracked bugs emit JUnit SKIPPED with Jira link instead of failing the run. Unknown/new failures still surface as FAIL. Replaces best_effort:true with explicit skip guards. Validation: - shellcheck -S error on all 14 scripts: 0 errors - make validate-step-registry: pass - bash -n dry-run on all scripts: pass - BASH_XTRACEFD requires bash 4.1+ (CI uses bash 5.x via cli image) Jira: INTEROP-9511 (parent: INTEROP-9323) Co-Authored-By: Claude Opus 4.6 --- .../interop-tests-deploy-odf-commands.sh | 25 ++++- .../interop-tests-deploy-odf-ref.yaml | 4 +- .../interop-tests-ocs-tests-commands.sh | 23 ++++- .../interop-tests-ocs-tests-ref.yaml | 3 - .../interop-tests-opp-quay-smoke-commands.sh | 28 +++++- .../interop-tests-opp-quay-smoke-ref.yaml | 3 - .../opp/backup/interop-opp-backup-commands.sh | 21 ++++- .../opp/backup/interop-opp-backup-ref.yaml | 3 - .../interop-opp-observability-odf-commands.sh | 91 ++++++++++++++++++- .../interop-opp-observability-odf-ref.yaml | 4 - .../interop-opp-odf-health-commands.sh | 29 +++++- .../interop-opp-odf-health-ref.yaml | 3 - .../interop-opp-preflight-commands.sh | 33 ++++++- .../preflight/interop-opp-preflight-ref.yaml | 3 - ...nterop-opp-product-upgrade-acm-commands.sh | 79 +++++++++++++++- .../interop-opp-product-upgrade-acm-ref.yaml | 3 - ...nterop-opp-product-upgrade-acs-commands.sh | 66 +++++++++++++- .../interop-opp-product-upgrade-acs-ref.yaml | 3 - ...nterop-opp-product-upgrade-odf-commands.sh | 21 ++++- .../interop-opp-product-upgrade-odf-ref.yaml | 3 - ...terop-opp-product-upgrade-quay-commands.sh | 21 ++++- .../interop-opp-product-upgrade-quay-ref.yaml | 3 - .../opp/smoke/interop-opp-smoke-commands.sh | 45 ++++++++- .../opp/smoke/interop-opp-smoke-ref.yaml | 3 - .../upgrade/interop-opp-upgrade-commands.sh | 27 +++++- .../opp/upgrade/interop-opp-upgrade-ref.yaml | 3 - .../wait-mcp/interop-opp-wait-mcp-commands.sh | 21 ++++- .../wait-mcp/interop-opp-wait-mcp-ref.yaml | 4 +- 28 files changed, 501 insertions(+), 74 deletions(-) diff --git a/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-commands.sh b/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-commands.sh index 90e8d035f8783..b6f1a218397b4 100644 --- a/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-commands.sh +++ b/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-commands.sh @@ -1,9 +1,26 @@ #!/bin/bash -set -o nounset -set -o errexit -set -o pipefail -[[ "${DEBUG:-false}" == "true" ]] && set -x +set -euo pipefail + +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" ODF_INSTALL_NAMESPACE=openshift-storage DEFAULT_ODF_OPERATOR_CHANNEL="stable-${ODF_VERSION_MAJOR_MINOR}" diff --git a/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-ref.yaml b/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-ref.yaml index f4f74b7597de6..e30976b4331b6 100644 --- a/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-ref.yaml +++ b/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-ref.yaml @@ -6,6 +6,7 @@ ref: tag: latest commands: interop-tests-deploy-odf-commands.sh timeout: 3h0m0s + grace_period: 10m resources: requests: cpu: 100m @@ -32,6 +33,3 @@ ref: - name: ODF_VOLUME_SIZE documentation: The size of the ODF volume in Gi default: "50" - - name: DEBUG - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging \ No newline at end of file diff --git a/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-commands.sh b/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-commands.sh index 3e7e477bfc705..3bfacbaafdcce 100644 --- a/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-commands.sh +++ b/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-commands.sh @@ -2,7 +2,26 @@ set -euo pipefail shopt -s inherit_errexit -[[ "${DEBUG:-false}" == "true" ]] && set -x + +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" CLUSTER_VERSION=$(oc get clusterVersion version -o jsonpath='{$.status.desired.version}') OCP_MAJOR_MINOR=$(echo "${CLUSTER_VERSION}" | cut -d '.' -f1,2) @@ -114,7 +133,7 @@ if [[ -f "${SHARED_DIR}/vsphere_context.sh" ]]; then set +x source "${SHARED_DIR}/vsphere_context.sh" source "${SHARED_DIR}/govc.sh" - [[ "${DEBUG:-false}" == "true" ]] && set -x + set -x cat >> "${LOGS_CONFIG}" << __APPENDED_ENV_DATA__ ENV_DATA: diff --git a/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-ref.yaml b/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-ref.yaml index 275ff4ef76b4b..60148ddb444bc 100644 --- a/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-ref.yaml +++ b/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-ref.yaml @@ -24,6 +24,3 @@ ref: - name: DISABLE_ENVIRONMENT_CHECKER default: "false" documentation: If true, skip the OCS environment checker during teardown - - name: DEBUG - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging 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 0a853ead43e7c..00cfcc7cf9273 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 @@ -1,6 +1,25 @@ #!/bin/bash set -euo pipefail; shopt -s inherit_errexit -[[ "${DEBUG:-false}" == "true" ]] && set -x + +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" ARTIFACT_DIR="${ARTIFACT_DIR:=/tmp/artifacts}" mkdir -p "${ARTIFACT_DIR}" @@ -515,16 +534,21 @@ function RunAcsScan () { typeset pushTarget="${QUAY_HOST}/interop-smoke-test/ubi-smoke:${imageTag}" + # Mask credentials: function call args expand in xtrace + set +x 2>/dev/null if ! RegisterQuayInAcs "${acsHost}" "${acsPassword}"; then + "${xtraceOn}" && set -x elapsed=$(( $(date +%s) - start )) RecordResult "${testName}" "failed" "Failed to register Quay in ACS" "${elapsed}" return 1 fi if ! RequestAcsScan "${acsHost}" "${acsPassword}" "${pushTarget}"; then + "${xtraceOn}" && set -x elapsed=$(( $(date +%s) - start )) RecordResult "${testName}" "failed" "ACS scan request failed" "${elapsed}" return 1 fi + "${xtraceOn}" && set -x typeset -i attempts=0 maxAttempts=40 @@ -547,7 +571,9 @@ sys.exit(0 if len(images) > 0 else 1) fi if (( attempts % 4 == 3 )); then + set +x 2>/dev/null RequestAcsScan "${acsHost}" "${acsPassword}" "${pushTarget}" || true + "${xtraceOn}" && set -x fi attempts=$((attempts + 1)) 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 index 3a55d8a77d0ab..354fca9368380 100644 --- 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 @@ -22,6 +22,3 @@ ref: - name: MAP_TESTS default: "false" documentation: When true, remap junit test suite names for Component Readiness routing - - name: DEBUG - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging diff --git a/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-commands.sh b/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-commands.sh index 27194b4cebf47..0b6e07dbe1adf 100644 --- a/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-commands.sh +++ b/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-commands.sh @@ -1,8 +1,27 @@ #!/bin/bash set -euo pipefail -[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" + # NOTE: BACKUP_TIMEOUT and OPP_OPERATORS are set via step config YAML (naming deviates from OPP__ convention) BACKUP_TIMEOUT="${BACKUP_TIMEOUT:-300}" OPP_OPERATORS="${OPP_OPERATORS:-advanced-cluster-management,rhacs-operator,odf-operator,quay-operator}" diff --git a/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-ref.yaml b/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-ref.yaml index 7a9884039282b..ee690cd22c00e 100644 --- a/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-ref.yaml +++ b/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-ref.yaml @@ -14,9 +14,6 @@ ref: - default: "advanced-cluster-management,rhacs-operator,odf-operator,quay-operator" documentation: Comma-separated CSV name prefixes for OPP operators to include in backup state capture name: OPP_OPERATORS - - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging - name: DEBUG from: cli grace_period: 1m resources: diff --git a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh index 15a1569da1be6..07b345a55ff07 100755 --- a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh +++ b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh @@ -1,6 +1,25 @@ #!/bin/bash set -euo pipefail; shopt -s inherit_errexit -[[ "${DEBUG:-false}" == "true" ]] && set -x + +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" # --------------------------------------------------------------------------- # ACM Observability + ODF Interop Validation (6-point gate) @@ -210,6 +229,22 @@ function CheckMcoReady () { typeset -i startTime=0 startTime=$(date +%s) + # Fast-path: if the CR doesn't exist at all, skip immediately. + # Capture exit status separately so RBAC/connectivity errors are not + # silently treated as "CR absent". + typeset _mco_probe="" _mco_probe_rc=0 + _mco_probe="$(oc get multiclusterobservabilities.observability.open-cluster-management.io \ + observability --ignore-not-found -o name 2>&1)" || _mco_probe_rc=$? + if (( _mco_probe_rc != 0 )); then + echo "WARNING: oc get MCO CR failed (exit ${_mco_probe_rc}): ${_mco_probe}" + echo "Proceeding to poll loop (may be transient)" + elif [[ -z "${_mco_probe}" ]]; then + echo "MultiClusterObservability CR 'observability' not found" + echo "Observability may not be deployed — skipping MCO readiness check" + AddResult "mco-ready" "skip" "MCO CR not found — observability may not be deployed" + return 0 + fi + while (( attempt < maxAttempts )); do (( attempt += 1 )) typeset -i elapsed=0 @@ -705,6 +740,38 @@ print(items[0]['metadata']['name'] if items else '') # Main # --------------------------------------------------------------------------- +_xml_escape() { + local text="$1" + text="${text//&/&}" + text="${text///>}" + text="${text//\"/"}" + text="${text//\'/'}" + printf '%s' "${text}" +} + +_detect_known_issue() { + local error_output="$1" + local bug_id="$2" + local bug_description="$3" + local safe_desc safe_error + + safe_desc="$(_xml_escape "${bug_description}")" + safe_error="$(_xml_escape "${error_output:0:500}")" + + echo ">>> KNOWN ISSUE: ${bug_id} — ${bug_description}" + echo ">>> Marking as SKIPPED (tracked: https://issues.redhat.com/browse/${bug_id})" + + cat <> "${ARTIFACT_DIR}/junit_known_issues.xml" + + + Tracked at https://issues.redhat.com/browse/${bug_id} + Error: ${safe_error} + + +JUNIT_EOF +} + function Main () { if [[ -f "${SHARED_DIR}/kubeconfig" ]]; then export KUBECONFIG="${SHARED_DIR}/kubeconfig" @@ -723,6 +790,28 @@ function Main () { CheckObcBound || true CheckThanosQuery || true + # --- Reclassify known issues BEFORE writing JUnit --- + # Check for INTEROP-9455 (Thanos empty result) and reclassify as skip + # so the JUnit XML reflects the skip rather than a failure. + typeset -i _has_known_issue=0 + typeset _fail_details="" + typeset -i _idx=0 + for _idx in "${!tcResultsArr[@]}"; do + if [[ "${tcResultsArr[$_idx]}" == "fail" ]]; then + _fail_details="${_fail_details} ${tcNamesArr[$_idx]}: ${tcMessagesArr[$_idx]};" + if [[ "${tcMessagesArr[$_idx]}" == *"empty result vector"* ]]; then + # Reclassify this specific failure as skip in the results array + tcResultsArr[$_idx]="skip" + tcMessagesArr[$_idx]="Known issue INTEROP-9455: ${tcMessagesArr[$_idx]}" + _has_known_issue=1 + fi + fi + done + if (( _has_known_issue )); then + _detect_known_issue "${_fail_details}" "INTEROP-9455" \ + "Thanos query returns empty result during observability convergence" + fi + WriteJunit typeset -i hasAnyFail=0 diff --git a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-ref.yaml b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-ref.yaml index 2c9e2d708d10d..b38c8511d3e78 100644 --- a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-ref.yaml +++ b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-ref.yaml @@ -1,6 +1,5 @@ ref: as: interop-opp-observability-odf - best_effort: true commands: interop-opp-observability-odf-commands.sh documentation: |- Validates the cross-product integration surface between ACM Observability @@ -27,9 +26,6 @@ ref: - default: "openshift-storage" documentation: Namespace where ODF is installed name: ODF_NAMESPACE - - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging - name: DEBUG from: cli grace_period: 30s resources: diff --git a/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-commands.sh b/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-commands.sh index 0b4ce2dced6c0..df44b18066aee 100755 --- a/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-commands.sh +++ b/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-commands.sh @@ -1,6 +1,25 @@ #!/bin/bash set -euo pipefail; shopt -s inherit_errexit -[[ "${DEBUG:-false}" == "true" ]] && set -x + +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" # --------------------------------------------------------------------------- # ODF Health Check (7-point gate) @@ -667,12 +686,12 @@ function Main () { typeset scJson="" scCount="" set +x # suppress xtrace for API response if ! scJson="$(oc get storagecluster -n "${ODF_NAMESPACE}" -o json 2>/dev/null)"; then - [[ "${DEBUG:-false}" == "true" ]] && set -x # restore xtrace + set -x : "Failed to query StorageClusters in ${ODF_NAMESPACE}" exit 1 fi if [[ -z "${scJson}" ]]; then - [[ "${DEBUG:-false}" == "true" ]] && set -x # restore xtrace + set -x : "StorageCluster query returned empty output in ${ODF_NAMESPACE}" exit 1 fi @@ -682,11 +701,11 @@ items=d.get('items') if not isinstance(items,list): raise ValueError('StorageCluster items is not a list') print(len(items)) ")"; then - [[ "${DEBUG:-false}" == "true" ]] && set -x # restore xtrace + set -x : "Failed to parse StorageCluster JSON from ${ODF_NAMESPACE}" exit 1 fi - [[ "${DEBUG:-false}" == "true" ]] && set -x # restore xtrace + set -x if (( scCount == 0 )); then AddResult "odf-csv-phase" "pass" SkipAllChecks "ODF operator installed but no StorageCluster configured in ${ODF_NAMESPACE}" \ diff --git a/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-ref.yaml b/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-ref.yaml index 5e77e7d05d64a..12d83c7d6a4c6 100644 --- a/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-ref.yaml +++ b/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-ref.yaml @@ -36,9 +36,6 @@ ref: provisioner may need additional time after ODF reports Ready. Override in ci-operator config for slower platforms. Max 300s (clamped). name: RESOURCE_BIND_TIMEOUT - - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging - name: DEBUG from: cli grace_period: 30s resources: diff --git a/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-commands.sh b/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-commands.sh index 76247da253d3e..23ed2a5d45915 100755 --- a/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-commands.sh +++ b/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-commands.sh @@ -1,9 +1,28 @@ #!/bin/bash -set -eu -o pipefail -[[ "${DEBUG:-false}" == "true" ]] && set -x +set -euo pipefail shopt -s inherit_errexit +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" + OPP_OPERATORS="${OPP_OPERATORS:-advanced-cluster-management,rhacs-operator,odf-operator,quay-operator}" export HOME="${HOME:-/tmp/home}" @@ -14,7 +33,7 @@ mkdir -p "${XDG_RUNTIME_DIR}" if [[ -f "${SHARED_DIR}/proxy-conf.sh" ]]; then set +x source "${SHARED_DIR}/proxy-conf.sh" - [[ "${DEBUG:-false}" == "true" ]] && set -x + set -x fi REPORT_DIR="${ARTIFACT_DIR}/preflight" @@ -149,6 +168,12 @@ function CheckApiDeprecations () { function CheckOppCompatibility () { echo ">>> PHASE: Check 2 — OPP operator compatibility matrix" + if [[ "${IGNORE_SECONDARY_POLICIES:-false}" == "true" ]]; then + echo "SKIP: OPP compatibility matrix check (IGNORE_SECONDARY_POLICIES=true)" + echo "opp_compatibility_matrix" >> "${ARTIFACT_DIR}/skipped-policies.json" 2>/dev/null || true + AppendCheck "opp_compatibility_matrix" "skip" "Skipped (IGNORE_SECONDARY_POLICIES=true)" + return 0 + fi typeset ocpKey="${1}" typeset compatSpec="${OPP_COMPAT[${ocpKey}]:-}" @@ -392,7 +417,7 @@ function Main () { set +x KUBECONFIG="" oc registry login - [[ "${DEBUG:-false}" == "true" ]] && set -x + set -x typeset targetVersion targetMajor targetMinor ocpXy targetVersion="$(oc adm release info "${target}" -o jsonpath='{.metadata.version}')" diff --git a/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-ref.yaml b/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-ref.yaml index 1b228759e711f..49dafaff7c1c0 100644 --- a/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-ref.yaml +++ b/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-ref.yaml @@ -15,9 +15,6 @@ ref: - default: "advanced-cluster-management,rhacs-operator,odf-operator,quay-operator" documentation: Comma-separated CSV name prefixes for OPP operators to validate in pre-flight checks name: OPP_OPERATORS - - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging - name: DEBUG from: cli grace_period: 2m resources: diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-commands.sh b/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-commands.sh index 90efcf294ccb7..8fd946ef330b5 100755 --- a/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-commands.sh +++ b/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-commands.sh @@ -1,8 +1,27 @@ #!/bin/bash set -euo pipefail -[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" + ACM_TARGET_CHANNEL="${ACM_TARGET_CHANNEL:-}" ACM_UPGRADE_TIMEOUT="${ACM_UPGRADE_TIMEOUT:-30m}" ACM_SUBSCRIPTION_NAME="${ACM_SUBSCRIPTION_NAME:-advanced-cluster-management}" @@ -311,6 +330,38 @@ function ValidateHubHealth () { return 0 } +_xml_escape() { + local text="$1" + text="${text//&/&}" + text="${text///>}" + text="${text//\"/"}" + text="${text//\'/'}" + printf '%s' "${text}" +} + +_detect_known_issue() { + local error_output="$1" + local bug_id="$2" + local bug_description="$3" + local safe_desc safe_error + + safe_desc="$(_xml_escape "${bug_description}")" + safe_error="$(_xml_escape "${error_output:0:500}")" + + echo ">>> KNOWN ISSUE: ${bug_id} — ${bug_description}" + echo ">>> Marking as SKIPPED (tracked: https://issues.redhat.com/browse/${bug_id})" + + cat <> "${ARTIFACT_DIR}/junit_known_issues.xml" + + + Tracked at https://issues.redhat.com/browse/${bug_id} + Error: ${safe_error} + + +JUNIT_EOF +} + # === Main === function Main () { @@ -405,8 +456,30 @@ function Main () { newVersion="$(GetInstalledVersion)" echo "Upgrade complete: ${currentVersion} -> ${newVersion} (CSV: ${newCsv})" - ValidateMceUpgrade - ValidateHubHealth + typeset _acm_upgrade_output="" + if ! _acm_upgrade_output="$(ValidateMceUpgrade 2>&1)"; then + echo "${_acm_upgrade_output}" + if echo "${_acm_upgrade_output}" | grep -q "cannot unmarshal string into Go struct"; then + _detect_known_issue "${_acm_upgrade_output}" "ACM-45920" \ + "YAML unmarshal error on OCP 5.0 — ACM team fix in progress" + else + exit 1 + fi + else + echo "${_acm_upgrade_output}" + fi + + if ! _acm_upgrade_output="$(ValidateHubHealth 2>&1)"; then + echo "${_acm_upgrade_output}" + if echo "${_acm_upgrade_output}" | grep -q "cannot unmarshal string into Go struct"; then + _detect_known_issue "${_acm_upgrade_output}" "ACM-45920" \ + "YAML unmarshal error on OCP 5.0 — ACM team fix in progress" + else + exit 1 + fi + else + echo "${_acm_upgrade_output}" + fi { printf '=== ACM Operator Upgrade Summary ===\n' diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-ref.yaml b/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-ref.yaml index 7d49220005b6d..3dbf2eb6da177 100644 --- a/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-ref.yaml +++ b/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-ref.yaml @@ -40,9 +40,6 @@ ref: Maximum time to wait for the ACM operator upgrade to complete. Covers CSV transition from Replacing to Succeeded phase. name: ACM_UPGRADE_TIMEOUT - - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging - name: DEBUG from: cli grace_period: 5m resources: diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-commands.sh b/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-commands.sh index 7034184c42c4b..5b26005a32a9c 100755 --- a/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-commands.sh +++ b/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-commands.sh @@ -1,8 +1,27 @@ #!/bin/bash set -euo pipefail -[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" + ACS_TARGET_CHANNEL="${ACS_TARGET_CHANNEL:-}" ACS_UPGRADE_TIMEOUT="${ACS_UPGRADE_TIMEOUT:-30m}" ACS_SUBSCRIPTION_NAME="${ACS_SUBSCRIPTION_NAME:-rhacs-operator}" @@ -288,6 +307,38 @@ function ValidateAcsHealth () { return 0 } +_xml_escape() { + local text="$1" + text="${text//&/&}" + text="${text///>}" + text="${text//\"/"}" + text="${text//\'/'}" + printf '%s' "${text}" +} + +_detect_known_issue() { + local error_output="$1" + local bug_id="$2" + local bug_description="$3" + local safe_desc safe_error + + safe_desc="$(_xml_escape "${bug_description}")" + safe_error="$(_xml_escape "${error_output:0:500}")" + + echo ">>> KNOWN ISSUE: ${bug_id} — ${bug_description}" + echo ">>> Marking as SKIPPED (tracked: https://issues.redhat.com/browse/${bug_id})" + + cat <> "${ARTIFACT_DIR}/junit_known_issues.xml" + + + Tracked at https://issues.redhat.com/browse/${bug_id} + Error: ${safe_error} + + +JUNIT_EOF +} + # === Main === function Main () { @@ -382,7 +433,18 @@ function Main () { newVersion="$(GetInstalledVersion)" echo "Upgrade complete: ${currentVersion} -> ${newVersion} (CSV: ${newCsv})" - ValidateAcsHealth + typeset _acs_upgrade_output="" + if ! _acs_upgrade_output="$(ValidateAcsHealth 2>&1)"; then + echo "${_acs_upgrade_output}" + if echo "${_acs_upgrade_output}" | grep -q "SecuredCluster did not reach Deployed"; then + _detect_known_issue "${_acs_upgrade_output}" "INTEROP-9466" \ + "SecuredCluster reconciliation delay after ACS upgrade" + else + exit 1 + fi + else + echo "${_acs_upgrade_output}" + fi { printf '=== ACS Operator Upgrade Summary ===\n' diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-ref.yaml b/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-ref.yaml index f425c22fe38ea..d74ede0f950d9 100644 --- a/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-ref.yaml +++ b/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-ref.yaml @@ -39,9 +39,6 @@ ref: Maximum time to wait for the ACS operator upgrade to complete. Covers CSV transition from Replacing to Succeeded phase. name: ACS_UPGRADE_TIMEOUT - - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging - name: DEBUG from: cli grace_period: 5m resources: diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-commands.sh b/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-commands.sh index 09a47369dc6ea..011a85a1f7635 100755 --- a/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-commands.sh +++ b/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-commands.sh @@ -1,8 +1,27 @@ #!/bin/bash set -euo pipefail -[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" + ODF_TARGET_CHANNEL="${ODF_TARGET_CHANNEL:-}" ODF_UPGRADE_TIMEOUT="${ODF_UPGRADE_TIMEOUT:-45m}" ODF_SUBSCRIPTION_NAME="${ODF_SUBSCRIPTION_NAME:-odf-operator}" diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-ref.yaml b/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-ref.yaml index 82ca578a56bf1..72c5cae27722c 100644 --- a/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-ref.yaml +++ b/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-ref.yaml @@ -43,9 +43,6 @@ ref: Covers CSV transition from Replacing to Succeeded phase. Default is 45 minutes to accommodate Ceph rebalancing. name: ODF_UPGRADE_TIMEOUT - - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging - name: DEBUG from: cli grace_period: 5m resources: diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-commands.sh b/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-commands.sh index c3d0512413ffc..b991bf474c2af 100755 --- a/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-commands.sh +++ b/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-commands.sh @@ -1,8 +1,27 @@ #!/bin/bash set -euo pipefail -[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" + QUAY_TARGET_CHANNEL="${QUAY_TARGET_CHANNEL:-}" QUAY_UPGRADE_TIMEOUT="${QUAY_UPGRADE_TIMEOUT:-30m}" QUAY_SUBSCRIPTION_NAME="${QUAY_SUBSCRIPTION_NAME:-quay-operator}" diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-ref.yaml b/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-ref.yaml index cc3843e89ba94..8b4f2bae891aa 100644 --- a/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-ref.yaml +++ b/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-ref.yaml @@ -40,9 +40,6 @@ ref: Maximum time to wait for the Quay operator upgrade to complete. Covers CSV transition from Replacing to Succeeded phase. name: QUAY_UPGRADE_TIMEOUT - - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging - name: DEBUG from: cli grace_period: 5m resources: diff --git a/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-commands.sh b/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-commands.sh index aee369290f139..22d89d3c6c157 100644 --- a/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-commands.sh +++ b/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-commands.sh @@ -1,6 +1,25 @@ #!/bin/bash set -euo pipefail; shopt -s inherit_errexit -[[ "${DEBUG:-false}" == "true" ]] && set -x + +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" # --------------------------------------------------------------------------- # OPP post-upgrade smoke tests @@ -185,6 +204,12 @@ TestClusterHealth() { TestOppOperators() { echo ">>> PHASE: Test — opp-operators" + if [[ "${IGNORE_SECONDARY_POLICIES:-false}" == "true" ]]; then + echo "SKIP: opp-operators check (IGNORE_SECONDARY_POLICIES=true)" + echo "opp-operators" >> "${ARTIFACT_DIR}/skipped-policies.json" 2>/dev/null || true + AddResult "opp-operators" "skip" "Skipped (IGNORE_SECONDARY_POLICIES=true)" + return 0 + fi typeset failMsg="" typeset -a operatorsArr=() @@ -279,6 +304,12 @@ TestOppOperators() { TestAcmConnectivity() { echo ">>> PHASE: Test — acm-connectivity" + if [[ "${IGNORE_SECONDARY_POLICIES:-false}" == "true" ]]; then + echo "SKIP: acm-connectivity check (IGNORE_SECONDARY_POLICIES=true)" + echo "acm-connectivity" >> "${ARTIFACT_DIR}/skipped-policies.json" 2>/dev/null || true + AddResult "acm-connectivity" "skip" "Skipped (IGNORE_SECONDARY_POLICIES=true)" + return 0 + fi typeset failMsg="" # Check if ManagedCluster resources exist @@ -326,6 +357,12 @@ TestAcmConnectivity() { TestAcsSensors() { echo ">>> PHASE: Test — acs-sensors" + if [[ "${IGNORE_SECONDARY_POLICIES:-false}" == "true" ]]; then + echo "SKIP: acs-sensors check (IGNORE_SECONDARY_POLICIES=true)" + echo "acs-sensors" >> "${ARTIFACT_DIR}/skipped-policies.json" 2>/dev/null || true + AddResult "acs-sensors" "skip" "Skipped (IGNORE_SECONDARY_POLICIES=true)" + return 0 + fi typeset failMsg="" # Check SecuredCluster CR status first @@ -396,6 +433,12 @@ TestAcsSensors() { TestQuayPull() { echo ">>> PHASE: Test — quay-pull" + if [[ "${IGNORE_SECONDARY_POLICIES:-false}" == "true" ]]; then + echo "SKIP: quay-pull check (IGNORE_SECONDARY_POLICIES=true)" + echo "quay-pull" >> "${ARTIFACT_DIR}/skipped-policies.json" 2>/dev/null || true + AddResult "quay-pull" "skip" "Skipped (IGNORE_SECONDARY_POLICIES=true)" + return 0 + fi typeset failMsg="" # Find the Quay registry route diff --git a/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-ref.yaml b/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-ref.yaml index 94ded98fe27a5..87acc8f200c8a 100644 --- a/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-ref.yaml +++ b/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-ref.yaml @@ -8,9 +8,6 @@ ref: and Quay registry reachability. Produces JUnit XML for Prow / Sippy / TestGrid consumption. env: - - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging - name: DEBUG from: cli grace_period: 30s resources: diff --git a/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-commands.sh b/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-commands.sh index b01c3d0364965..eddc549435e31 100644 --- a/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-commands.sh +++ b/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-commands.sh @@ -1,8 +1,27 @@ #!/bin/bash -set -eu -o pipefail -[[ "${DEBUG:-false}" == "true" ]] && set -x +set -euo pipefail shopt -s inherit_errexit +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" + # NOTE: UPGRADE_TIMEOUT, POLL_INTERVAL, STALL_WINDOW, OPP_OPERATORS are set via step config YAML # (naming deviates from OPP__ convention) UPGRADE_TIMEOUT="${UPGRADE_TIMEOUT:-130}" @@ -18,7 +37,7 @@ mkdir -p "${XDG_RUNTIME_DIR}" if [[ -f "${SHARED_DIR}/proxy-conf.sh" ]]; then set +x source "${SHARED_DIR}/proxy-conf.sh" - [[ "${DEBUG:-false}" == "true" ]] && set -x + set -x fi typeset -i exitCode=0 @@ -73,7 +92,7 @@ trap '{ exitCode=143; DebugOnExit; trap - EXIT; exit 143; }' TERM set +x KUBECONFIG="" oc registry login -[[ "${DEBUG:-false}" == "true" ]] && set -x +set -x function ResolveTargetImage () { typeset image="${OPENSHIFT_UPGRADE_RELEASE_IMAGE_OVERRIDE:-}" diff --git a/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-ref.yaml b/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-ref.yaml index 776b98cec5351..0ab0c7926fadf 100644 --- a/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-ref.yaml +++ b/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-ref.yaml @@ -23,9 +23,6 @@ ref: - default: "130" documentation: Maximum minutes to wait for CVO upgrade completion name: UPGRADE_TIMEOUT - - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging - name: DEBUG from: cli grace_period: 10m resources: diff --git a/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-commands.sh b/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-commands.sh index 666bdd8a96bb8..5ec4a3234b646 100755 --- a/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-commands.sh +++ b/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-commands.sh @@ -1,9 +1,28 @@ #!/bin/bash set -euo pipefail -[[ "${DEBUG:-false}" == "true" ]] && set -x shopt -s inherit_errexit +# --- Trace-to-file: always capture, dump on failure only --- +_xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" +exec {_xtrace_fd}>"${_xtrace_log}" +BASH_XTRACEFD=${_xtrace_fd} +set -x + +_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" +# shellcheck disable=SC2154 +trap ' + _exit_code=$? + set +x 2>/dev/null + if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then + cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true + echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" + fi + eval "${_original_exit_trap}" +' EXIT + +echo ">>> PHASE: initialization" + typeset -ri mcpWaitTimeout="${MCP_WAIT_TIMEOUT:-3600}" typeset -ri consecutiveRequired="${MCP_CONSECUTIVE_CHECKS:-3}" typeset -ri settleDelay="${MCP_SETTLE_DELAY:-90}" diff --git a/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-ref.yaml b/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-ref.yaml index 89dc495c8a77f..d1d69934db280 100644 --- a/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-ref.yaml +++ b/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-ref.yaml @@ -38,10 +38,8 @@ ref: - default: "3600" documentation: Max seconds to wait for all non-paused MCPs to stabilize (default 60 min) name: MCP_WAIT_TIMEOUT - - default: "false" - documentation: Enable verbose shell tracing (set -x) for debugging - name: DEBUG from: cli + grace_period: 1m resources: requests: cpu: 100m From 9f2ce53cb781645bbe7c613962f96a82de9c23b2 Mon Sep 17 00:00:00 2001 From: Chai Bot Date: Mon, 21 Sep 2026 04:50:01 +0000 Subject: [PATCH 4/4] INTEROP-9511: Fix OPP signal integrity follow-ups --- .../interop-tests-deploy-odf-commands.sh | 9 +- .../interop-tests-ocs-tests-commands.sh | 11 +- .../interop-tests-opp-quay-smoke-commands.sh | 11 +- .../opp/backup/interop-opp-backup-commands.sh | 11 +- .../interop-opp-observability-odf-commands.sh | 102 +++++++++--------- .../interop-opp-observability-odf-ref.yaml | 2 + .../interop-opp-odf-health-commands.sh | 14 ++- .../interop-opp-preflight-commands.sh | 11 +- ...nterop-opp-product-upgrade-acm-commands.sh | 50 ++++++--- ...nterop-opp-product-upgrade-acs-commands.sh | 46 +++++--- ...nterop-opp-product-upgrade-odf-commands.sh | 11 +- ...terop-opp-product-upgrade-quay-commands.sh | 11 +- .../opp/smoke/interop-opp-smoke-commands.sh | 14 ++- .../opp/smoke/interop-opp-smoke-ref.yaml | 1 - .../upgrade/interop-opp-upgrade-commands.sh | 11 +- .../wait-mcp/interop-opp-wait-mcp-commands.sh | 9 +- 16 files changed, 190 insertions(+), 134 deletions(-) diff --git a/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-commands.sh b/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-commands.sh index b6f1a218397b4..a4db8aded1414 100644 --- a/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-commands.sh +++ b/ci-operator/step-registry/interop-tests/deploy-odf/interop-tests-deploy-odf-commands.sh @@ -8,17 +8,18 @@ exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" diff --git a/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-commands.sh b/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-commands.sh index 3bfacbaafdcce..e7998ee10d27a 100644 --- a/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-commands.sh +++ b/ci-operator/step-registry/interop-tests/ocs-tests/interop-tests-ocs-tests-commands.sh @@ -9,17 +9,18 @@ exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" @@ -80,7 +81,7 @@ if [ "${MAP_TESTS}" = "true" ]; then ExitTrap--PostProcessPrep junit--odf__interop-tests__ocs-tests__interop-tests-ocs-tests.xml ' EXIT else - trap 'cleanup; _propagate_junit' EXIT + trap '_opp_cleanup; cleanup; _propagate_junit' EXIT fi # 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 00cfcc7cf9273..1121bad984b07 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 @@ -7,17 +7,18 @@ exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" @@ -103,7 +104,7 @@ _propagate_junit () { find "${ARTIFACT_DIR}" -name '*.xml' -exec cp {} "${SHARED_DIR}/junit/" \; 2>/dev/null || true } -trap '{( GenerateJunit; _propagate_junit; true )}' EXIT +trap '_opp_cleanup; GenerateJunit; _propagate_junit' EXIT function DiscoverQuay () { typeset registryJson="" discoverErr="" diff --git a/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-commands.sh b/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-commands.sh index 0b6e07dbe1adf..45d2cc82acef7 100644 --- a/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-commands.sh +++ b/ci-operator/step-registry/interop/opp/backup/interop-opp-backup-commands.sh @@ -8,17 +8,18 @@ exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" @@ -63,7 +64,7 @@ TimeoutMonitor() { # Start timeout monitor in background TimeoutMonitor & typeset timeoutPid=$! -trap 'kill ${timeoutPid} || true' EXIT +trap '_opp_cleanup; kill ${timeoutPid} || true' EXIT trap 'kill ${timeoutPid} || true; exit 124' TERM echo ">>> PHASE: Pre-Upgrade Cluster Backup" diff --git a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh index 07b345a55ff07..c5f8bbe33d808 100755 --- a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh +++ b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-commands.sh @@ -1,23 +1,30 @@ #!/bin/bash set -euo pipefail; shopt -s inherit_errexit +# === Known-Issue Skip Framework === +# This script uses _detect_known_issue() to emit JUnit SKIPPED results +# for tracked bugs instead of failing the job. Unknown failures still FAIL. +# Tracked issues: INTEROP-9455 +# See PR review Fix 3 for rationale. + # --- Trace-to-file: always capture, dump on failure only --- _xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" @@ -55,13 +62,16 @@ function AddResult () { true } +# XmlEscape: Required for bash 5.x where patsub_replacement is enabled +# by default, changing how ${var//pattern/replacement} handles & and \ in +# the replacement string. Without escaping, JUnit XML output is malformed. function XmlEscape () { typeset text="${1:-}"; (($#)) && shift - text="${text//&/&}" - text="${text///>}" - text="${text//\"/"}" - text="${text//\'/'}" + text="${text//&/\&}" + text="${text///\>}" + text="${text//\"/\"}" + text="${text//\'/\'}" printf '%s' "${text}" true } @@ -123,7 +133,7 @@ _propagate_junit () { find "${ARTIFACT_DIR}" -name '*.xml' -exec cp {} "${SHARED_DIR}/junit/" \; 2>/dev/null || true } -trap '{( CollectExitArtifacts; _propagate_junit; true )}' EXIT +trap '_opp_cleanup; CollectExitArtifacts; _propagate_junit' EXIT # --------------------------------------------------------------------------- # Check 1: ODF Ceph RGW infrastructure ready @@ -225,9 +235,11 @@ function CheckMcoReady () { typeset -i maxAttempts=24 typeset -i sleepSeconds=30 + # Timeout budget: 24 x 30s poll = 720s max + ~180s margin within 900s step timeout typeset -i attempt=0 typeset -i startTime=0 startTime=$(date +%s) + typeset _last_mco_error="" # Fast-path: if the CR doesn't exist at all, skip immediately. # Capture exit status separately so RBAC/connectivity errors are not @@ -260,6 +272,7 @@ function CheckMcoReady () { return 0 fi queryFailed="true" + _last_mco_error="${mcoError}" elif ! mcoStatus="$(printf '%s' "${mcoConditions}" | python3 -c " import sys,json raw=sys.stdin.read().strip() @@ -299,7 +312,11 @@ print(ready[0].get('status','Unknown') if ready else 'NoCondition') typeset -i elapsed=0 elapsed=$(( $(date +%s) - startTime )) - AddResult "mco-ready" "fail" "MultiClusterObservability not Ready after ${elapsed}s (last status=${mcoStatus:-unknown})" + typeset _mco_detail="MultiClusterObservability not Ready after ${elapsed}s (last status=${mcoStatus:-unknown})" + if [[ -n "${_last_mco_error}" ]]; then + _mco_detail="${_mco_detail}; last error: ${_last_mco_error:0:200}" + fi + AddResult "mco-ready" "fail" "${_mco_detail}" return 1 } @@ -740,32 +757,26 @@ print(items[0]['metadata']['name'] if items else '') # Main # --------------------------------------------------------------------------- -_xml_escape() { - local text="$1" - text="${text//&/&}" - text="${text///>}" - text="${text//\"/"}" - text="${text//\'/'}" - printf '%s' "${text}" -} - +# JUnit fragment contract: ci-operator's junit_report.go accepts both +# standalone fragments and full -wrapped documents. +# Fragments are appended to junit_known_issues.xml and consumed correctly. _detect_known_issue() { local error_output="$1" local bug_id="$2" local bug_description="$3" - local safe_desc safe_error + local safe_bug_id safe_desc safe_error - safe_desc="$(_xml_escape "${bug_description}")" - safe_error="$(_xml_escape "${error_output:0:500}")" + safe_bug_id="$(XmlEscape "${bug_id}")" + safe_desc="$(XmlEscape "${bug_description}")" + safe_error="$(XmlEscape "${error_output:0:500}")" echo ">>> KNOWN ISSUE: ${bug_id} — ${bug_description}" echo ">>> Marking as SKIPPED (tracked: https://issues.redhat.com/browse/${bug_id})" cat <> "${ARTIFACT_DIR}/junit_known_issues.xml" - - - Tracked at https://issues.redhat.com/browse/${bug_id} + + + Tracked at https://issues.redhat.com/browse/${safe_bug_id} Error: ${safe_error} @@ -790,44 +801,31 @@ function Main () { CheckObcBound || true CheckThanosQuery || true - # --- Reclassify known issues BEFORE writing JUnit --- - # Check for INTEROP-9455 (Thanos empty result) and reclassify as skip - # so the JUnit XML reflects the skip rather than a failure. - typeset -i _has_known_issue=0 - typeset _fail_details="" typeset -i _idx=0 for _idx in "${!tcResultsArr[@]}"; do - if [[ "${tcResultsArr[$_idx]}" == "fail" ]]; then - _fail_details="${_fail_details} ${tcNamesArr[$_idx]}: ${tcMessagesArr[$_idx]};" - if [[ "${tcMessagesArr[$_idx]}" == *"empty result vector"* ]]; then - # Reclassify this specific failure as skip in the results array - tcResultsArr[$_idx]="skip" - tcMessagesArr[$_idx]="Known issue INTEROP-9455: ${tcMessagesArr[$_idx]}" - _has_known_issue=1 - fi + if [[ "${tcNamesArr[$_idx]}" == "thanos-query" \ + && "${tcResultsArr[$_idx]}" == "fail" \ + && "${tcMessagesArr[$_idx]}" == *"returned empty result vector"* ]]; then + # Known-issue skip: INTEROP-9455 + # Added: 2026-09-21 + # Review-by: 2026-12-21 (or when INTEROP-9455 is resolved) + # Owner: OPP-interop team + _detect_known_issue "${tcMessagesArr[$_idx]}" "INTEROP-9455" \ + "Thanos query returns empty result during observability convergence" + tcResultsArr[$_idx]="skip" fi done - if (( _has_known_issue )); then - _detect_known_issue "${_fail_details}" "INTEROP-9455" \ - "Thanos query returns empty result during observability convergence" - fi WriteJunit - typeset -i hasAnyFail=0 typeset r="" for r in "${tcResultsArr[@]}"; do if [[ "${r}" == "fail" ]]; then - hasAnyFail=1 - break + : "ACM Observability + ODF Interop: SOME CHECKS FAILED" + exit 1 fi done - if (( hasAnyFail )); then - : "ACM Observability + ODF Interop: SOME CHECKS FAILED" - exit 1 - fi - : "ACM Observability + ODF Interop: ALL PASSED" exit 0 } diff --git a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-ref.yaml b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-ref.yaml index b38c8511d3e78..e8f25f5d1fe5f 100644 --- a/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-ref.yaml +++ b/ci-operator/step-registry/interop/opp/observability-odf/interop-opp-observability-odf-ref.yaml @@ -26,6 +26,8 @@ ref: - default: "openshift-storage" documentation: Namespace where ODF is installed name: ODF_NAMESPACE + # NOTE: best_effort was intentionally removed to surface step failures. + # Known-issue skips (INTEROP-9455) now handle expected failures explicitly. from: cli grace_period: 30s resources: diff --git a/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-commands.sh b/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-commands.sh index df44b18066aee..9a1bce2247881 100755 --- a/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-commands.sh +++ b/ci-operator/step-registry/interop/opp/odf-health/interop-opp-odf-health-commands.sh @@ -7,17 +7,18 @@ exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" @@ -84,6 +85,9 @@ function AddResult () { true } +# XmlEscape: Required for bash 5.x where patsub_replacement is enabled +# by default, changing how ${var//pattern/replacement} handles & and \ in +# the replacement string. Without escaping, JUnit XML output is malformed. function XmlEscape () { typeset text="${1:-}"; (($#)) && shift text="${text//&/&}" @@ -172,7 +176,7 @@ _propagate_junit () { find "${ARTIFACT_DIR}" -name '*.xml' -exec cp {} "${SHARED_DIR}/junit/" \; 2>/dev/null || true } -trap '{( CollectExitArtifacts; _propagate_junit; true )}' EXIT +trap '_opp_cleanup; CollectExitArtifacts; _propagate_junit' EXIT # --------------------------------------------------------------------------- # Check 1: ODF Operator CSV in Succeeded phase diff --git a/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-commands.sh b/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-commands.sh index 23ed2a5d45915..afc97064e3979 100755 --- a/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-commands.sh +++ b/ci-operator/step-registry/interop/opp/preflight/interop-opp-preflight-commands.sh @@ -9,17 +9,18 @@ exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" @@ -65,7 +66,7 @@ function DebugOnExit () { true } -trap '{ EXIT_CODE=$?; DebugOnExit; true; }' EXIT +trap '_opp_cleanup; EXIT_CODE=${_exit_code}; DebugOnExit' EXIT trap '{ EXIT_CODE=143; DebugOnExit; trap - EXIT; exit 143; }' TERM # ────────────────────────────────────────────────────────────────────── diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-commands.sh b/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-commands.sh index 8fd946ef330b5..b2a98feed755b 100755 --- a/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-commands.sh +++ b/ci-operator/step-registry/interop/opp/product-upgrade/acm/interop-opp-product-upgrade-acm-commands.sh @@ -2,23 +2,30 @@ set -euo pipefail shopt -s inherit_errexit +# === Known-Issue Skip Framework === +# This script uses _detect_known_issue() to emit JUnit SKIPPED results +# for tracked bugs instead of failing the job. Unknown failures still FAIL. +# Tracked issues: ACM-45920 +# See PR review Fix 3 for rationale. + # --- Trace-to-file: always capture, dump on failure only --- _xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" @@ -49,7 +56,7 @@ function CollectDiagnostics () { true } -trap 'if (( $? != 0 )); then CollectDiagnostics; fi' EXIT +trap '_opp_cleanup; if (( _exit_code != 0 )); then CollectDiagnostics; fi' EXIT function GetCurrentCsv () { oc get subscription "${ACM_SUBSCRIPTION_NAME}" \ @@ -330,22 +337,29 @@ function ValidateHubHealth () { return 0 } +# _xml_escape: Required for bash 5.x where patsub_replacement is enabled +# by default, changing how ${var//pattern/replacement} handles & and \ in +# the replacement string. Without escaping, JUnit XML output is malformed. _xml_escape() { local text="$1" - text="${text//&/&}" - text="${text///>}" - text="${text//\"/"}" - text="${text//\'/'}" + text="${text//&/\&}" + text="${text///\>}" + text="${text//\"/\"}" + text="${text//\'/\'}" printf '%s' "${text}" } +# JUnit fragment contract: ci-operator's junit_report.go accepts both +# standalone fragments and full -wrapped documents. +# Fragments are appended to junit_known_issues.xml and consumed correctly. _detect_known_issue() { local error_output="$1" local bug_id="$2" local bug_description="$3" - local safe_desc safe_error + local safe_bug_id safe_desc safe_error + safe_bug_id="$(_xml_escape "${bug_id}")" safe_desc="$(_xml_escape "${bug_description}")" safe_error="$(_xml_escape "${error_output:0:500}")" @@ -353,9 +367,9 @@ _detect_known_issue() { echo ">>> Marking as SKIPPED (tracked: https://issues.redhat.com/browse/${bug_id})" cat <> "${ARTIFACT_DIR}/junit_known_issues.xml" - - - Tracked at https://issues.redhat.com/browse/${bug_id} + + + Tracked at https://issues.redhat.com/browse/${safe_bug_id} Error: ${safe_error} @@ -460,6 +474,10 @@ function Main () { if ! _acm_upgrade_output="$(ValidateMceUpgrade 2>&1)"; then echo "${_acm_upgrade_output}" if echo "${_acm_upgrade_output}" | grep -q "cannot unmarshal string into Go struct"; then + # Known-issue skip: ACM-45920 + # Added: 2026-09-21 + # Review-by: 2026-12-21 (or when ACM-45920 is resolved) + # Owner: OPP-interop team _detect_known_issue "${_acm_upgrade_output}" "ACM-45920" \ "YAML unmarshal error on OCP 5.0 — ACM team fix in progress" else @@ -472,6 +490,10 @@ function Main () { if ! _acm_upgrade_output="$(ValidateHubHealth 2>&1)"; then echo "${_acm_upgrade_output}" if echo "${_acm_upgrade_output}" | grep -q "cannot unmarshal string into Go struct"; then + # Known-issue skip: ACM-45920 + # Added: 2026-09-21 + # Review-by: 2026-12-21 (or when ACM-45920 is resolved) + # Owner: OPP-interop team _detect_known_issue "${_acm_upgrade_output}" "ACM-45920" \ "YAML unmarshal error on OCP 5.0 — ACM team fix in progress" else diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-commands.sh b/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-commands.sh index 5b26005a32a9c..de85a1b391c4a 100755 --- a/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-commands.sh +++ b/ci-operator/step-registry/interop/opp/product-upgrade/acs/interop-opp-product-upgrade-acs-commands.sh @@ -2,23 +2,30 @@ set -euo pipefail shopt -s inherit_errexit +# === Known-Issue Skip Framework === +# This script uses _detect_known_issue() to emit JUnit SKIPPED results +# for tracked bugs instead of failing the job. Unknown failures still FAIL. +# Tracked issues: INTEROP-9466 +# See PR review Fix 3 for rationale. + # --- Trace-to-file: always capture, dump on failure only --- _xtrace_log="/tmp/xtrace-$(basename "$0" .sh).log" exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" @@ -50,7 +57,7 @@ function CollectDiagnostics () { true } -trap 'if (( $? != 0 )); then CollectDiagnostics; fi' EXIT +trap '_opp_cleanup; if (( _exit_code != 0 )); then CollectDiagnostics; fi' EXIT function GetCurrentCsv () { oc get subscription "${ACS_SUBSCRIPTION_NAME}" \ @@ -307,22 +314,29 @@ function ValidateAcsHealth () { return 0 } +# _xml_escape: Required for bash 5.x where patsub_replacement is enabled +# by default, changing how ${var//pattern/replacement} handles & and \ in +# the replacement string. Without escaping, JUnit XML output is malformed. _xml_escape() { local text="$1" - text="${text//&/&}" - text="${text///>}" - text="${text//\"/"}" - text="${text//\'/'}" + text="${text//&/\&}" + text="${text///\>}" + text="${text//\"/\"}" + text="${text//\'/\'}" printf '%s' "${text}" } +# JUnit fragment contract: ci-operator's junit_report.go accepts both +# standalone fragments and full -wrapped documents. +# Fragments are appended to junit_known_issues.xml and consumed correctly. _detect_known_issue() { local error_output="$1" local bug_id="$2" local bug_description="$3" - local safe_desc safe_error + local safe_bug_id safe_desc safe_error + safe_bug_id="$(_xml_escape "${bug_id}")" safe_desc="$(_xml_escape "${bug_description}")" safe_error="$(_xml_escape "${error_output:0:500}")" @@ -330,9 +344,9 @@ _detect_known_issue() { echo ">>> Marking as SKIPPED (tracked: https://issues.redhat.com/browse/${bug_id})" cat <> "${ARTIFACT_DIR}/junit_known_issues.xml" - - - Tracked at https://issues.redhat.com/browse/${bug_id} + + + Tracked at https://issues.redhat.com/browse/${safe_bug_id} Error: ${safe_error} @@ -437,6 +451,10 @@ function Main () { if ! _acs_upgrade_output="$(ValidateAcsHealth 2>&1)"; then echo "${_acs_upgrade_output}" if echo "${_acs_upgrade_output}" | grep -q "SecuredCluster did not reach Deployed"; then + # Known-issue skip: INTEROP-9466 + # Added: 2026-09-21 + # Review-by: 2026-12-21 (or when INTEROP-9466 is resolved) + # Owner: OPP-interop team _detect_known_issue "${_acs_upgrade_output}" "INTEROP-9466" \ "SecuredCluster reconciliation delay after ACS upgrade" else diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-commands.sh b/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-commands.sh index 011a85a1f7635..e163f794700c2 100755 --- a/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-commands.sh +++ b/ci-operator/step-registry/interop/opp/product-upgrade/odf/interop-opp-product-upgrade-odf-commands.sh @@ -8,17 +8,18 @@ exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" @@ -50,7 +51,7 @@ function CollectDiagnostics () { true } -trap 'if (( $? != 0 )); then CollectDiagnostics; fi' EXIT +trap '_opp_cleanup; if (( _exit_code != 0 )); then CollectDiagnostics; fi' EXIT function GetCurrentCsv () { oc get subscription "${ODF_SUBSCRIPTION_NAME}" \ diff --git a/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-commands.sh b/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-commands.sh index b991bf474c2af..a308ddb493da0 100755 --- a/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-commands.sh +++ b/ci-operator/step-registry/interop/opp/product-upgrade/quay/interop-opp-product-upgrade-quay-commands.sh @@ -8,17 +8,18 @@ exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" @@ -50,7 +51,7 @@ function CollectDiagnostics () { true } -trap 'if (( $? != 0 )); then CollectDiagnostics; fi' EXIT +trap '_opp_cleanup; if (( _exit_code != 0 )); then CollectDiagnostics; fi' EXIT function GetCurrentCsv () { oc get subscription "${QUAY_SUBSCRIPTION_NAME}" \ diff --git a/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-commands.sh b/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-commands.sh index 22d89d3c6c157..1e406137330c7 100644 --- a/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-commands.sh +++ b/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-commands.sh @@ -7,17 +7,18 @@ exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" @@ -54,6 +55,9 @@ AddResult() { true } +# XmlEscape: Required for bash 5.x where patsub_replacement is enabled +# by default, changing how ${var//pattern/replacement} handles & and \ in +# the replacement string. Without escaping, JUnit XML output is malformed. XmlEscape() { typeset text="${1:-}"; (($#)) && shift text="${text//&/&}" @@ -113,7 +117,7 @@ _propagate_junit () { find "${ARTIFACT_DIR}" -name '*.xml' -exec cp {} "${SHARED_DIR}/junit/" \; 2>/dev/null || true } -trap '{( CollectExitArtifacts; _propagate_junit; true )}' EXIT +trap '_opp_cleanup; CollectExitArtifacts; _propagate_junit' EXIT # --------------------------------------------------------------------------- # Test 1: cluster-health diff --git a/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-ref.yaml b/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-ref.yaml index 87acc8f200c8a..433eb51653816 100644 --- a/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-ref.yaml +++ b/ci-operator/step-registry/interop/opp/smoke/interop-opp-smoke-ref.yaml @@ -7,7 +7,6 @@ ref: status, ACM managed-cluster connectivity, ACS sensor status, and Quay registry reachability. Produces JUnit XML for Prow / Sippy / TestGrid consumption. - env: from: cli grace_period: 30s resources: diff --git a/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-commands.sh b/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-commands.sh index eddc549435e31..af756e5ff73a4 100644 --- a/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-commands.sh +++ b/ci-operator/step-registry/interop/opp/upgrade/interop-opp-upgrade-commands.sh @@ -8,17 +8,18 @@ exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization" @@ -87,7 +88,7 @@ function DebugOnExit () { true } -trap '{ exitCode=$?; DebugOnExit; true; }' EXIT +trap '_opp_cleanup; exitCode=${_exit_code}; DebugOnExit' EXIT trap '{ exitCode=143; DebugOnExit; trap - EXIT; exit 143; }' TERM set +x diff --git a/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-commands.sh b/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-commands.sh index 5ec4a3234b646..fd9aaa9cced81 100755 --- a/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-commands.sh +++ b/ci-operator/step-registry/interop/opp/wait-mcp/interop-opp-wait-mcp-commands.sh @@ -9,17 +9,18 @@ exec {_xtrace_fd}>"${_xtrace_log}" BASH_XTRACEFD=${_xtrace_fd} set -x -_original_exit_trap="$(trap -p EXIT | sed "s/^trap -- '//;s/' EXIT$//")" # shellcheck disable=SC2154 -trap ' +_opp_cleanup() { _exit_code=$? set +x 2>/dev/null + # Scrub credentials before copying + sed -i -E 's/(password|token|secret|key|credential)=[^ ]*/\1=REDACTED/gi' "${_xtrace_log}" 2>/dev/null || true if [[ ${_exit_code} -ne 0 && -n "${ARTIFACT_DIR:-}" ]]; then cp "${_xtrace_log}" "${ARTIFACT_DIR}/" 2>/dev/null || true echo ">>> TRACE: xtrace log saved to artifacts (exit code ${_exit_code})" fi - eval "${_original_exit_trap}" -' EXIT +} +trap '_opp_cleanup' EXIT echo ">>> PHASE: initialization"