Skip to content

Allow testing step registry changes via payload-job-with-prs#4965

Open
jianzhangbjz wants to merge 1 commit intoopenshift:mainfrom
jianzhangbjz:step
Open

Allow testing step registry changes via payload-job-with-prs#4965
jianzhangbjz wants to merge 1 commit intoopenshift:mainfrom
jianzhangbjz:step

Conversation

@jianzhangbjz
Copy link
Member

@jianzhangbjz jianzhangbjz commented Feb 25, 2026

Summary

When openshift/release is included in the PRs being tested via /payload-job-with-prs,
configure ci-operator to use the step registry from the local checkout instead of the
remote config resolver.

Problem

When using /payload-job-with-prs with a PR from openshift/release that modifies the
step registry, the changes were not applied because ci-operator was still loading the
step registry from config.ci.openshift.org.

Example error:
ERRO Failed to load arguments. error=[tests[...].steps.pre[3].from: unknown image "root"
(configuration is missing build_root)]

Solution

  • Add Registry() PodSpecMutator function in pkg/prowgen/podspec.go that adds the
    --registry flag to ci-operator
  • In prpqr_reconciler.go, detect when openshift/release is among the PRs and add
    the --registry flag pointing to the local checkout path
    (/go/src/github.com/openshift/release/ci-operator/step-registry)

Summary by CodeRabbit

  • New Features

    • Conditional use of a local step registry when certain pull requests are under test, improving job configuration and step resolution for CI runs.
  • Tests

    • Added tests covering various pull request compositions to validate the local step registry behavior and registry argument handling in generated pod specs.

@openshift-ci-robot
Copy link
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: automatic mode

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 25, 2026

@jianzhangbjz: This PR was included in a payload test run from openshift/operator-framework-olm#1236
trigger 1 job(s) for the /payload-(with-prs|job|aggregate|job-with-prs|aggregate-with-prs) command

  • periodic-ci-openshift-microshift-release-4.20-periodics-e2e-aws-ovn-ocp-conformance

See details on https://pr-payload-tests.ci.openshift.org/runs/ci/3bdae7b0-1233-11f1-8b95-7266b558161e-0

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Walkthrough

Adds detection for when openshift/release is among PRs under test and, when present, injects a local step registry path into generated ProwJob pod specs via a new PodSpec mutator. Includes unit tests for detection and mutator behavior.

Changes

Cohort / File(s) Summary
Release Repository Detection
pkg/controller/prpqr_reconciler/prpqr_reconciler.go, pkg/controller/prpqr_reconciler/prpqr_reconciler_test.go
Adds hasReleaseRepoPR() to detect openshift/release in PRs and conditions ProwJob generation to use the local release step registry path; tests cover multiple PR scenarios.
Registry Path Mutator
pkg/prowgen/podspec.go, pkg/prowgen/podspec_test.go
Adds exported Registry(registryPath string) PodSpecMutator that appends --registry=<path> to the first container when non-empty; tests verify presence/absence of the arg for populated and empty paths.

Sequence Diagram

sequenceDiagram
    participant Generator as ProwJob Generator
    participant Analyzer as PR Analyzer
    participant Mutator as Registry Mutator
    participant PodSpec as Pod Spec

    Generator->>Analyzer: hasReleaseRepoPR(prs)
    Analyzer-->>Generator: true/false

    alt release repo present
        Generator->>Mutator: Registry(releaseStepRegistryPath)
        Mutator->>PodSpec: append --registry=<path> to first container
        PodSpec-->>Mutator: mutated PodSpec
        Mutator-->>Generator: mutated PodSpec
    else release repo absent
        Generator->>Mutator: Registry("") (no-op)
        Mutator-->>Generator: original PodSpec
    end

    Generator-->>Generator: configure ProwJob with resulting PodSpec
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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: enabling testing of step registry changes using the payload-job-with-prs feature, which matches the core functionality added across all modified files.
Stable And Deterministic Test Names ✅ Passed Both test functions use stable, descriptive test names defined as static string literals with no dynamic information, UUIDs, timestamps, or generated suffixes.
Test Structure And Quality ✅ Passed Both new test functions follow quality requirements appropriate for their scope. TestHasReleaseRepoPR and TestRegistry each test a single, specific behavior using table-driven patterns consistent with the codebase.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

Copy link

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

🧹 Nitpick comments (3)
pkg/prowgen/podspec_test.go (1)

532-574: Consider using testhelper.CompareWithFixture for consistency with the rest of the file.

Every other mutator test in this file (TestVariant via TestNewCiOperatorPodSpecGenerator, TestGitHubToken, TestReleaseLatest, TestReleaseInitial, TestTargets, etc.) delegates assertion to testhelper.CompareWithFixture. TestRegistry is the only exception, relying on ad-hoc arg scanning. Using the fixture approach would also provide a golden-file record of the full expected PodSpec rather than just the presence of one arg.

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

In `@pkg/prowgen/podspec_test.go` around lines 532 - 574, TestRegistry uses ad-hoc
scanning for a single arg instead of the project's standard golden-file
assertion; change the test to generate the podspec using
NewCiOperatorPodSpecGenerator(), call g.Add(Registry(...)) and g.Build() as
before, then replace the manual arg loop with testhelper.CompareWithFixture(t,
"TestRegistry", podspec) (or an appropriate fixture name) so the full PodSpec is
compared against the fixture; ensure the fixture file is added/updated to
reflect both test cases (non-empty and empty registry) or split into two
fixtures if needed.
pkg/controller/prpqr_reconciler/prpqr_reconciler_test.go (1)

626-633: Subtests are missing t.Parallel().

TestReconcile's subtests call t.Parallel(), but these don't. Minor inconsistency.

♻️ Proposed fix
 	for _, tc := range tests {
+		tc := tc
 		t.Run(tc.name, func(t *testing.T) {
+			t.Parallel()
 			got := hasReleaseRepoPR(tc.prs)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/controller/prpqr_reconciler/prpqr_reconciler_test.go` around lines 626 -
633, Subtests iterating over tests and calling t.Run should be marked parallel
and must capture the loop variable to avoid races: inside the t.Run closure for
the table-driven loop that calls hasReleaseRepoPR, add t.Parallel() as the first
statement and also add a local capture like tc := tc (or copy the loop variable)
before using it in the closure so the test data for each subtest remains
isolated.
pkg/controller/prpqr_reconciler/prpqr_reconciler.go (1)

644-649: Extract the hardcoded registry path to a named constant.

The path /go/src/github.com/openshift/release/ci-operator/step-registry is a magic string with no symbolic name. Extracting it makes future updates and codebase searches easier.

♻️ Proposed refactor

At the top of the file alongside the existing const block:

const releaseStepRegistryPath = "/go/src/github.com/openshift/release/ci-operator/step-registry"

Then at lines 647-649:

-		if hasReleaseRepoPR(prs) {
-			jobBaseGen.PodSpec.Add(prowgen.Registry("/go/src/github.com/openshift/release/ci-operator/step-registry"))
-		}
+		if hasReleaseRepoPR(prs) {
+			jobBaseGen.PodSpec.Add(prowgen.Registry(releaseStepRegistryPath))
+		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/controller/prpqr_reconciler/prpqr_reconciler.go` around lines 644 - 649,
Extract the hardcoded path string used in the Registry call into a named
constant and use that constant instead of the literal; add a constant like
releaseStepRegistryPath =
"/go/src/github.com/openshift/release/ci-operator/step-registry" in the existing
const block at the top of the file, then replace the literal in the
hasReleaseRepoPR handling (the call
jobBaseGen.PodSpec.Add(prowgen.Registry(...))) with
prowgen.Registry(releaseStepRegistryPath).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/prowgen/podspec_test.go`:
- Around line 560-571: Add an explicit negative assertion for the empty-path
case so the test fails if a flag like "--registry=" is emitted; in the
podspec_test loop where tc.wantArg is checked, when tc.wantArg == "" iterate
podspec.Containers[0].Args and assert that no arg has the prefix "--registry"
(or equals "--registry=") — if any such arg is found call t.Errorf; keep the
existing positive check for non-empty tc.wantArg unchanged and reuse the podspec
and tc.wantArg identifiers.

---

Nitpick comments:
In `@pkg/controller/prpqr_reconciler/prpqr_reconciler_test.go`:
- Around line 626-633: Subtests iterating over tests and calling t.Run should be
marked parallel and must capture the loop variable to avoid races: inside the
t.Run closure for the table-driven loop that calls hasReleaseRepoPR, add
t.Parallel() as the first statement and also add a local capture like tc := tc
(or copy the loop variable) before using it in the closure so the test data for
each subtest remains isolated.

In `@pkg/controller/prpqr_reconciler/prpqr_reconciler.go`:
- Around line 644-649: Extract the hardcoded path string used in the Registry
call into a named constant and use that constant instead of the literal; add a
constant like releaseStepRegistryPath =
"/go/src/github.com/openshift/release/ci-operator/step-registry" in the existing
const block at the top of the file, then replace the literal in the
hasReleaseRepoPR handling (the call
jobBaseGen.PodSpec.Add(prowgen.Registry(...))) with
prowgen.Registry(releaseStepRegistryPath).

In `@pkg/prowgen/podspec_test.go`:
- Around line 532-574: TestRegistry uses ad-hoc scanning for a single arg
instead of the project's standard golden-file assertion; change the test to
generate the podspec using NewCiOperatorPodSpecGenerator(), call
g.Add(Registry(...)) and g.Build() as before, then replace the manual arg loop
with testhelper.CompareWithFixture(t, "TestRegistry", podspec) (or an
appropriate fixture name) so the full PodSpec is compared against the fixture;
ensure the fixture file is added/updated to reflect both test cases (non-empty
and empty registry) or split into two fixtures if needed.

ℹ️ 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 9908494 and ec042d0.

📒 Files selected for processing (4)
  • pkg/controller/prpqr_reconciler/prpqr_reconciler.go
  • pkg/controller/prpqr_reconciler/prpqr_reconciler_test.go
  • pkg/prowgen/podspec.go
  • pkg/prowgen/podspec_test.go

@bear-redhat
Copy link
Contributor

/approve

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 25, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: bear-redhat, jianzhangbjz

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

The pull request process is described here

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

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

@openshift-ci openshift-ci bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Feb 25, 2026
@droslean
Copy link
Member

@jianzhangbjz This looks like an attack vector since there can be an unmerged PR in the release repo that mutates the step registry. Can you elaborate more on why merging the step registry changes first in the release repo is a problem?

/hold

@openshift-ci openshift-ci bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Feb 25, 2026
@jianzhangbjz
Copy link
Member Author

Hi @droslean, good point about the security concern!

The use case is testing step registry changes (like from: root -> from_image fixes) before merging. Without this change, /payload-job-with-prs openshift/release#XXXXX cannot validate step registry modifications.
I can add a mitigation - for example:

  1. Only apply local registry when the release repo PR has approved label
  2. Or add a check that the author is a trusted member

Which approach would you prefer? Or any suggestion? Thanks!

Copy link

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

🧹 Nitpick comments (2)
pkg/prowgen/podspec_test.go (1)

533-583: Add idempotency coverage for repeated Registry() mutations.

This test covers add/no-op, but not repeated mutation behavior. Please add a case that applies Registry(path) multiple times and asserts only one --registry= arg remains.

Proposed test extension
 func TestRegistry(t *testing.T) {
 	t.Parallel()
 	tests := []struct {
 		name         string
 		registryPath string
 		wantArg      string
+		repeat       int
 	}{
 		{
 			name:         "registry path is added",
 			registryPath: "/go/src/github.com/openshift/release/ci-operator/step-registry",
 			wantArg:      "--registry=/go/src/github.com/openshift/release/ci-operator/step-registry",
+			repeat:       1,
+		},
+		{
+			name:         "registry mutator is idempotent",
+			registryPath: "/go/src/github.com/openshift/release/ci-operator/step-registry",
+			wantArg:      "--registry=/go/src/github.com/openshift/release/ci-operator/step-registry",
+			repeat:       2,
 		},
 		{
 			name:         "empty registry path is a nop",
 			registryPath: "",
 			wantArg:      "",
+			repeat:       1,
 		},
 	}
 	for _, tc := range tests {
 		tc := tc
 		t.Run(tc.name, func(t *testing.T) {
 			t.Parallel()
 			g := NewCiOperatorPodSpecGenerator()
-			g.Add(Registry(tc.registryPath))
+			for i := 0; i < tc.repeat; i++ {
+				g.Add(Registry(tc.registryPath))
+			}
 			podspec, err := g.Build()
 			if err != nil {
 				t.Fatalf("Unexpected error: %v", err)
 			}
+			registryArgCount := 0
+			for _, arg := range podspec.Containers[0].Args {
+				if strings.HasPrefix(arg, "--registry=") {
+					registryArgCount++
+				}
+			}
+			if registryArgCount > 1 {
+				t.Errorf("Expected at most one --registry arg, got %d: %v", registryArgCount, podspec.Containers[0].Args)
+			}
 			if tc.wantArg != "" {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/prowgen/podspec_test.go` around lines 533 - 583, Extend TestRegistry to
include a case that applies Registry(path) more than once and asserts only one
--registry= argument is present: create a subtest (within TestRegistry using
NewCiOperatorPodSpecGenerator, g.Add(Registry(path)) called multiple times) call
g.Build(), then iterate podspec.Containers[0].Args and count entries with
strings.HasPrefix(arg, "--registry=") (or exact match for the expected
"--registry="+path) and fail if count != 1; do this for a non-empty path to
verify idempotency of Registry and optionally for an empty path to verify
repeated no-op behavior. Ensure you reference the existing symbols
NewCiOperatorPodSpecGenerator, Registry, g.Add, Build and
podspec.Containers[0].Args when locating where to add the new assertion.
pkg/prowgen/podspec.go (1)

757-765: Handle conflicting --registry= args deterministically.

addUniqueParameter only avoids exact duplicates, so multiple Registry() mutators with different paths can emit multiple --registry= flags. Prefer replacing an existing registry flag instead of appending another one.

Proposed refactor
 func Registry(registryPath string) PodSpecMutator {
 	return func(spec *corev1.PodSpec) error {
 		if registryPath == "" {
 			return nil
 		}
 		container := &spec.Containers[0]
-		addUniqueParameter(container, fmt.Sprintf("--registry=%s", registryPath))
+		wantArg := fmt.Sprintf("--registry=%s", registryPath)
+		for i := range container.Args {
+			if strings.HasPrefix(container.Args[i], "--registry=") {
+				container.Args[i] = wantArg
+				return nil
+			}
+		}
+		addUniqueParameter(container, wantArg)
 		return nil
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/prowgen/podspec.go` around lines 757 - 765, The Registry mutator
currently calls addUniqueParameter which only prevents exact duplicates, so
multiple Registry(...) calls with different registryPath values can leave
multiple --registry= flags; update Registry (the func Registry(registryPath
string) PodSpecMutator) to scan the target container (container :=
&spec.Containers[0]) for any existing arg that starts with "--registry=" and
replace that entry with fmt.Sprintf("--registry=%s", registryPath), otherwise
append the flag—do this instead of calling addUniqueParameter so subsequent
Registry mutators deterministically override prior values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/prowgen/podspec_test.go`:
- Around line 533-583: Extend TestRegistry to include a case that applies
Registry(path) more than once and asserts only one --registry= argument is
present: create a subtest (within TestRegistry using
NewCiOperatorPodSpecGenerator, g.Add(Registry(path)) called multiple times) call
g.Build(), then iterate podspec.Containers[0].Args and count entries with
strings.HasPrefix(arg, "--registry=") (or exact match for the expected
"--registry="+path) and fail if count != 1; do this for a non-empty path to
verify idempotency of Registry and optionally for an empty path to verify
repeated no-op behavior. Ensure you reference the existing symbols
NewCiOperatorPodSpecGenerator, Registry, g.Add, Build and
podspec.Containers[0].Args when locating where to add the new assertion.

In `@pkg/prowgen/podspec.go`:
- Around line 757-765: The Registry mutator currently calls addUniqueParameter
which only prevents exact duplicates, so multiple Registry(...) calls with
different registryPath values can leave multiple --registry= flags; update
Registry (the func Registry(registryPath string) PodSpecMutator) to scan the
target container (container := &spec.Containers[0]) for any existing arg that
starts with "--registry=" and replace that entry with
fmt.Sprintf("--registry=%s", registryPath), otherwise append the flag—do this
instead of calling addUniqueParameter so subsequent Registry mutators
deterministically override prior values.

ℹ️ 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 ec042d0 and bcb18fb.

📒 Files selected for processing (4)
  • pkg/controller/prpqr_reconciler/prpqr_reconciler.go
  • pkg/controller/prpqr_reconciler/prpqr_reconciler_test.go
  • pkg/prowgen/podspec.go
  • pkg/prowgen/podspec_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/controller/prpqr_reconciler/prpqr_reconciler_test.go
  • pkg/controller/prpqr_reconciler/prpqr_reconciler.go

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 26, 2026

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

Test name Commit Details Required Rerun command
ci/prow/images bcb18fb link true /test images
ci/prow/breaking-changes bcb18fb link false /test breaking-changes

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@jianzhangbjz
Copy link
Member Author

CI Failures are infrastructure issues, not code problems:

@droslean
Copy link
Member

Hi @droslean, good point about the security concern!

The use case is testing step registry changes (like from: root -> from_image fixes) before merging. Without this change, /payload-job-with-prs openshift/release#XXXXX cannot validate step registry modifications. I can add a mitigation - for example:

  1. Only apply local registry when the release repo PR has approved label
  2. Or add a check that the author is a trusted member

Which approach would you prefer? Or any suggestion? Thanks!

Testing the step registry changes is already being done by the pj-rehearse tool. Why do you need to combine the testing PR with other PRs using the payload job plugin?

@jianzhangbjz
Copy link
Member Author

For example, I want to test openshift/operator-framework-olm#1236 (unmerged) together with openshift/release#75271 (step registry fix). How can I use pj-rehearse to test these two unmerged PRs together? Thanks!

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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants