From ec13353f443d1b5f9c4873a916b94355472cd743 Mon Sep 17 00:00:00 2001 From: Aleksandr Kurlov Date: Thu, 2 Jul 2026 13:58:16 +0200 Subject: [PATCH 01/12] Add ACS smoke test --- .github/workflows/acs-smoke-test.yml | 139 ++++++++++++++++ smoke-test/lib.sh | 230 +++++++++++++++++++++++++++ smoke-test/upgrade-latest-test.sh | 60 +++++++ smoke-test/upgrade-oldest-test.sh | 53 ++++++ 4 files changed, 482 insertions(+) create mode 100644 .github/workflows/acs-smoke-test.yml create mode 100755 smoke-test/lib.sh create mode 100755 smoke-test/upgrade-latest-test.sh create mode 100755 smoke-test/upgrade-oldest-test.sh diff --git a/.github/workflows/acs-smoke-test.yml b/.github/workflows/acs-smoke-test.yml new file mode 100644 index 00000000..4bb51f8b --- /dev/null +++ b/.github/workflows/acs-smoke-test.yml @@ -0,0 +1,139 @@ +name: "ACS Smoke Test" + +on: + workflow_dispatch: + inputs: + operator-index-image: + description: 'ACS Operator index image (e.g., quay.io/rhacs-eng/stackrox-operator-index:ocp-v4-22-...)' + required: true + type: string + acs-version: + description: 'ACS minor version under test (e.g. 4.10). Determines channel and Y-2 oldest.' + required: true + type: string + cluster-lifespan: + description: 'Cluster lifespan (e.g., 2h, 4h, 8h)' + required: false + default: '2h' + type: string + +env: + CLUSTER_NAME: smoke-${{ github.run_number }} + GH_TOKEN: ${{ github.token }} + GH_NO_UPDATE_NOTIFIER: 1 + +run-name: >- + ${{ format('ACS Smoke Test - {0}', inputs.operator-index-image) }} + +jobs: + create-cluster: + name: Create OpenShift cluster + runs-on: ubuntu-latest + outputs: + cluster-name: ${{ steps.info.outputs.name }} + steps: + - name: Set cluster name + id: info + run: echo "name=${{ env.CLUSTER_NAME }}" >> "$GITHUB_OUTPUT" + + - name: Create OpenShift cluster + uses: stackrox/actions/infra/create-cluster@v1 + with: + token: ${{ secrets.INFRA_TOKEN }} + flavor: ocp-default + name: ${{ env.CLUSTER_NAME }} + lifespan: ${{ inputs.cluster-lifespan }} + args: nodes=3,machine-type=e2-standard-8 + wait: true + + run-smoke-tests: + name: Run ACS smoke tests + needs: create-cluster + runs-on: ubuntu-latest + container: + image: quay.io/stackrox-io/apollo-ci:stackrox-test-0.5.11@sha256:df3459cf038e73775314b3a5d36d14dda41e6df438a0273c9efa8d23e0f2358a + steps: + - name: Check out code + uses: actions/checkout@v7 + + - name: Install infractl + uses: stackrox/actions/infra/install-infractl@v1 + + - name: Authenticate to GCP + uses: google-github-actions/auth@v3 + with: + credentials_json: ${{ secrets.GCP_RELEASE_AUTOMATION_SA }} + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v3 + + - name: Download cluster kubeconfig + run: | + infractl artifacts \ + --cluster-name=${{ needs.create-cluster.outputs.cluster-name }} \ + --download-dir=./artifacts + + - name: Set KUBECONFIG + run: echo "KUBECONFIG=${{ github.workspace }}/artifacts/kubeconfig" >> "$GITHUB_ENV" + + - name: "Test 1: upgrade oldest-supported → provided" + working-directory: ./smoke-test + env: + OPERATOR_INDEX_IMAGE: ${{ inputs.operator-index-image }} + ACS_VERSION: ${{ inputs.acs-version }} + run: bash upgrade-oldest-test.sh + + - name: Reset operator state between tests + working-directory: ./smoke-test + run: | + source lib.sh + reset_operator + shell: bash + + - name: "Test 2: install provided → optionally upgrade to latest GA" + working-directory: ./smoke-test + env: + OPERATOR_INDEX_IMAGE: ${{ inputs.operator-index-image }} + ACS_VERSION: ${{ inputs.acs-version }} + run: bash upgrade-latest-test.sh + + cleanup-cluster: + name: Cleanup cluster + needs: [create-cluster, run-smoke-tests] + runs-on: ubuntu-latest + if: always() && needs.create-cluster.result != 'cancelled' + steps: + - name: Install infractl + uses: stackrox/actions/infra/install-infractl@v1 + + - name: Authenticate to GCP + uses: google-github-actions/auth@v3 + with: + credentials_json: ${{ secrets.GCP_RELEASE_AUTOMATION_SA }} + + - name: Delete cluster + run: infractl delete --cluster-name=${{ needs.create-cluster.outputs.cluster-name }} + + report-status: + name: Report test status + needs: [create-cluster, run-smoke-tests, cleanup-cluster] + runs-on: ubuntu-latest + if: always() + steps: + - name: Write summary + run: | + cat <<'EOF' >> "$GITHUB_STEP_SUMMARY" + # ACS Smoke Test Results + + | Field | Value | + |-------|-------| + | **Operator image** | `${{ inputs.operator-index-image }}` | + | **Cluster** | `${{ needs.create-cluster.outputs.cluster-name }}` | + | **Create cluster** | ${{ needs.create-cluster.result }} | + | **Smoke tests** | ${{ needs.run-smoke-tests.result }} | + | **Cleanup** | ${{ needs.cleanup-cluster.result }} | + + ## Tests run: + 1. **Test 1** — install oldest supported ACS version, upgrade to the provided operator-index image + 2. **Test 2** — install provided operator-index image, upgrade to latest GA if minor is behind + EOF diff --git a/smoke-test/lib.sh b/smoke-test/lib.sh new file mode 100755 index 00000000..fda048d7 --- /dev/null +++ b/smoke-test/lib.sh @@ -0,0 +1,230 @@ +#!/usr/bin/env bash +# Shared functions for ACS operator smoke tests. + +set -euo pipefail + +# Logging helpers +step() { echo; echo "══════════════════════════════════════════════════════"; echo " $*"; echo "══════════════════════════════════════════════════════"; } +info() { echo " $*"; } +ok() { echo " ✅ $*"; } +warn() { echo " ⚠️ $*"; } +fail() { echo " ❌ $*" >&2; return 1; } + +# Splits "4.10" → ACS_MAJOR=4, ACS_MINOR=10 +parse_acs_version() { + local ver="$1" + ACS_MAJOR=$(echo "$ver" | cut -d. -f1) + ACS_MINOR=$(echo "$ver" | cut -d. -f2) + [[ "$ACS_MAJOR" =~ ^[0-9]+$ && "$ACS_MINOR" =~ ^[0-9]+$ ]] \ + || fail "ACS_VERSION must be MAJOR.MINOR (e.g. 4.10), got: ${ver}" +} + +# Prints the highest minor version in rhacs-MAJOR.N channels from the official catalog. +get_latest_official_minor() { + local major="${1:-4}" + oc get packagemanifest -n openshift-marketplace \ + -l catalog=redhat-operators \ + -o jsonpath='{range .items[?(@.metadata.name=="rhacs-operator")].status.channels[*]}{.name}{"\n"}{end}' \ + | grep -oE "rhacs-${major}\.[0-9]+" \ + | grep -oE '[0-9]+$' \ + | sort -n \ + | tail -1 \ + || true +} + +disable_default_sources() { + info "Disabling default OperatorHub sources..." + oc patch OperatorHub cluster --type json \ + -p '[{"op":"add","path":"/spec/disableAllDefaultSources","value":true}]' + ok "Default sources disabled" +} + +enable_default_sources() { + info "Enabling default OperatorHub sources..." + oc patch OperatorHub cluster --type json \ + -p '[{"op":"add","path":"/spec/disableAllDefaultSources","value":false}]' + ok "Default sources enabled" +} + +apply_custom_catalog() { + local index_image="$1" + info "Applying custom CatalogSource (image: ${index_image})..." + oc apply -f - </dev/null || true) + if [[ "$state" == "READY" ]]; then + ok "CatalogSource/${name} is READY" + return 0 + fi + info " state=${state:-pending}, retrying in 10s..." + sleep 10 + done + fail "CatalogSource/${name} not READY within ${timeout}s" +} + +apply_subscription() { + local channel="$1" source="$2" source_ns="${3:-openshift-marketplace}" + info "Applying Subscription (channel=${channel}, source=${source})..." + oc apply -f - </dev/null || true) + if [[ -n "$TARGET_CSV" ]]; then + info "Target CSV: ${TARGET_CSV}" + return 0 + fi + info " packagemanifest not ready yet, retrying in 10s..." + sleep 10 + done + fail "Could not resolve currentCSV for channel ${channel} from ${catalog_label} after 60s" +} + +# Install from official redhat-operators at channel rhacs-MAJOR.MINOR. +# Sets TARGET_CSV to the channel's currentCSV. +install_from_official() { + local major="$1" minor="$2" + local channel="rhacs-${major}.${minor}" + info "Installing ACS Operator from redhat-operators, channel ${channel}..." + wait_for_catalog "redhat-operators" "openshift-marketplace" 120 + _resolve_target_csv "redhat-operators" "$channel" + apply_subscription "$channel" "redhat-operators" +} + +# Install from the custom index image on the given channel (disables default sources). +# Sets TARGET_CSV to the channel's currentCSV. +install_from_custom() { + local index_image="$1" channel="$2" + info "Installing ACS Operator from custom index, channel ${channel}..." + disable_default_sources + apply_custom_catalog "$index_image" + wait_for_catalog "my-operator-catalog" + _resolve_target_csv "my-operator-catalog" "$channel" + apply_subscription "$channel" "my-operator-catalog" +} + +# Upgrade by refreshing the custom CatalogSource and pointing the subscription at it. +# Sets TARGET_CSV to the channel's currentCSV. +upgrade_via_custom() { + local index_image="$1" channel="$2" + info "Upgrading via custom catalog (channel: ${channel})..." + apply_custom_catalog "$index_image" + disable_default_sources + wait_for_catalog "my-operator-catalog" + _resolve_target_csv "my-operator-catalog" "$channel" + apply_subscription "$channel" "my-operator-catalog" + ok "Subscription updated — OLM will upgrade to ${TARGET_CSV}" +} + +# Upgrade to latest official GA via redhat-operators. +# Sets TARGET_CSV to the latest channel's currentCSV. +upgrade_to_latest_official() { + local major="${1:-4}" + enable_default_sources + wait_for_catalog "redhat-operators" "openshift-marketplace" 180 + local latest_minor + latest_minor=$(get_latest_official_minor "$major") || true + [[ -n "$latest_minor" ]] || fail "Could not determine latest GA minor from redhat-operators" + local channel="rhacs-${major}.${latest_minor}" + info "Upgrading to latest GA channel: ${channel}..." + _resolve_target_csv "redhat-operators" "$channel" + apply_subscription "$channel" "redhat-operators" + ok "Subscription updated to ${channel}, target: ${TARGET_CSV}" +} + +get_current_csv() { + oc get csv -n openshift-operators --no-headers 2>/dev/null \ + | awk '/rhacs-operator/ && /Succeeded/ {print $1}' \ + | head -1 +} + +# Waits for a specific CSV to reach Succeeded phase. +# Use after install/upgrade to wait for the exact target version, not just any change. +# This correctly handles multi-hop OLM upgrade graphs (e.g. 4.8→4.9→4.10). +wait_for_csv() { + local target_csv="$1" timeout="${2:-600}" + local deadline + deadline=$(( $(date +%s) + timeout )) + info "Waiting for ${target_csv} to reach Succeeded (timeout: ${timeout}s)..." + while (( $(date +%s) < deadline )); do + local phase + phase=$(oc get csv "$target_csv" -n openshift-operators \ + -o jsonpath='{.status.phase}' 2>/dev/null || true) + if [[ "$phase" == "Succeeded" ]]; then + ok "${target_csv} is Succeeded" + return 0 + fi + # Show in-progress CSVs so logs show the hop chain + local progress + progress=$(oc get csv -n openshift-operators --no-headers 2>/dev/null \ + | awk '/rhacs-operator/ {printf "%s(%s) ", $1, $7}' || true) + info " ${progress:+CSVs: ${progress}}phase=${phase:-not found}, waiting 15s..." + sleep 15 + done + fail "${target_csv} did not reach Succeeded within ${timeout}s" +} + +log_csv() { + local csv + csv=$(oc get csv -n openshift-operators --no-headers 2>/dev/null \ + | grep rhacs-operator || echo "") + info "Current CSV: ${csv}" +} + +# Reset between tests +reset_operator() { + info "Resetting operator state for next test..." + oc delete subscription rhacs-operator -n openshift-operators --ignore-not-found + oc get csv -n openshift-operators --no-headers 2>/dev/null \ + | awk '/rhacs-operator/ {print $1}' \ + | xargs -r oc delete csv -n openshift-operators --ignore-not-found || true + oc delete catalogsource my-operator-catalog -n openshift-marketplace --ignore-not-found || true + enable_default_sources + ok "Operator state reset" +} diff --git a/smoke-test/upgrade-latest-test.sh b/smoke-test/upgrade-latest-test.sh new file mode 100755 index 00000000..97a8b861 --- /dev/null +++ b/smoke-test/upgrade-latest-test.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# Test 2 — Install provided version, optionally upgrade to latest GA +# +# 1. Installs ACS Operator from OPERATOR_INDEX_IMAGE on the ACS_VERSION channel +# 2. If ACS_VERSION minor < latest GA minor: upgrades to latest GA via redhat-operators +# 3. Verifies CSV reaches Succeeded after each step +# +# Required env vars: +# OPERATOR_INDEX_IMAGE custom operator index image +# ACS_VERSION ACS minor version to test, e.g. "4.10" + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=lib.sh +source "${SCRIPT_DIR}/lib.sh" + +OPERATOR_INDEX_IMAGE="${OPERATOR_INDEX_IMAGE:-${MY_INDEX_IMAGE:-}}" +ACS_VERSION="${ACS_VERSION:-}" + +[[ -n "$OPERATOR_INDEX_IMAGE" ]] || fail "OPERATOR_INDEX_IMAGE is required" +[[ -n "$ACS_VERSION" ]] || fail "ACS_VERSION is required (e.g. 4.10)" + +parse_acs_version "$ACS_VERSION" +CHANNEL="rhacs-${ACS_MAJOR}.${ACS_MINOR}" + +echo "======================================================================" +echo " TEST 2: Install provided → optionally upgrade to latest GA" +echo " Image: ${OPERATOR_INDEX_IMAGE}" +echo " Version: ${ACS_MAJOR}.${ACS_MINOR} | Channel: ${CHANNEL}" +echo "======================================================================" + +# Query latest GA minor NOW while redhat-operators is still enabled. +# install_from_custom() disables default sources, making this unavailable later. +LATEST_MINOR="" +wait_for_catalog "redhat-operators" "openshift-marketplace" 180 +if latest=$(get_latest_official_minor "$ACS_MAJOR") && [[ -n "$latest" ]]; then + LATEST_MINOR="$latest" + info "Latest GA in redhat-operators: ${ACS_MAJOR}.${LATEST_MINOR}" +else + warn "Could not determine latest GA minor — upgrade check will be skipped" +fi + +step "Step 1: Install ACS Operator ${CHANNEL} from custom index" +install_from_custom "$OPERATOR_INDEX_IMAGE" "$CHANNEL" +wait_for_csv "$TARGET_CSV" 600 +log_csv + +if [[ -n "$LATEST_MINOR" ]] && (( ACS_MINOR < LATEST_MINOR )); then + step "Step 2: Upgrade ${ACS_MAJOR}.${ACS_MINOR} → ${ACS_MAJOR}.${LATEST_MINOR} (latest GA)" + upgrade_to_latest_official "$ACS_MAJOR" + wait_for_csv "$TARGET_CSV" 600 + log_csv +elif [[ -n "$LATEST_MINOR" ]]; then + info "${ACS_MAJOR}.${ACS_MINOR} is already at latest GA minor (${LATEST_MINOR}) — no upgrade needed" +fi + +echo +echo "======================================================================" +echo " ✅ TEST 2 PASSED" +echo "======================================================================" diff --git a/smoke-test/upgrade-oldest-test.sh b/smoke-test/upgrade-oldest-test.sh new file mode 100755 index 00000000..e587234b --- /dev/null +++ b/smoke-test/upgrade-oldest-test.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# Test 1 — Upgrade from oldest supported to provided version +# +# 1. Installs ACS Operator (oldest_supported_version from bundles.yaml) from official redhat-operators +# 2. Upgrades to the provided OPERATOR_INDEX_IMAGE on the ACS_VERSION channel +# 3. Verifies CSV reaches Succeeded after each step +# +# Required env vars: +# OPERATOR_INDEX_IMAGE custom operator index image +# ACS_VERSION ACS minor version to test, e.g. "4.10" + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=lib.sh +source "${SCRIPT_DIR}/lib.sh" + +OPERATOR_INDEX_IMAGE="${OPERATOR_INDEX_IMAGE:-${MY_INDEX_IMAGE:-}}" +ACS_VERSION="${ACS_VERSION:-}" + +[[ -n "$OPERATOR_INDEX_IMAGE" ]] || fail "OPERATOR_INDEX_IMAGE is required" +[[ -n "$ACS_VERSION" ]] || fail "ACS_VERSION is required (e.g. 4.10)" + +parse_acs_version "$ACS_VERSION" +CHANNEL="rhacs-${ACS_MAJOR}.${ACS_MINOR}" + +BUNDLES_YAML="${SCRIPT_DIR}/../bundles.yaml" +[[ -f "$BUNDLES_YAML" ]] || fail "bundles.yaml not found at ${BUNDLES_YAML}" +OLDEST_VERSION=$(awk '/^oldest_supported_version:/{print $2; exit}' "$BUNDLES_YAML") +[[ -n "$OLDEST_VERSION" ]] || fail "oldest_supported_version not found in bundles.yaml" +OLDEST_MAJOR=$(echo "$OLDEST_VERSION" | cut -d. -f1) +OLDEST_MINOR=$(echo "$OLDEST_VERSION" | cut -d. -f2) + +echo "======================================================================" +echo " TEST 1: Upgrade oldest-supported → provided" +echo " Image: ${OPERATOR_INDEX_IMAGE}" +echo " Version: ${ACS_MAJOR}.${ACS_MINOR} | Channel: ${CHANNEL}" +echo " Oldest: ${OLDEST_MAJOR}.${OLDEST_MINOR} (from bundles.yaml)" +echo "======================================================================" + +step "Step 1: Install ACS Operator ${OLDEST_MAJOR}.${OLDEST_MINOR} from redhat-operators" +install_from_official "$OLDEST_MAJOR" "$OLDEST_MINOR" +wait_for_csv "$TARGET_CSV" 300 +log_csv + +step "Step 2: Upgrade to ${CHANNEL} via custom index" +upgrade_via_custom "$OPERATOR_INDEX_IMAGE" "$CHANNEL" +wait_for_csv "$TARGET_CSV" 1200 +log_csv + +echo +echo "======================================================================" +echo " ✅ TEST 1 PASSED" +echo "======================================================================" From 629ad02fd1a93bf16ae70730e2b7d38cc4628e3f Mon Sep 17 00:00:00 2001 From: Aleksandr Kurlov Date: Mon, 6 Jul 2026 11:52:46 +0200 Subject: [PATCH 02/12] Add auto test --- .github/workflows/acs-smoke-test.yml | 15 ++++ .github/workflows/auto-smoke-test.yml | 114 ++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 .github/workflows/auto-smoke-test.yml diff --git a/.github/workflows/acs-smoke-test.yml b/.github/workflows/acs-smoke-test.yml index 4bb51f8b..b9b62b3b 100644 --- a/.github/workflows/acs-smoke-test.yml +++ b/.github/workflows/acs-smoke-test.yml @@ -16,6 +16,21 @@ on: required: false default: '2h' type: string + workflow_call: + inputs: + operator-index-image: + description: 'ACS Operator index image' + required: true + type: string + acs-version: + description: 'ACS minor version under test (e.g. 4.10). Determines channel and Y-2 oldest.' + required: true + type: string + cluster-lifespan: + description: 'Cluster lifespan (e.g., 2h, 4h, 8h)' + required: false + default: '2h' + type: string env: CLUSTER_NAME: smoke-${{ github.run_number }} diff --git a/.github/workflows/auto-smoke-test.yml b/.github/workflows/auto-smoke-test.yml new file mode 100644 index 00000000..718cbf6a --- /dev/null +++ b/.github/workflows/auto-smoke-test.yml @@ -0,0 +1,114 @@ +name: "Auto Smoke Test" + +on: + workflow_dispatch: + inputs: + acs-version-override: + description: 'Override ACS version, skips bundles.yaml diff (e.g. "4.10")' + required: false + type: string + operator-index-image-override: + description: 'Override operator-index image, skips Quay polling (e.g. quay.io/rhacs-eng/stackrox-operator-index:ocp-v4-22-...)' + required: false + type: string + push: + branches: [master] + paths: ['bundles.yaml'] + pull_request: + paths: ['bundles.yaml'] + +env: + # OCP version used to construct the Konflux-built operator-index image tag. + # Update when infra cluster uses a newer OCP version. + OCP_VERSION: v4-22 + +jobs: + derive-inputs: + name: Derive smoke test inputs + runs-on: ubuntu-latest + outputs: + acs-version: ${{ steps.version.outputs.acs-version }} + operator-index-image: ${{ steps.image.outputs.operator-index-image }} + should-run: ${{ steps.version.outputs.should-run }} + steps: + - name: Check out code + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Determine ACS version + id: version + run: | + if [ -n "${{ inputs.acs-version-override }}" ]; then + echo "acs-version=${{ inputs.acs-version-override }}" >> "$GITHUB_OUTPUT" + echo "should-run=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + if [ "${{ github.event_name }}" = "pull_request" ]; then + DIFF_BASE="origin/${{ github.base_ref }}" + else + DIFF_BASE="HEAD~1" + fi + + NEW_VERSIONS=$(git diff "$DIFF_BASE" -- bundles.yaml \ + | grep '^+.*version:' \ + | grep -v '^+++' \ + | sed 's/.*version: *//' \ + | sort -V) + + if [ -z "$NEW_VERSIONS" ]; then + echo "No new versions found in bundles.yaml diff, skipping smoke test" + echo "should-run=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + LATEST=$(echo "$NEW_VERSIONS" | tail -1) + MINOR=$(echo "$LATEST" | cut -d. -f1-2) + echo "New versions added: $(echo "$NEW_VERSIONS" | tr '\n' ' '), testing minor: $MINOR" + echo "acs-version=$MINOR" >> "$GITHUB_OUTPUT" + echo "should-run=true" >> "$GITHUB_OUTPUT" + + - name: Determine operator-index image + id: image + if: steps.version.outputs.should-run == 'true' + run: | + if [ -n "${{ inputs.operator-index-image-override }}" ]; then + echo "operator-index-image=${{ inputs.operator-index-image-override }}" >> "$GITHUB_OUTPUT" + exit 0 + fi + + if [ "${{ github.event_name }}" = "pull_request" ]; then + SHA="${{ github.event.pull_request.head.sha }}" + else + SHA="${{ github.sha }}" + fi + + TAG="ocp-${OCP_VERSION}-${SHA}-fast" + IMAGE="quay.io/rhacs-eng/stackrox-operator-index:${TAG}" + echo "Waiting for Konflux to build: $IMAGE" + + for i in $(seq 1 40); do + RESPONSE=$(curl -sf "https://quay.io/api/v1/repository/rhacs-eng/stackrox-operator-index/tag/?specificTag=${TAG}" || echo '{}') + FOUND=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print('true' if d.get('tags') else 'false')" 2>/dev/null || echo 'false') + if [ "$FOUND" = "true" ]; then + echo "Image available after attempt $i: $IMAGE" + echo "operator-index-image=$IMAGE" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "Attempt $i/40: image not yet available, retrying in 3 minutes..." + sleep 180 + done + + echo "Timed out after 2 hours waiting for: $IMAGE" + exit 1 + + smoke-test: + name: Run smoke test + needs: derive-inputs + if: needs.derive-inputs.outputs.should-run == 'true' + uses: ./.github/workflows/acs-smoke-test.yml + with: + operator-index-image: ${{ needs.derive-inputs.outputs.operator-index-image }} + acs-version: ${{ needs.derive-inputs.outputs.acs-version }} + secrets: inherit From 76416946882d39238732a89af47ace386692d0e5 Mon Sep 17 00:00:00 2001 From: Aleksandr Kurlov Date: Mon, 6 Jul 2026 11:53:09 +0200 Subject: [PATCH 03/12] Fix testing --- smoke-test/lib.sh | 16 ++++++++-------- smoke-test/upgrade-latest-test.sh | 5 +++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/smoke-test/lib.sh b/smoke-test/lib.sh index fda048d7..03bf234f 100755 --- a/smoke-test/lib.sh +++ b/smoke-test/lib.sh @@ -8,7 +8,7 @@ step() { echo; echo "═══════════════════ info() { echo " $*"; } ok() { echo " ✅ $*"; } warn() { echo " ⚠️ $*"; } -fail() { echo " ❌ $*" >&2; return 1; } +fail() { echo " ❌ $*" >&2; exit 1; } # Splits "4.10" → ACS_MAJOR=4, ACS_MINOR=10 parse_acs_version() { @@ -105,11 +105,11 @@ EOF } # Sets TARGET_CSV to the currentCSV for a given channel from the specified catalog label. -# Retries for up to 60s because packagemanifests can lag behind catalog READY state. +# Retries for up to timeout (default 60s) because packagemanifests can lag behind catalog READY state. _resolve_target_csv() { - local catalog_label="$1" channel="$2" + local catalog_label="$1" channel="$2" timeout="${3:-60}" local deadline - deadline=$(( $(date +%s) + 60 )) + deadline=$(( $(date +%s) + timeout )) info "Resolving currentCSV for channel ${channel} from ${catalog_label}..." while (( $(date +%s) < deadline )); do TARGET_CSV=$(oc get packagemanifest -n openshift-marketplace \ @@ -123,7 +123,7 @@ _resolve_target_csv() { info " packagemanifest not ready yet, retrying in 10s..." sleep 10 done - fail "Could not resolve currentCSV for channel ${channel} from ${catalog_label} after 60s" + fail "Could not resolve currentCSV for channel ${channel} from ${catalog_label} after ${timeout}s" } # Install from official redhat-operators at channel rhacs-MAJOR.MINOR. @@ -133,7 +133,7 @@ install_from_official() { local channel="rhacs-${major}.${minor}" info "Installing ACS Operator from redhat-operators, channel ${channel}..." wait_for_catalog "redhat-operators" "openshift-marketplace" 120 - _resolve_target_csv "redhat-operators" "$channel" + _resolve_target_csv "redhat-operators" "$channel" 120 apply_subscription "$channel" "redhat-operators" } @@ -154,8 +154,8 @@ install_from_custom() { upgrade_via_custom() { local index_image="$1" channel="$2" info "Upgrading via custom catalog (channel: ${channel})..." - apply_custom_catalog "$index_image" disable_default_sources + apply_custom_catalog "$index_image" wait_for_catalog "my-operator-catalog" _resolve_target_csv "my-operator-catalog" "$channel" apply_subscription "$channel" "my-operator-catalog" @@ -173,7 +173,7 @@ upgrade_to_latest_official() { [[ -n "$latest_minor" ]] || fail "Could not determine latest GA minor from redhat-operators" local channel="rhacs-${major}.${latest_minor}" info "Upgrading to latest GA channel: ${channel}..." - _resolve_target_csv "redhat-operators" "$channel" + _resolve_target_csv "redhat-operators" "$channel" 180 apply_subscription "$channel" "redhat-operators" ok "Subscription updated to ${channel}, target: ${TARGET_CSV}" } diff --git a/smoke-test/upgrade-latest-test.sh b/smoke-test/upgrade-latest-test.sh index 97a8b861..18a756dc 100755 --- a/smoke-test/upgrade-latest-test.sh +++ b/smoke-test/upgrade-latest-test.sh @@ -31,9 +31,10 @@ echo "======================================================================" # Query latest GA minor NOW while redhat-operators is still enabled. # install_from_custom() disables default sources, making this unavailable later. +# Use || true so a catalog timeout degrades gracefully to skipping the upgrade check. LATEST_MINOR="" -wait_for_catalog "redhat-operators" "openshift-marketplace" 180 -if latest=$(get_latest_official_minor "$ACS_MAJOR") && [[ -n "$latest" ]]; then +if wait_for_catalog "redhat-operators" "openshift-marketplace" 180 && + latest=$(get_latest_official_minor "$ACS_MAJOR") && [[ -n "$latest" ]]; then LATEST_MINOR="$latest" info "Latest GA in redhat-operators: ${ACS_MAJOR}.${LATEST_MINOR}" else From d58a4d854ccb917012a3c5f63f50b21f7aaf738c Mon Sep 17 00:00:00 2001 From: Aleksandr Kurlov Date: Mon, 6 Jul 2026 13:40:53 +0200 Subject: [PATCH 04/12] Fix auto smoke test --- .github/workflows/auto-smoke-test.yml | 57 +++++++++++++++++++-------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/.github/workflows/auto-smoke-test.yml b/.github/workflows/auto-smoke-test.yml index 718cbf6a..a7096d6a 100644 --- a/.github/workflows/auto-smoke-test.yml +++ b/.github/workflows/auto-smoke-test.yml @@ -38,9 +38,18 @@ jobs: - name: Determine ACS version id: version + env: + ACS_VERSION_OVERRIDE: ${{ inputs.acs-version-override }} + OPERATOR_INDEX_IMAGE_OVERRIDE: ${{ inputs.operator-index-image-override }} run: | - if [ -n "${{ inputs.acs-version-override }}" ]; then - echo "acs-version=${{ inputs.acs-version-override }}" >> "$GITHUB_OUTPUT" + # Image override without version override is ambiguous — fail fast. + if [ -n "$OPERATOR_INDEX_IMAGE_OVERRIDE" ] && [ -z "$ACS_VERSION_OVERRIDE" ]; then + echo "::error::operator-index-image-override requires acs-version-override to also be set" + exit 1 + fi + + if [ -n "$ACS_VERSION_OVERRIDE" ]; then + echo "acs-version=$ACS_VERSION_OVERRIDE" >> "$GITHUB_OUTPUT" echo "should-run=true" >> "$GITHUB_OUTPUT" exit 0 fi @@ -48,14 +57,21 @@ jobs: if [ "${{ github.event_name }}" = "pull_request" ]; then DIFF_BASE="origin/${{ github.base_ref }}" else - DIFF_BASE="HEAD~1" + # Use event.before to capture all commits in the push, not just the tip commit. + DIFF_BASE="${{ github.event.before }}" + # event.before is all-zeros on first push to a branch; fall back to parent. + if [[ "$DIFF_BASE" =~ ^0+$ ]]; then + DIFF_BASE="HEAD~1" + fi fi + # Exclude diff headers first, then match only the `version:` key (not + # `oldest_supported_version:` or other keys that contain "version"). NEW_VERSIONS=$(git diff "$DIFF_BASE" -- bundles.yaml \ - | grep '^+.*version:' \ | grep -v '^+++' \ - | sed 's/.*version: *//' \ - | sort -V) + | grep '^+[[:space:]]*version:' \ + | sed 's/.*version:[[:space:]]*//' \ + | sort -V || true) if [ -z "$NEW_VERSIONS" ]; then echo "No new versions found in bundles.yaml diff, skipping smoke test" @@ -72,9 +88,11 @@ jobs: - name: Determine operator-index image id: image if: steps.version.outputs.should-run == 'true' + env: + OPERATOR_INDEX_IMAGE_OVERRIDE: ${{ inputs.operator-index-image-override }} run: | - if [ -n "${{ inputs.operator-index-image-override }}" ]; then - echo "operator-index-image=${{ inputs.operator-index-image-override }}" >> "$GITHUB_OUTPUT" + if [ -n "$OPERATOR_INDEX_IMAGE_OVERRIDE" ]; then + echo "operator-index-image=$OPERATOR_INDEX_IMAGE_OVERRIDE" >> "$GITHUB_OUTPUT" exit 0 fi @@ -89,15 +107,21 @@ jobs: echo "Waiting for Konflux to build: $IMAGE" for i in $(seq 1 40); do - RESPONSE=$(curl -sf "https://quay.io/api/v1/repository/rhacs-eng/stackrox-operator-index/tag/?specificTag=${TAG}" || echo '{}') - FOUND=$(echo "$RESPONSE" | python3 -c "import json,sys; d=json.load(sys.stdin); print('true' if d.get('tags') else 'false')" 2>/dev/null || echo 'false') - if [ "$FOUND" = "true" ]; then - echo "Image available after attempt $i: $IMAGE" - echo "operator-index-image=$IMAGE" >> "$GITHUB_OUTPUT" - exit 0 + if RESPONSE=$(curl -sf --max-time 30 --connect-timeout 10 \ + "https://quay.io/api/v1/repository/rhacs-eng/stackrox-operator-index/tag/?specificTag=${TAG}"); then + FOUND=$(echo "$RESPONSE" | python3 -c \ + "import json,sys; d=json.load(sys.stdin); print('true' if d.get('tags') else 'false')" \ + 2>/dev/null || echo 'false') + if [ "$FOUND" = "true" ]; then + echo "Image available after attempt $i: $IMAGE" + echo "operator-index-image=$IMAGE" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "Attempt $i/40: image not yet in Quay, retrying in 3 minutes..." + else + echo "Attempt $i/40: Quay API request failed (network or HTTP error), retrying in 3 minutes..." fi - echo "Attempt $i/40: image not yet available, retrying in 3 minutes..." - sleep 180 + [ "$i" -lt 40 ] && sleep 180 done echo "Timed out after 2 hours waiting for: $IMAGE" @@ -111,4 +135,5 @@ jobs: with: operator-index-image: ${{ needs.derive-inputs.outputs.operator-index-image }} acs-version: ${{ needs.derive-inputs.outputs.acs-version }} + cluster-lifespan: 3h secrets: inherit From d2c4fc7fc73bab9785eb7172ee4ddc912054bcf5 Mon Sep 17 00:00:00 2001 From: Aleksandr Kurlov Date: Mon, 6 Jul 2026 13:46:14 +0200 Subject: [PATCH 05/12] Fix env --- .github/workflows/auto-smoke-test.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/auto-smoke-test.yml b/.github/workflows/auto-smoke-test.yml index a7096d6a..3712cab9 100644 --- a/.github/workflows/auto-smoke-test.yml +++ b/.github/workflows/auto-smoke-test.yml @@ -41,6 +41,9 @@ jobs: env: ACS_VERSION_OVERRIDE: ${{ inputs.acs-version-override }} OPERATOR_INDEX_IMAGE_OVERRIDE: ${{ inputs.operator-index-image-override }} + BASE_REF: ${{ github.base_ref }} + EVENT_NAME: ${{ github.event_name }} + BEFORE_SHA: ${{ github.event.before }} run: | # Image override without version override is ambiguous — fail fast. if [ -n "$OPERATOR_INDEX_IMAGE_OVERRIDE" ] && [ -z "$ACS_VERSION_OVERRIDE" ]; then @@ -54,11 +57,11 @@ jobs: exit 0 fi - if [ "${{ github.event_name }}" = "pull_request" ]; then - DIFF_BASE="origin/${{ github.base_ref }}" + if [ "$EVENT_NAME" = "pull_request" ]; then + DIFF_BASE="origin/$BASE_REF" else # Use event.before to capture all commits in the push, not just the tip commit. - DIFF_BASE="${{ github.event.before }}" + DIFF_BASE="$BEFORE_SHA" # event.before is all-zeros on first push to a branch; fall back to parent. if [[ "$DIFF_BASE" =~ ^0+$ ]]; then DIFF_BASE="HEAD~1" From 16d53aa87c4b2249c4c374793ad4eb71d06f0cde Mon Sep 17 00:00:00 2001 From: Aleksandr Kurlov Date: Mon, 6 Jul 2026 14:02:56 +0200 Subject: [PATCH 06/12] Add manual PR trigger --- .github/workflows/acs-smoke-test.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/acs-smoke-test.yml b/.github/workflows/acs-smoke-test.yml index b9b62b3b..c4949213 100644 --- a/.github/workflows/acs-smoke-test.yml +++ b/.github/workflows/acs-smoke-test.yml @@ -1,6 +1,9 @@ name: "ACS Smoke Test" on: + # TODO: remove pull_request trigger before merging + pull_request: + branches: [master] workflow_dispatch: inputs: operator-index-image: @@ -57,7 +60,7 @@ jobs: token: ${{ secrets.INFRA_TOKEN }} flavor: ocp-default name: ${{ env.CLUSTER_NAME }} - lifespan: ${{ inputs.cluster-lifespan }} + lifespan: ${{ inputs.cluster-lifespan || '2h' }} args: nodes=3,machine-type=e2-standard-8 wait: true @@ -94,8 +97,10 @@ jobs: - name: "Test 1: upgrade oldest-supported → provided" working-directory: ./smoke-test env: - OPERATOR_INDEX_IMAGE: ${{ inputs.operator-index-image }} - ACS_VERSION: ${{ inputs.acs-version }} + # Add a || operator-index image and version for testing in the PR. + # TODO: remove before merging! + OPERATOR_INDEX_IMAGE: ${{ inputs.operator-index-image || 'quay.io/rhacs-eng/stackrox-operator-index:ocp-v4-22-7a9fcba929e5858cca91a84c3e5da8c829a3c990-fast' }} + ACS_VERSION: ${{ inputs.acs-version || '4.11' }} run: bash upgrade-oldest-test.sh - name: Reset operator state between tests @@ -108,8 +113,10 @@ jobs: - name: "Test 2: install provided → optionally upgrade to latest GA" working-directory: ./smoke-test env: - OPERATOR_INDEX_IMAGE: ${{ inputs.operator-index-image }} - ACS_VERSION: ${{ inputs.acs-version }} + # Add a || operator-index image and version for testing in the PR. + # TODO: remove before merging! + OPERATOR_INDEX_IMAGE: ${{ inputs.operator-index-image || 'quay.io/rhacs-eng/stackrox-operator-index:ocp-v4-22-7a9fcba929e5858cca91a84c3e5da8c829a3c990-fast' }} + ACS_VERSION: ${{ inputs.acs-version || '4.11' }} run: bash upgrade-latest-test.sh cleanup-cluster: From efec9d97b8a6f8a7cebcdc34c5159b07b94f81b6 Mon Sep 17 00:00:00 2001 From: Aleksandr Kurlov Date: Mon, 6 Jul 2026 14:30:06 +0200 Subject: [PATCH 07/12] Fix infra provisioning --- .github/workflows/acs-smoke-test.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/acs-smoke-test.yml b/.github/workflows/acs-smoke-test.yml index c4949213..037b2078 100644 --- a/.github/workflows/acs-smoke-test.yml +++ b/.github/workflows/acs-smoke-test.yml @@ -58,10 +58,9 @@ jobs: uses: stackrox/actions/infra/create-cluster@v1 with: token: ${{ secrets.INFRA_TOKEN }} - flavor: ocp-default + flavor: openshift-4-demo name: ${{ env.CLUSTER_NAME }} lifespan: ${{ inputs.cluster-lifespan || '2h' }} - args: nodes=3,machine-type=e2-standard-8 wait: true run-smoke-tests: From 3d73d432150570e47e609c55b861b99b62f2b2a0 Mon Sep 17 00:00:00 2001 From: Aleksandr Kurlov Date: Mon, 6 Jul 2026 14:50:22 +0200 Subject: [PATCH 08/12] Fix flavor --- .github/workflows/acs-smoke-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/acs-smoke-test.yml b/.github/workflows/acs-smoke-test.yml index 037b2078..cdb193c8 100644 --- a/.github/workflows/acs-smoke-test.yml +++ b/.github/workflows/acs-smoke-test.yml @@ -58,7 +58,7 @@ jobs: uses: stackrox/actions/infra/create-cluster@v1 with: token: ${{ secrets.INFRA_TOKEN }} - flavor: openshift-4-demo + flavor: openshift-4 name: ${{ env.CLUSTER_NAME }} lifespan: ${{ inputs.cluster-lifespan || '2h' }} wait: true From 417c1809867ec66301dafca525a54f7a35001a80 Mon Sep 17 00:00:00 2001 From: Aleksandr Kurlov Date: Mon, 6 Jul 2026 19:34:08 +0200 Subject: [PATCH 09/12] Drop redundant GCP auth --- .github/workflows/acs-smoke-test.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/acs-smoke-test.yml b/.github/workflows/acs-smoke-test.yml index cdb193c8..7cbd6de6 100644 --- a/.github/workflows/acs-smoke-test.yml +++ b/.github/workflows/acs-smoke-test.yml @@ -76,14 +76,6 @@ jobs: - name: Install infractl uses: stackrox/actions/infra/install-infractl@v1 - - name: Authenticate to GCP - uses: google-github-actions/auth@v3 - with: - credentials_json: ${{ secrets.GCP_RELEASE_AUTOMATION_SA }} - - - name: Set up Cloud SDK - uses: google-github-actions/setup-gcloud@v3 - - name: Download cluster kubeconfig run: | infractl artifacts \ From f002468da488d4f2142d48ac4358f4f546499ee8 Mon Sep 17 00:00:00 2001 From: Aleksandr Kurlov Date: Mon, 6 Jul 2026 20:30:00 +0200 Subject: [PATCH 10/12] Fix infra cluster artifact fetch --- .github/workflows/acs-smoke-test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/acs-smoke-test.yml b/.github/workflows/acs-smoke-test.yml index 7cbd6de6..3872b100 100644 --- a/.github/workflows/acs-smoke-test.yml +++ b/.github/workflows/acs-smoke-test.yml @@ -79,8 +79,8 @@ jobs: - name: Download cluster kubeconfig run: | infractl artifacts \ - --cluster-name=${{ needs.create-cluster.outputs.cluster-name }} \ - --download-dir=./artifacts + --download-dir=./artifacts \ + ${{ needs.create-cluster.outputs.cluster-name }} - name: Set KUBECONFIG run: echo "KUBECONFIG=${{ github.workspace }}/artifacts/kubeconfig" >> "$GITHUB_ENV" @@ -125,7 +125,7 @@ jobs: credentials_json: ${{ secrets.GCP_RELEASE_AUTOMATION_SA }} - name: Delete cluster - run: infractl delete --cluster-name=${{ needs.create-cluster.outputs.cluster-name }} + run: infractl delete ${{ needs.create-cluster.outputs.cluster-name }} report-status: name: Report test status From e4e3123f6c8fde3c5c75e6da0936dee83f0a91aa Mon Sep 17 00:00:00 2001 From: Aleksandr Kurlov Date: Mon, 6 Jul 2026 22:10:32 +0200 Subject: [PATCH 11/12] Pass INFRA_TOKEN --- .github/workflows/acs-smoke-test.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/acs-smoke-test.yml b/.github/workflows/acs-smoke-test.yml index 3872b100..db21b93b 100644 --- a/.github/workflows/acs-smoke-test.yml +++ b/.github/workflows/acs-smoke-test.yml @@ -67,6 +67,8 @@ jobs: name: Run ACS smoke tests needs: create-cluster runs-on: ubuntu-latest + env: + INFRA_TOKEN: ${{ secrets.INFRA_TOKEN }} container: image: quay.io/stackrox-io/apollo-ci:stackrox-test-0.5.11@sha256:df3459cf038e73775314b3a5d36d14dda41e6df438a0273c9efa8d23e0f2358a steps: @@ -115,15 +117,12 @@ jobs: needs: [create-cluster, run-smoke-tests] runs-on: ubuntu-latest if: always() && needs.create-cluster.result != 'cancelled' + env: + INFRA_TOKEN: ${{ secrets.INFRA_TOKEN }} steps: - name: Install infractl uses: stackrox/actions/infra/install-infractl@v1 - - name: Authenticate to GCP - uses: google-github-actions/auth@v3 - with: - credentials_json: ${{ secrets.GCP_RELEASE_AUTOMATION_SA }} - - name: Delete cluster run: infractl delete ${{ needs.create-cluster.outputs.cluster-name }} From 190bb3f6aa3c3130792c5940d6369d7be0d73584 Mon Sep 17 00:00:00 2001 From: Aleksandr Kurlov Date: Tue, 7 Jul 2026 02:58:13 +0200 Subject: [PATCH 12/12] Simplify auto smoke test --- .github/workflows/auto-smoke-test.yml | 87 ++++++++++++++------------- 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/.github/workflows/auto-smoke-test.yml b/.github/workflows/auto-smoke-test.yml index 3712cab9..b646359f 100644 --- a/.github/workflows/auto-smoke-test.yml +++ b/.github/workflows/auto-smoke-test.yml @@ -27,7 +27,7 @@ jobs: name: Derive smoke test inputs runs-on: ubuntu-latest outputs: - acs-version: ${{ steps.version.outputs.acs-version }} + acs-versions: ${{ steps.version.outputs.acs-versions }} operator-index-image: ${{ steps.image.outputs.operator-index-image }} should-run: ${{ steps.version.outputs.should-run }} steps: @@ -43,16 +43,16 @@ jobs: OPERATOR_INDEX_IMAGE_OVERRIDE: ${{ inputs.operator-index-image-override }} BASE_REF: ${{ github.base_ref }} EVENT_NAME: ${{ github.event_name }} - BEFORE_SHA: ${{ github.event.before }} run: | - # Image override without version override is ambiguous — fail fast. + # Image override without version override is ambiguous then fail fast. if [ -n "$OPERATOR_INDEX_IMAGE_OVERRIDE" ] && [ -z "$ACS_VERSION_OVERRIDE" ]; then echo "::error::operator-index-image-override requires acs-version-override to also be set" exit 1 fi if [ -n "$ACS_VERSION_OVERRIDE" ]; then - echo "acs-version=$ACS_VERSION_OVERRIDE" >> "$GITHUB_OUTPUT" + echo "Version override provided, skipping bundles.yaml diff: $ACS_VERSION_OVERRIDE" + echo "acs-versions=[\"$ACS_VERSION_OVERRIDE\"]" >> "$GITHUB_OUTPUT" echo "should-run=true" >> "$GITHUB_OUTPUT" exit 0 fi @@ -60,21 +60,15 @@ jobs: if [ "$EVENT_NAME" = "pull_request" ]; then DIFF_BASE="origin/$BASE_REF" else - # Use event.before to capture all commits in the push, not just the tip commit. - DIFF_BASE="$BEFORE_SHA" - # event.before is all-zeros on first push to a branch; fall back to parent. - if [[ "$DIFF_BASE" =~ ^0+$ ]]; then - DIFF_BASE="HEAD~1" - fi + # Merge to master then diff the previous commit. + DIFF_BASE="HEAD~1" fi - # Exclude diff headers first, then match only the `version:` key (not - # `oldest_supported_version:` or other keys that contain "version"). + # Match only the `version:` key, ignore `oldest_supported_version:` and others. NEW_VERSIONS=$(git diff "$DIFF_BASE" -- bundles.yaml \ - | grep -v '^+++' \ | grep '^+[[:space:]]*version:' \ | sed 's/.*version:[[:space:]]*//' \ - | sort -V || true) + || true) if [ -z "$NEW_VERSIONS" ]; then echo "No new versions found in bundles.yaml diff, skipping smoke test" @@ -82,10 +76,13 @@ jobs: exit 0 fi - LATEST=$(echo "$NEW_VERSIONS" | tail -1) - MINOR=$(echo "$LATEST" | cut -d. -f1-2) - echo "New versions added: $(echo "$NEW_VERSIONS" | tr '\n' ' '), testing minor: $MINOR" - echo "acs-version=$MINOR" >> "$GITHUB_OUTPUT" + # Build version JSON array for the matrix. + MINORS_JSON=$(echo "$NEW_VERSIONS" \ + | sed 's/^\([0-9]*\.[0-9]*\).*/\1/' \ + | sort -uV \ + | jq -Rc '[.,inputs]') + echo "New versions added: $(echo "$NEW_VERSIONS" | tr '\n' ' '), testing minors: $MINORS_JSON" + echo "acs-versions=$MINORS_JSON" >> "$GITHUB_OUTPUT" echo "should-run=true" >> "$GITHUB_OUTPUT" - name: Determine operator-index image @@ -93,50 +90,58 @@ jobs: if: steps.version.outputs.should-run == 'true' env: OPERATOR_INDEX_IMAGE_OVERRIDE: ${{ inputs.operator-index-image-override }} + SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} run: | if [ -n "$OPERATOR_INDEX_IMAGE_OVERRIDE" ]; then echo "operator-index-image=$OPERATOR_INDEX_IMAGE_OVERRIDE" >> "$GITHUB_OUTPUT" exit 0 fi - if [ "${{ github.event_name }}" = "pull_request" ]; then - SHA="${{ github.event.pull_request.head.sha }}" - else - SHA="${{ github.sha }}" - fi - TAG="ocp-${OCP_VERSION}-${SHA}-fast" IMAGE="quay.io/rhacs-eng/stackrox-operator-index:${TAG}" - echo "Waiting for Konflux to build: $IMAGE" + + CHECK_NAME="Red Hat Konflux / operator-index-ocp-${OCP_VERSION}-on-push" + echo "Waiting for Konflux check '$CHECK_NAME' on $SHA..." for i in $(seq 1 40); do - if RESPONSE=$(curl -sf --max-time 30 --connect-timeout 10 \ - "https://quay.io/api/v1/repository/rhacs-eng/stackrox-operator-index/tag/?specificTag=${TAG}"); then - FOUND=$(echo "$RESPONSE" | python3 -c \ - "import json,sys; d=json.load(sys.stdin); print('true' if d.get('tags') else 'false')" \ - 2>/dev/null || echo 'false') - if [ "$FOUND" = "true" ]; then - echo "Image available after attempt $i: $IMAGE" - echo "operator-index-image=$IMAGE" >> "$GITHUB_OUTPUT" - exit 0 - fi - echo "Attempt $i/40: image not yet in Quay, retrying in 3 minutes..." - else - echo "Attempt $i/40: Quay API request failed (network or HTTP error), retrying in 3 minutes..." - fi + RESULT=$(gh api /repos/stackrox/operator-index/commits/"$SHA"/check-runs \ + | jq -r --arg name "$CHECK_NAME" \ + '.check_runs[] | select(.name == $name) | "\(.status) \(.conclusion)"') + case "$RESULT" in + "completed success") + echo "Konflux build succeeded, verifying image in Quay..." + if curl -sf --max-time 30 --connect-timeout 10 \ + "https://quay.io/api/v1/repository/rhacs-eng/stackrox-operator-index/tag/?specificTag=${TAG}" \ + | jq -e '.tags | length > 0' > /dev/null; then + echo "Image confirmed in Quay: $IMAGE" + echo "operator-index-image=$IMAGE" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "Konflux check passed but image not found in Quay: $IMAGE" + exit 1 ;; + "completed "*) + echo "Konflux build failed: $RESULT" + exit 1 ;; + esac + echo "Attempt $i/40: ${RESULT:-check not yet started}, retrying in 3 minutes..." [ "$i" -lt 40 ] && sleep 180 done - echo "Timed out after 2 hours waiting for: $IMAGE" + echo "Timed out after 2 hours waiting for Konflux build: $CHECK_NAME" exit 1 smoke-test: name: Run smoke test needs: derive-inputs if: needs.derive-inputs.outputs.should-run == 'true' + strategy: + matrix: + acs-version: ${{ fromJson(needs.derive-inputs.outputs.acs-versions) }} + # if any matrix job fails, continue to run the rest of the jobs + fail-fast: false uses: ./.github/workflows/acs-smoke-test.yml with: operator-index-image: ${{ needs.derive-inputs.outputs.operator-index-image }} - acs-version: ${{ needs.derive-inputs.outputs.acs-version }} + acs-version: ${{ matrix.acs-version }} cluster-lifespan: 3h secrets: inherit