Skip to content

test: retry operations in e2e tests#1071

Open
simonpasquier wants to merge 1 commit into
openshift:main-alerts-management-apifrom
simonpasquier:tests-with-retry
Open

test: retry operations in e2e tests#1071
simonpasquier wants to merge 1 commit into
openshift:main-alerts-management-apifrom
simonpasquier:tests-with-retry

Conversation

@simonpasquier

@simonpasquier simonpasquier commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Requests to the backend may fail for a variety of reasons such as network connectivity, API blips, etc. Instead of failing a test, this commit implements retries for API requests.

Summary by CodeRabbit

  • Tests
    • Improved end-to-end alert rule creation and deletion tests with retry and polling behavior.
    • Added validation for alert expressions, durations, labels, annotations, deletion responses, and remaining rules.
    • Reduced test flakiness by replacing single-shot checks with time-bounded polling until expected resources and API results are observed.
  • Chores
    • Added a build-e2e target to compile the e2e test suite into a standalone binary.
  • Style
    • Updated test execution configuration by ignoring the generated e2e.test binary in version control.

@openshift-ci
openshift-ci Bot requested review from etmurasaki and zhuje July 21, 2026 09:25
@openshift-ci

openshift-ci Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: simonpasquier
Once this PR has been reviewed and has the lgtm label, please assign jgbernalp for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 1c641eab-bfa5-4d47-9f94-a6a63c6211f9

📥 Commits

Reviewing files that changed from the base of the PR and between 330236d and 09b14a5.

📒 Files selected for processing (5)
  • .gitignore
  • Makefile
  • test/e2e/create_alert_rule_test.go
  • test/e2e/delete_alert_rule_test.go
  • test/e2e/helpers_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/e2e/create_alert_rule_test.go
  • test/e2e/helpers_test.go
  • test/e2e/delete_alert_rule_test.go

Walkthrough

Alert rule E2E tests now use shared polling and retry helpers for creation, deletion, and PrometheusRule verification. A Makefile target builds the E2E test binary, and .gitignore excludes that binary.

Changes

Alert rule E2E reliability

Layer / File(s) Summary
Shared polling and creation retries
test/e2e/helpers_test.go
Adds reusable timeout polling and retrying alert-rule creation helpers.
Create and delete verification flows
test/e2e/create_alert_rule_test.go, test/e2e/delete_alert_rule_test.go
Retries API operations and PrometheusRule checks while validating created fields, deletion results, and the remaining alert.
E2E build support
Makefile, .gitignore
Adds an E2E binary build target and ignores the generated e2e.test binary.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: etmurasaki, zhuje

🚥 Pre-merge checks | ✅ 15
✅ Passed checks (15 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding retry handling to e2e tests for transient API/backend failures.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed PASS: The modified e2e files only define static Go Test... functions; no Ginkgo It/Describe/Context/When titles or dynamic strings were added.
Test Structure And Quality ✅ Passed Tests stay focused, defer namespace cleanup, and use bounded polling with contextual error messages; no indefinite waits or resource leaks found.
Microshift Test Compatibility ✅ Passed PASS: The new e2e tests are plain Go tests, not Ginkgo specs, and they only use core K8s namespaces/RBAC plus PrometheusRule CRDs; no unsupported OpenShift APIs/namespaces are referenced.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The new e2e tests only exercise API/PrometheusRule CRUD and polling; no node-count, scheduling, topology, or HA assumptions were found.
Topology-Aware Scheduling Compatibility ✅ Passed Diff only updates e2e tests, helpers, Makefile, and .gitignore; no manifests, controllers, or scheduling logic were changed.
Ote Binary Stdout Contract ✅ Passed Only test functions/helpers changed; no main/init/TestMain/BeforeSuite code or stdout writes were added.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The new e2e tests contain no hardcoded IPv4s or public internet dependencies; they use cluster APIs and an env-based PluginURL.
No-Weak-Crypto ✅ Passed No MD5/SHA1/DES/RC4/3DES/Blowfish/ECB or custom crypto appeared in the touched files or diff; changes are retry/polling only.
Container-Privileges ✅ Passed Changed files are only Go tests/Makefile/.gitignore; diff scan found no privileged, hostPID/Network/IPC, SYS_ADMIN, root, or allowPrivilegeEscalation settings.
No-Sensitive-Data-In-Logs ✅ Passed No new logs print passwords, tokens, PII, hostnames, or customer data; only rule IDs and generic error bodies are surfaced.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
test/e2e/helpers_test.go (1)

23-37: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

poll never honors a caller-supplied context; it hardcodes context.Background().

poll doesn't accept a context.Context, so it can't be cancelled by the outer test context (e.g. a test-level deadline). All three call sites pass their own ctx into the retried operation but the polling loop itself runs on an unrelated background context. As per path instructions, Go code should use context.Context for cancellation and timeouts.

♻️ Suggested fix: thread ctx through
-func poll(interval, timeout time.Duration, f func() error) error {
+func poll(ctx context.Context, interval, timeout time.Duration, f func() error) error {
 	var lastErr error
-	err := wait.PollUntilContextTimeout(context.Background(), interval, timeout, true, func(context.Context) (bool, error) {
+	err := wait.PollUntilContextTimeout(ctx, interval, timeout, true, func(context.Context) (bool, error) {
 		if lastErr = f(); lastErr != nil {
 			return false, nil
 		}

(call sites would then pass their existing ctx)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/helpers_test.go` around lines 23 - 37, Update poll to accept a
context.Context parameter and pass it to wait.PollUntilContextTimeout instead of
context.Background(). Update all three poll call sites to provide their existing
ctx values, preserving the current retry and error-handling behavior.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/e2e/delete_alert_rule_test.go`:
- Line 95: Handle the error returned by resp.Body.Close() in the defer near the
affected test, rather than discarding it. Update the deferred cleanup to check
the close result and propagate or report any error using the test’s existing
error-handling mechanism.

In `@test/e2e/helpers_test.go`:
- Around line 39-50: The retries around non-idempotent alert-rule operations are
unsafe. In test/e2e/helpers_test.go:39-50, update createRuleViaAPIWithRetry to
verify existing state or otherwise avoid creating a duplicate when the server
may have already processed the request; in
test/e2e/delete_alert_rule_test.go:80-121, make the bulk-delete retry treat
already-deleted/not-found rules as success and avoid repeatedly failing on IDs
removed by an earlier attempt.

---

Nitpick comments:
In `@test/e2e/helpers_test.go`:
- Around line 23-37: Update poll to accept a context.Context parameter and pass
it to wait.PollUntilContextTimeout instead of context.Background(). Update all
three poll call sites to provide their existing ctx values, preserving the
current retry and error-handling behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: db90c7b5-bec6-45bc-81a7-c48e7cf18598

📥 Commits

Reviewing files that changed from the base of the PR and between 6810f45 and 330236d.

📒 Files selected for processing (3)
  • test/e2e/create_alert_rule_test.go
  • test/e2e/delete_alert_rule_test.go
  • test/e2e/helpers_test.go

if err != nil {
return fmt.Errorf("failed to make delete request: %w", err)
}
defer resp.Body.Close()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Unchecked resp.Body.Close() error return.

Flagged by errcheck; per path instructions, Go code should never ignore error returns.

🔧 Suggested fix
-		defer resp.Body.Close()
+		defer func() {
+			if cerr := resp.Body.Close(); cerr != nil {
+				t.Logf("failed to close response body: %v", cerr)
+			}
+		}()
🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 95-95: Error return value of resp.Body.Close is not checked

(errcheck)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/delete_alert_rule_test.go` at line 95, Handle the error returned by
resp.Body.Close() in the defer near the affected test, rather than discarding
it. Update the deferred cleanup to check the close result and propagate or
report any error using the test’s existing error-handling mechanism.

Sources: Path instructions, Linters/SAST tools

Comment thread test/e2e/helpers_test.go
Comment on lines +39 to +50
func createRuleViaAPIWithRetry(ctx context.Context, f *framework.Framework, createAlertRuleRequest managementrouter.CreateAlertRuleRequest) (string, error) {
var id string
err := poll(time.Second, 20*time.Second, func() error {
var err error
id, err = createRuleViaAPI(ctx, f, createAlertRuleRequest)
if err != nil {
return fmt.Errorf("failed to create alert rule: %w", err)
}
return nil
})
return id, err
}

@coderabbitai coderabbitai Bot Jul 21, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Retry-wrapping non-idempotent create/delete calls without idempotency safeguards. Both sites use poll to retry an HTTP write whose server-side effect may already have taken hold even though the client observed an error, so retries can create duplicates or fail permanently against already-mutated state — the opposite of the intended flakiness reduction.

  • test/e2e/helpers_test.go#L39-L50: createRuleViaAPIWithRetry may create a duplicate alert rule (different ID, same spec) if the create actually succeeded server-side but the client failed to observe it before retrying.
  • test/e2e/delete_alert_rule_test.go#L80-L121: the bulk-delete poll resends the same fixed ruleIDs[0]/ruleIDs[1] request; if one ID was already deleted by a prior attempt, the retry will keep failing on that ID until the 1-minute timeout, causing a false test failure.

Consider making retries idempotency-aware, e.g., treat "already deleted"/"not found" as success on delete, or query current state before retrying on create.

📍 Affects 2 files
  • test/e2e/helpers_test.go#L39-L50 (this comment)
  • test/e2e/delete_alert_rule_test.go#L80-L121
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/helpers_test.go` around lines 39 - 50, The retries around
non-idempotent alert-rule operations are unsafe. In
test/e2e/helpers_test.go:39-50, update createRuleViaAPIWithRetry to verify
existing state or otherwise avoid creating a duplicate when the server may have
already processed the request; in test/e2e/delete_alert_rule_test.go:80-121,
make the bulk-delete retry treat already-deleted/not-found rules as success and
avoid repeatedly failing on IDs removed by an earlier attempt.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is indeed a good remark but it also deserves a discussion with @sradco @PeterYurkovich and @jgbernalp about the semantics of the bulk delete API:

  • What's the API response if an id doesn't exist?
  • What's the API response if the delete operation partially succeeded?

Right now the API returns HTTP 200 OK on partial success which is a bit misleading IMHO

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't an HTTP code for partial success/failures, which is why the request returns a per-item response result in the body. I'd assume the response if the id doesn't exist should just be 200 OK on the request and then on the individual item in the body a "no ID found"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a "new" http code for multi status. Which internally assigns status to individual operations.

@PeterYurkovich PeterYurkovich Jul 23, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw that yesterday, but if you look at the description it states it is used exclusively for a specific usecase

This response is used exclusively in the context of Web Distributed Authoring and Versioning (WebDAV)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning "200 OK" when an ID doesn't exist is ok-ish but not a good option when the request partially fails for other reasons. In addition, you can't simply forward the downstream response codes because it could a mix of "200 OK", "400 Bad Request" and "500 Server Unavailable".
IMHO it just shows how problematic a bulk delete API endpoint is. In general I'd be more in a favor of supporting individual deletes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it just reflects that HTTP codes were created with single resource operations in mind, different from what we have today or in this context. A "200" could indicate that the bulk action itself was successful, but the results of individual updates can be included inside the payload.

@openshift-ci openshift-ci Bot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 22, 2026
Requests to the backend may fail for a variety of reasons such as
network connectivity, API blips, etc. Instead of failing a test, this
commit implements retries for API requests.

Signed-off-by: Simon Pasquier <spasquie@redhat.com>
@openshift-ci openshift-ci Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 22, 2026
@simonpasquier

Copy link
Copy Markdown
Contributor Author

/cc @PeterYurkovich

@etmurasaki

Copy link
Copy Markdown
Contributor

/test e2e-monitoring

@PeterYurkovich

Copy link
Copy Markdown
Contributor

/override-sticky ci/prow/e2e-monitoring

@openshift-ci

openshift-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

@PeterYurkovich: Overrode contexts on behalf of PeterYurkovich: ci/prow/e2e-monitoring

These overrides will persist across retests on the current HEAD SHA. Pushing a new commit will clear them. Use /override-cancel to remove them.

Details

In response to this:

/override-sticky ci/prow/e2e-monitoring

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@openshift-ci

openshift-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

@simonpasquier: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants