diff --git a/pkg/controller/prpqr_reconciler/prpqr_reconciler.go b/pkg/controller/prpqr_reconciler/prpqr_reconciler.go index 89e1788b75..3b28f935ad 100644 --- a/pkg/controller/prpqr_reconciler/prpqr_reconciler.go +++ b/pkg/controller/prpqr_reconciler/prpqr_reconciler.go @@ -51,6 +51,10 @@ const ( conditionWithErrors = "WithErrors" dependentProwJobsFinalizer = "pullrequestpayloadqualificationruns.ci.openshift.io/dependent-prowjobs" + + // releaseStepRegistryPath is the path where the openshift/release repo's step registry + // is cloned when it is included in the PRs under test. + releaseStepRegistryPath = "/go/src/github.com/openshift/release/ci-operator/step-registry" ) type injectingResolverClient interface { @@ -641,6 +645,13 @@ func (r *reconciler) generateProwjob(ciopConfig *api.ReleaseBuildConfiguration, jobBaseGen.PodSpec.Add(prowgen.ShardArgs(shardCount, shardIndex)) } + // If openshift/release is among the PRs being tested, configure ci-operator + // to use the step registry from the local checkout instead of the remote + // config resolver. This allows testing step registry changes via PRPQR. + if hasReleaseRepoPR(prs) { + jobBaseGen.PodSpec.Add(prowgen.Registry(releaseStepRegistryPath)) + } + // Avoid sharing when we run the same job multiple times. // PRPQR name should be safe to use as a discriminating input, because // there should never be more than one execution of a specific job per @@ -917,3 +928,14 @@ func hasDependantProwJobsFinalizer(objMeta *metav1.ObjectMeta) bool { } return false } + +// hasReleaseRepoPR checks if openshift/release is among the PRs being tested. +// When it is, we need to use the step registry from the local checkout. +func hasReleaseRepoPR(prs []v1.PullRequestUnderTest) bool { + for _, pr := range prs { + if pr.Org == "openshift" && pr.Repo == "release" { + return true + } + } + return false +} diff --git a/pkg/controller/prpqr_reconciler/prpqr_reconciler_test.go b/pkg/controller/prpqr_reconciler/prpqr_reconciler_test.go index 283f8db6c7..75018d2a23 100644 --- a/pkg/controller/prpqr_reconciler/prpqr_reconciler_test.go +++ b/pkg/controller/prpqr_reconciler/prpqr_reconciler_test.go @@ -580,3 +580,58 @@ func (f *fakeDispatcherClient) ClusterForJob(jobName string) (string, error) { return "build02", nil } } + +func TestHasReleaseRepoPR(t *testing.T) { + t.Parallel() + tests := []struct { + name string + prs []v1.PullRequestUnderTest + want bool + }{ + { + name: "no PRs", + prs: nil, + want: false, + }, + { + name: "single PR from different repo", + prs: []v1.PullRequestUnderTest{ + {Org: "openshift", Repo: "origin"}, + }, + want: false, + }, + { + name: "single PR from openshift/release", + prs: []v1.PullRequestUnderTest{ + {Org: "openshift", Repo: "release"}, + }, + want: true, + }, + { + name: "multiple PRs including openshift/release", + prs: []v1.PullRequestUnderTest{ + {Org: "openshift", Repo: "origin"}, + {Org: "openshift", Repo: "release"}, + }, + want: true, + }, + { + name: "multiple PRs not including openshift/release", + prs: []v1.PullRequestUnderTest{ + {Org: "openshift", Repo: "origin"}, + {Org: "openshift", Repo: "installer"}, + }, + want: false, + }, + } + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + got := hasReleaseRepoPR(tc.prs) + if got != tc.want { + t.Errorf("hasReleaseRepoPR() = %v, want %v", got, tc.want) + } + }) + } +} diff --git a/pkg/prowgen/podspec.go b/pkg/prowgen/podspec.go index 63472ccb18..91580c4956 100644 --- a/pkg/prowgen/podspec.go +++ b/pkg/prowgen/podspec.go @@ -750,3 +750,17 @@ func newFakePodSpecBuilder() CiOperatorPodSpecGenerator { f := fakePodSpecBuilder(0) return &f } + +// Registry configures ci-operator to use a local step registry path instead of +// the remote config resolver. This is useful when testing changes to the step +// registry from a PR in openshift/release. +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)) + return nil + } +} diff --git a/pkg/prowgen/podspec_test.go b/pkg/prowgen/podspec_test.go index a6e4e7fb1d..f5cc59b0c2 100644 --- a/pkg/prowgen/podspec_test.go +++ b/pkg/prowgen/podspec_test.go @@ -2,6 +2,7 @@ package prowgen import ( "errors" + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -528,3 +529,55 @@ func TestGSMConfig(t *testing.T) { testhelper.CompareWithFixture(t, podspec) }) } + +func TestRegistry(t *testing.T) { + t.Parallel() + tests := []struct { + name string + registryPath string + wantArg string + }{ + { + 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", + }, + { + name: "empty registry path is a nop", + registryPath: "", + wantArg: "", + }, + } + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + g := NewCiOperatorPodSpecGenerator() + g.Add(Registry(tc.registryPath)) + podspec, err := g.Build() + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + if tc.wantArg != "" { + // Positive case: verify the expected arg is present + found := false + for _, arg := range podspec.Containers[0].Args { + if arg == tc.wantArg { + found = true + break + } + } + if !found { + t.Errorf("Expected arg %q not found in args: %v", tc.wantArg, podspec.Containers[0].Args) + } + } else { + // Negative case: verify no --registry arg is present + for _, arg := range podspec.Containers[0].Args { + if strings.HasPrefix(arg, "--registry") { + t.Errorf("Unexpected --registry arg found: %q", arg) + } + } + } + }) + } +}