Skip to content

OCPBUGS-66263: Include index image sub-digests in dry run mapping.txt#1355

Open
dorzel wants to merge 3 commits intoopenshift:mainfrom
dorzel:OCPBUGS-66263
Open

OCPBUGS-66263: Include index image sub-digests in dry run mapping.txt#1355
dorzel wants to merge 3 commits intoopenshift:mainfrom
dorzel:OCPBUGS-66263

Conversation

@dorzel
Copy link
Copy Markdown
Member

@dorzel dorzel commented Feb 18, 2026

Description

This PR adds functionality to include index image sub-digests in mapping.txt, as part of the linked bug. This facilitates users who manually mirror based on mapping.txt or use automated security scanning which requires all digests to be listed.

Github / Jira issue: https://issues.redhat.com/browse/OCPBUGS-66263

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Code Improvements (Refactoring, Performance, CI upgrades, etc)
  • Internal repo assets (diagrams / docs on github repo)
  • This change requires a documentation update on openshift docs

How Has This Been Tested?

  • Ran unit test suite
  • Manually ran an m2d dry run and inspected the mapping.txt
kind: ImageSetConfiguration
apiVersion: mirror.openshift.io/v2alpha1
mirror:
  additionalImages:
    - name: registry.access.redhat.com/ubi9/ubi-minimal:9.4
    - name: docker.io/library/alpine:3.20
    - name: quay.io/podman/hello:latest
    - name: oci:///tmp/oci-test-image
    - name: registry.access.redhat.com/ubi9/ubi-minimal@sha256:c0e70387664f30cd9cf2795b547e4a9a51002c44a4a86aa9335ab030134bf392

Corresponding mapping.txt:

docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0=docker://localhost:55000/ubi9/ubi-minimal@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848=docker://localhost:55000/ubi9/ubi-minimal@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b=docker://localhost:55000/ubi9/ubi-minimal@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e=docker://localhost:55000/ubi9/ubi-minimal@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e
docker://docker.io/library/alpine:3.20=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:b0cb30c51c47cdfde647364301758b14c335dea2fddc9490d4f007d67ecb2538=docker://localhost:55000/library/alpine@sha256:b0cb30c51c47cdfde647364301758b14c335dea2fddc9490d4f007d67ecb2538
docker://docker.io/library/alpine:3.20@sha256:66f45812e9b5053abd384c1c6037261e526b1a0a4ac783ecaf3d5e04a94a0412=docker://localhost:55000/library/alpine@sha256:66f45812e9b5053abd384c1c6037261e526b1a0a4ac783ecaf3d5e04a94a0412
docker://docker.io/library/alpine:3.20@sha256:a4e2d43613b6ee71c96f9d26a2e515729fafeb75e4f0dfea524445b3344dccfe=docker://localhost:55000/library/alpine@sha256:a4e2d43613b6ee71c96f9d26a2e515729fafeb75e4f0dfea524445b3344dccfe
docker://docker.io/library/alpine:3.20@sha256:4135f5ea9c06f0857b68f7a6beaa40480d1f11d64e6341d11ed061cce5f66214=docker://localhost:55000/library/alpine@sha256:4135f5ea9c06f0857b68f7a6beaa40480d1f11d64e6341d11ed061cce5f66214
docker://docker.io/library/alpine:3.20@sha256:1e161cda0d91d4d1c28464a134f1c0fef668a7c6e486017314e06a64b875c288=docker://localhost:55000/library/alpine@sha256:1e161cda0d91d4d1c28464a134f1c0fef668a7c6e486017314e06a64b875c288
docker://docker.io/library/alpine:3.20@sha256:a1acb89d2fc6e0d5c1f3b3be4fd4bda3bd29ca8d5df1461b03aabefefa9d4321=docker://localhost:55000/library/alpine@sha256:a1acb89d2fc6e0d5c1f3b3be4fd4bda3bd29ca8d5df1461b03aabefefa9d4321
docker://docker.io/library/alpine:3.20@sha256:9ace55212059e43b0b246bb975a26b4f6fb41f7384bc87b57ad3aae2a6e5e817=docker://localhost:55000/library/alpine@sha256:9ace55212059e43b0b246bb975a26b4f6fb41f7384bc87b57ad3aae2a6e5e817
docker://docker.io/library/alpine:3.20@sha256:13688eb4bf30a8a94d920e2c6489aceadb6caa26325426b558c35af4322f797a=docker://localhost:55000/library/alpine@sha256:13688eb4bf30a8a94d920e2c6489aceadb6caa26325426b558c35af4322f797a
docker://docker.io/library/alpine:3.20@sha256:c4140ad89752b59d3d1567f6c2c5922d4b1a3eb7ab3ac41472772fcc4a77a447=docker://localhost:55000/library/alpine@sha256:c4140ad89752b59d3d1567f6c2c5922d4b1a3eb7ab3ac41472772fcc4a77a447
docker://docker.io/library/alpine:3.20@sha256:0ee208147b93c91fdda2ded4a5daf71c91d2a491e94b56e29c5edc11d16b5f11=docker://localhost:55000/library/alpine@sha256:0ee208147b93c91fdda2ded4a5daf71c91d2a491e94b56e29c5edc11d16b5f11
docker://docker.io/library/alpine:3.20@sha256:98d989819241cb7170e0660f1be3f4e1a6a9e70ba0c4f5c4c7bded2b531e46b6=docker://localhost:55000/library/alpine@sha256:98d989819241cb7170e0660f1be3f4e1a6a9e70ba0c4f5c4c7bded2b531e46b6
docker://docker.io/library/alpine:3.20@sha256:1b8c7e5752cc75d8be572ac609fc4e1e415387acac0e70b80922b8bfc82894f9=docker://localhost:55000/library/alpine@sha256:1b8c7e5752cc75d8be572ac609fc4e1e415387acac0e70b80922b8bfc82894f9
docker://docker.io/library/alpine:3.20@sha256:3e5e9c7e2b11707d02a8dde9f71dacb294c3bce820d013f817c57c52ee141570=docker://localhost:55000/library/alpine@sha256:3e5e9c7e2b11707d02a8dde9f71dacb294c3bce820d013f817c57c52ee141570
docker://docker.io/library/alpine:3.20@sha256:a5588164e8323980b589e3f4a8ca819e50816ec140fcf8f66904ed539047b30b=docker://localhost:55000/library/alpine@sha256:a5588164e8323980b589e3f4a8ca819e50816ec140fcf8f66904ed539047b30b
docker://docker.io/library/alpine:3.20@sha256:7c694b533384ef3d60b9743a8e3d50a8d1586d5253d2a60d227aa5526966a54b=docker://localhost:55000/library/alpine@sha256:7c694b533384ef3d60b9743a8e3d50a8d1586d5253d2a60d227aa5526966a54b
docker://docker.io/library/alpine:3.20@sha256:dfb98350c924a6ea7572103667da653b98c597bf22aadec19f27866c987457fa=docker://localhost:55000/library/alpine@sha256:dfb98350c924a6ea7572103667da653b98c597bf22aadec19f27866c987457fa
docker://quay.io/podman/hello:latest=docker://localhost:55000/podman/hello:latest
docker://quay.io/podman/hello:latest@sha256:43de9874507eaa8ffd88eac885b672b1dfc57cc583d9ad920850f97f19809f8f=docker://localhost:55000/podman/hello@sha256:43de9874507eaa8ffd88eac885b672b1dfc57cc583d9ad920850f97f19809f8f
docker://quay.io/podman/hello:latest@sha256:5c44ef36dc5e35a76904da0e028cf9413e0176a653525162368af13fed03571c=docker://localhost:55000/podman/hello@sha256:5c44ef36dc5e35a76904da0e028cf9413e0176a653525162368af13fed03571c
docker://quay.io/podman/hello:latest@sha256:92be1897bfcf5a8c9e59ce538645d7c159253717fa8edbb964ff5a259a5535ab=docker://localhost:55000/podman/hello@sha256:92be1897bfcf5a8c9e59ce538645d7c159253717fa8edbb964ff5a259a5535ab
docker://quay.io/podman/hello:latest@sha256:6fba1b311cad0201c377469c66c9cdfc4c48ea99a186869c7ce2609e0a013965=docker://localhost:55000/podman/hello@sha256:6fba1b311cad0201c377469c66c9cdfc4c48ea99a186869c7ce2609e0a013965
oci:///tmp/oci-test-image=docker://localhost:55000/tmp/oci-test-image:latest
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:c0e70387664f30cd9cf2795b547e4a9a51002c44a4a86aa9335ab030134bf392=docker://localhost:55000/ubi9/ubi-minimal:sha256-c0e70387664f30cd9cf2795b547e4a9a51002c44a4a86aa9335ab030134bf392
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0=docker://localhost:55000/ubi9/ubi-minimal@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848=docker://localhost:55000/ubi9/ubi-minimal@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b=docker://localhost:55000/ubi9/ubi-minimal@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e=docker://localhost:55000/ubi9/ubi-minimal@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e

Expected Outcome

All sub-digests of any index image in the mapping.txt are listed directly below it.

Summary by CodeRabbit

  • New Features

    • Dry-run now detects multi-architecture manifest lists, merges pre-collected and discovered variant digests, and emits per-variant source→digest-pinned destination mappings; missing-image reports include corresponding variant entries.
  • Tests

    • Added tests building an OCI layout with a manifest list, verifying per-variant mappings, digest-pinned destinations, and that unreachable sources warn but do not fail.

@openshift-ci-robot openshift-ci-robot added jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Feb 18, 2026
@openshift-ci-robot
Copy link
Copy Markdown

@dorzel: This pull request references Jira Issue OCPBUGS-66263, which is invalid:

  • expected the bug to target the "4.22.0" version, but no target version was set

Comment /jira refresh to re-evaluate validity if changes to the Jira bug are made, or edit the title of this pull request to link to a different bug.

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

Details

In response to this:

Description

This PR adds functionality to include index image sub-digests in mapping.txt, as part of the linked bug. This facilitates users who manually mirror based on mapping.txt or use automated security scanning which requires all digests to be listed.

Github / Jira issue: https://issues.redhat.com/browse/OCPBUGS-66263

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Code Improvements (Refactoring, Performance, CI upgrades, etc)
  • Internal repo assets (diagrams / docs on github repo)
  • This change requires a documentation update on openshift docs

How Has This Been Tested?

  • Ran unit test suite
  • Manually ran an m2d dry run and inspected the mapping.txt
kind: ImageSetConfiguration
apiVersion: mirror.openshift.io/v2alpha1
mirror:
 additionalImages:
   - name: registry.access.redhat.com/ubi9/ubi-minimal:9.4
   - name: docker.io/library/alpine:3.20
   - name: quay.io/podman/hello:latest
   - name: oci:///tmp/oci-test-image
   - name: registry.access.redhat.com/ubi9/ubi-minimal@sha256:b87097994ed62fbf1de5b4c35b4c28e5b654a1cc5e87f0b8a187e5c1f8cba0a0

Corresponding mapping.txt:

docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://docker.io/library/alpine:3.20=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:b0cb30c51c47cdfde647364301758b14c335dea2fddc9490d4f007d67ecb2538=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:66f45812e9b5053abd384c1c6037261e526b1a0a4ac783ecaf3d5e04a94a0412=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:a4e2d43613b6ee71c96f9d26a2e515729fafeb75e4f0dfea524445b3344dccfe=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:4135f5ea9c06f0857b68f7a6beaa40480d1f11d64e6341d11ed061cce5f66214=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:1e161cda0d91d4d1c28464a134f1c0fef668a7c6e486017314e06a64b875c288=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:a1acb89d2fc6e0d5c1f3b3be4fd4bda3bd29ca8d5df1461b03aabefefa9d4321=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:9ace55212059e43b0b246bb975a26b4f6fb41f7384bc87b57ad3aae2a6e5e817=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:13688eb4bf30a8a94d920e2c6489aceadb6caa26325426b558c35af4322f797a=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:c4140ad89752b59d3d1567f6c2c5922d4b1a3eb7ab3ac41472772fcc4a77a447=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:0ee208147b93c91fdda2ded4a5daf71c91d2a491e94b56e29c5edc11d16b5f11=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:98d989819241cb7170e0660f1be3f4e1a6a9e70ba0c4f5c4c7bded2b531e46b6=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:1b8c7e5752cc75d8be572ac609fc4e1e415387acac0e70b80922b8bfc82894f9=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:3e5e9c7e2b11707d02a8dde9f71dacb294c3bce820d013f817c57c52ee141570=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:a5588164e8323980b589e3f4a8ca819e50816ec140fcf8f66904ed539047b30b=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:7c694b533384ef3d60b9743a8e3d50a8d1586d5253d2a60d227aa5526966a54b=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:dfb98350c924a6ea7572103667da653b98c597bf22aadec19f27866c987457fa=docker://localhost:55000/library/alpine:3.20
docker://quay.io/podman/hello:latest=docker://localhost:55000/podman/hello:latest
docker://quay.io/podman/hello:latest@sha256:43de9874507eaa8ffd88eac885b672b1dfc57cc583d9ad920850f97f19809f8f=docker://localhost:55000/podman/hello:latest
docker://quay.io/podman/hello:latest@sha256:5c44ef36dc5e35a76904da0e028cf9413e0176a653525162368af13fed03571c=docker://localhost:55000/podman/hello:latest
docker://quay.io/podman/hello:latest@sha256:92be1897bfcf5a8c9e59ce538645d7c159253717fa8edbb964ff5a259a5535ab=docker://localhost:55000/podman/hello:latest
docker://quay.io/podman/hello:latest@sha256:6fba1b311cad0201c377469c66c9cdfc4c48ea99a186869c7ce2609e0a013965=docker://localhost:55000/podman/hello:latest
oci:///tmp/oci-test-image=docker://localhost:55000/tmp/oci-test-image:latest
oci:///tmp/oci-test-image@sha256:b5632fc886395e5595e9aed61f2e8473079fb81666f229ec5c3772d2ee64c389=docker://localhost:55000/tmp/oci-test-image:latest
oci:///tmp/oci-test-image@sha256:95db54aed932be984073d01b9d10b3e7b7673184328bcac26ea1f287d4d75da9=docker://localhost:55000/tmp/oci-test-image:latest
oci:///tmp/oci-test-image@sha256:223682a763ecb26ad463846e6112caf5b34630cca10cc67e8d044da28c760707=docker://localhost:55000/tmp/oci-test-image:latest
oci:///tmp/oci-test-image@sha256:2b54b7513fa93f65b168ce91c3658939b0169c87b675d902f7ccd1a8726db1f8=docker://localhost:55000/tmp/oci-test-image:latest
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:b87097994ed62fbf1de5b4c35b4c28e5b654a1cc5e87f0b8a187e5c1f8cba0a0=docker://localhost:55000/ubi9/ubi-minimal:sha256-b87097994ed62fbf1de5b4c35b4c28e5b654a1cc5e87f0b8a187e5c1f8cba0a0

Expected Outcome

All sub-digests of any index image in the mapping.txt are listed directly below it.

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 openshift-ci Bot requested review from adolfo-ab and aguidirh February 18, 2026 20:07
@dorzel
Copy link
Copy Markdown
Member Author

dorzel commented Feb 18, 2026

/jira refresh

@openshift-ci-robot openshift-ci-robot added jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. and removed jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Feb 18, 2026
@openshift-ci-robot
Copy link
Copy Markdown

@dorzel: This pull request references Jira Issue OCPBUGS-66263, 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 (4.22.0) matches configured target version for branch (4.22.0)
  • bug is in the state ASSIGNED, which is one of the valid states (NEW, ASSIGNED, POST)

Requesting review from QA contact:
/cc @nidangavali

Details

In response to this:

/jira refresh

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 openshift-ci Bot requested a review from nidangavali February 18, 2026 20:10
@dorzel dorzel force-pushed the OCPBUGS-66263 branch 2 times, most recently from ed935b0 to 054890d Compare February 18, 2026 20:25
@dorzel
Copy link
Copy Markdown
Member Author

dorzel commented Feb 18, 2026

/retest

Copy link
Copy Markdown
Contributor

@adolfo-ab adolfo-ab left a comment

Choose a reason for hiding this comment

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

Just a small suggestion related to the test code, otherwise lgtm.

Comment thread internal/pkg/cli/dryrun_test.go Outdated
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented Feb 19, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: adolfo-ab, dorzel
Once this PR has been reviewed and has the lgtm label, please assign aguidirh 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
Copy link
Copy Markdown

coderabbitai Bot commented Feb 23, 2026

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

Dry-run now discovers OCI manifest lists at runtime and via pre-collected data, expands them into per-instance digest mappings (source@sha256 → destination), appends sub-digest entries to missing-image lists, and exposes helpers to produce digest-pinned destinations. Tests exercise OCI layout creation and unreachable-image behavior.

Changes

Cohort / File(s) Summary
Dry-run core logic
internal/pkg/cli/dryrun.go
DryRun signature extended to accept preCollectedManifestLists; added runtime manifest-list discovery (inspectManifestLists, getManifestListDigests), merge of pre-collected and discovered sub-digests, emission of per-instance subSource=subDest mapping lines, missing-image sub-digest handling, and subDigestDestination helper.
Dry-run tests
internal/pkg/cli/dryrun_test.go
Added createTestOCILayout helper and tests: TestDryRunWithManifestList, TestDryRunUnreachableImagesWarnButDontFail, TestSubDigestDestination. Updated existing DryRun calls to pass nil pre-collected maps; added imports for OCI/digest helpers.
Schema: manifest-list storage
internal/pkg/api/v2alpha1/type_internal.go
Added ManifestListDigests map[string][]string to CopyImageSchemaMap to store manifest-list sub-digests keyed by origin reference.
Executor call sites
internal/pkg/cli/executor.go
Updated DryRun invocations in RunMirrorToDisk, RunMirrorToMirror, and RunDiskToMirror to pass collectorSchema.CopyImageSchemaMap.ManifestListDigests as the new argument.
Collector: pre-collecting manifest lists
internal/pkg/operator/filtered_collector.go
Extended copyImageSchemaMap initialization to include ManifestListDigests; modified getCatalogDigest to fetch manifest bytes (via ImageManifest) even in some digest-only cases during dry-run, compute digest from manifest bytes, and when encountering manifest lists record instance digests into copyImageSchemaMap.ManifestListDigests.
Collector tests
internal/pkg/operator/filtered_collector_test.go
Adjusted MockManifest.ImageManifest test stub signature (instanceDigest) and returned a deterministic OCI image manifest payload instead of an error to support new manifest-bytes flow.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as DryRun Executor
    participant Parser as alltransports.ParseImageName
    participant ImageSrc as containers/image Source (Opts.SrcImage)
    participant Manifest as Manifest Parser (imgmanifest)
    participant Mapper as mapping writer / missing-image tracker

    CLI->>Parser: Parse image reference (source or docker:// fallback)
    Parser-->>CLI: parsed reference
    CLI->>ImageSrc: Open image reference (system context)
    ImageSrc->>Manifest: Fetch manifest bytes & media type
    Manifest-->>CLI: manifest bytes, media type
    alt manifest is multi-image (manifest list)
        CLI->>Manifest: Parse index -> instance digests
        Manifest-->>CLI: [digest1, digest2, ...]
        loop per instance digest
            CLI->>Mapper: write "source@digest = subDest" (subDigestDestination)
            Mapper-->>CLI: mapping recorded
            CLI->>Mapper: add sub-digest to missing-image entries if needed
        end
    else single-image
        CLI->>Mapper: write base "source = destination"
        Mapper-->>CLI: mapping recorded
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 12
✅ Passed checks (12 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding index image sub-digests to the dry run mapping.txt output, with a direct reference to the bug ticket.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
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 custom check for stable and deterministic Ginkgo test names is not applicable to this pull request. The codebase does not use the Ginkgo BDD testing framework—it uses standard Go testing with testing.T and t.Run(). All test names in the modified files are static and descriptive with no dynamic information (generated IDs, timestamps, environment-specific values) that would change between test runs. The new tests (TestDryRunWithManifestList, TestDryRunUnreachableImagesWarnButDontFail, TestSubDigestDestination) and their subtests all use clear, constant naming.
Test Structure And Quality ✅ Passed The custom check assesses Ginkgo test code quality, but this PR uses standard Go testing patterns with testing.T and testify/assert, not Ginkgo.
Microshift Test Compatibility ✅ Passed New tests are standard Go unit tests using *testing.T pattern, not Ginkgo e2e tests. MicroShift compatibility check does not apply.
Single Node Openshift (Sno) Test Compatibility ✅ Passed PR adds only unit tests using Go's standard testing package to internal/pkg/cli/dryrun_test.go, not Ginkgo e2e tests which the custom check targets.
Topology-Aware Scheduling Compatibility ✅ Passed PR introduces CLI-level business logic for manifest list detection during dry runs with no Kubernetes scheduling constraints, deployment manifests, or topology-dependent configurations.
Ote Binary Stdout Contract ✅ Passed The PR changes do not violate the OTE Binary Stdout Contract. All modified files are internal packages without process-level code, and no stdout writes were detected.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The PR adds only standard Go unit tests using the testing package, not Ginkgo e2e tests, making the custom check not applicable.

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

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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

@openshift-ci-robot
Copy link
Copy Markdown

@dorzel: This pull request references Jira Issue OCPBUGS-66263, which is valid.

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

Requesting review from QA contact:
/cc @nidangavali

Details

In response to this:

Description

This PR adds functionality to include index image sub-digests in mapping.txt, as part of the linked bug. This facilitates users who manually mirror based on mapping.txt or use automated security scanning which requires all digests to be listed.

Github / Jira issue: https://issues.redhat.com/browse/OCPBUGS-66263

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Code Improvements (Refactoring, Performance, CI upgrades, etc)
  • Internal repo assets (diagrams / docs on github repo)
  • This change requires a documentation update on openshift docs

How Has This Been Tested?

  • Ran unit test suite
  • Manually ran an m2d dry run and inspected the mapping.txt
kind: ImageSetConfiguration
apiVersion: mirror.openshift.io/v2alpha1
mirror:
 additionalImages:
   - name: registry.access.redhat.com/ubi9/ubi-minimal:9.4
   - name: docker.io/library/alpine:3.20
   - name: quay.io/podman/hello:latest
   - name: oci:///tmp/oci-test-image
   - name: registry.access.redhat.com/ubi9/ubi-minimal@sha256:b87097994ed62fbf1de5b4c35b4c28e5b654a1cc5e87f0b8a187e5c1f8cba0a0

Corresponding mapping.txt:

docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://docker.io/library/alpine:3.20=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:b0cb30c51c47cdfde647364301758b14c335dea2fddc9490d4f007d67ecb2538=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:66f45812e9b5053abd384c1c6037261e526b1a0a4ac783ecaf3d5e04a94a0412=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:a4e2d43613b6ee71c96f9d26a2e515729fafeb75e4f0dfea524445b3344dccfe=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:4135f5ea9c06f0857b68f7a6beaa40480d1f11d64e6341d11ed061cce5f66214=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:1e161cda0d91d4d1c28464a134f1c0fef668a7c6e486017314e06a64b875c288=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:a1acb89d2fc6e0d5c1f3b3be4fd4bda3bd29ca8d5df1461b03aabefefa9d4321=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:9ace55212059e43b0b246bb975a26b4f6fb41f7384bc87b57ad3aae2a6e5e817=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:13688eb4bf30a8a94d920e2c6489aceadb6caa26325426b558c35af4322f797a=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:c4140ad89752b59d3d1567f6c2c5922d4b1a3eb7ab3ac41472772fcc4a77a447=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:0ee208147b93c91fdda2ded4a5daf71c91d2a491e94b56e29c5edc11d16b5f11=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:98d989819241cb7170e0660f1be3f4e1a6a9e70ba0c4f5c4c7bded2b531e46b6=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:1b8c7e5752cc75d8be572ac609fc4e1e415387acac0e70b80922b8bfc82894f9=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:3e5e9c7e2b11707d02a8dde9f71dacb294c3bce820d013f817c57c52ee141570=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:a5588164e8323980b589e3f4a8ca819e50816ec140fcf8f66904ed539047b30b=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:7c694b533384ef3d60b9743a8e3d50a8d1586d5253d2a60d227aa5526966a54b=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:dfb98350c924a6ea7572103667da653b98c597bf22aadec19f27866c987457fa=docker://localhost:55000/library/alpine:3.20
docker://quay.io/podman/hello:latest=docker://localhost:55000/podman/hello:latest
docker://quay.io/podman/hello:latest@sha256:43de9874507eaa8ffd88eac885b672b1dfc57cc583d9ad920850f97f19809f8f=docker://localhost:55000/podman/hello:latest
docker://quay.io/podman/hello:latest@sha256:5c44ef36dc5e35a76904da0e028cf9413e0176a653525162368af13fed03571c=docker://localhost:55000/podman/hello:latest
docker://quay.io/podman/hello:latest@sha256:92be1897bfcf5a8c9e59ce538645d7c159253717fa8edbb964ff5a259a5535ab=docker://localhost:55000/podman/hello:latest
docker://quay.io/podman/hello:latest@sha256:6fba1b311cad0201c377469c66c9cdfc4c48ea99a186869c7ce2609e0a013965=docker://localhost:55000/podman/hello:latest
oci:///tmp/oci-test-image=docker://localhost:55000/tmp/oci-test-image:latest
oci:///tmp/oci-test-image@sha256:b5632fc886395e5595e9aed61f2e8473079fb81666f229ec5c3772d2ee64c389=docker://localhost:55000/tmp/oci-test-image:latest
oci:///tmp/oci-test-image@sha256:95db54aed932be984073d01b9d10b3e7b7673184328bcac26ea1f287d4d75da9=docker://localhost:55000/tmp/oci-test-image:latest
oci:///tmp/oci-test-image@sha256:223682a763ecb26ad463846e6112caf5b34630cca10cc67e8d044da28c760707=docker://localhost:55000/tmp/oci-test-image:latest
oci:///tmp/oci-test-image@sha256:2b54b7513fa93f65b168ce91c3658939b0169c87b675d902f7ccd1a8726db1f8=docker://localhost:55000/tmp/oci-test-image:latest
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:b87097994ed62fbf1de5b4c35b4c28e5b654a1cc5e87f0b8a187e5c1f8cba0a0=docker://localhost:55000/ubi9/ubi-minimal:sha256-b87097994ed62fbf1de5b4c35b4c28e5b654a1cc5e87f0b8a187e5c1f8cba0a0

Expected Outcome

All sub-digests of any index image in the mapping.txt are listed directly below it.

Summary by CodeRabbit

  • New Features

  • Dry-run output now detects multi-architecture container images and includes individual sub-digest mappings in addition to the primary image mapping.

  • Tests

  • Added test coverage for manifest list detection and validation of sub-digest mappings during dry-run execution.

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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)
internal/pkg/cli/dryrun_test.go (1)

189-190: ⚠️ Potential issue | 🟡 Minor

Pre-existing: assert.FileExists on line 190 checks mappingPath instead of missingImgsPath.

This appears to be a copy-paste bug in the existing test — it asserts existence of mappingPath (already checked on line 177) instead of the newly constructed missingImgsPath. Not introduced by this PR, but worth fixing while you're here.

Proposed fix
-		assert.FileExists(t, mappingPath)
+		assert.FileExists(t, missingImgsPath)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/pkg/cli/dryrun_test.go` around lines 189 - 190, The test currently
constructs missingImgsPath but then mistakenly calls assert.FileExists(t,
mappingPath) again; update the assertion to check the correct variable by
replacing the second assert.FileExists invocation to assert.FileExists(t,
missingImgsPath) so the test verifies the presence of the missing images file
(variable missingImgsPath) rather than re-checking mappingPath.
🧹 Nitpick comments (2)
internal/pkg/cli/dryrun_test.go (1)

295-296: Nit: os.RemoveAll is redundant for t.TempDir().

t.TempDir() already registers automatic cleanup when the test ends. The explicit defer os.RemoveAll(testFolder) is unnecessary. The same pattern also appears on line 48 and line 120 in the pre-existing tests.

Proposed fix
 		testFolder := t.TempDir()
-		defer os.RemoveAll(testFolder)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/pkg/cli/dryrun_test.go` around lines 295 - 296, The test creates
testFolder via t.TempDir() and then redundantly calls defer
os.RemoveAll(testFolder); remove the unnecessary explicit cleanup calls (the
defer os.RemoveAll(...) lines) so the tests rely on t.TempDir()'s automatic
cleanup—search for occurrences around testFolder/t.TempDir() and delete the
os.RemoveAll usages.
internal/pkg/cli/dryrun.go (1)

44-57: Performance: getManifestListDigests is called for every image, adding network I/O to the dry-run path.

For docker:// sources this opens a connection and fetches the manifest from a remote registry. In large image sets (hundreds/thousands of images), this could significantly degrade dry-run speed. Consider:

  1. Parallelizing the manifest lookups (e.g., bounded goroutine pool), or
  2. Adding a flag/option to opt into sub-digest expansion, or
  3. Caching the system context and reusing connections where possible.

Not a blocker since errors are gracefully handled and logged at debug level, but worth considering for large-scale usage.

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

In `@internal/pkg/cli/dryrun.go` around lines 44 - 57, The dry-run loop currently
calls getManifestListDigests for every image (in internal/pkg/cli/dryrun.go),
causing remote registry network I/O per image; change this by adding an opt-in
flag (e.g., --expand-subdigests) checked in the dry-run path and only call
getManifestListDigests when enabled, and/or replace the sequential calls with a
bounded goroutine worker pool that performs getManifestListDigests concurrently
for images (use a shared cached system context/reused HTTP client across workers
to avoid repeated connection setup), then collect results and produce the same
subSource -> destination writes to buff as before; ensure getManifestListDigests
is called from the worker pool and errors remain debug-logged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@internal/pkg/cli/dryrun_test.go`:
- Around line 189-190: The test currently constructs missingImgsPath but then
mistakenly calls assert.FileExists(t, mappingPath) again; update the assertion
to check the correct variable by replacing the second assert.FileExists
invocation to assert.FileExists(t, missingImgsPath) so the test verifies the
presence of the missing images file (variable missingImgsPath) rather than
re-checking mappingPath.

---

Nitpick comments:
In `@internal/pkg/cli/dryrun_test.go`:
- Around line 295-296: The test creates testFolder via t.TempDir() and then
redundantly calls defer os.RemoveAll(testFolder); remove the unnecessary
explicit cleanup calls (the defer os.RemoveAll(...) lines) so the tests rely on
t.TempDir()'s automatic cleanup—search for occurrences around
testFolder/t.TempDir() and delete the os.RemoveAll usages.

In `@internal/pkg/cli/dryrun.go`:
- Around line 44-57: The dry-run loop currently calls getManifestListDigests for
every image (in internal/pkg/cli/dryrun.go), causing remote registry network I/O
per image; change this by adding an opt-in flag (e.g., --expand-subdigests)
checked in the dry-run path and only call getManifestListDigests when enabled,
and/or replace the sequential calls with a bounded goroutine worker pool that
performs getManifestListDigests concurrently for images (use a shared cached
system context/reused HTTP client across workers to avoid repeated connection
setup), then collect results and produce the same subSource -> destination
writes to buff as before; ensure getManifestListDigests is called from the
worker pool and errors remain debug-logged.

ℹ️ Review info

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

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between b27c4b9 and 06bc475.

📒 Files selected for processing (2)
  • internal/pkg/cli/dryrun.go
  • internal/pkg/cli/dryrun_test.go

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
internal/pkg/cli/dryrun.go (1)

59-68: ⚠️ Potential issue | 🟡 Minor

Sub-digest entries are not checked against the local cache

Sub-digest lines are now written to mapping.txt but are never checked via o.Mirror.Check and are never added to missingImgsBuff. In a partial-cache scenario (e.g. the manifest list index is present but one or more architecture blobs are absent), these missing entries will go unreported, and the warning counter on line 87 will undercount.

Consider extending the cache-check loop to cover the expanded sub-digest entries, or at a minimum add a comment explaining that sub-digest cache validation is intentionally deferred to the full mirror run.

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

In `@internal/pkg/cli/dryrun.go` around lines 59 - 68, The current loop only
checks img.Destination against the local cache (o.Mirror.Check) and therefore
ignores any expanded sub-digest entries written into mapping.txt; update the
logic in the dry-run path so that after expanding an image into sub-digest
destinations you iterate each sub-digest destination and call
o.Mirror.Check(ctx, subDest, o.Opts, false), and for any err or !exists write
the mapping (use img.Source or the appropriate sub-source mapping) into
missingImgsBuff and increment nbMissingImgs; if intentional deferral is required
instead, add a clear comment near o.Opts.IsMirrorToDisk() explaining that
sub-digest cache validation is deferred to the full mirror run and why.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/pkg/cli/dryrun.go`:
- Around line 45-57: Currently o.getManifestListDigests is called for every
img.Source during dry-run which triggers a full remote manifest fetch per image;
update the logic to avoid unnecessary network calls by only invoking
o.getManifestListDigests when the source is not already digest-pinned (i.e.,
img.Source does not contain a digest "@...") or when a new boolean flag on the
CopyImageSchema (e.g., IsManifestList) is unset — alternatively compute and set
IsManifestList during the collection phase where manifests are already fetched
and consult that flag here; also change the logger call from o.Log.Debug(...) to
o.Log.Warn(...) when getManifestListDigests returns an error so users see
missing sub-digests without debug logging.
- Around line 52-56: The current loop writing sub-digest mappings uses
sourceBase (from img.Source) but leaves img.Destination with its tag, causing
each arch-specific line to map to the same dest tag and overwrite; update the
loop that iterates manifestDigests (the block building subSource and calling
buff.WriteString) to compute a destBase that has any tag removed before
appending the sub-digest. Implement tag-stripping for img.Destination by
removing the last ":tag" occurrence (i.e., split at the last ':' after the final
'/') or, better, reuse the same image-reference parsing used for img.Source to
produce a destination reference without tag, then append "@"+digest so lines
become destBase+"@"+digest; use that destBase when writing buff.WriteString
instead of the original img.Destination.
- Around line 98-106: getManifestListDigests currently calls
alltransports.ParseImageName and fails for Cincinnati-style image names that
lack a transport prefix; update getManifestListDigests to detect this case and
retry parsing by prepending "docker://" when alltransports.ParseImageName
returns a transport-not-found/parse error (or when source does not contain
"://"), so that images like
"quay.io/openshift-release-dev/ocp-release:4.13.10-x86_64" are parsed and
manifest-list expansion proceeds; keep the original error handling for other
parse failures and ensure the retry only happens when the input appears to lack
a transport prefix.

---

Outside diff comments:
In `@internal/pkg/cli/dryrun.go`:
- Around line 59-68: The current loop only checks img.Destination against the
local cache (o.Mirror.Check) and therefore ignores any expanded sub-digest
entries written into mapping.txt; update the logic in the dry-run path so that
after expanding an image into sub-digest destinations you iterate each
sub-digest destination and call o.Mirror.Check(ctx, subDest, o.Opts, false), and
for any err or !exists write the mapping (use img.Source or the appropriate
sub-source mapping) into missingImgsBuff and increment nbMissingImgs; if
intentional deferral is required instead, add a clear comment near
o.Opts.IsMirrorToDisk() explaining that sub-digest cache validation is deferred
to the full mirror run and why.

ℹ️ Review info

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

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 06bc475 and 1a213a7.

📒 Files selected for processing (2)
  • internal/pkg/cli/dryrun.go
  • internal/pkg/cli/dryrun_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/pkg/cli/dryrun_test.go

Comment thread internal/pkg/cli/dryrun.go Outdated
Comment thread internal/pkg/cli/dryrun.go Outdated
Comment thread internal/pkg/cli/dryrun.go
Copy link
Copy Markdown
Contributor

@aguidirh aguidirh left a comment

Choose a reason for hiding this comment

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

Thanks @dorzel, I've added some comments, I'm wondering if we should include the child digests of the manifest list only when the customer asks for it (maybe a flag where they can select the archs or all) ?

I would like to discuss this option with the team (@r4f4 and @adolfo-ab) to avoid breaking changes. I know some customers currently are depending on the current mappings.txt to copy the images with skopeo.

Comment thread internal/pkg/cli/dryrun.go Outdated
Comment thread internal/pkg/cli/dryrun.go Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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)
internal/pkg/cli/dryrun.go (1)

73-100: ⚠️ Potential issue | 🟡 Minor

Use a consistent total when reporting missing images.

nbMissingImgs is incremented for sub-digest entries (Line 77), but the denominator remains len(allImages) (Line 99). This can produce misleading ratios (e.g., 8/3).

Proposed fix
-	nbMissingImgs := 0
+	nbMissingImgs := 0
+	totalMappings := 0
@@
 	for _, img := range allImages {
 		buff.WriteString(img.Source + "=" + img.Destination + "\n")
+		totalMappings++
@@
 			for _, digest := range manifestDigests {
 				subSource := sourceBase + "@" + digest
 				subDest := subDigestDestination(img.Destination, digest)
 				buff.WriteString(subSource + "=" + subDest + "\n")
+				totalMappings++
 				subDigestEntries = append(subDigestEntries, subDigestEntry{source: subSource, dest: subDest})
 			}
 		}
@@
-		o.Log.Warn(emoji.Warning+"  %d/%d images necessary for mirroring are not available in the cache.", nbMissingImgs, len(allImages))
+		o.Log.Warn(emoji.Warning+"  %d/%d images necessary for mirroring are not available in the cache.", nbMissingImgs, totalMappings)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/pkg/cli/dryrun.go` around lines 73 - 100, The warning uses
nbMissingImgs for the numerator but len(allImages) for the denominator which is
inconsistent because nbMissingImgs is incremented for sub-digest entries; fix by
computing a consistent total (e.g., totalExpectedImgs) before writing warnings:
initialize totalExpectedImgs := len(allImages), when iterating subDigestEntries
add len(subDigestEntries) (or increment totalExpectedImgs in the same loop where
nbMissingImgs is incremented), then use totalExpectedImgs as the denominator in
o.Log.Warn instead of len(allImages); reference nbMissingImgs, subDigestEntries,
allImages and the two o.Log.Warn calls to update the message.
♻️ Duplicate comments (1)
internal/pkg/cli/dryrun.go (1)

51-55: ⚠️ Potential issue | 🟠 Major

Avoid unconditional manifest lookups in the dry-run image loop.

ExecutorSchema.DryRun still calls ExecutorSchema.getManifestListDigests for every image entry. That introduces one remote manifest fetch per image and can significantly slow dry-runs or hit registry limits at scale.

#!/bin/bash
# Verify unconditional invocation of getManifestListDigests inside the per-image loop.
cat -n internal/pkg/cli/dryrun.go | sed -n '44,70p'

# Confirm call site(s) and surrounding guards.
rg -nP --type=go -C3 'getManifestListDigests\(ctx,\s*img\.Source\)' internal/pkg/cli/dryrun.go
🧹 Nitpick comments (1)
internal/pkg/cli/dryrun_test.go (1)

314-321: Fail fast on registry setup errors in this test path.

Using t.Errorf during test bootstrap lets execution continue after setup failure, which can obscure the real failure cause.

Proposed fix
 		regCfg, err := setupRegForTest(testFolder)
 		if err != nil {
-			t.Errorf("storage cache error: %v ", err)
+			t.Fatalf("storage cache error: %v", err)
 		}
 		reg, err := registry.NewRegistry(context.Background(), regCfg)
 		if err != nil {
-			t.Errorf("storage cache error: %v ", err)
+			t.Fatalf("storage cache error: %v", err)
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/pkg/cli/dryrun_test.go` around lines 314 - 321, The test currently
uses t.Errorf when setupRegForTest(testFolder) or registry.NewRegistry(...)
fails, which allows the test to continue; change both t.Errorf calls to t.Fatalf
(or call t.FailNow) so the test aborts immediately on setup failure, and include
context in the message referencing setupRegForTest/regCfg and
registry.NewRegistry/reg to make the failure point clear.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@internal/pkg/cli/dryrun.go`:
- Around line 73-100: The warning uses nbMissingImgs for the numerator but
len(allImages) for the denominator which is inconsistent because nbMissingImgs
is incremented for sub-digest entries; fix by computing a consistent total
(e.g., totalExpectedImgs) before writing warnings: initialize totalExpectedImgs
:= len(allImages), when iterating subDigestEntries add len(subDigestEntries) (or
increment totalExpectedImgs in the same loop where nbMissingImgs is
incremented), then use totalExpectedImgs as the denominator in o.Log.Warn
instead of len(allImages); reference nbMissingImgs, subDigestEntries, allImages
and the two o.Log.Warn calls to update the message.

---

Nitpick comments:
In `@internal/pkg/cli/dryrun_test.go`:
- Around line 314-321: The test currently uses t.Errorf when
setupRegForTest(testFolder) or registry.NewRegistry(...) fails, which allows the
test to continue; change both t.Errorf calls to t.Fatalf (or call t.FailNow) so
the test aborts immediately on setup failure, and include context in the message
referencing setupRegForTest/regCfg and registry.NewRegistry/reg to make the
failure point clear.

ℹ️ Review info

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

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 1a213a7 and a63ea77.

📒 Files selected for processing (2)
  • internal/pkg/cli/dryrun.go
  • internal/pkg/cli/dryrun_test.go

@openshift-ci-robot
Copy link
Copy Markdown

@dorzel: An error was encountered querying GitHub for users with public email (ngavali@redhat.com) for bug OCPBUGS-66263 on the Jira server at https://issues.redhat.com/. No known errors were detected, please see the full error message for details.

Full error message. Post "http://ghproxy/graphql": dial tcp 172.30.229.2:80: connect: connection refused

Please contact an administrator to resolve this issue, then request a bug refresh with /jira refresh.

Details

In response to this:

Description

This PR adds functionality to include index image sub-digests in mapping.txt, as part of the linked bug. This facilitates users who manually mirror based on mapping.txt or use automated security scanning which requires all digests to be listed.

Github / Jira issue: https://issues.redhat.com/browse/OCPBUGS-66263

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Code Improvements (Refactoring, Performance, CI upgrades, etc)
  • Internal repo assets (diagrams / docs on github repo)
  • This change requires a documentation update on openshift docs

How Has This Been Tested?

  • Ran unit test suite
  • Manually ran an m2d dry run and inspected the mapping.txt
kind: ImageSetConfiguration
apiVersion: mirror.openshift.io/v2alpha1
mirror:
 additionalImages:
   - name: registry.access.redhat.com/ubi9/ubi-minimal:9.4
   - name: docker.io/library/alpine:3.20
   - name: quay.io/podman/hello:latest
   - name: oci:///tmp/oci-test-image
   - name: registry.access.redhat.com/ubi9/ubi-minimal@sha256:c0e70387664f30cd9cf2795b547e4a9a51002c44a4a86aa9335ab030134bf392

Corresponding mapping.txt:

docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0=docker://localhost:55000/ubi9/ubi-minimal@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848=docker://localhost:55000/ubi9/ubi-minimal@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b=docker://localhost:55000/ubi9/ubi-minimal@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e=docker://localhost:55000/ubi9/ubi-minimal@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e
docker://docker.io/library/alpine:3.20=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:b0cb30c51c47cdfde647364301758b14c335dea2fddc9490d4f007d67ecb2538=docker://localhost:55000/library/alpine@sha256:b0cb30c51c47cdfde647364301758b14c335dea2fddc9490d4f007d67ecb2538
docker://docker.io/library/alpine:3.20@sha256:66f45812e9b5053abd384c1c6037261e526b1a0a4ac783ecaf3d5e04a94a0412=docker://localhost:55000/library/alpine@sha256:66f45812e9b5053abd384c1c6037261e526b1a0a4ac783ecaf3d5e04a94a0412
docker://docker.io/library/alpine:3.20@sha256:a4e2d43613b6ee71c96f9d26a2e515729fafeb75e4f0dfea524445b3344dccfe=docker://localhost:55000/library/alpine@sha256:a4e2d43613b6ee71c96f9d26a2e515729fafeb75e4f0dfea524445b3344dccfe
docker://docker.io/library/alpine:3.20@sha256:4135f5ea9c06f0857b68f7a6beaa40480d1f11d64e6341d11ed061cce5f66214=docker://localhost:55000/library/alpine@sha256:4135f5ea9c06f0857b68f7a6beaa40480d1f11d64e6341d11ed061cce5f66214
docker://docker.io/library/alpine:3.20@sha256:1e161cda0d91d4d1c28464a134f1c0fef668a7c6e486017314e06a64b875c288=docker://localhost:55000/library/alpine@sha256:1e161cda0d91d4d1c28464a134f1c0fef668a7c6e486017314e06a64b875c288
docker://docker.io/library/alpine:3.20@sha256:a1acb89d2fc6e0d5c1f3b3be4fd4bda3bd29ca8d5df1461b03aabefefa9d4321=docker://localhost:55000/library/alpine@sha256:a1acb89d2fc6e0d5c1f3b3be4fd4bda3bd29ca8d5df1461b03aabefefa9d4321
docker://docker.io/library/alpine:3.20@sha256:9ace55212059e43b0b246bb975a26b4f6fb41f7384bc87b57ad3aae2a6e5e817=docker://localhost:55000/library/alpine@sha256:9ace55212059e43b0b246bb975a26b4f6fb41f7384bc87b57ad3aae2a6e5e817
docker://docker.io/library/alpine:3.20@sha256:13688eb4bf30a8a94d920e2c6489aceadb6caa26325426b558c35af4322f797a=docker://localhost:55000/library/alpine@sha256:13688eb4bf30a8a94d920e2c6489aceadb6caa26325426b558c35af4322f797a
docker://docker.io/library/alpine:3.20@sha256:c4140ad89752b59d3d1567f6c2c5922d4b1a3eb7ab3ac41472772fcc4a77a447=docker://localhost:55000/library/alpine@sha256:c4140ad89752b59d3d1567f6c2c5922d4b1a3eb7ab3ac41472772fcc4a77a447
docker://docker.io/library/alpine:3.20@sha256:0ee208147b93c91fdda2ded4a5daf71c91d2a491e94b56e29c5edc11d16b5f11=docker://localhost:55000/library/alpine@sha256:0ee208147b93c91fdda2ded4a5daf71c91d2a491e94b56e29c5edc11d16b5f11
docker://docker.io/library/alpine:3.20@sha256:98d989819241cb7170e0660f1be3f4e1a6a9e70ba0c4f5c4c7bded2b531e46b6=docker://localhost:55000/library/alpine@sha256:98d989819241cb7170e0660f1be3f4e1a6a9e70ba0c4f5c4c7bded2b531e46b6
docker://docker.io/library/alpine:3.20@sha256:1b8c7e5752cc75d8be572ac609fc4e1e415387acac0e70b80922b8bfc82894f9=docker://localhost:55000/library/alpine@sha256:1b8c7e5752cc75d8be572ac609fc4e1e415387acac0e70b80922b8bfc82894f9
docker://docker.io/library/alpine:3.20@sha256:3e5e9c7e2b11707d02a8dde9f71dacb294c3bce820d013f817c57c52ee141570=docker://localhost:55000/library/alpine@sha256:3e5e9c7e2b11707d02a8dde9f71dacb294c3bce820d013f817c57c52ee141570
docker://docker.io/library/alpine:3.20@sha256:a5588164e8323980b589e3f4a8ca819e50816ec140fcf8f66904ed539047b30b=docker://localhost:55000/library/alpine@sha256:a5588164e8323980b589e3f4a8ca819e50816ec140fcf8f66904ed539047b30b
docker://docker.io/library/alpine:3.20@sha256:7c694b533384ef3d60b9743a8e3d50a8d1586d5253d2a60d227aa5526966a54b=docker://localhost:55000/library/alpine@sha256:7c694b533384ef3d60b9743a8e3d50a8d1586d5253d2a60d227aa5526966a54b
docker://docker.io/library/alpine:3.20@sha256:dfb98350c924a6ea7572103667da653b98c597bf22aadec19f27866c987457fa=docker://localhost:55000/library/alpine@sha256:dfb98350c924a6ea7572103667da653b98c597bf22aadec19f27866c987457fa
docker://quay.io/podman/hello:latest=docker://localhost:55000/podman/hello:latest
docker://quay.io/podman/hello:latest@sha256:43de9874507eaa8ffd88eac885b672b1dfc57cc583d9ad920850f97f19809f8f=docker://localhost:55000/podman/hello@sha256:43de9874507eaa8ffd88eac885b672b1dfc57cc583d9ad920850f97f19809f8f
docker://quay.io/podman/hello:latest@sha256:5c44ef36dc5e35a76904da0e028cf9413e0176a653525162368af13fed03571c=docker://localhost:55000/podman/hello@sha256:5c44ef36dc5e35a76904da0e028cf9413e0176a653525162368af13fed03571c
docker://quay.io/podman/hello:latest@sha256:92be1897bfcf5a8c9e59ce538645d7c159253717fa8edbb964ff5a259a5535ab=docker://localhost:55000/podman/hello@sha256:92be1897bfcf5a8c9e59ce538645d7c159253717fa8edbb964ff5a259a5535ab
docker://quay.io/podman/hello:latest@sha256:6fba1b311cad0201c377469c66c9cdfc4c48ea99a186869c7ce2609e0a013965=docker://localhost:55000/podman/hello@sha256:6fba1b311cad0201c377469c66c9cdfc4c48ea99a186869c7ce2609e0a013965
oci:///tmp/oci-test-image=docker://localhost:55000/tmp/oci-test-image:latest
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:c0e70387664f30cd9cf2795b547e4a9a51002c44a4a86aa9335ab030134bf392=docker://localhost:55000/ubi9/ubi-minimal:sha256-c0e70387664f30cd9cf2795b547e4a9a51002c44a4a86aa9335ab030134bf392
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0=docker://localhost:55000/ubi9/ubi-minimal@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848=docker://localhost:55000/ubi9/ubi-minimal@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b=docker://localhost:55000/ubi9/ubi-minimal@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e=docker://localhost:55000/ubi9/ubi-minimal@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e

Expected Outcome

All sub-digests of any index image in the mapping.txt are listed directly below it.

Summary by CodeRabbit

  • New Features

  • Dry-run now detects multi-architecture (manifest list) images and emits per-variant sub-digest source→destination mappings; docker destinations are also generated as digest-pinned targets and included in missing-image reporting.

  • Tests

  • Added tests that construct an OCI layout with a manifest list and verify dry-run includes expected per-sub-digest mappings and destination generation.

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.

@MayXuQQ
Copy link
Copy Markdown

MayXuQQ commented Mar 6, 2026

@openshift-ci-robot openshift-ci-robot added the verified Signifies that the PR passed pre-merge verification criteria label Mar 6, 2026
@openshift-ci-robot
Copy link
Copy Markdown

@MayXuQQ: This PR has been marked as verified by @MayXuQQ.

Details

In response to this:

/verified by @MayXuQQ
test case OCP-88200 - [OCPBUGS-66263] Include index image sub-digests in dry run mapping.txt [v2]

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.

Copy link
Copy Markdown
Contributor

@aguidirh aguidirh left a comment

Choose a reason for hiding this comment

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

With the following ImageSetConfig:

kind: ImageSetConfiguration
apiVersion: mirror.openshift.io/v1alpha2
mirror:
  platform:
    channels:
    - name: stable-4.17
      minVersion: 4.17.0
      maxVersion: 4.17.0
    - name: stable-4.16
      minVersion: 4.16.0
      maxVersion: 4.16.0
    graph: true

...ommited operators/additionalImages/Helm

I got the following error on the graph image:

2026/03/06 17:21:27 [WARN] : unable to get manifest list info for docker://localhost:55000/openshift/graph-image:latest: error creating image source for docker://localhost:55000/openshift/graph-image:latest: pinging container registry localhost:55000: Get "https://localhost:55000/v2/": http: server gave HTTP response to HTTPS client

aguidi@fedora-redhat:~/go/src/github.com/aguidirh/oc-mirror$ ./bin/oc-mirror -c ./alex-tests/alex-isc/isc2.yaml file://alex-tests/ocpbugs66263 --v2 --dry-run
2026/03/06 16:58:45  [INFO]   : 👋 Hello, welcome to oc-mirror
2026/03/06 16:58:45  [INFO]   : ⚙️  setting up the environment for you...
2026/03/06 16:58:45  [INFO]   : ⚙️  environment version: v0.2.0-alpha.1-541-ga51c524
2026/03/06 16:58:45  [INFO]   : 🔀 workflow mode: mirrorToDisk 
2026/03/06 16:58:46  [INFO]   : 🕵  going to discover the necessary images...
2026/03/06 16:58:46  [INFO]   : 🔍 collecting release images...
 ✓   (22s) Collecting release quay.io/openshift-release-dev/ocp-release:4.17.0-x86_64 
 ✓   (36s) Collecting release quay.io/openshift-release-dev/ocp-release:4.16.0-x86_64 
 ✓   (28s) Collecting release quay.io/openshift-release-dev/ocp-release:4.16.55-x86_64 
2026/03/06 17:02:35  [INFO]   : 🔍 collecting operator images...
 ✓   (1m54s) Collecting catalog registry.redhat.io/redhat/redhat-operator-index:v4.20 
 ✓   (2m57s) Collecting catalog registry.redhat.io/redhat/redhat-operator-index:v4.18 
 ✓   (41s) Collecting catalog oci:///home/aguidi/go/src/github.com/aguidirh/oc-mirror/alex-tests/redhat-operator-index-oci 
 ✓   (1s) Collecting catalog oci:///home/aguidi/go/src/github.com/aguidirh/oc-mirror/alex-tests/ibm-catalog 
2026/03/06 17:08:10  [INFO]   : 🔍 collecting additional images...
2026/03/06 17:08:10  [WARN]   : [AdditionalImagesCollector] registry.redhat.io/ubi8/ubi:latest@sha256:44d75007b39e0e1bbf1bcfd0721245add54c54c3f83903f8926fb4bef6827aa2 has both tag and digest : using digest to pull, but tag only for mirroring
2026/03/06 17:08:10  [INFO]   : 🔍 collecting helm images...
2026/03/06 17:21:27  [WARN]   : unable to get manifest list info for docker://localhost:55000/openshift/graph-image:latest: error creating image source for docker://localhost:55000/openshift/graph-image:latest: pinging container registry localhost:55000: Get "https://localhost:55000/v2/": http: server gave HTTP response to HTTPS client
2026/03/06 17:22:40  [WARN]   : ⚠️  627/628 images necessary for mirroring are not available in the cache.
2026/03/06 17:22:40  [WARN]   : List of missing images in : alex-tests/ocpbugs66263/working-dir/dry-run/missing.txt.
please re-run the mirror to disk process
2026/03/06 17:22:40  [INFO]   : 📄 list of all images for mirroring in : alex-tests/ocpbugs66263/working-dir/dry-run/mapping.txt
2026/03/06 17:22:40  [INFO]   : mirror time     : 23m54.144253148s
2026/03/06 17:22:40  [INFO]   : 👋 Goodbye, thank you for using oc-mirror

Also I realized that the graph image was included in the mapping.txt but not in the missing.txt

The WARN comes from L54

Could you please take a look?

@aguidirh
Copy link
Copy Markdown
Contributor

aguidirh commented Mar 6, 2026

Another thing I would like you to check is about performance, I ran the same image set config in main branch in the PR.

Main branch above took 9 minutes, see below:

aguidi@fedora-redhat:~/go/src/github.com/aguidirh/oc-mirror$ ./bin/oc-mirror -c ./alex-tests/alex-isc/isc2.yaml file://alex-tests/ocpbugs66263-main --v2 --dry-run
2026/03/06 17:42:07  [INFO]   : 👋 Hello, welcome to oc-mirror
2026/03/06 17:42:07  [INFO]   : ⚙️  setting up the environment for you...
2026/03/06 17:42:07  [INFO]   : ⚙️  environment version: v0.2.0-alpha.1-544-g739eebc
2026/03/06 17:42:07  [INFO]   : 🔀 workflow mode: mirrorToDisk 
2026/03/06 17:42:07  [INFO]   : 🕵  going to discover the necessary images...
2026/03/06 17:42:07  [INFO]   : 🔍 collecting release images...
 ✓   (28s) Collecting release quay.io/openshift-release-dev/ocp-release:4.17.0-x86_64 
 ✓   (26s) Collecting release quay.io/openshift-release-dev/ocp-release:4.16.0-x86_64 
 ✓   (27s) Collecting release quay.io/openshift-release-dev/ocp-release:4.16.55-x86_64 
2026/03/06 17:45:04  [INFO]   : 🔍 collecting operator images...
 ✓   (2m17s) Collecting catalog registry.redhat.io/redhat/redhat-operator-index:v4.20 
 ✓   (3m25s) Collecting catalog registry.redhat.io/redhat/redhat-operator-index:v4.18 
 ✓   (41s) Collecting catalog oci:///home/aguidi/go/src/github.com/aguidirh/oc-mirror/alex-tests/redhat-operator-index-oci 
 ✓   (1s) Collecting catalog oci:///home/aguidi/go/src/github.com/aguidirh/oc-mirror/alex-tests/ibm-catalog 
2026/03/06 17:51:30  [INFO]   : 🔍 collecting additional images...
2026/03/06 17:51:30  [WARN]   : [AdditionalImagesCollector] registry.redhat.io/ubi8/ubi:latest@sha256:44d75007b39e0e1bbf1bcfd0721245add54c54c3f83903f8926fb4bef6827aa2 has both tag and digest : using digest to pull, but tag only for mirroring
2026/03/06 17:51:30  [INFO]   : 🔍 collecting helm images...
2026/03/06 17:51:39  [WARN]   : ⚠️  627/628 images necessary for mirroring are not available in the cache.
2026/03/06 17:51:39  [WARN]   : List of missing images in : alex-tests/ocpbugs66263-main/working-dir/dry-run/missing.txt.
please re-run the mirror to disk process
2026/03/06 17:51:39  [INFO]   : 📄 list of all images for mirroring in : alex-tests/ocpbugs66263-main/working-dir/dry-run/mapping.txt
2026/03/06 17:51:39  [INFO]   : mirror time     : 9m31.544678531s
2026/03/06 17:51:39  [INFO]   : 👋 Goodbye, thank you for using oc-mirror

While in the PR it took 23 minutes, see below:

aguidi@fedora-redhat:~/go/src/github.com/aguidirh/oc-mirror$ ./bin/oc-mirror -c ./alex-tests/alex-isc/isc2.yaml file://alex-tests/ocpbugs66263 --v2 --dry-run
2026/03/06 16:58:45  [INFO]   : 👋 Hello, welcome to oc-mirror
2026/03/06 16:58:45  [INFO]   : ⚙️  setting up the environment for you...
2026/03/06 16:58:45  [INFO]   : ⚙️  environment version: v0.2.0-alpha.1-541-ga51c524
2026/03/06 16:58:45  [INFO]   : 🔀 workflow mode: mirrorToDisk 
2026/03/06 16:58:46  [INFO]   : 🕵  going to discover the necessary images...
2026/03/06 16:58:46  [INFO]   : 🔍 collecting release images...
 ✓   (22s) Collecting release quay.io/openshift-release-dev/ocp-release:4.17.0-x86_64 
 ✓   (36s) Collecting release quay.io/openshift-release-dev/ocp-release:4.16.0-x86_64 
 ✓   (28s) Collecting release quay.io/openshift-release-dev/ocp-release:4.16.55-x86_64 
2026/03/06 17:02:35  [INFO]   : 🔍 collecting operator images...
 ✓   (1m54s) Collecting catalog registry.redhat.io/redhat/redhat-operator-index:v4.20 
 ✓   (2m57s) Collecting catalog registry.redhat.io/redhat/redhat-operator-index:v4.18 
 ✓   (41s) Collecting catalog oci:///home/aguidi/go/src/github.com/aguidirh/oc-mirror/alex-tests/redhat-operator-index-oci 
 ✓   (1s) Collecting catalog oci:///home/aguidi/go/src/github.com/aguidirh/oc-mirror/alex-tests/ibm-catalog 
2026/03/06 17:08:10  [INFO]   : 🔍 collecting additional images...
2026/03/06 17:08:10  [WARN]   : [AdditionalImagesCollector] registry.redhat.io/ubi8/ubi:latest@sha256:44d75007b39e0e1bbf1bcfd0721245add54c54c3f83903f8926fb4bef6827aa2 has both tag and digest : using digest to pull, but tag only for mirroring
2026/03/06 17:08:10  [INFO]   : 🔍 collecting helm images...
2026/03/06 17:21:27  [WARN]   : unable to get manifest list info for docker://localhost:55000/openshift/graph-image:latest: error creating image source for docker://localhost:55000/openshift/graph-image:latest: pinging container registry localhost:55000: Get "https://localhost:55000/v2/": http: server gave HTTP response to HTTPS client
2026/03/06 17:22:40  [WARN]   : ⚠️  627/628 images necessary for mirroring are not available in the cache.
2026/03/06 17:22:40  [WARN]   : List of missing images in : alex-tests/ocpbugs66263/working-dir/dry-run/missing.txt.
please re-run the mirror to disk process
2026/03/06 17:22:40  [INFO]   : 📄 list of all images for mirroring in : alex-tests/ocpbugs66263/working-dir/dry-run/mapping.txt
2026/03/06 17:22:40  [INFO]   : mirror time     : 23m54.144253148s
2026/03/06 17:22:40  [INFO]   : 👋 Goodbye, thank you for using oc-mirror

This is happening because we are calling the getManifestListDigests for all the images. One way to improve the performance is calling getManifestListDigests only for the manifest lists.

In order to do that, during the collection you could create a slice in the struct CopyImageSchemaMap (or in another place) and during the loop that already exists in the collection, you could add the manifest list to this slice, so in the dry-run you call getManifestListDigests only for the manifest lists.

If during the collection the children of the manifest list are already available, it could be also an idea to add them to the slice instead of the manifest list itself.

Feel free to contact me if you have questions about it. This is related to this code rabbit potential issue.

@openshift-ci-robot openshift-ci-robot added jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. and removed verified Signifies that the PR passed pre-merge verification criteria jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. labels Apr 22, 2026
@openshift-ci-robot
Copy link
Copy Markdown

@dorzel: This pull request references Jira Issue OCPBUGS-66263, which is invalid:

  • expected the bug to target either version "5.0." or "openshift-5.0.", but it targets "4.22.0" instead

Comment /jira refresh to re-evaluate validity if changes to the Jira bug are made, or edit the title of this pull request to link to a different bug.

Details

In response to this:

Description

This PR adds functionality to include index image sub-digests in mapping.txt, as part of the linked bug. This facilitates users who manually mirror based on mapping.txt or use automated security scanning which requires all digests to be listed.

Github / Jira issue: https://issues.redhat.com/browse/OCPBUGS-66263

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Code Improvements (Refactoring, Performance, CI upgrades, etc)
  • Internal repo assets (diagrams / docs on github repo)
  • This change requires a documentation update on openshift docs

How Has This Been Tested?

  • Ran unit test suite
  • Manually ran an m2d dry run and inspected the mapping.txt
kind: ImageSetConfiguration
apiVersion: mirror.openshift.io/v2alpha1
mirror:
 additionalImages:
   - name: registry.access.redhat.com/ubi9/ubi-minimal:9.4
   - name: docker.io/library/alpine:3.20
   - name: quay.io/podman/hello:latest
   - name: oci:///tmp/oci-test-image
   - name: registry.access.redhat.com/ubi9/ubi-minimal@sha256:c0e70387664f30cd9cf2795b547e4a9a51002c44a4a86aa9335ab030134bf392

Corresponding mapping.txt:

docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4=docker://localhost:55000/ubi9/ubi-minimal:9.4
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0=docker://localhost:55000/ubi9/ubi-minimal@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848=docker://localhost:55000/ubi9/ubi-minimal@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b=docker://localhost:55000/ubi9/ubi-minimal@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b
docker://registry.access.redhat.com/ubi9/ubi-minimal:9.4@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e=docker://localhost:55000/ubi9/ubi-minimal@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e
docker://docker.io/library/alpine:3.20=docker://localhost:55000/library/alpine:3.20
docker://docker.io/library/alpine:3.20@sha256:b0cb30c51c47cdfde647364301758b14c335dea2fddc9490d4f007d67ecb2538=docker://localhost:55000/library/alpine@sha256:b0cb30c51c47cdfde647364301758b14c335dea2fddc9490d4f007d67ecb2538
docker://docker.io/library/alpine:3.20@sha256:66f45812e9b5053abd384c1c6037261e526b1a0a4ac783ecaf3d5e04a94a0412=docker://localhost:55000/library/alpine@sha256:66f45812e9b5053abd384c1c6037261e526b1a0a4ac783ecaf3d5e04a94a0412
docker://docker.io/library/alpine:3.20@sha256:a4e2d43613b6ee71c96f9d26a2e515729fafeb75e4f0dfea524445b3344dccfe=docker://localhost:55000/library/alpine@sha256:a4e2d43613b6ee71c96f9d26a2e515729fafeb75e4f0dfea524445b3344dccfe
docker://docker.io/library/alpine:3.20@sha256:4135f5ea9c06f0857b68f7a6beaa40480d1f11d64e6341d11ed061cce5f66214=docker://localhost:55000/library/alpine@sha256:4135f5ea9c06f0857b68f7a6beaa40480d1f11d64e6341d11ed061cce5f66214
docker://docker.io/library/alpine:3.20@sha256:1e161cda0d91d4d1c28464a134f1c0fef668a7c6e486017314e06a64b875c288=docker://localhost:55000/library/alpine@sha256:1e161cda0d91d4d1c28464a134f1c0fef668a7c6e486017314e06a64b875c288
docker://docker.io/library/alpine:3.20@sha256:a1acb89d2fc6e0d5c1f3b3be4fd4bda3bd29ca8d5df1461b03aabefefa9d4321=docker://localhost:55000/library/alpine@sha256:a1acb89d2fc6e0d5c1f3b3be4fd4bda3bd29ca8d5df1461b03aabefefa9d4321
docker://docker.io/library/alpine:3.20@sha256:9ace55212059e43b0b246bb975a26b4f6fb41f7384bc87b57ad3aae2a6e5e817=docker://localhost:55000/library/alpine@sha256:9ace55212059e43b0b246bb975a26b4f6fb41f7384bc87b57ad3aae2a6e5e817
docker://docker.io/library/alpine:3.20@sha256:13688eb4bf30a8a94d920e2c6489aceadb6caa26325426b558c35af4322f797a=docker://localhost:55000/library/alpine@sha256:13688eb4bf30a8a94d920e2c6489aceadb6caa26325426b558c35af4322f797a
docker://docker.io/library/alpine:3.20@sha256:c4140ad89752b59d3d1567f6c2c5922d4b1a3eb7ab3ac41472772fcc4a77a447=docker://localhost:55000/library/alpine@sha256:c4140ad89752b59d3d1567f6c2c5922d4b1a3eb7ab3ac41472772fcc4a77a447
docker://docker.io/library/alpine:3.20@sha256:0ee208147b93c91fdda2ded4a5daf71c91d2a491e94b56e29c5edc11d16b5f11=docker://localhost:55000/library/alpine@sha256:0ee208147b93c91fdda2ded4a5daf71c91d2a491e94b56e29c5edc11d16b5f11
docker://docker.io/library/alpine:3.20@sha256:98d989819241cb7170e0660f1be3f4e1a6a9e70ba0c4f5c4c7bded2b531e46b6=docker://localhost:55000/library/alpine@sha256:98d989819241cb7170e0660f1be3f4e1a6a9e70ba0c4f5c4c7bded2b531e46b6
docker://docker.io/library/alpine:3.20@sha256:1b8c7e5752cc75d8be572ac609fc4e1e415387acac0e70b80922b8bfc82894f9=docker://localhost:55000/library/alpine@sha256:1b8c7e5752cc75d8be572ac609fc4e1e415387acac0e70b80922b8bfc82894f9
docker://docker.io/library/alpine:3.20@sha256:3e5e9c7e2b11707d02a8dde9f71dacb294c3bce820d013f817c57c52ee141570=docker://localhost:55000/library/alpine@sha256:3e5e9c7e2b11707d02a8dde9f71dacb294c3bce820d013f817c57c52ee141570
docker://docker.io/library/alpine:3.20@sha256:a5588164e8323980b589e3f4a8ca819e50816ec140fcf8f66904ed539047b30b=docker://localhost:55000/library/alpine@sha256:a5588164e8323980b589e3f4a8ca819e50816ec140fcf8f66904ed539047b30b
docker://docker.io/library/alpine:3.20@sha256:7c694b533384ef3d60b9743a8e3d50a8d1586d5253d2a60d227aa5526966a54b=docker://localhost:55000/library/alpine@sha256:7c694b533384ef3d60b9743a8e3d50a8d1586d5253d2a60d227aa5526966a54b
docker://docker.io/library/alpine:3.20@sha256:dfb98350c924a6ea7572103667da653b98c597bf22aadec19f27866c987457fa=docker://localhost:55000/library/alpine@sha256:dfb98350c924a6ea7572103667da653b98c597bf22aadec19f27866c987457fa
docker://quay.io/podman/hello:latest=docker://localhost:55000/podman/hello:latest
docker://quay.io/podman/hello:latest@sha256:43de9874507eaa8ffd88eac885b672b1dfc57cc583d9ad920850f97f19809f8f=docker://localhost:55000/podman/hello@sha256:43de9874507eaa8ffd88eac885b672b1dfc57cc583d9ad920850f97f19809f8f
docker://quay.io/podman/hello:latest@sha256:5c44ef36dc5e35a76904da0e028cf9413e0176a653525162368af13fed03571c=docker://localhost:55000/podman/hello@sha256:5c44ef36dc5e35a76904da0e028cf9413e0176a653525162368af13fed03571c
docker://quay.io/podman/hello:latest@sha256:92be1897bfcf5a8c9e59ce538645d7c159253717fa8edbb964ff5a259a5535ab=docker://localhost:55000/podman/hello@sha256:92be1897bfcf5a8c9e59ce538645d7c159253717fa8edbb964ff5a259a5535ab
docker://quay.io/podman/hello:latest@sha256:6fba1b311cad0201c377469c66c9cdfc4c48ea99a186869c7ce2609e0a013965=docker://localhost:55000/podman/hello@sha256:6fba1b311cad0201c377469c66c9cdfc4c48ea99a186869c7ce2609e0a013965
oci:///tmp/oci-test-image=docker://localhost:55000/tmp/oci-test-image:latest
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:c0e70387664f30cd9cf2795b547e4a9a51002c44a4a86aa9335ab030134bf392=docker://localhost:55000/ubi9/ubi-minimal:sha256-c0e70387664f30cd9cf2795b547e4a9a51002c44a4a86aa9335ab030134bf392
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0=docker://localhost:55000/ubi9/ubi-minimal@sha256:f5d2c6a1e0c86e4234ea601552dbabb4ced0e013a1efcbfb439f1f6a7a9275b0
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848=docker://localhost:55000/ubi9/ubi-minimal@sha256:96f4394d39e6edb69ca51f000f3e7dfb62990f55868134cfd83c82177651e848
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b=docker://localhost:55000/ubi9/ubi-minimal@sha256:39c59a30e3ecae689c23b27e54a81e03d9a5db22d11890edaf6bb16bac783e8b
docker://registry.access.redhat.com/ubi9/ubi-minimal@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e=docker://localhost:55000/ubi9/ubi-minimal@sha256:d187f310724694b1daae2f99f6f86ae05b573eed6826fa40d4233e76bd07312e

Expected Outcome

All sub-digests of any index image in the mapping.txt are listed directly below it.

Summary by CodeRabbit

  • New Features

  • Dry-run now detects multi-architecture manifest lists, merges pre-collected and discovered variant digests, and emits per-variant source→digest-pinned destination mappings; missing-image reports include corresponding variant entries.

  • Tests

  • Added tests building an OCI layout with a manifest list, verifying per-variant mappings, digest-pinned destinations, and that unreachable sources warn but do not fail.

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.

@dorzel
Copy link
Copy Markdown
Member Author

dorzel commented Apr 22, 2026

/jira refresh

@openshift-ci-robot openshift-ci-robot added jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. and removed jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Apr 22, 2026
@openshift-ci-robot
Copy link
Copy Markdown

@dorzel: This pull request references Jira Issue OCPBUGS-66263, which is valid.

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 POST, which is one of the valid states (NEW, ASSIGNED, POST)

Requesting review from QA contact:
/cc @MayXuQQ

Details

In response to this:

/jira refresh

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
Copy link
Copy Markdown

openshift-ci Bot commented Apr 22, 2026

@openshift-ci-robot: GitHub didn't allow me to request PR reviews from the following users: MayXuQQ.

Note that only openshift members and repo collaborators can review this PR, and authors cannot review their own PRs.

Details

In response to this:

@dorzel: This pull request references Jira Issue OCPBUGS-66263, which is valid.

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 POST, which is one of the valid states (NEW, ASSIGNED, POST)

Requesting review from QA contact:
/cc @MayXuQQ

In response to this:

/jira refresh

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.

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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)
internal/pkg/operator/filtered_collector.go (1)

349-385: ⚠️ Potential issue | 🟡 Minor

Don't silently swallow ListFromBlob errors; digest-only catalogs can also short-circuit after list detection.

Two small points on getCatalogDigest:

  1. Lines 374–382: the ListFromBlob error is silently discarded. If it fails for a legitimate multi-image manifest, the pre-collection optimization that motivated this collector-side change is lost and dry-run will fall back to re-inspecting the same catalog downstream — without any signal that pre-collection failed. A single debug/warn log would make this diagnosable without changing behavior.
  2. Line 349 vs lines 367–371: for digest-only catalogs during dry-run you already have imgSpec.Digest in hand; re-computing it via imgmanifest.Digest(manifestBytes).Encoded() is redundant (and relies on the registry returning byte-for-byte canonical manifest bytes to match). You can still fetch/list-detect, but return imgSpec.Digest directly as the catalog digest.
Suggested diff
-	if o.Opts.IsDryRun && imgmanifest.MIMETypeIsMultiImage(manifestType) {
-		list, err := imgmanifest.ListFromBlob(manifestBytes, manifestType)
-		if err == nil {
-			instances := list.Instances()
-			digests := make([]string, 0, len(instances))
-			for _, inst := range instances {
-				digests = append(digests, inst.String())
-			}
-			copyImageSchemaMap.ManifestListDigests[imgSpec.ReferenceWithTransport] = digests
-		}
-	}
-
-	return catalogDigest, nil
+	if o.Opts.IsDryRun && imgmanifest.MIMETypeIsMultiImage(manifestType) {
+		list, err := imgmanifest.ListFromBlob(manifestBytes, manifestType)
+		if err != nil {
+			o.Log.Debug("failed to parse manifest list for %s, dry-run will re-inspect: %v", imgSpec.ReferenceWithTransport, err)
+		} else {
+			instances := list.Instances()
+			digests := make([]string, 0, len(instances))
+			for _, inst := range instances {
+				digests = append(digests, inst.String())
+			}
+			copyImageSchemaMap.ManifestListDigests[imgSpec.ReferenceWithTransport] = digests
+		}
+	}
+
+	if imgSpec.IsImageByDigestOnly() {
+		return imgSpec.Digest, nil
+	}
+	return catalogDigest, nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/pkg/operator/filtered_collector.go` around lines 349 - 385, In
getCatalogDigest (in filtered_collector.go) avoid recomputing the catalog digest
for digest-only images and surface ListFromBlob failures: when
imgSpec.IsImageByDigestOnly() && o.Opts.IsDryRun return imgSpec.Digest
immediately instead of calling imgmanifest.Digest(manifestBytes).Encoded(); and
when calling imgmanifest.ListFromBlob(manifestBytes, manifestType) do not
silently ignore errors — log the error (debug or warn) with context (e.g., the
reference imgSpec.ReferenceWithTransport) before proceeding, and only populate
copyImageSchemaMap.ManifestListDigests on success as you already do.
🧹 Nitpick comments (2)
internal/pkg/operator/filtered_collector_test.go (1)

1033-1036: Consider adding a failure toggle for ImageManifest (minor test-coverage gap).

The updated mock always returns a valid single-arch manifest and nil error. The existing FailImageManifest flag only gates GetImageManifest/GetOCIImageManifest, not this method. As a result, the new error paths added to getCatalogDigest in filtered_collector.go (ImageManifest failure, imgmanifest.Digest failure, multi-image branch) aren't reachable from any test that uses this mock. Adding something like a FailImageManifestFetch toggle (and/or a configurable payload) would let you cover the new error/multi-image paths.

Not blocking — flagging for future coverage.

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

In `@internal/pkg/operator/filtered_collector_test.go` around lines 1033 - 1036,
The mock MockManifest.ImageManifest currently always returns a valid manifest
and nil error, leaving the new error/multi-image branches in getCatalogDigest
(in filtered_collector.go) untested; add a boolean flag (e.g.,
FailImageManifestFetch) to the MockManifest struct and have ImageManifest check
it and return a non-nil error when set, and optionally allow injecting a
configurable manifest payload (or a list of manifests) so tests can exercise
imgmanifest.Digest failures and the multi-image branch; update tests to toggle
FailImageManifestFetch (and payload) to cover those paths.
internal/pkg/cli/dryrun_test.go (1)

406-410: Nit: .invalid is reserved but a DNS lookup still happens.

Using the RFC 2606 .invalid TLD is the right choice for an unresolvable hostname, but on hosts with captive DNS or certain corporate resolvers the lookup can still take a few seconds before failing. If this test is observed to be slow in CI, consider pairing with opts.CommandTimeout or pointing Source at a 127.0.0.1:<unused-port> instead. Non-blocking.

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

In `@internal/pkg/cli/dryrun_test.go` around lines 406 - 410, The test uses
hostnames under the .invalid TLD in the imgs slice (v2alpha1.CopyImageSchema
entries with Source built via consts.DockerProtocol) which can still incur DNS
resolution delays on some CI environments; update the test to avoid DNS lookup
latency by either (A) setting the invocation to use opts.CommandTimeout (or
equivalent command timeout in the test harness) around the manifest inspection
calls that use imgs so the test fails fast, or (B) change the Source fields in
the imgs array to point to 127.0.0.1:<unused-port> (loopback with a port
guaranteed unused) instead of fake-registry.invalid to eliminate external DNS
resolution; adjust references to CopyImageSchema.Source and the imgs variable
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@internal/pkg/operator/filtered_collector.go`:
- Around line 349-385: In getCatalogDigest (in filtered_collector.go) avoid
recomputing the catalog digest for digest-only images and surface ListFromBlob
failures: when imgSpec.IsImageByDigestOnly() && o.Opts.IsDryRun return
imgSpec.Digest immediately instead of calling
imgmanifest.Digest(manifestBytes).Encoded(); and when calling
imgmanifest.ListFromBlob(manifestBytes, manifestType) do not silently ignore
errors — log the error (debug or warn) with context (e.g., the reference
imgSpec.ReferenceWithTransport) before proceeding, and only populate
copyImageSchemaMap.ManifestListDigests on success as you already do.

---

Nitpick comments:
In `@internal/pkg/cli/dryrun_test.go`:
- Around line 406-410: The test uses hostnames under the .invalid TLD in the
imgs slice (v2alpha1.CopyImageSchema entries with Source built via
consts.DockerProtocol) which can still incur DNS resolution delays on some CI
environments; update the test to avoid DNS lookup latency by either (A) setting
the invocation to use opts.CommandTimeout (or equivalent command timeout in the
test harness) around the manifest inspection calls that use imgs so the test
fails fast, or (B) change the Source fields in the imgs array to point to
127.0.0.1:<unused-port> (loopback with a port guaranteed unused) instead of
fake-registry.invalid to eliminate external DNS resolution; adjust references to
CopyImageSchema.Source and the imgs variable accordingly.

In `@internal/pkg/operator/filtered_collector_test.go`:
- Around line 1033-1036: The mock MockManifest.ImageManifest currently always
returns a valid manifest and nil error, leaving the new error/multi-image
branches in getCatalogDigest (in filtered_collector.go) untested; add a boolean
flag (e.g., FailImageManifestFetch) to the MockManifest struct and have
ImageManifest check it and return a non-nil error when set, and optionally allow
injecting a configurable manifest payload (or a list of manifests) so tests can
exercise imgmanifest.Digest failures and the multi-image branch; update tests to
toggle FailImageManifestFetch (and payload) to cover those paths.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: bf8bc58f-f8cd-4681-bde2-a248da9f22e0

📥 Commits

Reviewing files that changed from the base of the PR and between a51c524 and d80b34a.

📒 Files selected for processing (6)
  • internal/pkg/api/v2alpha1/type_internal.go
  • internal/pkg/cli/dryrun.go
  • internal/pkg/cli/dryrun_test.go
  • internal/pkg/cli/executor.go
  • internal/pkg/operator/filtered_collector.go
  • internal/pkg/operator/filtered_collector_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/pkg/cli/dryrun.go

@aguidirh
Copy link
Copy Markdown
Contributor

/retest

Copy link
Copy Markdown
Contributor

@aguidirh aguidirh left a comment

Choose a reason for hiding this comment

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

Thanks for the changes @dorzel, the performance issue is fixed and the graph image issue is not happening anymore.

I added few suggestions, please let me know if you have question about them.

Comment thread internal/pkg/cli/dryrun.go
Comment thread internal/pkg/cli/executor.go Outdated
Comment thread internal/pkg/cli/dryrun.go Outdated
Comment thread internal/pkg/operator/filtered_collector.go Outdated
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 5, 2026

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

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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants