Skip to content

Make dry-run also generate cluster-resources#1394

Open
javipolo wants to merge 1 commit intoopenshift:mainfrom
javipolo:dry-run-cluster-resources
Open

Make dry-run also generate cluster-resources#1394
javipolo wants to merge 1 commit intoopenshift:mainfrom
javipolo:dry-run-cluster-resources

Conversation

@javipolo
Copy link
Copy Markdown
Member

@javipolo javipolo commented Apr 24, 2026

Description

Create cluster-resource manifests while running with --dry-run flag

Github / Jira issue:

https://redhat.atlassian.net/browse/RFE-4107

Type of change

Please delete options that are not relevant.

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Run oc-mirror with --dry-run and manifests should apear in working-dir/cluster-resources
It was tested manually

Expected Outcome

Manifests should be present in cluster-resources directory

Summary by CodeRabbit

  • New Features
    • Dry-run output now generates cluster resources including IDMS/ITMS, catalog sources, and cluster catalogs
    • UpdateService automatically generated when OCP release images are present
    • Enhanced error handling for signature configuration with graceful degradation

Signed-off-by: Javi Polo <jpolo@redhat.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

Walkthrough

Dry-run functionality now generates cluster resources after writing image mapping log output. When ClusterResources is initialized, a helper generates IDMS/ITMS, catalog source, and cluster catalog unconditionally, attempts signature configmap generation with failure downgrade to warning, and conditionally generates UpdateService for OCP release images using a new graph-image reference constructor.

Changes

Cohort / File(s) Summary
Cluster Resource Generation
internal/pkg/cli/dryrun.go
Added logic to trigger cluster resource generation after image mapping log output. Implements conditional generation of IDMS/ITMS, catalog source, cluster catalog, signature configmap (with warning fallback), and UpdateService (for TypeOCPRelease). Includes new helper for graph-image reference construction with registry destination validation.
Dry-Run Tests
internal/pkg/cli/dryrun_test.go
Added test validating that cluster-resource generation in ExecutorSchema produces expected filesystem outputs. Verifies both mapping.txt and cluster-resources directory creation during DryRun execution with generic and OCP-release typed images.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant DryRun as Dry-Run Handler
    participant ResourceGen as Cluster Resource Generator
    participant IDMS as IDMS/ITMS Generator
    participant CatalogGen as Catalog Generator
    participant SigGen as Signature ConfigMap Generator
    participant UpdateSvcGen as UpdateService Generator
    participant FileSystem as File System

    User->>DryRun: Execute DryRun
    DryRun->>FileSystem: Write mapping.txt
    DryRun->>ResourceGen: Trigger cluster resource generation
    
    ResourceGen->>IDMS: Generate IDMS/ITMS
    IDMS->>FileSystem: Write IDMS/ITMS resources
    
    ResourceGen->>CatalogGen: Generate catalog source & cluster catalog
    CatalogGen->>FileSystem: Write catalog resources
    
    ResourceGen->>SigGen: Attempt signature configmap generation
    alt Signature ConfigMap Success
        SigGen->>FileSystem: Write signature configmap
    else Signature ConfigMap Fails
        SigGen->>ResourceGen: Downgrade to warning
    end
    
    alt OCP Release Image Present
        ResourceGen->>UpdateSvcGen: Generate UpdateService
        UpdateSvcGen->>FileSystem: Write UpdateService resource
    end
    
    ResourceGen-->>DryRun: Cluster resources complete
    DryRun-->>User: Dry-run finished
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 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 'Make dry-run also generate cluster-resources' directly and accurately reflects the main change: enabling cluster-resource generation during dry-run execution.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% 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 This PR uses standard Go testing with testing.T and t.Run() instead of Ginkgo framework. All test names are static, deterministic strings with no dynamic values.
Test Structure And Quality ✅ Passed The custom check for Ginkgo test code is not applicable to this PR. The test code uses standard Go testing with testing.T and t.Run() subtests, along with testify assertions, rather than Ginkgo's BDD-style testing framework.
Microshift Test Compatibility ✅ Passed The PR adds standard Go unit tests using testing.T and t.Run(), not Ginkgo e2e tests. The MicroShift Test Compatibility check applies only to Ginkgo e2e tests with patterns like Describe(), Context(), It(), or When().
Single Node Openshift (Sno) Test Compatibility ✅ Passed This PR adds standard Go unit tests, not Ginkgo e2e tests, so the check is not applicable.
Topology-Aware Scheduling Compatibility ✅ Passed PR introduces cluster-resource generation during dry-run mode with static YAML configuration manifests. UpdateService contains only Replicas, Releases, and GraphDataImage fields with no scheduling constraints.
Ote Binary Stdout Contract ✅ Passed Pull request contains only library function additions with no process-level code that could violate OTE Binary Stdout Contract; all logging properly abstracted through custom logger.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The test added is a standard Go unit test, not a Ginkgo e2e test. It uses local temporary directories and storage caches without IPv4 assumptions or external connectivity requirements.

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

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@openshift-ci openshift-ci Bot requested review from adolfo-ab and aguidirh April 24, 2026 09:35
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented Apr 24, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

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

🧹 Nitpick comments (2)
internal/pkg/cli/dryrun.go (1)

77-91: Consider downgrading cluster-resource failures to warnings, and drop the ClusterResources == nil guard in favor of test mocks.

Two small points:

  • DryRun previously always succeeded when the mapping and (optionally) missing files were produced. Now any failure from IDMS_ITMSGenerator, CatalogSourceGenerator, ClusterCatalogGenerator, or UpdateServiceGenerator fails the whole dry-run. GenerateSignatureConfigMap is already correctly downgraded to a warning — consider the same treatment for the others so cluster-resource generation issues don't mask the main dry-run output.
  • The if o.ClusterResources == nil check (with the comment "e.g., in tests") is production code working around test setup. The existing executor_test.go has a MockClusterResources no-op implementation — updating the older TestDryRun sub-tests to inject that mock (like the new sub-test does with clusterresources.New) removes the need for the nil guard.
🤖 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 77 - 91, Adjust
generateClusterResourcesForDryRun so cluster-resource generator failures are
logged as warnings instead of returning errors: for IDMS_ITMSGenerator,
CatalogSourceGenerator, ClusterCatalogGenerator, and UpdateServiceGenerator
catch errors from their calls, call o.Log.Warnf with a descriptive message and
continue (mirroring the existing treatment of GenerateSignatureConfigMap), and
return nil at the end unless a non-cluster-resource fatal condition occurs; also
remove the if o.ClusterResources == nil guard and update tests to inject
MockClusterResources (e.g., in TestDryRun) so production code no longer
compensates for test setup.
internal/pkg/cli/dryrun_test.go (1)

199-203: Shared global is mutated across sub-tests.

global is declared once at the top of TestDryRun and each sub-test does global.WorkingDir = testFolder. That's fine while t.Run sub-tests execute serially, but it's a foot-gun if anyone later adds t.Parallel(). Consider constructing a fresh *mirror.GlobalOptions per sub-test.

🤖 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 199 - 203, The test mutates the
package-level variable global across sub-tests (global.WorkingDir = testFolder),
which is unsafe for parallel runs; instead, create a fresh *mirror.GlobalOptions
per sub-test (e.g., local := &mirror.GlobalOptions{WorkingDir: testFolder, ...})
inside the t.Run closure and use that local instance in calls that currently
rely on the package-global, or assign the package-global once from that local
before invoking the code under test. Update uses of global in TestDryRun
sub-tests to reference the new local variable (or set global = local at the
start of the subtest) so each sub-test has its own independent GlobalOptions.
🤖 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_test.go`:
- Around line 282-289: The test's assertions are too weak — they only check that
cluster-resources/ exists but not that the new manifests are created or correct;
update the test in dryrun_test.go to assert specific files exist under
clusterResourcesDir (e.g., idms-oc-mirror.yaml, itms-oc-mirror.yaml, the catalog
source and cluster catalog manifests, the signature ConfigMap, and the
UpdateService manifest for the OCPRelease image) and then read the UpdateService
manifest file to assert its graph image reference matches the value produced by
constructGraphImageRef (or at least contains the expected repository/name like
"graph-data" and not the hardcoded "localhost:5000"), so the test fails if the
OCP-release path or graph image generation regresses.

In `@internal/pkg/cli/dryrun.go`:
- Around line 142-169: Replace the custom, hardcoded logic in
constructGraphImageRef with a call to the real implementation: invoke
o.Release.GraphImage() and return its result (propagate any error), removing the
unused releaseImageParts split and the bogus localhost:5000 fallback and the
incorrect "openshift/graph-data:latest" name; this ensures the dry-run uses the
same registry and image name (openshift/graph-image) as the real flow and stops
relying on ImageSetConfigurationSpec.Mirror.AdditionalImages parsing.
- Around line 116-137: The test data includes release image Origins with the
"docker://" transport prefix which must be stripped before calling
UpdateServiceGenerator; in the loop that finds v2alpha1.TypeOCPRelease (using
allImages and releaseImage), call strings.TrimPrefix(img.Origin,
consts.DockerProtocol) (or trim releaseImage right after assignment) and pass
that trimmed value to o.ClusterResources.UpdateServiceGenerator(graphImageRef,
releaseImage) (ensure constructGraphImageRef still receives the correct input),
so UpdateServiceGenerator gets the unprefixed release image string matching
production ReleaseImage() behavior.

---

Nitpick comments:
In `@internal/pkg/cli/dryrun_test.go`:
- Around line 199-203: The test mutates the package-level variable global across
sub-tests (global.WorkingDir = testFolder), which is unsafe for parallel runs;
instead, create a fresh *mirror.GlobalOptions per sub-test (e.g., local :=
&mirror.GlobalOptions{WorkingDir: testFolder, ...}) inside the t.Run closure and
use that local instance in calls that currently rely on the package-global, or
assign the package-global once from that local before invoking the code under
test. Update uses of global in TestDryRun sub-tests to reference the new local
variable (or set global = local at the start of the subtest) so each sub-test
has its own independent GlobalOptions.

In `@internal/pkg/cli/dryrun.go`:
- Around line 77-91: Adjust generateClusterResourcesForDryRun so
cluster-resource generator failures are logged as warnings instead of returning
errors: for IDMS_ITMSGenerator, CatalogSourceGenerator, ClusterCatalogGenerator,
and UpdateServiceGenerator catch errors from their calls, call o.Log.Warnf with
a descriptive message and continue (mirroring the existing treatment of
GenerateSignatureConfigMap), and return nil at the end unless a
non-cluster-resource fatal condition occurs; also remove the if
o.ClusterResources == nil guard and update tests to inject MockClusterResources
(e.g., in TestDryRun) so production code no longer compensates for test setup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: e0215fdc-bd4e-4be5-9a4a-e764c5506b54

📥 Commits

Reviewing files that changed from the base of the PR and between ca5eebd and 73fa5c4.

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

Comment on lines +282 to +289
// Verify that the mapping file is still created (existing functionality)
mappingPath := filepath.Join(testFolder, dryRunOutDir, "mapping.txt")
assert.FileExists(t, mappingPath)

// Verify that cluster-resources directory is created
clusterResourcesPath := filepath.Join(testFolder, clusterResourcesDir)
assert.DirExists(t, clusterResourcesPath)
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Assertions are too weak to cover the actual feature.

The whole point of this PR is that cluster-resources/ now contains IDMS/ITMS, catalog source, cluster catalog, signature configmap, and UpdateService manifests. Asserting only that the directory exists doesn't verify anything new — an empty directory would pass, as would a run that silently dropped the OCP-release path. At minimum, check that the expected files are created (e.g., idms-oc-mirror.yaml, itms-oc-mirror.yaml, and the UpdateService manifest for the OCPRelease image) and, ideally, spot-check that the UpdateService references a sensible graph image. The latter would have surfaced the hardcoded localhost:5000 / openshift/graph-data:latest problem in constructGraphImageRef.

🧪 Suggested stronger assertions
 		// Verify that cluster-resources directory is created
 		clusterResourcesPath := filepath.Join(testFolder, clusterResourcesDir)
 		assert.DirExists(t, clusterResourcesPath)
+
+		// Verify that the expected manifests were actually generated
+		entries, err := os.ReadDir(clusterResourcesPath)
+		if err != nil {
+			t.Fatalf("failed to read cluster-resources dir: %v", err)
+		}
+		assert.NotEmpty(t, entries, "cluster-resources directory should contain generated manifests")
+
+		// Spot-check that UpdateService manifest references the intended graph image,
+		// not a hardcoded localhost:5000 placeholder.
+		// ... read update-service YAML and assert the image reference matches the destination registry
🤖 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 282 - 289, The test's
assertions are too weak — they only check that cluster-resources/ exists but not
that the new manifests are created or correct; update the test in dryrun_test.go
to assert specific files exist under clusterResourcesDir (e.g.,
idms-oc-mirror.yaml, itms-oc-mirror.yaml, the catalog source and cluster catalog
manifests, the signature ConfigMap, and the UpdateService manifest for the
OCPRelease image) and then read the UpdateService manifest file to assert its
graph image reference matches the value produced by constructGraphImageRef (or
at least contains the expected repository/name like "graph-data" and not the
hardcoded "localhost:5000"), so the test fails if the OCP-release path or graph
image generation regresses.

Comment on lines +116 to +137
// Generate UpdateService if we have release images
hasReleaseImages := false
var releaseImage string
for _, img := range allImages {
if img.Type == v2alpha1.TypeOCPRelease {
hasReleaseImages = true
releaseImage = img.Origin
break
}
}

if hasReleaseImages {
// We need to generate a graph image reference for the update service
// In dry-run mode, we'll use a placeholder or construct the expected reference
graphImageRef, err := o.constructGraphImageRef(releaseImage)
if err != nil {
return err
}
if err := o.ClusterResources.UpdateServiceGenerator(graphImageRef, releaseImage); err != nil {
return err
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm how the non-dry-run path calls UpdateServiceGenerator — particularly
# the form of the releaseImage argument (with or without docker:// prefix).
rg -nP --type=go -C8 'UpdateServiceGenerator\s*\('
# Check whether the production release flow iterates over multiple releases.
rg -nP --type=go -C5 'TypeOCPRelease' -g '!**/dryrun*.go'

Repository: openshift/oc-mirror

Length of output: 50375


🏁 Script executed:

# Find the dryrun test file and see what test data is used
fd -i dryrun -type f | grep test

# Also check what the test data setup looks like in dryrun context
rg -nP --type=go 'Origin.*docker://' -g '**dryrun*' -A2 -B2

# Check how allImages is populated in dryrun context
rg -nP --type=go -B10 'hasReleaseImages.*:=' | grep -A10 'dryrun'

# Check if there's a TrimPrefix or similar for docker:// in the codebase
rg -nP --type=go 'TrimPrefix.*docker|DockerProtocol' | head -20

Repository: openshift/oc-mirror

Length of output: 4681


🏁 Script executed:

# Find dryrun test file
git ls-files | grep -i dryrun

# Check for TrimPrefix usage with docker protocol to understand the pattern
rg -nP --type=go 'strings.TrimPrefix.*docker|TrimPrefix.*DockerProtocol' -B2 -A2

# Look for how allImages is built in dryrun context
rg -nP --type=go -B20 'hasReleaseImages := false' internal/pkg/cli/dryrun.go

Repository: openshift/oc-mirror

Length of output: 3609


🏁 Script executed:

# Read the dryrun_test.go file to see test data
cat -n internal/pkg/cli/dryrun_test.go | head -200

# Also check how allImages is constructed in the dryrun.go function
rg -nP --type=go -A50 'func.*DryRun' internal/pkg/cli/dryrun.go | head -80

Repository: openshift/oc-mirror

Length of output: 11633


🏁 Script executed:

# Look for tests that include TypeOCPRelease in dryrun_test.go context
rg -nP --type=go 'TypeOCPRelease' internal/pkg/cli/dryrun_test.go

# Check the CopyImageSchema struct to understand Origin field
rg -nP --type=go -A5 'type CopyImageSchema struct' 

# Search for test data with release images in dryrun test
rg -nP --type=go -B5 -A5 'generateClusterResourcesForDryRun' internal/pkg/cli/dryrun_test.go

Repository: openshift/oc-mirror

Length of output: 594


🏁 Script executed:

# Get the full context around line 273 in dryrun_test.go
sed -n '250,300p' internal/pkg/cli/dryrun_test.go

# Get the full CopyImageSchema struct definition
rg -nP --type=go -A15 'type CopyImageSchema struct'

# Check if there's a test for release images in dryrun
rg -nP --type=go -B20 -A20 'TypeOCPRelease' internal/pkg/cli/dryrun_test.go

Repository: openshift/oc-mirror

Length of output: 4947


🏁 Script executed:

# Find ReleaseImage() method implementation
rg -nP --type=go 'func.*ReleaseImage\(' -A10

# Check what ReleaseImage returns - look at the Release interface/struct
rg -nP --type=go -B5 'ReleaseImage.*func' | head -40

# Check the clusterresources UpdateServiceGenerator implementation to see what it expects
sed -n '608,650p' internal/pkg/clusterresources/clusterresources.go

Repository: openshift/oc-mirror

Length of output: 7847


🏁 Script executed:

# Check the ReleaseImage implementation more carefully - what does it return?
sed -n '451,475p' internal/pkg/release/local_stored_collector.go

# Check if there are scenarios with multiple releases - look for loops over Releases
rg -nP --type=go -B5 -A5 'for.*Releases\[' internal/pkg/release/

# Check how production code handles multiple releases in executor.go around UpdateServiceGenerator calls
sed -n '960,985p' internal/pkg/cli/executor.go

# Look at the production release flow to see if it generates one UpdateService per release
rg -nP --type=go 'UpdateServiceGenerator' -B15 -A5 internal/pkg/cli/executor.go | head -60

Repository: openshift/oc-mirror

Length of output: 2784


🏁 Script executed:

# Check ReleaseImage() full implementation to understand what it returns
sed -n '451,480p' internal/pkg/release/local_stored_collector.go

# Check if there are multiple TypeOCPRelease entries in typical scenarios
rg -nP --type=go 'o.Releases\[' -B3 -A3 internal/pkg/release/local_stored_collector.go | head -40

# Look at the actual return value from ReleaseImage
rg -nP --type=go -A20 'func.*ReleaseImage.*Context' internal/pkg/release/local_stored_collector.go

Repository: openshift/oc-mirror

Length of output: 4021


🏁 Script executed:

# Get the complete ReleaseImage function - need to see the return statement
sed -n '451,500p' internal/pkg/release/local_stored_collector.go

# Check what identifyReleases returns - specifically the .Image field
rg -nP --type=go -B5 'func.*identifyReleases' -A30 internal/pkg/release/local_stored_collector.go | head -50

Repository: openshift/oc-mirror

Length of output: 3333


🏁 Script executed:

# Check if GetReleaseReferenceImages can return multiple releases
rg -nP --type=go -B5 'GetReleaseReferenceImages' | head -50

# Check the Cincinnati interface to understand how multiple releases are handled
rg -nP --type=go 'type Cincinnati interface' -A20

# Verify UpdateServiceGenerator is called only once in production paths or once per release
rg -nP --type=go -B30 'UpdateServiceGenerator' internal/pkg/cli/executor.go | grep -A30 'for.*release\|for.*Release'

Repository: openshift/oc-mirror

Length of output: 3806


🏁 Script executed:

# Check if there's a loop over releases in production code for UpdateServiceGenerator
rg -nP --type=go -B50 'UpdateServiceGenerator' internal/pkg/cli/executor.go | grep -B50 'for.*release\|for.*Releases\|for _'

# Check if GetReleaseReferenceImages is called multiple times or if it returns multiple but only first is used
rg -nP --type=go 'GetReleaseReferenceImages' -A10 internal/pkg/release/cincinnati.go | head -80

Repository: openshift/oc-mirror

Length of output: 1717


🏁 Script executed:

# Check if there's any documentation or comments about multiple releases handling
rg -nP --type=go 'UpdateService.*release\|release.*UpdateService' -B2 -A2 | head -40

# Look at OpenShift docs link in the code to understand UpdateService requirements
rg -nP --type=go 'update-service-create' -B5 -A5

# Check if there are test scenarios with multiple release images
rg -nP --type=go 'TypeOCPRelease.*TypeOCPRelease' internal/pkg/cli/

Repository: openshift/oc-mirror

Length of output: 1239


Strip docker:// prefix before passing releaseImage to UpdateServiceGenerator.

The Origin field from the test data (line 272 of dryrun_test.go) contains the docker:// transport prefix—for example, docker://quay.io/openshift-release-dev/ocp-release:4.14.0-x86_64—and is passed directly to UpdateServiceGenerator(graphImageRef, releaseImage) without removing it. The production code calls o.Release.ReleaseImage(), which returns the unprefixed destination reference. Use strings.TrimPrefix(img.Origin, consts.DockerProtocol) before passing the value to UpdateServiceGenerator.

Regarding the loop breaking on the first release image: this matches production behavior, where ReleaseImage() returns only o.Releases[0], so a single UpdateService per execution is the intended design.

🤖 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 116 - 137, The test data includes
release image Origins with the "docker://" transport prefix which must be
stripped before calling UpdateServiceGenerator; in the loop that finds
v2alpha1.TypeOCPRelease (using allImages and releaseImage), call
strings.TrimPrefix(img.Origin, consts.DockerProtocol) (or trim releaseImage
right after assignment) and pass that trimmed value to
o.ClusterResources.UpdateServiceGenerator(graphImageRef, releaseImage) (ensure
constructGraphImageRef still receives the correct input), so
UpdateServiceGenerator gets the unprefixed release image string matching
production ReleaseImage() behavior.

Comment on lines +142 to +169
// constructGraphImageRef constructs the graph image reference that would be used for UpdateService
func (o *ExecutorSchema) constructGraphImageRef(releaseImageRef string) (string, error) {
// In the normal workflow, the graph image is generated by Release.GraphImage()
// For dry-run, we need to construct what the reference would be
releaseImageParts := strings.Split(releaseImageRef, "/")
if len(releaseImageParts) < 3 {
return "", fmt.Errorf("invalid release image reference: %s", releaseImageRef)
}

// Extract destination registry information from the first image
var destinationRegistry string
for _, img := range o.Config.ImageSetConfigurationSpec.Mirror.AdditionalImages {
if strings.Contains(img.Name, "/") {
// This is a placeholder - in actual implementation, we'd use the destination registry
// For now, we'll use localhost as placeholder for dry-run
destinationRegistry = "localhost:5000"
break
}
}

if destinationRegistry == "" {
destinationRegistry = "localhost:5000" // Default for dry-run
}

// Construct the graph image reference
graphImageRef := fmt.Sprintf("%s/openshift/graph-data:latest", destinationRegistry)
return graphImageRef, nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate the real graph image reference construction in the codebase so we can compare
# against what constructGraphImageRef invents.
rg -nP --type=go -C3 '\bGraphImage\s*\('
rg -nP --type=go -C3 'graph-data|graph-image'
# Confirm Origin convention: does it carry the docker:// prefix in TypeOCPRelease entries?
ast-grep --pattern $'CopyImageSchema{
  $$$
  Type: v2alpha1.TypeOCPRelease,
  $$$
}'
# Find where UpdateServiceGenerator is invoked in the non-dry-run path to see
# exactly what form of releaseImage/graphImage it expects.
rg -nP --type=go -C5 '\bUpdateServiceGenerator\s*\('

Repository: openshift/oc-mirror

Length of output: 30443


constructGraphImageRef hardcodes a bogus destination registry and reinvents the graph image reference instead of reusing the real implementation.

A few compounding problems make this function fundamentally incorrect:

  1. Hardcoded localhost:5000. The registry is always set to localhost:5000, regardless of whether the user is running M2D, M2M, or D2M. The loop over o.Config.ImageSetConfigurationSpec.Mirror.AdditionalImages never reads any value from the images — it only uses the presence of a / as a trigger to assign the literal "localhost:5000". The resulting UpdateService manifest will reference a registry that has nothing to do with the user's actual destination, producing completely incorrect dry-run artifacts.

  2. Wrong graph image name. The code constructs openshift/graph-data:latest, but the actual graph image name is openshift/graph-image (see internal/pkg/release/const.go:10). This mismatch means the dry-run output won't match what the real mirror run produces.

  3. releaseImageParts is computed but never used. The split happens at line 146 for validation, but the array is discarded immediately after the length check (line 147).

  4. Should reuse the real implementation. The non-dry-run flow (executor.go lines 970, 1051) directly calls o.Release.GraphImage() to get the correct reference based on LocalStorageFQDN and the actual release collector state. Dry-run should do the same—invoke o.Release.GraphImage() so the output matches what the actual mirror run will produce, rather than reimplementing and guessing.

🤖 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 142 - 169, Replace the custom,
hardcoded logic in constructGraphImageRef with a call to the real
implementation: invoke o.Release.GraphImage() and return its result (propagate
any error), removing the unused releaseImageParts split and the bogus
localhost:5000 fallback and the incorrect "openshift/graph-data:latest" name;
this ensures the dry-run uses the same registry and image name
(openshift/graph-image) as the real flow and stops relying on
ImageSetConfigurationSpec.Mirror.AdditionalImages parsing.

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented Apr 24, 2026

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

Test name Commit Details Required Rerun command
ci/prow/lint 73fa5c4 link true /test lint

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.

@aguidirh
Copy link
Copy Markdown
Contributor

aguidirh commented May 8, 2026

@javipolo could you please have a look on the codeRabbit suggestions and also the linter job that is failing?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants