Skip to content

DPTP-3787: Resolve QCI digest post-mirror via oc image info to pin spec.from in quay ImageStreams - #5123

Merged
openshift-merge-bot[bot] merged 1 commit into
openshift:mainfrom
deepsm007:fix/quay-is-digest-from-status
Apr 23, 2026
Merged

openshift-merge-bot[bot] merged 1 commit into
openshift:mainfrom
deepsm007:fix/quay-is-digest-from-status

Conversation

@deepsm007

@deepsm007 deepsm007 commented Apr 22, 2026 •

Copy link
Copy Markdown
Contributor

/cc @openshift/test-platform

Summary by CodeRabbit

Release Notes

  • New Features

    • Image promotions to Quay now use digest-based resolution with automatic retry logic, ensuring images are anchored to specific content digests rather than mutable tags.
  • Bug Fixes

    • Improved handling of tag-only image references that contain underlying digest information.
  • Tests

    • Added test coverage for Quay image reference parsing and digest-resolution behavior in the promotion workflow.

@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: automatic mode

@openshift-ci
openshift-ci Bot requested a review from a team April 22, 2026 19:21
@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 22, 2026
@coderabbitai

coderabbitai Bot commented Apr 22, 2026 •

Copy link
Copy Markdown

Walkthrough

The changes refactor the quay promotion process to dynamically resolve image digests at runtime instead of using pre-computed tags. New helper functions parse ImageStream keys and construct shell commands for digest extraction and tag anchoring, while the promotion pod generation now handles concrete quay targets with digest-based tagging and retry logic.

Changes

Cohort / File(s) Summary
Promotion Logic Implementation
pkg/steps/release/promote.go
Added quayProxyTagFromISKey to parse ImageStream tag keys into quay-proxy image tags with validation. Added getResolveAndTagCommand to build shell snippets that extract image digests via oc image info and jq, then tag destinations with digest anchors. Modified getPromotionPod to collect and process concrete quay targets (resolveAndTagPairs) with digest resolution flow, keeping prior retry behavior for template-based tags and falling back when parsing fails. Updated findDockerImageReference to return digest-anchored pullspecs when DockerImageReference is tag-only but Image contains a digest.
Test Coverage
pkg/steps/release/promote_test.go
Extended TestGetImageMirror with a case validating digest-anchored quay-proxy sources when DockerImageReference is tag-only but an image digest is present. Added new test TestQuayProxyTagFromISKey covering parsing behavior for standard and hyphenated tag formats, template component keys, and invalid input validation.
Test Fixture Update
pkg/steps/release/testdata/zz_fixture_TestGetPromotionPod_promotion_quay_multiple_tags.yaml
Updated container command fixture to replace fixed-digest tagging with dynamic digest discovery via oc image info and jq, applying individual retry loops for each quay target with the resolved digest.

Sequence Diagram(s)

sequenceDiagram
    participant Pod as Promotion Pod
    participant Registry as Container Registry
    participant IS as ImageStream
    participant Shell as Shell Commands

    rect rgba(100, 150, 200, 0.5)
    Note over Pod,Shell: New Digest Resolution Flow
    Pod->>IS: Parse concrete *-quay targets from key
    IS-->>Pod: Extracted tag information
    Pod->>Shell: Generate oc image info command
    Shell->>Registry: Query mirrored image for digest
    Registry-->>Shell: Return image manifest digest
    Shell->>Pod: Extract digest via jq
    Pod->>Shell: Build oc tag command with `@digest`
    Shell->>IS: Apply digest-anchored tag
    IS-->>Shell: Tag updated
    end

    rect rgba(150, 100, 150, 0.5)
    Note over Pod,Shell: Prior Template-Based Flow (Fallback)
    Pod->>Shell: Use pre-computed ${component} tags
    Shell->>IS: Apply tag directly
    IS-->>Shell: Tag applied
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 11 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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 PR contains no Ginkgo-style tests; all tests use Go's standard testing package with static, descriptive test case names.
Test Structure And Quality ✅ Passed Tests use Go's standard testing framework with table-driven patterns, proper single responsibility, meaningful assertions, and consistency with existing test patterns.
Microshift Test Compatibility ✅ Passed This PR does not add any Ginkgo e2e tests. The changes are limited to standard Go unit tests in pkg/steps/release/promote_test.go using the testing package with *testing.T, modifications to utility functions in promote.go, and test fixture data. The PR adds extended test cases to TestGetImageMirror and a new unit test TestQuayProxyTagFromISKey, but these are traditional table-driven unit tests, not Ginkgo e2e tests. Since the MicroShift compatibility check specifically applies to Ginkgo e2e tests with patterns like It(), Describe(), Context(), and When(), and no such tests are present in this PR, the check is not applicable.
Single Node Openshift (Sno) Test Compatibility ✅ Passed This PR does not add any Ginkgo e2e tests; only standard Go unit tests for utility functions.
Topology-Aware Scheduling Compatibility ✅ Passed Pull request modifies CI tooling for container image mirroring operations without introducing topology-aware scheduling constraints, affinity rules, or replica logic.
Ote Binary Stdout Contract ✅ Passed PR modifies pkg/steps/release/promote.go with utility functions for ImageStream tag parsing and command building with no stdout writes in process-level code.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed PR contains only standard Go unit tests using testing package, not Ginkgo e2e tests. Check targets only Ginkgo e2e tests with IPv6 and disconnected network requirements.
Title check ✅ Passed The title directly matches the main change: resolving QCI digest post-mirror via oc image info and pinning spec.from in quay ImageStreams, which are the core modifications in pkg/steps/release/promote.go.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@deepsm007 deepsm007 changed the title Resolve QCI digest post-mirror via oc image info to pin spec.from in quay ImageStreams DPTP-3787: Resolve QCI digest post-mirror via oc image info to pin spec.from in quay ImageStreams Apr 22, 2026
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Apr 22, 2026
@openshift-ci-robot

openshift-ci-robot commented Apr 22, 2026 •

Copy link
Copy Markdown
Contributor

@deepsm007: This pull request references DPTP-3787 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

/cc @openshift/test-platform

Summary by CodeRabbit

Release Notes

  • New Features

  • Image promotions to Quay now use digest-based resolution with automatic retry logic, ensuring images are anchored to specific content digests rather than mutable tags.

  • Bug Fixes

  • Improved handling of tag-only image references that contain underlying digest information.

  • Tests

  • Added test coverage for Quay image reference parsing and digest-resolution behavior in the promotion workflow.

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.

@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 (3)
pkg/steps/release/testdata/zz_fixture_TestGetPromotionPod_promotion_quay_multiple_tags.yaml (1)

14-16: Consider validating the digest before using it.

The shell commands capture the digest without validation. If oc image info returns malformed JSON or the .digest field is missing, jq -r '.digest' outputs null, which would cause oc tag to fail with a confusing @null pullspec error.

The retry loop does catch failures, so this isn't critical, but a more robust command would provide clearer error diagnostics:

💡 Optional improvement
-_digest=$(oc image info --output=json --registry-config=/etc/push-secret/.dockerconfigjson quay-proxy.ci.openshift.org/openshift/ci:ocp_4.21_ovn-kubernetes | jq -r '.digest') && oc tag ...
+_digest=$(oc image info --output=json --registry-config=/etc/push-secret/.dockerconfigjson quay-proxy.ci.openshift.org/openshift/ci:ocp_4.21_ovn-kubernetes | jq -r '.digest') && [ -n "$_digest" ] && [ "$_digest" != "null" ] && oc tag ...

This would need to be applied in getResolveAndTagCommand in promote.go.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@pkg/steps/release/testdata/zz_fixture_TestGetPromotionPod_promotion_quay_multiple_tags.yaml`
around lines 14 - 16, The digest extracted in getResolveAndTagCommand is used
without validation, so when jq returns null (missing/malformed .digest) the
subsequent oc tag gets an invalid `@null` pullspec; update getResolveAndTagCommand
to validate the _digest value after running oc image info | jq -r '.digest'
(check for empty/null or non-hex format), and if invalid emit a clear error/log
message and force the loop to retry (or continue) instead of passing the bad
value to oc tag; ensure the validated-variable name (_digest) and the
retry/break logic in the existing loop are reused so failures still trigger
backoff.
pkg/steps/release/promote.go (2)

269-278: Add bounds check for defensive coding.

strings.LastIndex(quayProxyTag, ":") returns -1 if no colon is found, which would cause a panic on the slice operation quayProxyTag[:idx]. While the current callers always pass output from quayProxyTagFromISKey (which guarantees a colon), a defensive check would prevent future misuse.

💡 Optional improvement
 func getResolveAndTagCommand(registryConfig, quayProxyTag, isTag string, loglevel int) string {
-	repo := quayProxyTag[:strings.LastIndex(quayProxyTag, ":")]
+	idx := strings.LastIndex(quayProxyTag, ":")
+	if idx == -1 {
+		// Should not happen with valid quayProxyTag from quayProxyTagFromISKey
+		idx = len(quayProxyTag)
+	}
+	repo := quayProxyTag[:idx]
 	return fmt.Sprintf(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/steps/release/promote.go` around lines 269 - 278, Add a defensive bounds
check in getResolveAndTagCommand: compute idx := strings.LastIndex(quayProxyTag,
":") and if idx == -1 set repo = quayProxyTag (or otherwise handle the
missing-colon case) instead of slicing directly, otherwise set repo =
quayProxyTag[:idx]; then use repo in the fmt.Sprintf as before so the function
cannot panic when quayProxyTag lacks a colon.

457-463: Assumption: DockerImageReference always contains a tag.

The code uses strings.LastIndex(ref, ":") to find the tag separator. This works correctly for refs like registry:5000/repo:tag (finds the tag separator) but would produce incorrect results for refs without a tag (e.g., registry:5000/repo), where it would find the port separator instead.

In practice, DockerImageReference from ImageStream status should always include a tag or digest. Consider adding a brief comment documenting this assumption for future maintainers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/steps/release/promote.go` around lines 457 - 463, The logic in the block
that builds a digest ref from t.Items[0].DockerImageReference assumes
DockerImageReference always contains a tag (it uses strings.LastIndex(ref, ":")
which can hit a registry port colon); update the code by adding a brief comment
above this block noting the explicit assumption that ImageStream status
DockerImageReference always includes a tag or digest (and that the LastIndex
colon is intended to find the tag separator), or alternatively make the
detection robust by ensuring the colon found is after the last '/' before
replacing the tag; reference the variables/ref check in this snippet
(t.Items[0].DockerImageReference, t.Items[0].Image, strings.LastIndex) so
maintainers can locate and understand the assumption or apply the safer check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/steps/release/promote.go`:
- Around line 269-278: Add a defensive bounds check in getResolveAndTagCommand:
compute idx := strings.LastIndex(quayProxyTag, ":") and if idx == -1 set repo =
quayProxyTag (or otherwise handle the missing-colon case) instead of slicing
directly, otherwise set repo = quayProxyTag[:idx]; then use repo in the
fmt.Sprintf as before so the function cannot panic when quayProxyTag lacks a
colon.
- Around line 457-463: The logic in the block that builds a digest ref from
t.Items[0].DockerImageReference assumes DockerImageReference always contains a
tag (it uses strings.LastIndex(ref, ":") which can hit a registry port colon);
update the code by adding a brief comment above this block noting the explicit
assumption that ImageStream status DockerImageReference always includes a tag or
digest (and that the LastIndex colon is intended to find the tag separator), or
alternatively make the detection robust by ensuring the colon found is after the
last '/' before replacing the tag; reference the variables/ref check in this
snippet (t.Items[0].DockerImageReference, t.Items[0].Image, strings.LastIndex)
so maintainers can locate and understand the assumption or apply the safer
check.

In
`@pkg/steps/release/testdata/zz_fixture_TestGetPromotionPod_promotion_quay_multiple_tags.yaml`:
- Around line 14-16: The digest extracted in getResolveAndTagCommand is used
without validation, so when jq returns null (missing/malformed .digest) the
subsequent oc tag gets an invalid `@null` pullspec; update getResolveAndTagCommand
to validate the _digest value after running oc image info | jq -r '.digest'
(check for empty/null or non-hex format), and if invalid emit a clear error/log
message and force the loop to retry (or continue) instead of passing the bad
value to oc tag; ensure the validated-variable name (_digest) and the
retry/break logic in the existing loop are reused so failures still trigger
backoff.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b9f3072-d884-4cf1-b2f0-73fd5160984f

📥 Commits

Reviewing files that changed from the base of the PR and between 8d45148 and 36c8fd9.

📒 Files selected for processing (3)
  • pkg/steps/release/promote.go
  • pkg/steps/release/promote_test.go
  • pkg/steps/release/testdata/zz_fixture_TestGetPromotionPod_promotion_quay_multiple_tags.yaml

@deepsm007

Copy link
Copy Markdown
Contributor Author

verified using https://prow.ci.openshift.org/view/gs/test-platform-results/logs/branch-ci-openshift-console-main-images/2047040221760983040 as custom ci-operator image.
spec.tags[].from.name now contains the digest as required

@deepsm007

Copy link
Copy Markdown
Contributor Author

/test e2e

@jupierce

Copy link
Copy Markdown
Contributor

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

openshift-ci Bot commented Apr 22, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: deepsm007, jupierce

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-merge-bot

Copy link
Copy Markdown
Contributor

Tests from second stage were triggered manually. Pipeline can be controlled only manually, until HEAD changes. Use command to trigger second stage.

2 similar comments
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Tests from second stage were triggered manually. Pipeline can be controlled only manually, until HEAD changes. Use command to trigger second stage.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Tests from second stage were triggered manually. Pipeline can be controlled only manually, until HEAD changes. Use command to trigger second stage.

@redhat-chai-bot

Copy link
Copy Markdown
Contributor

/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 Apr 23, 2026
@redhat-chai-bot

Copy link
Copy Markdown
Contributor

/unhold

4 similar comments
@redhat-chai-bot

Copy link
Copy Markdown
Contributor

/unhold

@redhat-chai-bot

Copy link
Copy Markdown
Contributor

/unhold

@redhat-chai-bot

Copy link
Copy Markdown
Contributor

/unhold

@redhat-chai-bot

Copy link
Copy Markdown
Contributor

/unhold

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

/retest-required

Remaining retests: 0 against base HEAD 4a37ae5 and 2 for PR HEAD 36c8fd9 in total

@openshift-ci

openshift-ci Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

@deepsm007: The following test 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/breaking-changes 36c8fd9 link false /test breaking-changes

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 be5724b into openshift:main Apr 23, 2026
16 of 17 checks passed
@deepsm007
deepsm007 deleted the fix/quay-is-digest-from-status branch September 21, 2026 15:50
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-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants