Skip to content

OCPBUGS-101783: reconcile canary certificate on dependency creation - #1538

Merged
openshift-merge-bot[bot] merged 3 commits into
openshift:masterfrom
not-stbenjam:agent/canary-certificate-dependency-watches
Aug 18, 2026
Merged

OCPBUGS-101783: reconcile canary certificate on dependency creation#1538
openshift-merge-bot[bot] merged 3 commits into
openshift:masterfrom
not-stbenjam:agent/canary-certificate-dependency-watches

Conversation

@not-stbenjam

Copy link
Copy Markdown
Contributor

The canary-certificate controller can permanently miss reconciliation when its dependencies are created in an unlucky order. It watches only Secrets, and its default-certificate predicate rejects the Secret event when the default IngressController does not exist yet. If the IngressController appears afterward, no watched event retries the controller.

A failed 5.0 HyperShift AKS job captured the exact race: the default certificate existed, its initial event was rejected at 02:25:32.434Z, and the default IngressController appeared about two seconds later. canary-serving-cert was never created; every canary pod remained Pending on the missing Secret, the Service had no endpoints, the admitted route returned EOF, and ingress degraded. A same-payload control with safe object ordering created the certificate successfully. This was the job's sole terminal cause, for a conservative historical impact of one green Prow job.

This change:

  • watches the default IngressController and the canary DaemonSet, the two non-Secret reconciliation dependencies;
  • maps their semantic create/update/delete events to the single canary-certificate request;
  • filters status-only updates with the generation predicate;
  • treats an expected not-yet-created IngressController in the Secret predicate as dependency-not-ready rather than a noisy error;
  • adds focused predicate and event-mapping coverage, including unrelated-resource rejection.

Validation:

  • GOTOOLCHAIN=auto GOMAXPROCS=2 go test -mod=vendor -p=2 ./pkg/operator/controller/canary-certificate
  • git diff --check

Representative job: https://prow.ci.openshift.org/view/gs/test-platform-results/logs/periodic-ci-openshift-hypershift-release-5.0-periodics-e2e-aks/2082648859074367488

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@not-stbenjam: This pull request explicitly references no jira issue.

Details

In response to this:

The canary-certificate controller can permanently miss reconciliation when its dependencies are created in an unlucky order. It watches only Secrets, and its default-certificate predicate rejects the Secret event when the default IngressController does not exist yet. If the IngressController appears afterward, no watched event retries the controller.

A failed 5.0 HyperShift AKS job captured the exact race: the default certificate existed, its initial event was rejected at 02:25:32.434Z, and the default IngressController appeared about two seconds later. canary-serving-cert was never created; every canary pod remained Pending on the missing Secret, the Service had no endpoints, the admitted route returned EOF, and ingress degraded. A same-payload control with safe object ordering created the certificate successfully. This was the job's sole terminal cause, for a conservative historical impact of one green Prow job.

This change:

  • watches the default IngressController and the canary DaemonSet, the two non-Secret reconciliation dependencies;
  • maps their semantic create/update/delete events to the single canary-certificate request;
  • filters status-only updates with the generation predicate;
  • treats an expected not-yet-created IngressController in the Secret predicate as dependency-not-ready rather than a noisy error;
  • adds focused predicate and event-mapping coverage, including unrelated-resource rejection.

Validation:

  • GOTOOLCHAIN=auto GOMAXPROCS=2 go test -mod=vendor -p=2 ./pkg/operator/controller/canary-certificate
  • git diff --check

Representative job: https://prow.ci.openshift.org/view/gs/test-platform-results/logs/periodic-ci-openshift-hypershift-release-5.0-periodics-e2e-aks/2082648859074367488

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 openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Aug 2, 2026
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 2, 2026
@coderabbitai

coderabbitai Bot commented Aug 2, 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: Pro Plus

Run ID: d5c8c371-a217-45d3-a89a-3fbbdf9b8a16

📥 Commits

Reviewing files that changed from the base of the PR and between acca964 and 4a89a91.

📒 Files selected for processing (2)
  • pkg/operator/controller/canary-certificate/controller.go
  • pkg/operator/controller/canary-certificate/controller_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/operator/controller/canary-certificate/controller.go
  • pkg/operator/controller/canary-certificate/controller_test.go

📝 Walkthrough

Walkthrough

The canary certificate controller now watches the default IngressController and canary DaemonSet in addition to certificate Secret resources. Matching dependency events enqueue the canary certificate reconciliation target, with generation filtering applied. Certificate and dependency predicates use reusable helpers. Default IngressController not-found errors are no longer logged, while other lookup errors remain logged. Tests cover matching and unrelated dependency events.

Suggested reviewers: rikatz

🚥 Pre-merge checks | ✅ 15
✅ Passed checks (15 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the canary certificate reconciliation change caused by dependency creation.
Description check ✅ Passed The description directly explains the dependency-ordering race, implemented watches, predicates, tests, and validation.
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 The changed tests use Go test functions and t.Run with fixed table descriptions; no Ginkgo titles or run-varying values appear in the diff.
Test Structure And Quality ✅ Passed The PR adds standard Go testing unit tests, not Ginkgo tests; no It blocks, cluster operations, Eventually/Consistently waits, or Ginkgo setup/cleanup apply.
Microshift Test Compatibility ✅ Passed The added tests use standard Go testing.T, not Ginkgo e2e constructs. The MicroShift compatibility check does not apply.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The PR adds standard Go unit tests in pkg/operator/controller/canary-certificate, not Ginkgo e2e tests; no SNO multi-node compatibility check is applicable.
Topology-Aware Scheduling Compatibility ✅ Passed The diff changes only canary-certificate watches, predicates, logging, and tests; it adds no scheduling constraints, replica settings, node selectors, affinities, tolerations, topology spreads, or...
Ote Binary Stdout Contract ✅ Passed The PR changes only controller and unit-test files; it adds no stdout writes or process-level entry points. The OTE main is unchanged, and its explicit diagnostic uses os.Stderr.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The changed tests use standard testing.T, not Ginkgo e2e constructs, and contain no IPv4 literals, network parsing, URLs, or external connectivity.
No-Weak-Crypto ✅ Passed The PR changes only controller watches, predicates, logging, and tests; the added diff contains no MD5, SHA1, DES, RC4, Blowfish, ECB, custom crypto, or secret/token comparison.
Container-Privileges ✅ Passed The diff changes only Go controller code and tests. It adds no manifests or privilege-related settings such as privileged, hostPID, hostNetwork, SYS_ADMIN, or allowPrivilegeEscalation.
No-Sensitive-Data-In-Logs ✅ Passed The diff adds watches, predicates, and tests; it adds no sensitive logging. Existing logs emit only reconcile/resource namespace and name, and NotFound errors are now suppressed.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@openshift-ci openshift-ci Bot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Aug 2, 2026
@openshift-ci

openshift-ci Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Hi @not-stbenjam. Thanks for your PR.

I'm waiting for a openshift member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Tip

We noticed you've done this a few times! Consider joining the org to skip this step and gain /lgtm and other bot rights. We recommend asking approvers on your previous PRs to sponsor you.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

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.

@stbenjam

stbenjam commented Aug 3, 2026

Copy link
Copy Markdown
Member

/ok-to-test

@stbenjam
stbenjam marked this pull request as ready for review August 3, 2026 17:04
@openshift-ci openshift-ci Bot added ok-to-test Indicates a non-member PR verified by an org member that is safe to test. and removed do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. labels Aug 3, 2026
@openshift-ci
openshift-ci Bot requested review from Miciah and rikatz August 3, 2026 17:04

@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.

🧹 Nitpick comments (5)
pkg/operator/controller/canary-certificate/controller.go (2)

87-89: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add resource identity to the lookup error log.

Line 88 logs the failure without the IngressController namespace or name. Include defaultICName.Namespace and defaultICName.Name as structured fields.

Proposed change
-log.Error(err, "Failed to get default IngressController")
+log.Error(err, "Failed to get default IngressController",
+    "namespace", defaultICName.Namespace,
+    "name", defaultICName.Name,
+)

As per coding guidelines, “Use structured logging via go-logr/logr with relevant context (namespace, name, resource type).”

🤖 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 `@pkg/operator/controller/canary-certificate/controller.go` around lines 87 -
89, Update the error log in the default IngressController lookup to include the
resource identity as structured fields, using defaultICName.Namespace and
defaultICName.Name alongside the existing error and message. Keep the existing
not-found filtering and failure handling unchanged.

Source: Coding guidelines


100-102: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Wrap each Watch setup error with operation context.

Lines 101, 112, and 120 return raw errors. Identify the failed resource watch and preserve the error chain with %w.

Proposed change
- return nil, err
+ return nil, fmt.Errorf("failed to watch canary certificate Secrets: %w", err)

Apply equivalent context for the IngressController and DaemonSet watches.

As per coding guidelines, “Return errors with context using fmt.Errorf with %w format specifier for error wrapping to allow errors.Is/errors.As unwrapping.”

Also applies to: 111-113, 119-120

🤖 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 `@pkg/operator/controller/canary-certificate/controller.go` around lines 100 -
102, Wrap the errors returned by each Watch setup in the controller’s
watch-registration flow with fmt.Errorf using %w, adding resource-specific
context for the Secret, IngressController, and DaemonSet watches while
preserving the original error chain.

Source: Coding guidelines

pkg/operator/controller/canary-certificate/controller_test.go (3)

20-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use the required specific-test naming convention.

Rename these tests to end with Functionality.

Proposed change
-func TestCanaryCertificateDependencyEvents(t *testing.T) {
+func TestCanaryCertificateDependencyEventsFunctionality(t *testing.T) {

-func TestHasNamespacedNameRejectsUnrelatedDependencies(t *testing.T) {
+func TestHasNamespacedNameRejectsUnrelatedDependenciesFunctionality(t *testing.T) {

As per coding guidelines, “Follow test naming conventions: Test_foo for general tests, TestFooFunctionality for specific functionality tests.”

Also applies to: 84-84

🤖 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 `@pkg/operator/controller/canary-certificate/controller_test.go` at line 20,
Rename the specific functionality tests, including
TestCanaryCertificateDependencyEvents and the additionally referenced test, so
each name ends with Functionality while preserving their existing descriptive
prefixes and test behavior.

Source: Coding guidelines


67-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use testify/assert for test assertions.

Replace the direct t.Fatalf assertion blocks with assert.True, assert.False, assert.Len, and assert.Equal. Keep the current failure context in assertion messages.

As per coding guidelines, “Use testify/assert for assertions in tests.”

Also applies to: 105-109

🤖 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 `@pkg/operator/controller/canary-certificate/controller_test.go` around lines
67 - 80, Update the test assertions in the t.Run cases around hasNamespacedName,
tc.matches, and requests to use testify/assert instead of direct t.Fatalf calls.
Use assert.True, assert.False, assert.Len, and assert.Equal as appropriate,
preserving the existing failure context in assertion messages and applying the
same change to the additional assertion block.

Source: Coding guidelines


105-109: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Run each negative dependency case as a subtest.

This table has three distinct cases. Add a static description field and wrap each iteration in t.Run(tc.description, ...). This isolates failures and identifies the rejected resource in test output.

As per coding guidelines, “Use t.Run() with descriptive names for nested subtests in test files.” Based on learnings, use t.Run() when a table has two or more distinct cases.

🤖 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 `@pkg/operator/controller/canary-certificate/controller_test.go` around lines
105 - 109, Update the negative dependency test loop to include a static
description field in each test case and execute each case with
t.Run(tc.description, ...), keeping the existing tc.matches assertion inside the
subtest so failures identify the rejected resource.

Sources: Coding guidelines, Learnings

🤖 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.

Nitpick comments:
In `@pkg/operator/controller/canary-certificate/controller_test.go`:
- Line 20: Rename the specific functionality tests, including
TestCanaryCertificateDependencyEvents and the additionally referenced test, so
each name ends with Functionality while preserving their existing descriptive
prefixes and test behavior.
- Around line 67-80: Update the test assertions in the t.Run cases around
hasNamespacedName, tc.matches, and requests to use testify/assert instead of
direct t.Fatalf calls. Use assert.True, assert.False, assert.Len, and
assert.Equal as appropriate, preserving the existing failure context in
assertion messages and applying the same change to the additional assertion
block.
- Around line 105-109: Update the negative dependency test loop to include a
static description field in each test case and execute each case with
t.Run(tc.description, ...), keeping the existing tc.matches assertion inside the
subtest so failures identify the rejected resource.

In `@pkg/operator/controller/canary-certificate/controller.go`:
- Around line 87-89: Update the error log in the default IngressController
lookup to include the resource identity as structured fields, using
defaultICName.Namespace and defaultICName.Name alongside the existing error and
message. Keep the existing not-found filtering and failure handling unchanged.
- Around line 100-102: Wrap the errors returned by each Watch setup in the
controller’s watch-registration flow with fmt.Errorf using %w, adding
resource-specific context for the Secret, IngressController, and DaemonSet
watches while preserving the original error chain.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: 8632d40c-63f9-489a-9a26-c3a1a1c872f7

📥 Commits

Reviewing files that changed from the base of the PR and between dc076b7 and c7d0225.

📒 Files selected for processing (2)
  • pkg/operator/controller/canary-certificate/controller.go
  • pkg/operator/controller/canary-certificate/controller_test.go

@stbenjam stbenjam changed the title NO-JIRA: reconcile canary certificate on dependency creation OCPBUGS-101783: reconcile canary certificate on dependency creation Aug 3, 2026
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@not-stbenjam: This pull request references Jira Issue OCPBUGS-101783, which is valid. The bug has been moved to the POST state.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state New, which is one of the valid states (NEW, ASSIGNED, POST)

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

The canary-certificate controller can permanently miss reconciliation when its dependencies are created in an unlucky order. It watches only Secrets, and its default-certificate predicate rejects the Secret event when the default IngressController does not exist yet. If the IngressController appears afterward, no watched event retries the controller.

A failed 5.0 HyperShift AKS job captured the exact race: the default certificate existed, its initial event was rejected at 02:25:32.434Z, and the default IngressController appeared about two seconds later. canary-serving-cert was never created; every canary pod remained Pending on the missing Secret, the Service had no endpoints, the admitted route returned EOF, and ingress degraded. A same-payload control with safe object ordering created the certificate successfully. This was the job's sole terminal cause, for a conservative historical impact of one green Prow job.

This change:

  • watches the default IngressController and the canary DaemonSet, the two non-Secret reconciliation dependencies;
  • maps their semantic create/update/delete events to the single canary-certificate request;
  • filters status-only updates with the generation predicate;
  • treats an expected not-yet-created IngressController in the Secret predicate as dependency-not-ready rather than a noisy error;
  • adds focused predicate and event-mapping coverage, including unrelated-resource rejection.

Validation:

  • GOTOOLCHAIN=auto GOMAXPROCS=2 go test -mod=vendor -p=2 ./pkg/operator/controller/canary-certificate
  • git diff --check

Representative job: https://prow.ci.openshift.org/view/gs/test-platform-results/logs/periodic-ci-openshift-hypershift-release-5.0-periodics-e2e-aks/2082648859074367488

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 openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot openshift-ci-robot added the jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. label Aug 3, 2026
@redhat-chai-bot

Copy link
Copy Markdown
Contributor

Review: Canary certificate dependency watch fix

I was asked to review this PR by the team in #forum-ocp-network-edge. Here's my analysis — I'm an AI assistant (Chai Bot), so this is supplemental to human reviewer judgment.

Root cause analysis ✅

The race condition is accurately identified and well-documented. The existing New() function only watches Secret resources:

c.Watch(source.Kind[client.Object](operatorCache, &corev1.Secret{}, ...))

The isDefaultIngressCert predicate calls reconciler.client.Get() to look up the default IngressController. If that object doesn't exist yet when the Secret event fires, the predicate returns false and the event is permanently discarded. Since no watch covers the IngressController or DaemonSet, there's no retry path — the canary certificate is never created, leading to the chain of failures described (canary pods Pending → no endpoints → route EOF → ingress degraded).

This is a classic controller-runtime dependency-ordering race.

Fix assessment ✅

The approach is correct and minimal:

  1. IngressController watch — Ensures that when the default IngressController is created (even after the Secret events have fired), the canary-certificate controller retries reconciliation. GenerationChangedPredicate correctly filters out status-only updates.

  2. DaemonSet watch — The canary DaemonSet is needed as an owner reference in desiredCanaryCertificate(). Watching it ensures its creation also triggers reconciliation, independently of error-retry backoff.

  3. NotFound suppression — In the Secret predicate, treating a not-yet-created IngressController as a silent dependency-not-ready state (rather than logging an error) is appropriate. Actual errors (RBAC, network) are still logged.

  4. Refactoring — Extracting inline closures into named, testable functions (isCanaryCertificate, isDefaultIngressControllerDependency, isCanaryDaemonSetDependency, hasNamespacedName, toCanaryCertificate) improves readability and enables the unit test coverage added in the test file.

  5. Test coverage — Positive matching for all three dependency types, event mapping to the correct reconciliation target, and negative cases rejecting unrelated resources. Solid coverage for the new logic.

Observations

  • The ensureCanaryCertificate() reconciliation method (controller.go:122) already handles IngressController or DaemonSet not-found gracefully by returning an error, which triggers controller-runtime's rate-limited requeue. The new watches provide a faster, event-driven retry path instead of relying solely on the error requeue backoff. This is the idiomatic controller-runtime approach.
  • CodeRabbit flagged some nitpicks (structured log fields, error wrapping on Watch calls, test naming conventions, testify assertions). These are all trivial and shouldn't block.
  • CI first-stage checks (unit, verify, images, verify-deps) have all passed. Second-stage e2e tests are pending.

Verdict

The fix looks correct and well-scoped to me. It addresses a real payload-killing race condition with the standard controller-runtime pattern of watching all dependencies. I'd recommend an Ingress domain expert (cc @jcmoraisjr per OWNERS) give this a look and approve if they agree with the approach.


Review requested from Slack by the release team. This is an AI-assisted review — final approval should come from a domain expert.


AI-generated. Review for accuracy.

@bentito

bentito commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

/lgtm
/assign @rfredette
@rfredette I think our #1334 introduced a kind of subtle race. I think these changes seem like a reasonable remediation. If you agree /approve ?

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Aug 3, 2026
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling tests matching the pipeline_run_if_changed or not excluded by pipeline_skip_if_only_changed parameters:
/test e2e-aws-operator
/test e2e-aws-ovn
/test e2e-aws-ovn-hypershift-conformance
/test e2e-aws-ovn-serial-1of2
/test e2e-aws-ovn-serial-2of2
/test e2e-aws-ovn-upgrade
/test e2e-azure-operator
/test e2e-gcp-operator
/test e2e-hypershift
/test e2e-vsphere-static-metallb-operator-gwapi
/test e2e-vsphere-static-metallb-operator-gwapi-techpreview
/test hypershift-e2e-aks

@bentito

bentito commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai are the failing checks due to code or infra?

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="openshift/cluster-ingress-operator"
pr_number="1538"

echo "== Pull request checks =="
gh pr checks "$pr_number" --repo "$repo" --json name,state,workflow,link,startedAt,completedAt

echo
echo "== Failed check runs =="
gh api "repos/$repo/commits/$(gh pr view "$pr_number" --repo "$repo" --json headRefOid --jq .headRefOid)/check-runs?status=completed&per_page=100" \
  --jq '.check_runs[] | select(.conclusion == "failure" or .conclusion == "timed_out" or .conclusion == "cancelled" or .conclusion == "action_required") | {name, conclusion, details_url, output: {title: .output.title, summary: .output.summary, text: .output.text}}'

Length of output: 7338


🏁 Script executed:

#!/bin/bash
set -euo pipefail

urls=(
  "https://prow.ci.openshift.org/view/gs/test-platform-results/pr-logs/pull/openshift_cluster-ingress-operator/1538/pull-ci-openshift-cluster-ingress-operator-master-e2e-aws-operator/2084341714843078656"
  "https://prow.ci.openshift.org/view/gs/test-platform-results/pr-logs/pull/openshift_cluster-ingress-operator/1538/pull-ci-openshift-cluster-ingress-operator-master-e2e-aws-ovn-hypershift-conformance/2084341714922770432"
  "https://prow.ci.openshift.org/view/gs/test-platform-results/pr-logs/pull/openshift_cluster-ingress-operator/1538/pull-ci-openshift-cluster-ingress-operator-master-e2e-azure-operator/2084341715069571072"
  "https://prow.ci.openshift.org/view/gs/test-platform-results/pr-logs/pull/openshift_cluster-ingress-operator/1538/pull-ci-openshift-cluster-ingress-operator-master-e2e-gcp-operator/2084341715090542592"
  "https://prow.ci.openshift.org/view/gs/test-platform-results/pr-logs/pull/openshift_cluster-ingress-operator/1538/pull-ci-openshift-cluster-ingress-operator-master-e2e-vsphere-static-metallb-operator-gwapi/2084341715166040064"
)

for url in "${urls[@]}"; do
  echo "===== $url ====="
  page="$(curl -fsSL "$url")"
  printf '%s' "$page" |
    rg -io 'https?[^"[:space:]<>]+(build-log\.txt|finished\.json|junit[^"[:space:]<>]*\.xml|artifacts[^"[:space:]<>]*)' |
    sed 's/&amp;/\&/g' |
    sort -u |
    head -30
done

Length of output: 1912


🏁 Script executed:

#!/bin/bash
set -u

prefixes=(
  "pr-logs/pull/openshift_cluster-ingress-operator/1538/pull-ci-openshift-cluster-ingress-operator-master-e2e-aws-operator/2084341714843078656"
  "pr-logs/pull/openshift_cluster-ingress-operator/1538/pull-ci-openshift-cluster-ingress-operator-master-e2e-aws-ovn-hypershift-conformance/2084341714922770432"
  "pr-logs/pull/openshift_cluster-ingress-operator/1538/pull-ci-openshift-cluster-ingress-operator-master-e2e-azure-operator/2084341715069571072"
  "pr-logs/pull/openshift_cluster-ingress-operator/1538/pull-ci-openshift-cluster-ingress-operator-master-e2e-gcp-operator/2084341715090542592"
  "pr-logs/pull/openshift_cluster-ingress-operator/1538/pull-ci-openshift-cluster-ingress-operator-master-e2e-vsphere-static-metallb-operator-gwapi/2084341715166040064"
)

for prefix in "${prefixes[@]}"; do
  echo "===== $prefix ====="
  echo "-- finished.json --"
  curl -fsSL "https://storage.googleapis.com/test-platform-results/$prefix/finished.json" 2>&1 || true
  echo
  echo "-- terminal build-log lines --"
  curl -fsSL "https://storage.googleapis.com/test-platform-results/$prefix/build-log.txt" 2>&1 |
    tail -250 |
    rg -in -C 3 'fail|error|timeout|panic|degrad|install|test' || true
  echo
done

Length of output: 118174


@bentito, the failures are not caused by this PR's canary-certificate change.

The evidence does not show one common CI infrastructure outage. The failures are unrelated platform-suite failures:

  • e2e-aws-ovn-hypershift-conformance failed two upstream DRA kubelet tests.
  • e2e-azure-operator failed TestGatewayAPI/testGatewayAPIObjects.
  • e2e-gcp-operator failed Gateway API DNS subtests.
  • e2e-vsphere-static-metallb-operator-gwapi failed TestGatewayAPI/testGatewayAPIObjects.
  • e2e-aws-operator has unreachable kubelets during failure collection. The available terminal log does not show the original failed test.

The modified canary paths passed in the Azure and GCP jobs. This includes TestDefaultIngressCertificate, TestCanaryRoute, and TestCanaryWithMTLS.

I would classify these as unrelated e2e or payload/platform instability, not as failures introduced by this PR.

You are interacting with an AI system.

@bentito

bentito commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

/retest

1 similar comment
@not-stbenjam

Copy link
Copy Markdown
Contributor Author

/retest

@openshift-ci openshift-ci Bot removed the lgtm Indicates that a PR is ready to be merged. label Aug 5, 2026
Comment thread pkg/operator/controller/canary-certificate/controller.go Outdated
@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Aug 7, 2026
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling tests matching the pipeline_run_if_changed or not excluded by pipeline_skip_if_only_changed parameters:
/test e2e-aws-operator
/test e2e-aws-ovn
/test e2e-aws-ovn-hypershift-conformance
/test e2e-aws-ovn-serial-1of2
/test e2e-aws-ovn-serial-2of2
/test e2e-aws-ovn-upgrade
/test e2e-azure-operator
/test e2e-gcp-operator
/test e2e-hypershift
/test e2e-vsphere-static-metallb-operator-gwapi
/test e2e-vsphere-static-metallb-operator-gwapi-techpreview
/test hypershift-e2e-aks

@stbenjam

Copy link
Copy Markdown
Member

/verified by CI

@openshift-ci-robot openshift-ci-robot added the verified Signifies that the PR passed pre-merge verification criteria label Aug 12, 2026
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@stbenjam: This PR has been marked as verified by CI.

Details

In response to this:

/verified by CI

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 openshift-eng/jira-lifecycle-plugin repository.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

/retest-required

Remaining retests: 0 against base HEAD acca964 and 2 for PR HEAD 6364c97 in total

Comment on lines +106 to +111
// permanently discard that Secret event. Generation filtering avoids
// reconciling on status-only updates.
isDefaultIngressController := predicate.NewPredicateFuncs(func(o client.Object) bool {
return isDefaultIngressControllerDependency(o, config.OperatorNamespace)
})
if err := c.Watch(source.Kind[client.Object](operatorCache, &operatorv1.IngressController{}, enqueueRequestForCanaryCertificate, predicate.And(isDefaultIngressController, predicate.GenerationChangedPredicate{}))); err != nil {

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 thought we only cared about an out-of-order create event. Why do we care about update events?

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.

The create event is what fixes this bug, but update events matter too: the effective default-certificate Secret name is derived from the IngressController's spec.defaultCertificate. If that field is updated to point at a Secret that already exists, no Secret event fires (the isDefaultIngressCert predicate computes the effective name at event time, so it would only match a future event on the new Secret), and the canary certificate would keep mirroring the old certificate until something else nudged the controller. Watching spec updates closes that gap, and GenerationChangedPredicate keeps status-only updates from triggering reconciles.

@Miciah

Miciah commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

You need a rebase to eliminate the merge commit in https://github.com/openshift/cluster-ingress-operator/pull/1538/commits.

Less importantly, it would be nice to put the refactoring (introducing the isCanaryCertificate and toCanaryCertificate helpers) and logging changes into separate commits from the actual fix (the added watches) and tests.

@not-stbenjam
not-stbenjam force-pushed the agent/canary-certificate-dependency-watches branch from 6364c97 to 4a89a91 Compare August 12, 2026 18:39
@openshift-ci-robot openshift-ci-robot removed the verified Signifies that the PR passed pre-merge verification criteria label Aug 12, 2026
@openshift-ci openshift-ci Bot removed the lgtm Indicates that a PR is ready to be merged. label Aug 12, 2026
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@not-stbenjam

not-stbenjam commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

@Miciah Rebased onto master to drop the merge commit and restructured the branch into three commits, with the fix first so it can be cherry-picked to release branches on its own (verified it applies cleanly to release-4.20 and passes unit tests there):

  1. canary-certificate: watch reconciliation dependencies — the actual fix (IngressController + DaemonSet watches) and tests, self-contained against the pre-existing code
  2. canary-certificate: extract isCanaryCertificate predicate helper — pure refactor (no behavior change)
  3. canary-certificate: improve default IngressController lookup logging — stops logging expected NotFound as an error and adds namespace/name fields

While restructuring, I also addressed the inline comments: reverted the c.Watch error wrapping to match the rest of the repo, and moved HasNamespacedName out of names.go back to an unexported helper in the canary-certificate package. Replied on the update-events question in-thread.

not-stbenjam and others added 3 commits August 12, 2026 14:49
The canary-certificate controller watches only Secrets. Its
default-certificate predicate rejects the Secret event when the default
IngressController does not exist yet, and if the IngressController is
created afterward, no watched event retries the controller, so
canary-serving-cert is never created and ingress degrades.

Watch the default IngressController and the canary DaemonSet, the two
non-Secret reconciliation dependencies, and map their events to the
controller's single reconcile target so that dependency creation order
cannot permanently discard the Secret event. Filter status-only updates
with the generation predicate. Add predicate and event-mapping unit
tests, including unrelated-resource rejection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the inline canary-certificate predicate closure with a named
isCanaryCertificate function built on the hasNamespacedName helper, and
extend the dependency-event unit tests to cover it. No behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The default-certificate predicate logs an error whenever the default
IngressController lookup fails. A NotFound result is expected while the
IngressController has not been created yet, so stop logging it as an
error, and include the namespace and name in the log fields for the
remaining unexpected failures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@not-stbenjam
not-stbenjam force-pushed the agent/canary-certificate-dependency-watches branch from 4a89a91 to fe1b58a Compare August 12, 2026 18:52
@stbenjam

Copy link
Copy Markdown
Member

/pipeline required

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling tests matching the pipeline_run_if_changed or not excluded by pipeline_skip_if_only_changed parameters:
/test e2e-aws-operator
/test e2e-aws-ovn
/test e2e-aws-ovn-hypershift-conformance
/test e2e-aws-ovn-serial-1of2
/test e2e-aws-ovn-serial-2of2
/test e2e-aws-ovn-upgrade
/test e2e-azure-operator
/test e2e-gcp-operator
/test e2e-hypershift
/test e2e-vsphere-static-metallb-operator-gwapi
/test e2e-vsphere-static-metallb-operator-gwapi-techpreview
/test hypershift-e2e-aks

@bentito

bentito commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

/lgtm

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Aug 14, 2026
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling tests matching the pipeline_run_if_changed or not excluded by pipeline_skip_if_only_changed parameters:
/test e2e-aws-operator
/test e2e-aws-ovn
/test e2e-aws-ovn-hypershift-conformance
/test e2e-aws-ovn-serial-1of2
/test e2e-aws-ovn-serial-2of2
/test e2e-aws-ovn-upgrade
/test e2e-azure-operator
/test e2e-gcp-operator
/test e2e-hypershift
/test e2e-vsphere-static-metallb-operator-gwapi
/test e2e-vsphere-static-metallb-operator-gwapi-techpreview
/test hypershift-e2e-aks

@stbenjam

Copy link
Copy Markdown
Member

/retest-required
/verified by CI

@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@stbenjam: This PR has been marked as verified by CI.

Details

In response to this:

/retest-required
/verified by CI

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 openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot openshift-ci-robot added the verified Signifies that the PR passed pre-merge verification criteria label Aug 18, 2026
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

/retest-required

Remaining retests: 0 against base HEAD eaa9948 and 2 for PR HEAD fe1b58a in total

@openshift-ci

openshift-ci Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

@not-stbenjam: 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.

@openshift-merge-bot
openshift-merge-bot Bot merged commit b1cbed2 into openshift:master Aug 18, 2026
19 checks passed
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@not-stbenjam: Jira Issue Verification Checks: Jira Issue OCPBUGS-101783
✔️ This pull request was pre-merge verified.
✔️ All associated pull requests have merged.
✔️ All associated, merged pull requests were pre-merge verified.

Jira Issue OCPBUGS-101783 has been moved to the MODIFIED state and will move to the VERIFIED state when the change is available in an accepted nightly payload. 🕓

Details

In response to this:

The canary-certificate controller can permanently miss reconciliation when its dependencies are created in an unlucky order. It watches only Secrets, and its default-certificate predicate rejects the Secret event when the default IngressController does not exist yet. If the IngressController appears afterward, no watched event retries the controller.

A failed 5.0 HyperShift AKS job captured the exact race: the default certificate existed, its initial event was rejected at 02:25:32.434Z, and the default IngressController appeared about two seconds later. canary-serving-cert was never created; every canary pod remained Pending on the missing Secret, the Service had no endpoints, the admitted route returned EOF, and ingress degraded. A same-payload control with safe object ordering created the certificate successfully. This was the job's sole terminal cause, for a conservative historical impact of one green Prow job.

This change:

  • watches the default IngressController and the canary DaemonSet, the two non-Secret reconciliation dependencies;
  • maps their semantic create/update/delete events to the single canary-certificate request;
  • filters status-only updates with the generation predicate;
  • treats an expected not-yet-created IngressController in the Secret predicate as dependency-not-ready rather than a noisy error;
  • adds focused predicate and event-mapping coverage, including unrelated-resource rejection.

Validation:

  • GOTOOLCHAIN=auto GOMAXPROCS=2 go test -mod=vendor -p=2 ./pkg/operator/controller/canary-certificate
  • git diff --check

Representative job: https://prow.ci.openshift.org/view/gs/test-platform-results/logs/periodic-ci-openshift-hypershift-release-5.0-periodics-e2e-aks/2082648859074367488

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 openshift-eng/jira-lifecycle-plugin repository.

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged. ok-to-test Indicates a non-member PR verified by an org member that is safe to test. verified Signifies that the PR passed pre-merge verification criteria

Projects

None yet

Development

Successfully merging this pull request may close these issues.