Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ci-operator.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build_root_image:
name: boilerplate
namespace: openshift
tag: image-v8.4.1
tag: image-v8.4.3
14 changes: 0 additions & 14 deletions .github/dependabot.yml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ spec:
- name: url
value: https://github.com/openshift/boilerplate
- name: revision
value: 663c1d851e3bcb533a7be50936a0d55c732b7321
value: b361d1a597f5a3a840ccea7efd40dc92f8cfa789
- name: pathInRepo
value: pipelines/agentic-sdlc-check/pipeline.yaml
status: {}
1 change: 1 addition & 0 deletions OWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ approvers:
- bergmannf
- tkong-redhat
- rbhilare
- srep-functional-team-hulk
- rosa-staff-engineers
reviewers:
- iamkirkbater
Expand Down
7 changes: 1 addition & 6 deletions OWNERS_ALIASES
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ aliases:
- Makdaam
- Nikokolas3270
- RaphaelBut
- MateSaary
- rolandmkunkel
- petrkotas
- zmird-r
Expand All @@ -56,7 +55,6 @@ aliases:
- feichashao
- samanthajayasinghe
- xiaoyu74
- Tessg22
- smarthall
rosa-staff-engineers:
- Ajpantuso
Expand All @@ -65,6 +63,7 @@ aliases:
- bpresnel-rh
- clcollins
- dustman9000
- geowa4
- joshbranham
- psav
- rafael-azevedo
Expand All @@ -75,18 +74,14 @@ aliases:
- typeid
rosa-managers:
- afreiberger
- c-e-brumm
- drewandersonnz
- evalis1
- geowa4
- jcaianirh
- jnewton75
- krishvoor
- kseiter-rh
- OliviaHY
- syncrou
- tdrozdowski
- tkiss28
- vkumar51
hp-architects:
- deads2k
Expand Down
2 changes: 1 addition & 1 deletion boilerplate/_data/backing-image-tag
Original file line number Diff line number Diff line change
@@ -1 +1 @@
image-v8.4.1
image-v8.4.3
2 changes: 1 addition & 1 deletion boilerplate/_data/last-boilerplate-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
663c1d851e3bcb533a7be50936a0d55c732b7321
b361d1a597f5a3a840ccea7efd40dc92f8cfa789
42 changes: 41 additions & 1 deletion boilerplate/openshift/golang-osd-e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,48 @@ following:
|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `e2e-binary-build` | Compiles ginkgo tests under test/e2e and creates the ginkgo binary. |
| `e2e-image-build-push` | Builds e2e image and pushes to operator's quay repo. Image name is defaulted to <operator-image-name>-test-harness. Quay repository must be created beforehand. |
| `e2e-local` | Builds the e2e binary and runs it against a cluster via KUBECONFIG or backplane. Supports focused tests via GINKGO_FOCUS. |

#### E2E Local Testing

Please follow [this README](https://github.com/openshift/ops-sop/blob/master/v4/howto/osde2e/operator-test-harnesses.md#using-ginkgo) to run your e2e tests locally
Run e2e tests locally against a managed cluster without waiting for the full Prow CI pipeline.

**Prerequisites:**
- `ocm` CLI logged into the appropriate environment (`ocm login --use-auth-code --url staging`)
- Access to a managed cluster (via KUBECONFIG or backplane)
- Go toolchain installed

**Option 1: Using backplane (recommended)**

```bash
# Run all tests against a cluster by ID
make e2e-local CLUSTER_ID=2rmlgv5dbdp2285n85o7h3aaa6pafkpq

# Run focused tests
make e2e-local CLUSTER_ID=2rmlgv5dbdp2285n85o7h3aaa6pafkpq GINKGO_FOCUS="is installed"

# Run tests with a label filter
make e2e-local CLUSTER_ID=2rmlgv5dbdp2285n85o7h3aaa6pafkpq GINKGO_LABEL_FILTER="!slow"
```

**Option 2: Using an existing KUBECONFIG**

```bash
# Set KUBECONFIG to your cluster's kubeconfig
export KUBECONFIG=/path/to/kubeconfig

# Run all tests
make e2e-local

# Run a single test
make e2e-local GINKGO_FOCUS="reconciles required resources"
```

**Output:**
- Test results print to stdout with verbose Ginkgo output
- JUnit XML report saved to `e2e-local-junit.xml`

**Tips:**
- Use lease clusters for testing (check `#rosa-prow-info` for available clusters)
- The operator must be deployed on the target cluster (via PKO or OLM)
- If tests fail with "not found" errors, verify the operator is running: `oc get deployment -n <operator-namespace>`
146 changes: 130 additions & 16 deletions boilerplate/openshift/golang-osd-e2e/gangway-bridge-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,20 @@ parameters:
required: true
description: Prow periodic job name to trigger via Gangway
- name: POLL_INTERVAL
value: "60"
value: "120"
description: Seconds between status polls
- name: TIMEOUT
value: "7200"
description: Maximum seconds to wait for job completion
description: Maximum seconds to wait per attempt for job completion
- name: MAX_RETRIES
value: "5"
description: Number of times to retry the Prow job on failure before reporting failure
- name: ACTIVE_DEADLINE
value: "54000"
description: Kubernetes Job deadline in seconds (must exceed all attempts plus backoff delays)
- name: INITIAL_DELAY
value: "0"
description: Seconds to sleep before the first Gangway call; stagger concurrent jobs to avoid shared rate limit saturation
- name: JOB_ENVS
value: ""
description: Comma-separated KEY=VALUE pairs passed to the Prow job
Expand All @@ -29,7 +38,7 @@ objects:
name: gangway-bridge-${IMAGE_TAG}-${JOBID}
spec:
backoffLimit: 0
activeDeadlineSeconds: ${{TIMEOUT}}
activeDeadlineSeconds: ${{ACTIVE_DEADLINE}}
template:
spec:
automountServiceAccountToken: false
Expand All @@ -46,28 +55,127 @@ objects:

[[ "${TIMEOUT}" =~ ^[1-9][0-9]*$ ]] || { log "ERROR: TIMEOUT must be a positive integer"; exit 1; }
[[ "${POLL_INTERVAL}" =~ ^[1-9][0-9]*$ ]] || { log "ERROR: POLL_INTERVAL must be a positive integer"; exit 1; }
[[ "${MAX_RETRIES}" =~ ^[0-9]+$ ]] || { log "ERROR: MAX_RETRIES must be a non-negative integer"; exit 1; }
[[ "${INITIAL_DELAY}" =~ ^[0-9]+$ ]] || { log "ERROR: INITIAL_DELAY must be a non-negative integer"; exit 1; }

if [[ "${INITIAL_DELAY}" -gt 0 ]]; then
log "Waiting ${INITIAL_DELAY}s before first Gangway call (INITIAL_DELAY)..."
sleep "${INITIAL_DELAY}"
fi

# Backoff sum: base 30s doubling each retry, capped at 900s, plus 15s max jitter
MAX_BACKOFF_SUM=$(( 30 * ((1 << MAX_RETRIES) - 1) + MAX_RETRIES * 15 ))
# Each attempt may overshoot TIMEOUT by up to max(POLL_INTERVAL, 300s max backoff) +
# status-request max-time (30s) on the last poll cycle
POLL_OVERSHOOT=$(( (POLL_INTERVAL > 300 ? POLL_INTERVAL : 300) + 30 ))
# Trigger POST max-time (60s) + worst-case Retry-After (600s) per attempt
TRIGGER_OVERHEAD=$(( 60 + 600 ))
# INITIAL_DELAY is a one-time cost at job startup, not per attempt
REQUIRED_DEADLINE=$(( (MAX_RETRIES + 1) * (TIMEOUT + POLL_OVERSHOOT + TRIGGER_OVERHEAD) + MAX_BACKOFF_SUM + INITIAL_DELAY ))
if [[ "${ACTIVE_DEADLINE}" -lt "${REQUIRED_DEADLINE}" ]]; then
log "ERROR: ACTIVE_DEADLINE (${ACTIVE_DEADLINE}s) is less than the minimum required for ${MAX_RETRIES} retries with TIMEOUT=${TIMEOUT}s (need at least ${REQUIRED_DEADLINE}s)"
exit 1
fi

BODY='{"job_execution_type":"1"}'
if [[ -n "${JOB_ENVS:-}" ]]; then
ENVS=$(echo "${JOB_ENVS}" | jq -Rn '[inputs // input | split(",")[] | split("=") | {(.[0]): .[1:] | join("=")}] | add' <<< "${JOB_ENVS}")
BODY=$(jq -cn --argjson e "$ENVS" '{"job_execution_type":"1","pod_spec_options":{"envs":$e}}')
fi

RESP=$(curl -sfSL --retry 3 --retry-delay 10 -X POST -H "Authorization: Bearer ${GANGWAY_TOKEN}" -H "Content-Type: application/json" -d "${BODY}" "${GW}/${JOB_NAME}")
ID=$(echo "$RESP" | jq -re .id)
PROW_URL="https://prow.ci.openshift.org/view/gs/test-platform-results/logs/${JOB_NAME}/${ID}"
log "Triggered ${JOB_NAME} -> ${ID}"
log "Prow logs: ${PROW_URL}"
RATE_LIMITED_WAITED=0
trigger_and_poll() {
local resp_file="/dev/shm/gw_resp.$$" header_file="/dev/shm/gw_hdr.$$"
trap 'rm -f "$resp_file" "$header_file"' RETURN
HTTP_CODE=$(curl -sSL --max-time 60 -X POST \
-H "Authorization: Bearer ${GANGWAY_TOKEN}" \
-H "Content-Type: application/json" \
-d "${BODY}" \
-o "$resp_file" -D "$header_file" \
-w '%{http_code}' "${GW}/${JOB_NAME}" 2>/dev/null) || HTTP_CODE=000

# Handle 429 — parse Retry-After header (capped at 600s)
if [[ "$HTTP_CODE" == "429" ]]; then
local retry_after
retry_after=$(grep -i '^retry-after:' "$header_file" | awk '{print $2}' | tr -d '\r')
if [[ "$retry_after" =~ ^[0-9]+$ ]] && [[ "$retry_after" -gt 0 ]] && [[ "$retry_after" -le 600 ]]; then
log "Rate limited (429) — sleeping ${retry_after}s (Retry-After)"
sleep "$retry_after"
RATE_LIMITED_WAITED=1
else
log "Rate limited (429) — no valid Retry-After header"
fi
return 1 # falls through to outer retry with backoff
fi

# Fail on non-2xx
if [[ "$HTTP_CODE" -lt 200 || "$HTTP_CODE" -ge 300 ]]; then
log "Failed to trigger ${JOB_NAME} (HTTP ${HTTP_CODE})"
return 1
fi

RESP=$(cat "$resp_file")
if ! ID=$(echo "$RESP" | jq -re .id); then
log "Gangway did not return a valid execution ID"
return 1
fi
PROW_URL="https://prow.ci.openshift.org/view/gs/test-platform-results/logs/${JOB_NAME}/${ID}"
log "Triggered ${JOB_NAME} -> ${ID}"
log "Prow logs: ${PROW_URL}"

END=$((SECONDS + ${TIMEOUT}))
local poll_backoff="${POLL_INTERVAL}"
while [[ $SECONDS -lt $END ]]; do
sleep "$poll_backoff"
local poll_file="/dev/shm/gw_poll.$$"
local poll_code
poll_code=$(curl -sSL --max-time 30 \
-H "Authorization: Bearer ${GANGWAY_TOKEN}" \
-o "$poll_file" -w '%{http_code}' \
"${GW}/${ID}" 2>/dev/null) || poll_code=000
if [[ "$poll_code" == "429" ]]; then
rm -f "$poll_file"
poll_backoff=$(( poll_backoff * 2 ))
[[ $poll_backoff -gt 300 ]] && poll_backoff=300
log "Rate limited polling status (429) — backing off ${poll_backoff}s"
continue
fi
poll_backoff="${POLL_INTERVAL}"
S=$(jq -r .job_status "$poll_file" 2>/dev/null) || S=UNKNOWN
rm -f "$poll_file"
log "${S} ($((SECONDS))s)"
case $S in
SUCCESS) log "Prow logs: ${PROW_URL}"; return 0;;
FAILURE|ABORTED|ERROR) log "Prow logs: ${PROW_URL}"; return 1;;
esac
done
log "Prow logs: ${PROW_URL}"
log "Timeout"; return 1
}

END=$((SECONDS + ${TIMEOUT}))
while [[ $SECONDS -lt $END ]]; do
sleep "${POLL_INTERVAL}"
S=$(curl -sfSL -H "Authorization: Bearer ${GANGWAY_TOKEN}" "${GW}/${ID}" | jq -r .job_status) || S=UNKNOWN
log "${S} ($((SECONDS))s)"
case $S in SUCCESS) log "Prow logs: ${PROW_URL}"; exit 0;; FAILURE|ABORTED|ERROR) log "Prow logs: ${PROW_URL}"; exit 1;; esac
ATTEMPT=0
while true; do
ATTEMPT=$((ATTEMPT + 1))
log "Attempt ${ATTEMPT} of $((MAX_RETRIES + 1))"
if trigger_and_poll; then
exit 0
fi
if [[ $ATTEMPT -gt $MAX_RETRIES ]]; then
log "All attempts exhausted"
exit 1
fi
if [[ $RATE_LIMITED_WAITED -eq 1 ]]; then
log "Skipping backoff (already waited for Retry-After)"
RATE_LIMITED_WAITED=0
else
BACKOFF=$(( 30 * (1 << (ATTEMPT - 1)) ))
[[ $BACKOFF -gt 900 ]] && BACKOFF=900
JITTER=$(( RANDOM % 16 ))
DELAY=$(( BACKOFF + JITTER ))
log "Retrying in ${DELAY}s (backoff=${BACKOFF}s, jitter=${JITTER}s)..."
sleep "$DELAY"
fi
done
log "Prow logs: ${PROW_URL}"
log "Timeout"; exit 1
env:
- name: JOB_NAME
value: ${JOB_NAME}
Expand All @@ -82,6 +190,12 @@ objects:
value: ${TIMEOUT}
- name: JOB_ENVS
value: ${JOB_ENVS}
- name: MAX_RETRIES
value: ${MAX_RETRIES}
- name: INITIAL_DELAY
value: ${INITIAL_DELAY}
- name: ACTIVE_DEADLINE
value: ${ACTIVE_DEADLINE}
resources:
requests:
cpu: "50m"
Expand Down
20 changes: 20 additions & 0 deletions boilerplate/openshift/golang-osd-e2e/standard.mk
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,26 @@ e2e-binary-build:
go mod tidy
go test ./test/e2e -v -c --tags=osde2e -o e2e.test

# Run e2e tests locally against a cluster accessible via KUBECONFIG.
# Usage:
# make e2e-local # run all tests
# make e2e-local GINKGO_FOCUS="test name" # run matching tests
# make e2e-local CLUSTER_ID=<id> # use backplane for cluster access
#
# Requires: KUBECONFIG set, or CLUSTER_ID + ocm login for backplane access.
.PHONY: e2e-local
e2e-local: e2e-binary-build
@if [ -n "$(CLUSTER_ID)" ] && [ -z "$(KUBECONFIG)" ]; then \
echo "Logging into cluster $(CLUSTER_ID) via backplane..."; \
ocm backplane login $(CLUSTER_ID); \
fi
@echo "Running e2e tests against $${KUBECONFIG:-backplane cluster}..."
DISABLE_JUNIT_REPORT=true ./e2e.test \
--ginkgo.v \
--ginkgo.junit-report=e2e-local-junit.xml \
$(if $(GINKGO_FOCUS),--ginkgo.focus="$(GINKGO_FOCUS)") \
$(if $(GINKGO_LABEL_FILTER),--ginkgo.label-filter="$(GINKGO_LABEL_FILTER)")

# push e2e image tagged as latest and as repo commit hash
.PHONY: e2e-image-build-push
e2e-image-build-push: container-engine-login
Expand Down
7 changes: 1 addition & 6 deletions boilerplate/openshift/golang-osd-operator/OWNERS_ALIASES
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ aliases:
- Makdaam
- Nikokolas3270
- RaphaelBut
- MateSaary
- rolandmkunkel
- petrkotas
- zmird-r
Expand All @@ -56,7 +55,6 @@ aliases:
- feichashao
- samanthajayasinghe
- xiaoyu74
- Tessg22
- smarthall
rosa-staff-engineers:
- Ajpantuso
Expand All @@ -65,6 +63,7 @@ aliases:
- bpresnel-rh
- clcollins
- dustman9000
- geowa4
- joshbranham
- psav
- rafael-azevedo
Expand All @@ -75,18 +74,14 @@ aliases:
- typeid
rosa-managers:
- afreiberger
- c-e-brumm
- drewandersonnz
- evalis1
- geowa4
- jcaianirh
- jnewton75
- krishvoor
- kseiter-rh
- OliviaHY
- syncrou
- tdrozdowski
- tkiss28
- vkumar51
hp-architects:
- deads2k
Expand Down
Loading