Skip to content

OTA-1956: oc adm release new: Include base image's image-references in pruning#2287

Open
jhadvig wants to merge 2 commits into
openshift:mainfrom
jhadvig:include_base_image
Open

OTA-1956: oc adm release new: Include base image's image-references in pruning#2287
jhadvig wants to merge 2 commits into
openshift:mainfrom
jhadvig:include_base_image

Conversation

@jhadvig

@jhadvig jhadvig commented Jun 11, 2026

Copy link
Copy Markdown
Member

Summary

The CVO image is the release base image (ToImageBaseTag: "cluster-version-operator") but does not carry the io.openshift.release.operator=true label. This means extractManifests skips it entirely, and any images declared in the CVO's install/image-references are invisible to pruneUnreferencedImageStreams — they get dropped from the release payload.

This blocks openshift/cluster-version-operator#1398, which adds a cluster-update-console-plugin deployment to the CVO's own manifests. The plugin image exists in the CI ImageStream but is pruned from the release because no operator's image-references declares it.

Root cause

  1. extractManifests iterates all tags and checks for io.openshift.release.operator=true (line 1037). CVO has no such label → extraction is skipped → no files in the temp directory.
  2. pruneUnreferencedImageStreams (line 1591) scans each extracted operator's directory for image-references. Since CVO was never extracted, its image-references is never read.
  3. cluster-update-console-plugin is not referenced by any operator and not in AlwaysInclude → it is pruned from the release.

Implementation

Two small changes in pkg/cli/admin/release/new.go:

  1. extractManifests ConditionFn: When an image has no operator label but IS the ToImageBaseTag, still create the destination directory and proceed with extraction. This makes the CVO's image-references file available on disk for the pruning step.

  2. Exclude base image from ordered: After the metadata-based filter, remove the base image tag from ordered. Its manifests already live in /manifests/ from the base layer and must not be duplicated into release-manifests/.

Tests

Added TestPruneUnreferencedImageStreams (3 cases):

  • Operator image-references keeps referenced images (existing behavior)
  • Base image image-references prevents pruning (new behavior)
  • Without base image image-references, the image is pruned (the bug)

Added TestBaseImageExcludedFromOrdered (2 cases):

  • Base image tag is removed from ordered
  • No-op when base image is not in the list

Related

/assign @wking @DavidHurta

Summary by CodeRabbit

  • Bug Fixes

    • Base image tags are now excluded from component ordering during release construction.
    • Manifest extraction now explicitly handles base image tags so their manifests are extracted when present.
  • Tests

    • Added tests covering pruning of unreferenced image tags based on operator and base image references.
    • Added tests for base image tag list handling.

The CVO image is the release base image but lacks the
io.openshift.release.operator=true label, so its manifests are not
extracted during release building. This means any images declared in
the CVO's install/image-references (like cluster-update-console-plugin)
are not seen by pruneUnreferencedImageStreams and get dropped from the
release payload.

When the base image has no operator label, still extract its manifests
so the image-references file is available during pruning. Exclude the
base image from the ordered list so its manifests are not duplicated
into release-manifests/ — they already live in /manifests/ from the
base layer.
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Walkthrough

The PR removes the configured base image tag from the release ordered list and adds a manifest extraction special-case that allows creating the destination directory and extracting the base tag's image-references independently of operator annotations. Test helpers and a new test verify pruning of unreferenced ImageStream tags across operator/base-image scenarios.

Changes

Base image tag handling

Layer / File(s) Summary
Release ordering change
pkg/cli/admin/release/new.go
Imports slices and deletes any ordered entry equal to o.ToImageBaseTag when ToImageBaseTag is set.
Manifest extraction special-case
pkg/cli/admin/release/new.go
In extractManifests selection ConditionFn, when tag.Name == o.ToImageBaseTag the code creates the destination directory and permits extraction/logging for image-references regardless of operator annotation.
Test helpers and pruning tests
pkg/cli/admin/release/new_test.go
Adds createImageStream, writeImageReferences, tagNames helpers and TestPruneUnreferencedImageStreams which asserts pruning behavior across operator-referenced tags, base-image-referenced tags, and unprotected tags.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (14 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: including the base image's image-references in the pruning logic for the OpenShift release assembly process.
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 Checked pkg/cli/admin/release/new_test.go: Test names and t.Run subtest titles are static literals; no fmt/UUID/time/pod/namespace dynamic content, and no Ginkgo It/Describe/Context/When calls found.
Test Structure And Quality ✅ Passed PR adds standard Go unit tests (no Ginkgo Describe/It); tests use t.TempDir for setup/cleanup, have no cluster interactions/timeouts, and each subtest targets a distinct prune scenario.
Microshift Test Compatibility ✅ Passed PR 2287 changes only pkg/cli/admin/release/new.go and new_test.go (Go unit tests); no Ginkgo e2e tests or MicroShift-incompatible APIs/resources were added.
Single Node Openshift (Sno) Test Compatibility ✅ Passed PR #2287 changes only pkg/cli/admin/release/new.go and new_test.go (Go unit tests); no new Ginkgo e2e Describe/It blocks or SNO multi-node assumptions found.
Topology-Aware Scheduling Compatibility ✅ Passed PR only updates pkg/cli/admin/release/new.go and new_test.go (release manifest extraction/pruning). No scheduling/topology constraints keywords (affinity/topologyKey/nodeSelector/anti-affinity/repl...
Ote Binary Stdout Contract ✅ Passed PR changes are limited to pkg/cli/admin/release/new.go and new_test.go; neither has stdout writes in main/init/TestMain or suite setup (no fmt.Print*/os.Stdout/klog.SetOutput).
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e tests: pkg/cli/admin/release/new_test.go uses stdlib testing only (no ginkgo Describe/It) and has no hardcoded IPv4 literals or network fetches.
No-Weak-Crypto ✅ Passed Scanned changed files pkg/cli/admin/release/new.go and new_test.go for md5/sha1/DES/3DES/RC4/Blowfish/ECB, crypto/ imports, and token/secret comparisons—no weak crypto or custom crypto found.
Container-Privileges ✅ Passed PR #2287 changes only pkg/cli/admin/release/new.go and new_test.go (Go code); no Kubernetes/container manifests or privileged/hostPID/allowPrivilegeEscalation settings found.
No-Sensitive-Data-In-Logs ✅ Passed Reviewed pkg/cli/admin/release/new.go and new_test.go klog/t.Log/err outputs; no logging of tokens/passwords/API keys/PII—only image refs/paths and non-sensitive errors.

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

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

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

@openshift-ci openshift-ci Bot requested review from ardaguclu and ingvagabund June 11, 2026 10:04
@jhadvig jhadvig changed the title oc adm release new: Include base image's image-references in pruning OTA-1956: oc adm release new: Include base image's image-references in pruning Jun 11, 2026
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jun 11, 2026
@openshift-ci-robot

openshift-ci-robot commented Jun 11, 2026

Copy link
Copy Markdown

@jhadvig: This pull request references OTA-1956 which is a valid jira issue.

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

Details

In response to this:

Summary

The CVO image is the release base image (ToImageBaseTag: "cluster-version-operator") but does not carry the io.openshift.release.operator=true label. This means extractManifests skips it entirely, and any images declared in the CVO's install/image-references are invisible to pruneUnreferencedImageStreams — they get dropped from the release payload.

This blocks openshift/cluster-version-operator#1398, which adds a cluster-update-console-plugin deployment to the CVO's own manifests. The plugin image exists in the CI ImageStream but is pruned from the release because no operator's image-references declares it.

Root cause

  1. extractManifests iterates all tags and checks for io.openshift.release.operator=true (line 1037). CVO has no such label → extraction is skipped → no files in the temp directory.
  2. pruneUnreferencedImageStreams (line 1591) scans each extracted operator's directory for image-references. Since CVO was never extracted, its image-references is never read.
  3. cluster-update-console-plugin is not referenced by any operator and not in AlwaysInclude → it is pruned from the release.

Implementation

Two small changes in pkg/cli/admin/release/new.go:

  1. extractManifests ConditionFn: When an image has no operator label but IS the ToImageBaseTag, still create the destination directory and proceed with extraction. This makes the CVO's image-references file available on disk for the pruning step.

  2. Exclude base image from ordered: After the metadata-based filter, remove the base image tag from ordered. Its manifests already live in /manifests/ from the base layer and must not be duplicated into release-manifests/.

Tests

Added TestPruneUnreferencedImageStreams (3 cases):

  • Operator image-references keeps referenced images (existing behavior)
  • Base image image-references prevents pruning (new behavior)
  • Without base image image-references, the image is pruned (the bug)

Added TestBaseImageExcludedFromOrdered (2 cases):

  • Base image tag is removed from ordered
  • No-op when base image is not in the list

Related

/assign @wking @DavidHurta

Summary by CodeRabbit

  • Bug Fixes

  • Base image tags are now properly excluded from component ordering during release construction.

  • Manifest extraction now includes special handling for base image tags.

  • Tests

  • Added test coverage for image stream pruning with operator image references.

  • Added test coverage for base image tag list handling.

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.

@jhadvig

jhadvig commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

Local validation

Tested the fix locally using --from-dir to simulate the release assembly pipeline without needing registry auth.

Setup

Created a minimal release directory with a cluster-version-operator subdirectory containing an image-references file that declares cluster-update-console-plugin:

/tmp/release-test/
├── image-references              # release image-references (192 tags including cluster-update-console-plugin)
├── release-metadata              # Cincinnati metadata
└── cluster-version-operator/
    └── image-references          # CVO's image-references declaring cluster-update-console-plugin

With the fix — CVO's image-references prevents pruning

$ /tmp/oc-custom adm release new --from-dir=/tmp/release-test --to-file=/tmp/test-release.tar
info: Using /tmp/release-test as the input to the release
info: Found 1 operator manifest directories on disk
info: Included 2 images from 1 input operators into the release

Output release contains both cluster-version-operator and cluster-update-console-plugin.

Without CVO's image-references — image is pruned

$ rm /tmp/release-test/cluster-version-operator/image-references
$ /tmp/oc-custom adm release new --from-dir=/tmp/release-test --to-file=/tmp/test-release-no-ref.tar
info: Using /tmp/release-test as the input to the release
info: Found 1 operator manifest directories on disk
info: Included 1 images from 1 input operators into the release

Output release only contains cluster-version-operatorcluster-update-console-plugin was pruned because nothing references it.

Comment thread pkg/cli/admin/release/new_test.go Outdated
t.Errorf("expected %s to be removed from ordered", baseTag)
}
if len(ordered) != 2 {
t.Errorf("expected 2 entries, got %d", len(ordered))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is this just excercising the slices library? I'm ok trusting the stdlib to do what it says it will do, and not building our own custom tests that just excercise the stdlib. Maybe we can drop this? Or maybe you want to bump up a level and excercise some of the new.go logic you're adding that uses those slices functions?

@jhadvig jhadvig Jun 12, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good point, dropped it 🎤

Comment thread pkg/cli/admin/release/new_test.go Outdated
for _, name := range names {
is.Spec.Tags = append(is.Spec.Tags, imageapi.TagReference{
Name: name,
From: &corev1.ObjectReference{Kind: "DockerImage", Name: "example.com/" + name + ":latest"},

@wking wking Jun 11, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Overlaps a bit with writeImageReferences. Maybe adjust writeImageReferences to return the ImageStream structure it wrote to disk, if we need an in-memory copy? Or refactor to pull the ImageStream structure creation out into a createImageStream helper that both the tests and writeImageReferences can consume?

@jhadvig jhadvig Jun 12, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Refactored... extracted a shared createImageStream helper that both writeImageReferences and the test cases use.

@wking wking left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I left two nits with the test suite, but I don't think either needs to block the merge. I'm approving and holding, in case you want to address either. But feel free to lift the hold whenever you like, whether or not you make any changes.

/llgtm
/hold in case you want to address test-case feedback

@openshift-ci openshift-ci Bot added do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. lgtm Indicates that a PR is ready to be merged. approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Jun 11, 2026
- Drop TestBaseImageExcludedFromOrdered (just exercised stdlib slices)
- Extract createImageStream helper shared by writeImageReferences and tests
@openshift-ci openshift-ci Bot removed the lgtm Indicates that a PR is ready to be merged. label Jun 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
pkg/cli/admin/release/new_test.go (1)

62-150: ⚡ Quick win

Convert this into a table-driven test and compare the final tag set in one assertion.

These three subtests are the same arrange/act/assert shape with different inputs and expected tags. Folding them into a table and asserting tagNames(is) with a single cmp.Diff would match the repo’s test conventions and make future pruning cases easier to extend.

As per coding guidelines, "Write unit tests for every change using table-driven test patterns and standard testing.T" and "Use google/go-cmp to compare expected and actual objects rather than checking individual fields with if statements."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/cli/admin/release/new_test.go` around lines 62 - 150, The test
TestPruneUnreferencedImageStreams should be converted to a table-driven test:
replace the three near-duplicate t.Run blocks with a single slice of test cases
(fields: name, setup inputs such as operator/base image references created via
writeImageReferences and createImageStream, metadata map, include list, and
expected tag slice), loop over cases calling pruneUnreferencedImageStreams, then
compute actual := tagNames(is) and assert equality with cmp.Diff in one
assertion per case (use t.Fatalf on prune error and t.Errorf with the diff on
mismatch). Keep references to the existing helpers
pruneUnreferencedImageStreams, tagNames, writeImageReferences, and
createImageStream so the setup/act/assert logic is identical but consolidated
into the table-driven structure.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@pkg/cli/admin/release/new_test.go`:
- Around line 62-150: The test TestPruneUnreferencedImageStreams should be
converted to a table-driven test: replace the three near-duplicate t.Run blocks
with a single slice of test cases (fields: name, setup inputs such as
operator/base image references created via writeImageReferences and
createImageStream, metadata map, include list, and expected tag slice), loop
over cases calling pruneUnreferencedImageStreams, then compute actual :=
tagNames(is) and assert equality with cmp.Diff in one assertion per case (use
t.Fatalf on prune error and t.Errorf with the diff on mismatch). Keep references
to the existing helpers pruneUnreferencedImageStreams, tagNames,
writeImageReferences, and createImageStream so the setup/act/assert logic is
identical but consolidated into the table-driven structure.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: 00971d67-9a3a-4511-bc11-2f8fab30fc0f

📥 Commits

Reviewing files that changed from the base of the PR and between 1477468 and 6af7486.

📒 Files selected for processing (1)
  • pkg/cli/admin/release/new_test.go

@openshift-ci

openshift-ci Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

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

@wking wking left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

/lgtm

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jun 12, 2026
@openshift-ci

openshift-ci Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: jhadvig, wking

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

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants