diff --git a/.tekton/rbac-permissions-operator-agentic-sdlc-check-pull-request.yaml b/.tekton/rbac-permissions-operator-agentic-sdlc-check-pull-request.yaml index e13fd17f..d063859a 100644 --- a/.tekton/rbac-permissions-operator-agentic-sdlc-check-pull-request.yaml +++ b/.tekton/rbac-permissions-operator-agentic-sdlc-check-pull-request.yaml @@ -43,7 +43,7 @@ spec: - name: url value: https://github.com/openshift/boilerplate - name: revision - value: bdf2c387c9898cbc14ae4f6631125b7050508fc2 + value: d857c46dc89292ff1b6aae2830654e30007a9f2b - name: pathInRepo value: pipelines/agentic-sdlc-check/pipeline.yaml status: {} diff --git a/boilerplate/_data/last-boilerplate-commit b/boilerplate/_data/last-boilerplate-commit index f1399268..0d688e2d 100644 --- a/boilerplate/_data/last-boilerplate-commit +++ b/boilerplate/_data/last-boilerplate-commit @@ -1 +1 @@ -bdf2c387c9898cbc14ae4f6631125b7050508fc2 +d857c46dc89292ff1b6aae2830654e30007a9f2b diff --git a/boilerplate/openshift/golang-osd-e2e/gangway-bridge-template.yml b/boilerplate/openshift/golang-osd-e2e/gangway-bridge-template.yml index ec701e6d..410d18cb 100644 --- a/boilerplate/openshift/golang-osd-e2e/gangway-bridge-template.yml +++ b/boilerplate/openshift/golang-osd-e2e/gangway-bridge-template.yml @@ -12,7 +12,13 @@ parameters: 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: "1" + description: Number of times to retry the Prow job on failure before reporting failure + - name: ACTIVE_DEADLINE + value: "14430" + description: Kubernetes Job deadline in seconds (should exceed TIMEOUT * (MAX_RETRIES + 1)) - name: JOB_ENVS value: "" description: Comma-separated KEY=VALUE pairs passed to the Prow job @@ -29,7 +35,7 @@ objects: name: gangway-bridge-${IMAGE_TAG}-${JOBID} spec: backoffLimit: 0 - activeDeadlineSeconds: ${{TIMEOUT}} + activeDeadlineSeconds: ${{ACTIVE_DEADLINE}} template: spec: automountServiceAccountToken: false @@ -46,6 +52,13 @@ 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; } + + REQUIRED_DEADLINE=$(( (MAX_RETRIES + 1) * TIMEOUT + (MAX_RETRIES * 30) )) + 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 @@ -53,21 +66,47 @@ objects: 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}" + trigger_and_poll() { + if ! RESP=$(curl -sfSL --max-time 60 --retry 3 --retry-delay 10 -X POST -H "Authorization: Bearer ${GANGWAY_TOKEN}" -H "Content-Type: application/json" -d "${BODY}" "${GW}/${JOB_NAME}"); then + log "Failed to trigger ${JOB_NAME}" + return 1 + fi + 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})) + while [[ $SECONDS -lt $END ]]; do + sleep "${POLL_INTERVAL}" + S=$(curl -sfSL --max-time 30 -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}"; 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 + log "Retrying in 30s..." + sleep 30 done - log "Prow logs: ${PROW_URL}" - log "Timeout"; exit 1 env: - name: JOB_NAME value: ${JOB_NAME} @@ -82,6 +121,10 @@ objects: value: ${TIMEOUT} - name: JOB_ENVS value: ${JOB_ENVS} + - name: MAX_RETRIES + value: ${MAX_RETRIES} + - name: ACTIVE_DEADLINE + value: ${ACTIVE_DEADLINE} resources: requests: cpu: "50m" diff --git a/boilerplate/openshift/golang-osd-operator/codecov.sh b/boilerplate/openshift/golang-osd-operator/codecov.sh index 8fc79bd0..95fb5a90 100755 --- a/boilerplate/openshift/golang-osd-operator/codecov.sh +++ b/boilerplate/openshift/golang-osd-operator/codecov.sh @@ -23,11 +23,11 @@ rm -f "${COVER_PROFILE}.tmp" # Configure the git refs and job link based on how the job was triggered via prow if [[ "${JOB_TYPE}" == "presubmit" ]]; then echo "detected PR code coverage job for #${PULL_NUMBER}" - REF_FLAGS="-P ${PULL_NUMBER} -C ${PULL_PULL_SHA}" + REF_FLAGS="--pr ${PULL_NUMBER} --commit-sha ${PULL_PULL_SHA}" JOB_LINK="${CI_SERVER_URL}/pr-logs/pull/${REPO_OWNER}_${REPO_NAME}/${PULL_NUMBER}/${JOB_NAME}/${BUILD_ID}" elif [[ "${JOB_TYPE}" == "postsubmit" ]]; then echo "detected branch code coverage job for ${PULL_BASE_REF}" - REF_FLAGS="-B ${PULL_BASE_REF} -C ${PULL_BASE_SHA}" + REF_FLAGS="--branch ${PULL_BASE_REF} --commit-sha ${PULL_BASE_SHA}" JOB_LINK="${CI_SERVER_URL}/logs/${JOB_NAME}/${BUILD_ID}" elif [[ "${JOB_TYPE}" == "local" ]]; then echo "coverage report available at ${COVER_PROFILE}" @@ -43,12 +43,17 @@ export CI_BUILD_ID="${JOB_NAME}" export CI_JOB_ID="${BUILD_ID}" if [[ "${JOB_TYPE}" != "local" ]]; then - if [[ -z "${ARTIFACT_DIR:-}" ]] || [[ ! -d "${ARTIFACT_DIR}" ]] || [[ ! -w "${ARTIFACT_DIR}" ]]; then - echo '${ARTIFACT_DIR} must be set for non-local jobs, and must point to a writable directory' >&2 - exit 1 - fi - curl -sS https://codecov.io/bash -o "${ARTIFACT_DIR}/codecov.sh" - bash <(cat "${ARTIFACT_DIR}/codecov.sh") -Z -K -f "${COVER_PROFILE}" -r "${REPO_OWNER}/${REPO_NAME}" ${REF_FLAGS} + CODECOV_VERSION="${CODECOV_VERSION:-v11.3.1}" + CODECOV_SHA256="${CODECOV_SHA256:-ca1d64196d2d34771084afe76ea657d581bf628e31d993ff8e52ea09cc88a56d}" + CODECOV_BIN="$(mktemp -d)/codecov" + + curl -sSfL "https://github.com/codecov/codecov-cli/releases/download/${CODECOV_VERSION}/codecovcli_linux" \ + -o "${CODECOV_BIN}" + echo "${CODECOV_SHA256} ${CODECOV_BIN}" | sha256sum -c - + chmod +x "${CODECOV_BIN}" + + "${CODECOV_BIN}" upload-process --fail-on-error --git-service github \ + --file "${COVER_PROFILE}" --slug "${REPO_OWNER}/${REPO_NAME}" ${REF_FLAGS} else - bash <(curl -s https://codecov.io/bash) -Z -K -f "${COVER_PROFILE}" -r "${REPO_OWNER}/${REPO_NAME}" ${REF_FLAGS} + echo "coverage report available at ${COVER_PROFILE} (no upload in local mode)" fi diff --git a/build/Dockerfile b/build/Dockerfile index 546bda41..e923387c 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -8,7 +8,7 @@ COPY . . RUN make go-build #### -FROM registry.access.redhat.com/ubi9/ubi-minimal:9.8-1786380870 +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.8-1786987521 ENV USER_UID=1001 \ USER_NAME=rbac-permissions-operator diff --git a/build/Dockerfile.olm-registry b/build/Dockerfile.olm-registry index 85f60aad..ef9c2588 100644 --- a/build/Dockerfile.olm-registry +++ b/build/Dockerfile.olm-registry @@ -4,7 +4,7 @@ COPY ${SAAS_OPERATOR_DIR} manifests RUN initializer --permissive # ubi-micro does not work for clusters with fips enabled unless we make OpenSSL available -FROM registry.access.redhat.com/ubi9/ubi-minimal:9.8-1786380870 +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.8-1786987521 COPY --from=builder /bin/registry-server /bin/registry-server COPY --from=builder /bin/grpc_health_probe /bin/grpc_health_probe diff --git a/test/e2e/gangway-bridge-template.yml b/test/e2e/gangway-bridge-template.yml index ec701e6d..410d18cb 100644 --- a/test/e2e/gangway-bridge-template.yml +++ b/test/e2e/gangway-bridge-template.yml @@ -12,7 +12,13 @@ parameters: 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: "1" + description: Number of times to retry the Prow job on failure before reporting failure + - name: ACTIVE_DEADLINE + value: "14430" + description: Kubernetes Job deadline in seconds (should exceed TIMEOUT * (MAX_RETRIES + 1)) - name: JOB_ENVS value: "" description: Comma-separated KEY=VALUE pairs passed to the Prow job @@ -29,7 +35,7 @@ objects: name: gangway-bridge-${IMAGE_TAG}-${JOBID} spec: backoffLimit: 0 - activeDeadlineSeconds: ${{TIMEOUT}} + activeDeadlineSeconds: ${{ACTIVE_DEADLINE}} template: spec: automountServiceAccountToken: false @@ -46,6 +52,13 @@ 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; } + + REQUIRED_DEADLINE=$(( (MAX_RETRIES + 1) * TIMEOUT + (MAX_RETRIES * 30) )) + 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 @@ -53,21 +66,47 @@ objects: 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}" + trigger_and_poll() { + if ! RESP=$(curl -sfSL --max-time 60 --retry 3 --retry-delay 10 -X POST -H "Authorization: Bearer ${GANGWAY_TOKEN}" -H "Content-Type: application/json" -d "${BODY}" "${GW}/${JOB_NAME}"); then + log "Failed to trigger ${JOB_NAME}" + return 1 + fi + 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})) + while [[ $SECONDS -lt $END ]]; do + sleep "${POLL_INTERVAL}" + S=$(curl -sfSL --max-time 30 -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}"; 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 + log "Retrying in 30s..." + sleep 30 done - log "Prow logs: ${PROW_URL}" - log "Timeout"; exit 1 env: - name: JOB_NAME value: ${JOB_NAME} @@ -82,6 +121,10 @@ objects: value: ${TIMEOUT} - name: JOB_ENVS value: ${JOB_ENVS} + - name: MAX_RETRIES + value: ${MAX_RETRIES} + - name: ACTIVE_DEADLINE + value: ${ACTIVE_DEADLINE} resources: requests: cpu: "50m"