Skip to content

Prevent sync phase errors from blocking subsequent phases#796

Open
hoxhaeris wants to merge 4 commits into
openshift:mainfrom
hoxhaeris:hoxhaeris/sync-phase-error-isolation
Open

Prevent sync phase errors from blocking subsequent phases#796
hoxhaeris wants to merge 4 commits into
openshift:mainfrom
hoxhaeris:hoxhaeris/sync-phase-error-isolation

Conversation

@hoxhaeris

@hoxhaeris hoxhaeris commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Handle an edge case introduced by Sync pending migration part 2 #792 where stale ReleasePayload CRDs (with all-Unknown conditions) cause syncPending to error, aborting the entire 4-stable sync loop and preventing syncReady/syncAccepted from ever running
  • 4.22.5 verification jobs were never launched because of this, tests stuck pending for 2+ days, requiring ART to manually force-accept (ART-21568)
  • Sync phase errors (syncPending, syncReady, syncAccepted) are now collected instead of fatal, all three phases always run
  • Per-tag errors in stable-mode syncPending are isolated via syncPendingStableTag so one broken tag cannot block others
  • Ensure accepted releases are mirrored to the alternate image repository (e.g. quay.io) even if they skip syncReady

Root Cause

PR #792 changed calculateSyncActions to use GetTagPhase() (which prefers CRD-derived phases) instead of reading annotations directly. Old tags like 4.7.0-fc.0 have ReleasePayload CRDs that were never backfilled, all conditions are Unknown, so GetReleasePhase() returns Pending even though the annotation says Accepted. This causes the tag to re-enter syncPending, hit a mirror hash mismatch error, and abort the sync loop.

Additionally, the mirror job (ensureReleaseMirrorJob) was only called during syncReady. If a release transitions to Accepted before syncReady processes it (due to the release-payload-controller updating the CRD first), the mirror job is never created and the image never appears on quay.io.

Summary by CodeRabbit

  • Bug Fixes
    • Improved release synchronization to continue processing independent tags when one encounters an error.
    • Prevented stale payload status from incorrectly blocking synchronization of other tags.
    • Improved handling of pending releases, including image imports, rewrites, and transitions to ready or failed states.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The release controller now aggregates non-conflict synchronization errors across stages and pending tags, extracts stable pending-tag processing into a helper, and adds regression coverage for stale payload phases.

Changes

Release synchronization

Layer / File(s) Summary
Aggregate synchronization errors
cmd/release-controller/sync.go
Controller.sync and stable pending processing collect non-conflict failures while retaining immediate conflict handling and returning aggregated errors.
Process stable pending tags
cmd/release-controller/sync.go, cmd/release-controller/sync_test.go
Stable pending tags are processed individually through image readiness, metadata, mirrors, payloads, jobs, and phase transitions; regression coverage verifies stale payload phases do not misclassify unrelated ready tags.

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

Sequence Diagram(s)

sequenceDiagram
  participant ControllerSync
  participant SyncPending
  participant SyncPendingStableTag
  participant ReleasePayload
  ControllerSync->>SyncPending: process pending releases
  SyncPending->>SyncPendingStableTag: process each stable pending tag
  SyncPendingStableTag->>ReleasePayload: read payload phase
  ReleasePayload-->>SyncPendingStableTag: return phase
  SyncPendingStableTag-->>SyncPending: return per-tag result
  SyncPending-->>ControllerSync: return aggregated errors
Loading

Suggested reviewers: alexnpavel, bradmwilliams

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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: sync-phase errors no longer stop later phases from running.
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.
✨ 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.

A regression in 1dcc49e ("Sync pending migration part 2", PR openshift#792)
causes stale ReleasePayload CRDs (with all-Unknown conditions) to be
misclassified as Pending by GetTagPhase, which now prefers CRD-derived
phases over annotations. Old tags like 4.7.0-fc.0 re-enter syncPending,
hit a mirror hash mismatch error, and abort the entire sync loop for the
4-stable stream. This prevents syncReady from ever running, so releases
like 4.22.5 never get their verification jobs launched — tests stay
pending indefinitely.

This change makes sync phase errors non-fatal:
- syncPending, syncReady, and syncAccepted errors are collected instead
  of causing an immediate return. All three phases always run.
- Per-tag errors in the stable-mode syncPending loop are isolated via a
  new syncPendingStableTag helper, so one broken tag cannot block others.
- An aggregate error is returned at the end so the item is still requeued.
- Conflict errors remain an immediate return nil (resource was modified,
  resync is imminent).

Adds a regression test reproducing the exact scenario: a stale CRD
returning Pending for an Accepted tag must not cause an unrelated Ready
tag to be misclassified.
@hoxhaeris hoxhaeris force-pushed the hoxhaeris/sync-phase-error-isolation branch from 6ac3284 to 309227f Compare July 13, 2026 12:40
@openshift-ci

openshift-ci Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: hoxhaeris

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

The pull request process is described 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

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 13, 2026
@hoxhaeris

Copy link
Copy Markdown
Contributor Author

/hold

@openshift-ci openshift-ci Bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 13, 2026
@hoxhaeris

Copy link
Copy Markdown
Contributor Author

@CodeRabbit help

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

CodeRabbit commands

These commands are invoked using PR/Issue comments.

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai rate limit to show your current review rate limit status.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai evaluate custom pre-merge check --instructions <custom-checks-instructions> --name <custom-checks-title> [--mode <error|warning>] to validate the custom pre-merge checks instructions. Defaults to error when --mode is omitted.
  • @coderabbitai ignore pre-merge checks to override pre-merge checks and get an approval on PR.
  • @coderabbitai run pre-merge checks to run pre-merge checks on the pull request.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai emit path instructions to emit generated path-specific review instructions for this repository.
  • @coderabbitai resolve merge conflict to automatically resolve merge conflicts.
  • @coderabbitai autofix to automatically fix issues identified in unresolved review comments.
  • @coderabbitai resolve to resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai generate configuration to create a PR that adds the current resolved configuration as .coderabbit.yaml (or show it if already present).
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, support, documentation and community

  • Visit our status page to check the current availability of CodeRabbit.
  • Create a ticket on our support page for assistance with any issues or questions.
  • Visit our documentation site for detailed information on how to use CodeRabbit.
  • Join our Discord community to connect with other users and get help from the community.
  • Follow us on X/Twitter for updates and announcements.

@hoxhaeris

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 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 `@cmd/release-controller/sync.go`:
- Around line 292-303: The stable-tag branch in Controller.syncPending must
preserve conflict errors from syncPendingStableTag instead of hiding them inside
utilerrors.NewAggregate. Detect a conflict while processing pendingTags and
return it directly, while retaining aggregation for non-conflict errors and the
existing per-tag logging.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 94b16720-1200-43df-90f0-d43b4615a25b

📥 Commits

Reviewing files that changed from the base of the PR and between b3be4f5 and 309227f.

📒 Files selected for processing (2)
  • cmd/release-controller/sync.go
  • cmd/release-controller/sync_test.go

Comment thread cmd/release-controller/sync.go
Conflict errors should bypass aggregation and return immediately so
that sync() sees them as conflicts (fast retry) rather than generic
errors (warning event + workqueue backoff).
The mirror job (ensureReleaseMirrorJob) that copies releases to the
alternate image repository (e.g. quay.io) was only called during
syncReady. If a release transitions directly from Ready to Accepted
before syncReady processes it — which happens when the
release-payload-controller updates the CRD to Accepted before the
release-controller runs syncReady — the mirror job is never created
and the release never appears on quay.io.

This caused all 4.23 multi nightlies after July 8 to be missing from
quay.io/openshift-release-dev/dev-release, even though they were
accepted by the release controller.

Call ensureReleaseMirrorJob in syncAccepted as well. The function is
idempotent (ensureJob checks for existing jobs), so releases that were
already mirrored during syncReady are not affected.
@hoxhaeris

Copy link
Copy Markdown
Contributor Author

Added a third commit: call ensureReleaseMirrorJob in syncAccepted.

The mirror job that copies releases to the alternate image repository (e.g. quay.io) was only called during syncReady. If a release transitions to Accepted before syncReady processes it, the mirror job is never created and the image never appears on quay.io. This is a race between the release-payload-controller (which updates the CRD) and the release-controller (which runs syncReady).

Calling ensureReleaseMirrorJob in syncAccepted as well closes this gap. The function is idempotent, so releases already mirrored during syncReady are not affected. Once deployed, this will also retroactively create mirror jobs for accepted releases that were missed, as long as they have not been garbage-collected from the internal registry.

@hoxhaeris

Copy link
Copy Markdown
Contributor Author

/unhold

@openshift-ci openshift-ci Bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 14, 2026
@hoxhaeris

Copy link
Copy Markdown
Contributor Author

/hold

@openshift-ci openshift-ci Bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 14, 2026

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/release-controller/sync.go (1)

407-407: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Prevent potential nil pointer dereference on tag.Generation.

The Generation field in imagev1.TagReference is a pointer (*int64). If a tag is newly submitted or partially formed, tag.Generation could be nil until the API server populates it. Dereferencing it directly can cause a panic and crash the controller.

🔒️ Proposed fix to add a nil check
-	hash := fmt.Sprintf("%s-%d", tag.Name, *tag.Generation)
+	var gen int64
+	if tag.Generation != nil {
+		gen = *tag.Generation
+	}
+	hash := fmt.Sprintf("%s-%d", tag.Name, gen)
🤖 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 `@cmd/release-controller/sync.go` at line 407, Update the hash construction
near the tag processing logic to handle a nil tag.Generation before
dereferencing it. Preserve the existing tag.Name-generation hash format for
populated generations, and define the appropriate safe behavior for newly
submitted or partially formed tags without allowing the controller to panic.
🧹 Nitpick comments (1)
cmd/release-controller/sync.go (1)

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

Update the event reason to accurately reflect the failure.

syncAccepted handles the publish steps for accepted tags. Logging UnableToVerifyRelease appears to be a copy-paste error from syncReady. Consider updating the event reason to better describe the publishing failure.

♻️ Proposed refactor
-		c.eventRecorder.Eventf(release.Source, corev1.EventTypeWarning, "UnableToVerifyRelease", "%v", err)
+		c.eventRecorder.Eventf(release.Source, corev1.EventTypeWarning, "UnableToPublishRelease", "%v", err)
🤖 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 `@cmd/release-controller/sync.go` around lines 127 - 129, Update the Eventf
call in syncAccepted to use an event reason describing the publish failure
instead of the copied UnableToVerifyRelease reason, while preserving the
existing warning type, message formatting, and syncErrors handling.
🤖 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.

Outside diff comments:
In `@cmd/release-controller/sync.go`:
- Line 407: Update the hash construction near the tag processing logic to handle
a nil tag.Generation before dereferencing it. Preserve the existing
tag.Name-generation hash format for populated generations, and define the
appropriate safe behavior for newly submitted or partially formed tags without
allowing the controller to panic.

---

Nitpick comments:
In `@cmd/release-controller/sync.go`:
- Around line 127-129: Update the Eventf call in syncAccepted to use an event
reason describing the publish failure instead of the copied
UnableToVerifyRelease reason, while preserving the existing warning type,
message formatting, and syncErrors handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 59f9d37b-f6c8-46df-b7d9-cd63549c9e49

📥 Commits

Reviewing files that changed from the base of the PR and between a18a508 and 260c141.

📒 Files selected for processing (1)
  • cmd/release-controller/sync.go

@openshift-ci

openshift-ci Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

@hoxhaeris: 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

approved Indicates a PR has been approved by an approver from all required OWNERS files. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant