Skip to content

fix(sync-ci-images): fix oc wait, manifest-list digests, and IS import - #3199

Merged
ashwindasr merged 5 commits into
openshift-eng:mainfrom
ashwindasr:fix-oc-wait-and-semantics
Jul 27, 2026
Merged

fix(sync-ci-images): fix oc wait, manifest-list digests, and IS import#3199
ashwindasr merged 5 commits into
openshift-eng:mainfrom
ashwindasr:fix-oc-wait-and-semantics

Conversation

@ashwindasr

@ashwindasr ashwindasr commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes three bugs in the sync-ci-images pipeline that were causing stale ImageStream tags, failed oc image info lookups, and outdated golang versions in CI images.

1. Replace impossible oc wait AND condition with polling

The post-build wait in start-builds used two --for=jsonpath flags (phase=Complete and phase=Failed). In modern oc/kubectl, multiple --for flags are ANDed, making this condition impossible to satisfy. The wait always timed out after 1 hour, preventing the post-build IS tag update from ever running.

Fix: Replaced with a polling loop that checks the build phase every 15s until it reaches a terminal state (Complete, Failed, Cancelled, Error).

2. Use source digest for manifest-list mirrors

For images mirrored with --keep-manifest-list from a digest-pinned source, oc image info cannot resolve the manifest list tag on Quay. This caused get_image_digest to fail, returning None, which skipped the IS tag update entirely — leaving stale digests and outdated image content (e.g. golang v1.25.9 instead of v1.25.11).

Fix: When --keep-manifest-list is used with a digest-pinned source (@sha256:), extract the source digest directly instead of calling get_image_digest. Manifest list digests are content-addressable and preserved during mirroring.

3. Remove reference=True from ImageStream tag patches

The QCI migration (79f685261) added reference: true to all IS tag patches. This tells the image controller not to import the image into the integrated registry, causing oc image info registry.ci.openshift.org/... to fail with "manifest unknown". Before the migration, images were natively in the integrated registry via BuildConfig pushes.

Fix: Removed reference: true from all five IS tag patch sites. The image controller now imports manifests from quay-proxy into the integrated registry. referencePolicy: Source and importPolicy: PreserveOriginal are retained.

4. Remove redundant 20-minute sleep

The _wait_for_builds method had a 20-minute sleep that was redundant now that build completion is polled in start-builds.

Verified

$ oc image info registry.ci.openshift.org/openshift/release:rhel-9-release-golang-1.25-openshift-4.22 --filter-by-os amd64 | grep GO_VERSION
               GO_VERSION=v1.25.11

Test plan

  • Existing unit tests pass (doozer/tests/cli/test_images_streams.py)
  • oc image info against registry.ci resolves correctly after fix
  • Full sync-ci-images run for 4.22 completes without errors

The post-build wait in `start-builds` used two `--for=jsonpath` flags:
  --for=jsonpath='{.status.phase}'=Complete
  --for=jsonpath='{.status.phase}'=Failed

In modern oc/kubectl, multiple --for flags are ANDed, so this waited
for phase=Complete AND phase=Failed simultaneously — an impossible
condition that always timed out after 1 hour.

This meant the post-build imagestream tag update never ran, leaving
IS tags pointing to stale digests. When the old digest became
unreachable through quay-proxy, `prs open` failed with
"manifest unknown" because `oc image info` could not resolve the
stale reference.

Replace with a polling loop that checks the build phase every 15s
until it reaches a terminal state (Complete, Failed, Cancelled, Error).

Co-authored-by: Cursor <cursoragent@cursor.com>

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED
@openshift-ci

openshift-ci Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign thegreyd 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 24, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Image mirroring now reuses digest-pinned sources when applicable, imagestream patches omit reference, build preservation polls terminal phases, and reconciliation PR creation ignores missing images.

Changes

Image synchronization

Layer / File(s) Summary
Mirror digests and imagestream references
doozer/doozerlib/cli/images_streams.py
Digest-pinned manifest-list mirrors derive the QCI digest from the source; QCI-related imagestream patches retain import and reference policies without setting reference: True.
Build preservation polling
doozer/doozerlib/cli/images_streams.py
The preservation flow polls .status.phase every 15 seconds, stops on terminal phases, and exits when the one-hour timeout is exceeded.
Synchronization pipeline wiring
pyartcd/pyartcd/pipelines/sync_ci_images.py
_wait_for_builds becomes a no-op hook, and reconciliation PR arguments include --ignore-missing-images.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 10 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Ai-Attribution ⚠️ Warning AI use is explicit and every commit uses Co-authored-by: Cursor; no Assisted-by or Generated-by trailer is present. Replace AI Co-authored-by trailers with the required Assisted-by or Generated-by Red Hat trailer on the AI-assisted commits.
✅ Passed checks (10 passed)
Check name Status Explanation
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.
No-Weak-Crypto ✅ Passed The PR only changes image digest handling and build polling; no MD5/SHA1/DES/RC4, custom crypto, or non-constant-time secret comparisons were introduced.
Container-Privileges ✅ Passed PR only changes Python sources; no modified manifest contains privileged:true, hostPID/Network/IPC, SYS_ADMIN, runAsUser:0, or allowPrivilegeEscalation:true.
No-Sensitive-Data-In-Logs ✅ Passed The PR diff only removes reference: True and adds comments; it introduces no new logging of secrets, tokens, PII, or hostnames.
No-Hardcoded-Secrets ✅ Passed No hardcoded secrets, credential URLs, or long base64 strings were added in the PR diff; only build-flow and imagestream logic changed.
No-Injection-Vectors ✅ Passed PASS: The modified code adds no eval/exec/shell=True/yaml.load/pickle.loads/os.system or similar injection primitives; only digest/patch logic changed.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: build waiting, manifest-list digest handling, and imagestream import updates.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

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

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 `@doozer/doozerlib/cli/images_streams.py`:
- Around line 981-999: The polling loop in the build-status wait flow must
enforce overall_timeout for both sleeping and command execution. Update the
logic around exectools.cmd_gather and time.sleep so each subprocess wait and
sleep is bounded by the remaining overall timeout, and exit promptly when the
deadline is reached while preserving terminal-phase handling.
🪄 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: openshift-eng/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b2269e6c-3d48-434a-b363-88fa9fab9d3f

📥 Commits

Reviewing files that changed from the base of the PR and between 666e957 and db3af99.

📒 Files selected for processing (1)
  • doozer/doozerlib/cli/images_streams.py

Comment on lines +981 to +999
poll_interval = 15
phase = ''
while True:
elapsed_wait = time.time() - overall_start_time
if elapsed_wait >= overall_timeout:
runtime.logger.warning(f'Overall timeout exceeded while waiting for {build_name}')
break

check_phase_cmd = f'oc -n ci get build/{build_name} -o jsonpath={{.status.phase}}'
if as_user:
check_phase_cmd += f' --as {as_user}'

rc_phase, phase_stdout, _ = exectools.cmd_gather(check_phase_cmd)
if rc_phase == 0:
phase = phase_stdout.strip()
if phase in ('Complete', 'Failed', 'Cancelled', 'Error'):
break

time.sleep(poll_interval)

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.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C 6 '\bdef cmd_gather\b|\bcmd_gather\s*=' doozer
rg -n -C 3 '\bcmd_gather\(' doozer

Repository: openshift-eng/art-tools

Length of output: 33415


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '99,190p' doozer/rundoozer/rundoozer.py
printf '\n---\n'
sed -n '970,1005p' doozer/doozerlib/cli/images_streams.py

Repository: openshift-eng/art-tools

Length of output: 234


🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -a 'rundoozer' doozer
printf '\n---\n'
git ls-files 'doozer/rundoozer*'
printf '\n---\n'
rg -n -C 8 '\bdef cmd_gather\b|\bcmd_gather\s*=' doozer

Repository: openshift-eng/art-tools

Length of output: 1307


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '99,190p' doozer/rundoozer/rundoozer
printf '\n---\n'
sed -n '978,1002p' doozer/doozerlib/cli/images_streams.py

Repository: openshift-eng/art-tools

Length of output: 4507


Cap the polling loop and subprocess wait to the shared timeout. The 15-second sleep can overshoot overall_timeout, and cmd_gather() has no subprocess timeout here, so a hung oc get can still block past the deadline.

🤖 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 `@doozer/doozerlib/cli/images_streams.py` around lines 981 - 999, The polling
loop in the build-status wait flow must enforce overall_timeout for both
sleeping and command execution. Update the logic around exectools.cmd_gather and
time.sleep so each subprocess wait and sleep is bounded by the remaining overall
timeout, and exit promptly when the deadline is reached while preserving
terminal-phase handling.

ashwindasr and others added 3 commits July 24, 2026 10:56
Now that start-builds polls for build completion, builds are already
finished when _wait_for_builds runs. The 20-minute static sleep was
only needed because the old oc wait never detected completion.

Signed-off-by: Ashwin Das <ashwindasr@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED
`oc image info` cannot resolve manifest list (multi-arch) tags,
causing get_image_digest to always return None for images mirrored
with --keep-manifest-list. This meant the IS tag update was skipped
for every manifest-list image (golang, base-rhel9, centos streams),
leaving imagestreams pointing to stale digests indefinitely.

For digest-pinned sources mirrored with --keep-manifest-list, the
manifest list digest is content-addressable and preserved at the
destination. Extract the source digest directly from the mirror
command instead of calling the broken oc image info.

This fixes the stale golang IS (e.g. openshift/release:rhel-9-
release-golang-1.25-openshift-4.22 stuck on Go 1.25.9 instead of
1.25.11) that has been broken since mirror_manifest_list was enabled.

Signed-off-by: Ashwin Das <ashwindasr@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…manifests

The QCI migration (79f6852) added reference=True to all ImageStream
tag patches. This prevents the image controller from importing the
image into the integrated registry, causing `oc image info` against
registry.ci.openshift.org to fail with "manifest unknown".

Remove reference=True so the image controller imports manifests from
quay-proxy into the integrated registry.  referencePolicy=Source and
importPolicy=PreserveOriginal are retained.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ashwindasr
ashwindasr force-pushed the fix-oc-wait-and-semantics branch from fe921f0 to 5633bf6 Compare July 24, 2026 15:29
@ashwindasr

Copy link
Copy Markdown
Contributor Author

@ashwindasr ashwindasr changed the title fix(start-builds): replace impossible oc wait AND condition with polling fix(sync-ci-images): fix oc wait, manifest-list digests, and IS import Jul 24, 2026
Comment thread pyartcd/pyartcd/pipelines/sync_ci_images.py Outdated
start-builds already polls for build completion, so this method was a
dead no-op left over from that change.

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED
@fgallott

Copy link
Copy Markdown
Contributor

@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 27, 2026
@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jul 27, 2026
@openshift-ci

openshift-ci Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

@ashwindasr: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/art-pre-commit-check 98fea0a link false /test art-pre-commit-check
ci/prow/security 98fea0a link false /test security

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.

@ashwindasr

Copy link
Copy Markdown
Contributor Author

commit check failing on a false positive - proceeding to merge

@ashwindasr
ashwindasr merged commit e241826 into openshift-eng:main Jul 27, 2026
2 of 5 checks passed
@ashwindasr
ashwindasr deleted the fix-oc-wait-and-semantics branch July 27, 2026 13:43
pruan-rht pushed a commit to pruan-rht/art-tools that referenced this pull request Jul 27, 2026
openshift-eng#3199)

* fix(start-builds): replace impossible oc wait AND condition with polling

The post-build wait in `start-builds` used two `--for=jsonpath` flags:
  --for=jsonpath='{.status.phase}'=Complete
  --for=jsonpath='{.status.phase}'=Failed

In modern oc/kubectl, multiple --for flags are ANDed, so this waited
for phase=Complete AND phase=Failed simultaneously — an impossible
condition that always timed out after 1 hour.

This meant the post-build imagestream tag update never ran, leaving
IS tags pointing to stale digests. When the old digest became
unreachable through quay-proxy, `prs open` failed with
"manifest unknown" because `oc image info` could not resolve the
stale reference.

Replace with a polling loop that checks the build phase every 15s
until it reaches a terminal state (Complete, Failed, Cancelled, Error).

Co-authored-by: Cursor <cursoragent@cursor.com>

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED

* fix(sync-ci-images): remove redundant 20-minute sleep after builds

Now that start-builds polls for build completion, builds are already
finished when _wait_for_builds runs. The 20-minute static sleep was
only needed because the old oc wait never detected completion.

Signed-off-by: Ashwin Das <ashwindasr@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED

* fix(mirror): use source digest for manifest-list mirrors

`oc image info` cannot resolve manifest list (multi-arch) tags,
causing get_image_digest to always return None for images mirrored
with --keep-manifest-list. This meant the IS tag update was skipped
for every manifest-list image (golang, base-rhel9, centos streams),
leaving imagestreams pointing to stale digests indefinitely.

For digest-pinned sources mirrored with --keep-manifest-list, the
manifest list digest is content-addressable and preserved at the
destination. Extract the source digest directly from the mirror
command instead of calling the broken oc image info.

This fixes the stale golang IS (e.g. openshift/release:rhel-9-
release-golang-1.25-openshift-4.22 stuck on Go 1.25.9 instead of
1.25.11) that has been broken since mirror_manifest_list was enabled.

Signed-off-by: Ashwin Das <ashwindasr@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(imagestreams): remove reference=True so image controller imports manifests

The QCI migration (79f6852) added reference=True to all ImageStream
tag patches. This prevents the image controller from importing the
image into the integrated registry, causing `oc image info` against
registry.ci.openshift.org to fail with "manifest unknown".

Remove reference=True so the image controller imports manifests from
quay-proxy into the integrated registry.  referencePolicy=Source and
importPolicy=PreserveOriginal are retained.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(sync-ci-images): remove no-op _wait_for_builds hook

start-builds already polls for build completion, so this method was a
dead no-op left over from that change.

rh-pre-commit.version: 2.3.2
rh-pre-commit.check-secrets: ENABLED

---------

Signed-off-by: Ashwin Das <ashwindasr@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants