Skip to content

OCPCLOUD-3319: Create installer controller - #494

Merged
openshift-merge-bot[bot] merged 9 commits into
openshift:mainfrom
openshift-cloud-team:installer-controller
Mar 27, 2026
Merged

OCPCLOUD-3319: Create installer controller#494
openshift-merge-bot[bot] merged 9 commits into
openshift:mainfrom
openshift-cloud-team:installer-controller

Conversation

@mdbooth

@mdbooth mdbooth commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@openshift-ci-robot

Copy link
Copy Markdown

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: LGTM mode

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Mar 17, 2026
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 17, 2026
@openshift-ci-robot

openshift-ci-robot commented Mar 17, 2026

Copy link
Copy Markdown

@mdbooth: This pull request references OCPCLOUD-3319 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 "4.22.0" version, but no target version was set.

Details

In response to this:

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 commented Mar 17, 2026

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@coderabbitai

coderabbitai Bot commented Mar 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🚫 Review skipped — only excluded labels are configured. (1)
  • do-not-merge/work-in-progress

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: eede8d6e-50e2-4ef4-951c-bb169730204e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Replaced the legacy capiinstaller implementation with a new installer controller and revision-based engine (boxcutter); removed capiinstaller sources and tests; added installer probes, relatedObjects handling, extensive installer tests and helpers; updated main wiring and multiple go.mod files; added a ClusterAPI manifest.

Changes

Cohort / File(s) Summary
Go module dependency updates
go.mod, e2e/go.mod, hack/tools/go.mod, manifests-gen/go.mod
Bumped Go toolchain and many dependencies (k8s.io/* to v0.35.2, klog, go-openapi/swag, otel, golang.org/x/*), added pkg.package-operator.run/boxcutter v0.12.0, refreshed indirects.
Main operator wiring
cmd/capi-operator/main.go
loadProviderImages now returns provider profiles only; removed feature-gate env var helper; setupControllers/installer setup now delegates to installer.SetupWithManager(mgr, providerProfiles) instead of constructing the installer controller directly.
Removed capiinstaller package
pkg/controllers/capiinstaller/...
Deleted entire capiinstaller implementation and tests: controller, CRD apply logic, predicates, component customizer, and test suite files.
New installer controller & engine
pkg/controllers/installer/installer_controller.go, pkg/controllers/installer/revision_reconciler.go, pkg/controllers/installer/boxcutter.go
Added InstallerController, revision reconciler, and boxcutter adapter implementing revision-based reconcile/teardown, tracking-cache watches, and phase orchestration.
Installer probes & related objects
pkg/controllers/installer/probes.go, pkg/controllers/installer/related_objects.go, pkg/controllers/installer/related_objects_test.go
Added probe selectors/predicates, relatedObjects merge/write helpers, and unit tests; deterministic dedupe/sort and status-only patching implemented.
Installer tests & test infra
pkg/controllers/installer/installer_controller_test.go, pkg/controllers/installer/suite_test.go, pkg/controllers/installer/helpers_test.go, pkg/controllers/installer/revision_reconciler_test.go
Comprehensive Ginkgo/Gomega tests and envtest setup covering revisions, lifecycle, error cases, object resolution, and relatedObjects behavior.
Test utilities
pkg/test/namespace_cleanup.go, pkg/test/matchers.go
Added namespace-finalizer cleanup controller for tests; replaced BeK8SNotFound matcher with explicit matcher implementation.
Operator status API changes
pkg/operatorstatus/controller_status.go, pkg/operatorstatus/operator_status_test.go, pkg/operatorstatus/operator_status.go
Exported ClusterOperator name/domain constants and removed RelatedObjects support from SyncStatus; updated tests to use exported names.
Revision generator extension
pkg/revisiongenerator/revision.go
Rendered revision API accepts variadic options and object collectors (WithObjectCollectors) invoked during rendering.
CoreCluster minor change
pkg/controllers/corecluster/corecluster_controller.go
Stopped passing relatedObjects into SetStatusAvailable (now calls with empty string only).
Manifest added
manifests/0000_30_cluster-api-installer_06_clusterapi.yaml
New ClusterAPI CR manifest (empty spec) with release/profile annotations for installer operator.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

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

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

- group: apiextensions.openshift.io
resource: compatibilityrequirements

# the installer controller dynamically adds entries for CAPI providers it installs No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit, missing final new line character

Comment thread pkg/test/provider_fixtures.go Outdated
// CRDAsYAML marshals a CRD object to YAML. Use with GenerateCRD or
// GenerateSchemalessSpecStatusCRD from crdbuilder.go to create CRD fixtures
// for use with WithManifests.
func CRDAsYAML(crd *apiextensionsv1.CustomResourceDefinition) string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Took me a moment to realise this is CRD As YAML instead of CRDAs YAML, what is a CRDA 😂

Comment thread pkg/revisiongenerator/helpers_test.go Outdated
Profile: profileName,
ManifestsPath: writeManifestFile(t, manifestContent),
}
return test.NewTestProvider(t, name,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is this NewTestProvider and not NewTestProviderImageManifests? A Provider feels different to me

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in #498

Comment on lines +48 to +50
// reconcileCh allows tests to explicitly trigger reconciliation,
// even when no ClusterAPI object exists.
reconcileCh chan event.TypedGenericEvent[client.Object]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not just always have a ClusterAPI object?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There's a wait test for if CVO creates us before the ClusterAPI object.

Comment thread pkg/controllers/installer/helpers_test.go Outdated
type revisionHandler func(context.Context, []operatorv1alpha1.ClusterAPIInstallerRevision) (bool, []string, []error)

// mergeWithTail merges the results of the head revision with the results of calling the tail handler on the tail revisions.
func mergeWithTail(ctx context.Context, tailHandler revisionHandler, tailRevisions []operatorv1alpha1.ClusterAPIInstallerRevision) func(bool, string, error) (bool, []string, []error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The head and tail semantics here are not immediately obvious, might be worth some further comments to explain what's going on in these various flows

DiscoveryClient: discoveryClient,
RestMapper: mgr.GetRESTMapper(),
Writer: mgr.GetClient(),
Reader: trackingCache,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is interesting, why do they have their own tracking cache? Why not read from the informers like a normal controller would?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The tracking cache dynamically adds and removes informers as required according to whatever's in the manifests.

Comment thread pkg/controllers/installer/installer_controller.go
return gvk.String()
}), ", "))

err := c.trackingCache.Watch(ctx, clusterAPI, allGVKs)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How do you stop watching a GVK if it is no longer relevant?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The tracking cache automatically prunes them when allGVKs passed here no longer contains the GVK.

@mdbooth
mdbooth force-pushed the installer-controller branch 2 times, most recently from 7cae2d6 to 04780de Compare March 23, 2026 17:15
@mdbooth

mdbooth commented Mar 23, 2026

Copy link
Copy Markdown
Contributor Author

/test e2e-aws-capi-techpreview

@mdbooth

mdbooth commented Mar 23, 2026

Copy link
Copy Markdown
Contributor Author

/pipeline required

@openshift-ci-robot

Copy link
Copy Markdown

Scheduling tests matching the pipeline_run_if_changed or not excluded by pipeline_skip_if_only_changed parameters:
/test e2e-aws-capi-techpreview
/test e2e-aws-ovn
/test e2e-aws-ovn-serial-1of2
/test e2e-aws-ovn-serial-2of2
/test e2e-aws-ovn-techpreview
/test e2e-aws-ovn-techpreview-upgrade
/test e2e-azure-capi-techpreview
/test e2e-azure-ovn-techpreview
/test e2e-azure-ovn-techpreview-upgrade
/test e2e-gcp-capi-techpreview
/test e2e-gcp-ovn-techpreview
/test e2e-metal3-capi-techpreview
/test e2e-openstack-capi-techpreview
/test e2e-openstack-ovn-techpreview
/test e2e-vsphere-capi-techpreview
/test regression-clusterinfra-aws-ipi-techpreview-capi

Comment on lines +43 to +82
/*
* The entry point to this file is the revisionReconciler.reconcile() function.
* revisionReconciler.reconcile() reconciles a list of revisions.
*
* Revisions are created by the revision controller. They represent all the
* manifests from the current release which are relevant to the current cluster.
* The revision controller will add a new revision with a higher revision number
* whenever there is any change in the manifests to be applied.
*
* The goal of the installer controller is to ensure all manifests specified by
* the most recent revision are applied to the cluster, and that objects which
* are no longer required are removed.
*
* The installer controller manages objects with Boxcutter. Boxcutter defines 2
* operations on a revision:
* * reconcile (create)
* * teardown (remove)
* We want to 'reconcile' the newest revision, and 'teardown' any older
* revisions. Note that a newer revision will typically be similar to an older
* revision, so an object will usually be in multiple revisions. Boxcutter
* stores metadata on objects it installs to track the most recent revision
* which was successfully applied the object. During teardown, it won't remove
* an object if it's managed by a newer revision. Therefore it's crtically
* important that we successfully reconcile the new revision before tearing down
* any older revisions.
*
* An additional goal is that if the newest revision can't be applied for any
* reason, for example because its CRD Requirements are not compatible with the
* cluster, we should continue to reconcile the next most recently applied
* revision.
*
* The algorithm here is to try to reconcile the newest revision. If it succeeds
* we teardown all older revisions. If it does not succeed, either because of
* failure or just because it's incomplete, we try to reconcile the next
* revision according to the same algorithm. This maps nicely to tail recursion
* because at any point in the revision list we are, we are always in one of
* two modes:
* * reconcile the head and teardown the tail, or
* * teardown everything
*/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@JoelSpeed I added this in response to your comment below. Does it read ok?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yep, this is helpful for future us i think, thanks

@mdbooth

mdbooth commented Mar 24, 2026

Copy link
Copy Markdown
Contributor Author

/test e2e-aws-ovn-techpreview

@mdbooth
mdbooth force-pushed the installer-controller branch 2 times, most recently from 5de775c to 51a5361 Compare March 25, 2026 13:22
@mdbooth

mdbooth commented Mar 25, 2026

Copy link
Copy Markdown
Contributor Author

/test e2e-aws-capi-techpreview

@mdbooth
mdbooth force-pushed the installer-controller branch from 51a5361 to dfefa3f Compare March 25, 2026 21:25
@mdbooth
mdbooth marked this pull request as ready for review March 25, 2026 21:25
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 25, 2026
@mdbooth

mdbooth commented Mar 25, 2026

Copy link
Copy Markdown
Contributor Author

/pipeline auto

@openshift-ci-robot

Copy link
Copy Markdown

Pipeline controller notification

The pipeline-auto label has been added to this PR. Second-stage tests will be triggered automatically when all first-stage tests pass.

@openshift-ci
openshift-ci Bot requested review from RadekManak and nrb March 25, 2026 21:25

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

Actionable comments posted: 4

🧹 Nitpick comments (4)
cmd/capi-operator/main.go (1)

167-185: Ensure deterministic provider image ordering throughout the pipeline.

The issue runs deeper than map iteration order. The ReadProviderImages function uses concurrent goroutine processing (errgroup with limit 5) to fetch provider metadata, and results are appended to the output slice in the order they arrive from the channel—making the output order nondeterministic regardless of input order. This can cause unnecessary revision churn and unpredictable reconciliation behavior.

Sort containerImageRefs before passing to ReadProviderImages as a defensive measure to ensure at least one stable boundary:

♻️ Suggested change
  containerImageRefs := slices.Collect(maps.Values(containerImages))
+ slices.Sort(containerImageRefs)

  providerProfiles, err := providerimages.ReadProviderImages(ctx, mgr.GetAPIReader(), mgr.GetLogger(), containerImageRefs, providerImageDir)

For a complete fix, ReadProviderImages should also sort its results before returning to guarantee stable output across restarts.

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

In `@cmd/capi-operator/main.go` around lines 167 - 185, The provider image output
is nondeterministic because concurrent ReadProviderImages appends results in
arrival order; to mitigate, sort the input slice before calling
ReadProviderImages by deterministically ordering containerImageRefs (e.g.,
lexicographically) so loadProviderImages feeds a stable sequence, and also
update ReadProviderImages to sort its final
[]providerimages.ProviderImageManifests result before returning to guarantee
stable output across runs; reference loadProviderImages, containerImageRefs, and
ReadProviderImages when making these changes.
pkg/controllers/installer/related_objects.go (1)

54-71: Deduplicate the dynamic tail as well.

Right now only overlaps with static are removed. The installer aggregates related objects across revisions before calling this helper (pkg/controllers/installer/installer_controller.go Lines 212-217), so the same CRD/RBAC object can still be appended multiple times during revision overlap and leak duplicates into status.relatedObjects.

Proposed fix
 func mergeRelatedObjects(static, dynamic []configv1.ObjectReference) []configv1.ObjectReference {
-	staticSet := make(map[configv1.ObjectReference]struct{}, len(static))
+	seen := make(map[configv1.ObjectReference]struct{}, len(static)+len(dynamic))
 
 	for _, obj := range static {
-		staticSet[obj] = struct{}{}
+		seen[obj] = struct{}{}
 	}
 
 	var deduped []configv1.ObjectReference
 
 	for _, obj := range dynamic {
-		if _, exists := staticSet[obj]; !exists {
+		if _, exists := seen[obj]; !exists {
+			seen[obj] = struct{}{}
 			deduped = append(deduped, obj)
 		}
 	}
 
 	slices.SortFunc(deduped, compareObjectReference)

Please add a related_objects_test.go case for duplicate dynamic entries alongside the code change.

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

In `@pkg/controllers/installer/related_objects.go` around lines 54 - 71, The
mergeRelatedObjects function currently only removes entries from dynamic that
overlap static; update mergeRelatedObjects to also deduplicate duplicate entries
within the dynamic slice itself (e.g., track seen entries with a
map[configv1.ObjectReference]struct{} while iterating dynamic, skip already-seen
ones and ones present in staticSet), then sort the deduped dynamic tail with
slices.SortFunc(compareObjectReference) and append to a cloned static slice;
also add a unit test in related_objects_test.go that calls mergeRelatedObjects
with dynamic containing duplicate CRD/RBAC entries (and overlapping entries with
static) and asserts the returned slice has no duplicates in the dynamic tail and
preserves expected ordering.
pkg/operatorstatus/controller_status.go (1)

37-42: Avoid a second source of truth for the ClusterOperator name.

pkg/operatorstatus/operator_status.go still reads/creates the singleton via controllers.ClusterOperatorName, while this file now patches it via ClusterOperatorName. Keeping both exported constants around makes future drift a correctness bug. Please collapse these call sites onto one shared constant before more code depends on both names.

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

In `@pkg/operatorstatus/controller_status.go` around lines 37 - 42, There are two
exported constants for the ClusterOperator name (ClusterOperatorName in
pkg/operatorstatus/controller_status.go and controllers.ClusterOperatorName used
in pkg/operatorstatus/operator_status.go); collapse them to a single shared
constant and update call sites to use that one symbol. Choose one location
(preferably the existing controllers.ClusterOperatorName) to hold the canonical
constant, remove the duplicate ClusterOperatorName declaration, and update any
references in operator_status.go and controller_status.go to import and use
controllers.ClusterOperatorName (or conversely move the canonical constant and
update the other file) so there is only one exported source of truth.
pkg/controllers/installer/revision_reconciler.go (1)

459-466: Naive pluralization may produce incorrect resource names.

strings.ToLower(gvk.Kind) + "s" doesn't handle irregular plurals (e.g., Ingressingresss instead of ingresses). While this is informational for relatedObjects/must-gather and won't break functionality, consider using a proper pluralization helper or the REST mapper if accuracy matters.

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

In `@pkg/controllers/installer/revision_reconciler.go` around lines 459 - 466, The
code naively builds a plural by doing strings.ToLower(gvk.Kind)+"s" which yields
incorrect plurals; replace this with a proper resource lookup (e.g., use
meta.UnsafeGuessKindToResource(gvk) or the controller's RESTMapper) and use the
returned GroupVersionResource's Resource value when calling
r.relatedObjects.Insert. Update the branch handling obj.GetNamespace()=="" to
compute resource via meta.UnsafeGuessKindToResource(gvk) (or call
r.restMapper.ResourceFor(...) if you have a RESTMapper instance available) and
fall back to the current naive plural only if the lookup fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/go.mod`:
- Around line 23-25: The e2e module's Kubernetes and klog versions (k8s.io/api,
k8s.io/apimachinery, k8s.io/client-go pinned to v0.35.2 and k8s.io/klog at
v2.140.0) drift from the root module's replace pins (which set the operator to
v0.35.1 and v2.130.1); fix by making the pins consistent—either add matching
replace directives in e2e/go.mod to force the same replacements as the root
(e.g., replace k8s.io/* and k8s.io/klog to the root's pinned module versions) or
update the root module's replace block to the e2e versions—ensure you target the
symbols k8s.io/api, k8s.io/apimachinery, k8s.io/client-go, and k8s.io/klog so
e2e uses identical Kubernetes/klog patch versions as the operator.

In `@pkg/controllers/installer/installer_controller_test.go`:
- Line 179: The test is calling checkConfigMap(ctx, coreCMName) immediately and
passing its error result to Eventually, so Gomega polls a fixed value; change
the Eventually argument to a closure that calls checkConfigMap each poll (e.g.,
pass func() error { return checkConfigMap(ctx, coreCMName) }) and keep the
existing Should(Satisfy(apierrors.IsNotFound)) assertion so Eventually
re-evaluates the checkConfigMap call on each retry.

In `@pkg/controllers/installer/installer_controller.go`:
- Around line 203-209: The early return when clusterAPI.Status.Revisions is
empty skips calling updateWatches(), leaving stale tracking-cache watches
registered; change the branch so that before returning (after
writeRelatedObjects/staticRelatedObjects), you invoke updateWatches(...) to
clear or reconcile watches for the now-empty revisions set (i.e., call the
controller's updateWatches(ctx, ...) / tracking-cache cleanup function using the
current Cluster API object), then return opresult.WaitingOnExternal("ClusterAPI
revisions"); ensure you reference clusterAPI.Status.Revisions and
updateWatches() so the watches are updated even when revisions go from non-empty
to empty.

In `@pkg/controllers/installer/revision_reconciler.go`:
- Around line 139-146: reconcile() currently assumes converted[0] exists when
isComplete is true — add a guard to avoid an index-out-of-bounds panic by
checking that the converted (or revisions) slice has at least one element before
accessing converted[0]; if the slice is empty, return nil (or an appropriate nil
name) with messages and errs instead of dereferencing converted[0]; update the
block around the call to reconcileRevisions in reconcile() so it validates
len(converted) > 0 (or len(revisions) > 0) before calling RevisionName() on
converted[0].

---

Nitpick comments:
In `@cmd/capi-operator/main.go`:
- Around line 167-185: The provider image output is nondeterministic because
concurrent ReadProviderImages appends results in arrival order; to mitigate,
sort the input slice before calling ReadProviderImages by deterministically
ordering containerImageRefs (e.g., lexicographically) so loadProviderImages
feeds a stable sequence, and also update ReadProviderImages to sort its final
[]providerimages.ProviderImageManifests result before returning to guarantee
stable output across runs; reference loadProviderImages, containerImageRefs, and
ReadProviderImages when making these changes.

In `@pkg/controllers/installer/related_objects.go`:
- Around line 54-71: The mergeRelatedObjects function currently only removes
entries from dynamic that overlap static; update mergeRelatedObjects to also
deduplicate duplicate entries within the dynamic slice itself (e.g., track seen
entries with a map[configv1.ObjectReference]struct{} while iterating dynamic,
skip already-seen ones and ones present in staticSet), then sort the deduped
dynamic tail with slices.SortFunc(compareObjectReference) and append to a cloned
static slice; also add a unit test in related_objects_test.go that calls
mergeRelatedObjects with dynamic containing duplicate CRD/RBAC entries (and
overlapping entries with static) and asserts the returned slice has no
duplicates in the dynamic tail and preserves expected ordering.

In `@pkg/controllers/installer/revision_reconciler.go`:
- Around line 459-466: The code naively builds a plural by doing
strings.ToLower(gvk.Kind)+"s" which yields incorrect plurals; replace this with
a proper resource lookup (e.g., use meta.UnsafeGuessKindToResource(gvk) or the
controller's RESTMapper) and use the returned GroupVersionResource's Resource
value when calling r.relatedObjects.Insert. Update the branch handling
obj.GetNamespace()=="" to compute resource via
meta.UnsafeGuessKindToResource(gvk) (or call r.restMapper.ResourceFor(...) if
you have a RESTMapper instance available) and fall back to the current naive
plural only if the lookup fails.

In `@pkg/operatorstatus/controller_status.go`:
- Around line 37-42: There are two exported constants for the ClusterOperator
name (ClusterOperatorName in pkg/operatorstatus/controller_status.go and
controllers.ClusterOperatorName used in pkg/operatorstatus/operator_status.go);
collapse them to a single shared constant and update call sites to use that one
symbol. Choose one location (preferably the existing
controllers.ClusterOperatorName) to hold the canonical constant, remove the
duplicate ClusterOperatorName declaration, and update any references in
operator_status.go and controller_status.go to import and use
controllers.ClusterOperatorName (or conversely move the canonical constant and
update the other file) so there is only one exported source of truth.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: af313bf3-68c0-4bd5-9bd7-92a57b7bf4b5

📥 Commits

Reviewing files that changed from the base of the PR and between 2090823 and dfefa3f.

⛔ Files ignored due to path filters (273)
  • e2e/go.sum is excluded by !**/*.sum
  • go.sum is excluded by !**/*.sum
  • hack/tools/go.sum is excluded by !**/*.sum
  • manifests-gen/go.sum is excluded by !**/*.sum
  • vendor/github.com/go-openapi/jsonpointer/.cliff.toml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/.gitignore is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/CODE_OF_CONDUCT.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/NOTICE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/SECURITY.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/errors.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonpointer/pointer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/.editorconfig is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/.gitignore is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/CODE_OF_CONDUCT.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/CONTRIBUTORS.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/NOTICE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/SECURITY.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/internal/normalize_url.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/jsonreference/reference.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/.codecov.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/.gitignore is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/.mockery.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/CODE_OF_CONDUCT.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/CONTRIBUTORS.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/SECURITY.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/cmdutils/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/cmdutils/cmd_utils.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/cmdutils/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/cmdutils_iface.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/conv/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/conv/convert.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/conv/convert_types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/conv/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/conv/format.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/conv/sizeof.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/conv/type_constraints.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/conv_iface.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/convert.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/convert_types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/errors.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/file.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/fileutils/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/fileutils/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/fileutils/file.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/fileutils/path.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/fileutils_iface.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/go.work is excluded by !**/*.work, !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/go.work.sum is excluded by !**/*.sum, !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/initialism_index.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/json.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonname/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonname/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonname/name_provider.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonname_iface.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/adapters/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/ifaces.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/adapters/ifaces/registry_iface.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/adapters/registry.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/adapter.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/lexer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/ordered_map.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/pool.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/writer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/concat.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/json.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils/ordered_map.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/jsonutils_iface.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/loading/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/loading/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/loading/errors.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/loading/json.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/loading/loading.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/loading/options.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/loading/yaml.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/loading_iface.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/mangling/BENCHMARK.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/mangling/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/mangling/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/mangling/initialism_index.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/mangling/name_lexem.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/mangling/name_mangler.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/mangling/options.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/mangling/pools.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/mangling/split.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/mangling/string_bytes.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/mangling/util.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/mangling_iface.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/name_lexem.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/net.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/netutils/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/netutils/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/netutils/net.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/netutils_iface.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/split.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/stringutils/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/stringutils/collection_formats.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/stringutils/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/stringutils/strings.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/stringutils_iface.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/typeutils/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/typeutils/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/typeutils/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/typeutils_iface.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/util.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/yaml.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/yamlutils/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/yamlutils/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/yamlutils/errors.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/yamlutils/ordered_map.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/yamlutils/yaml.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-openapi/swag/yamlutils_iface.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/cel/BUILD.bazel is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/cel/library.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/cel/optimizer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/checker/checker.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/checker/env.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/checker/scopes.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/ast/ast.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/debug/debug.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/env/BUILD.bazel is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/env/env.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/BUILD.bazel is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/bool.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/bytes.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/double.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/duration.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/int.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/json_value.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/list.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/map.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/null.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/object.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/string.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/timestamp.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/common/types/uint.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/ext/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/ext/bindings.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/ext/comprehensions.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/ext/extension_option_factory.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/ext/formatting_v2.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/ext/native.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/ext/regex.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/interpreter/attribute_patterns.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/interpreter/attributes.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/interpreter/interpretable.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/interpreter/interpreter.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/interpreter/planner.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/cel-go/parser/helper.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/gnostic-models/extensions/extension.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/gnostic-models/openapiv2/OpenAPIv2.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/gnostic-models/openapiv3/OpenAPIv3.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/google/gnostic-models/openapiv3/annotations.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/josharian/intern/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/josharian/intern/intern.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/josharian/intern/license.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/buffer/pool.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jlexer/bytestostr.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jlexer/bytestostr_nounsafe.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jlexer/error.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jlexer/lexer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/mailru/easyjson/jwriter/writer.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/common/expfmt/expfmt.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/common/expfmt/fuzz.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/Makefile.common is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_armx.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_loong64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_mipsx.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_others.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_ppcx.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_riscvx.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_s390x.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/cpuinfo_x86.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/fs_statfs_notype.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/fs_statfs_type.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/internal/util/sysreadfile_compat.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/kernel_hung.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/kernel_random.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/net_tcp.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/proc_interrupts.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/proc_maps.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/proc_smaps.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/proc_statm.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/proc_status.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/vm.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/prometheus/procfs/zoneinfo.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/stoewer/go-strcase/.gitignore is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/stoewer/go-strcase/.golangci.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/stoewer/go-strcase/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/stoewer/go-strcase/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/stoewer/go-strcase/camel.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/stoewer/go-strcase/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/stoewer/go-strcase/helper.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/stoewer/go-strcase/kebab.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/stoewer/go-strcase/snake.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/stretchr/objx/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/CHANGELOG.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/CONTRIBUTING.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/propagation/trace_context.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/requirements.txt is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.39.0/httpconv/metric.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.40.0/MIGRATION.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.40.0/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.40.0/attribute_group.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.40.0/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.40.0/error_type.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.40.0/exception.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/semconv/v1.40.0/schema.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/auto.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/trace.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/trace/tracestate.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/version.go is excluded by !**/vendor/**, !vendor/**
  • vendor/go.opentelemetry.io/otel/versions.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/net/http2/http2.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/net/http2/server.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/net/http2/transport.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/net/http2/writesched.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/net/http2/writesched_priority_rfc7540.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/net/http2/writesched_random.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sync/singleflight/singleflight.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/cpu/asm_darwin_arm64_gc.s is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/cpu/cpu_arm64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/cpu/cpu_darwin_arm64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/cpu/cpu_darwin_arm64_other.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/cpu/cpu_gccgo_arm64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/cpu/cpu_other_arm64.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/cpu/syscall_darwin_arm64_gc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/plan9/syscall_plan9.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/unix/ztypes_linux.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/windows/aliases.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/windows/registry/key.go is excluded by !**/vendor/**, !vendor/**
  • vendor/golang.org/x/sys/windows/syscall_windows.go is excluded by !**/vendor/**, !vendor/**
  • vendor/k8s.io/client-go/openapi3/root.go is excluded by !**/vendor/**, !vendor/**
  • vendor/k8s.io/client-go/util/csaupgrade/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/k8s.io/client-go/util/csaupgrade/options.go is excluded by !**/vendor/**, !vendor/**
  • vendor/k8s.io/client-go/util/csaupgrade/upgrade.go is excluded by !**/vendor/**, !vendor/**
  • vendor/k8s.io/kube-openapi/pkg/validation/spec/ref.go is excluded by !**/vendor/**, !vendor/**
  • vendor/k8s.io/kube-openapi/pkg/validation/validate/schema.go is excluded by !**/vendor/**, !vendor/**
  • vendor/k8s.io/kube-openapi/pkg/validation/validate/validator.go is excluded by !**/vendor/**, !vendor/**
  • vendor/k8s.io/utils/buffer/ring_fixed.go is excluded by !**/vendor/**, !vendor/**
  • vendor/k8s.io/utils/strings/slices/slices.go is excluded by !**/vendor/**, !vendor/**
  • vendor/modules.txt is excluded by !**/vendor/**, !vendor/**
  • vendor/pkg.package-operator.run/boxcutter/.codecov.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/pkg.package-operator.run/boxcutter/.gitignore is excluded by !**/vendor/**, !vendor/**
  • vendor/pkg.package-operator.run/boxcutter/.golangci.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/pkg.package-operator.run/boxcutter/.pre-commit-config.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/pkg.package-operator.run/boxcutter/CONTRIBUTING.md is excluded by !**/vendor/**, !vendor/**
  • vendor/pkg.package-operator.run/boxcutter/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/pkg.package-operator.run/boxcutter/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/pkg.package-operator.run/boxcutter/boxcutter.go is excluded by !**/vendor/**, !vendor/**
  • vendor/pkg.package-operator.run/boxcutter/do is excluded by !**/vendor/**, !vendor/**
  • vendor/pkg.package-operator.run/boxcutter/machinery/comparator.go is excluded by !**/vendor/**, !vendor/**
  • vendor/pkg.package-operator.run/boxcutter/machinery/doc.go is excluded by !**/vendor/**, !vendor/**
📒 Files selected for processing (27)
  • cmd/capi-operator/main.go
  • e2e/go.mod
  • go.mod
  • hack/tools/go.mod
  • manifests-gen/go.mod
  • manifests/0000_30_cluster-api-installer_06_clusterapi.yaml
  • pkg/controllers/capiinstaller/apiextensions.go
  • pkg/controllers/capiinstaller/capi_installer_controller.go
  • pkg/controllers/capiinstaller/capi_installer_controller_test.go
  • pkg/controllers/capiinstaller/component_customizer.go
  • pkg/controllers/capiinstaller/suite_test.go
  • pkg/controllers/capiinstaller/watch_predicates.go
  • pkg/controllers/corecluster/corecluster_controller.go
  • pkg/controllers/installer/boxcutter.go
  • pkg/controllers/installer/helpers_test.go
  • pkg/controllers/installer/installer_controller.go
  • pkg/controllers/installer/installer_controller_test.go
  • pkg/controllers/installer/probes.go
  • pkg/controllers/installer/related_objects.go
  • pkg/controllers/installer/related_objects_test.go
  • pkg/controllers/installer/revision_reconciler.go
  • pkg/controllers/installer/suite_test.go
  • pkg/operatorstatus/controller_status.go
  • pkg/operatorstatus/controller_status_test.go
  • pkg/operatorstatus/operator_status.go
  • pkg/revisiongenerator/revision.go
  • pkg/test/namespace_cleanup.go
💤 Files with no reviewable changes (6)
  • pkg/controllers/capiinstaller/suite_test.go
  • pkg/controllers/capiinstaller/capi_installer_controller_test.go
  • pkg/controllers/capiinstaller/component_customizer.go
  • pkg/controllers/capiinstaller/apiextensions.go
  • pkg/controllers/capiinstaller/watch_predicates.go
  • pkg/controllers/capiinstaller/capi_installer_controller.go

Comment thread e2e/go.mod

// Delete the ConfigMap and wait for it to be not found
Expect(cl.Delete(ctx, cm)).To(Succeed())
Eventually(checkConfigMap(ctx, coreCMName)).Should(Satisfy(apierrors.IsNotFound))

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

Eventually receives a pre-evaluated error instead of a polling function.

Eventually(checkConfigMap(ctx, coreCMName)) calls checkConfigMap once and passes its result (an error) to Eventually. Gomega will then repeatedly check the same error value rather than re-calling the function. Wrap it in a closure:

🐛 Proposed fix
-			Eventually(checkConfigMap(ctx, coreCMName)).Should(Satisfy(apierrors.IsNotFound))
+			Eventually(func() error { return checkConfigMap(ctx, coreCMName) }).Should(Satisfy(apierrors.IsNotFound))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Eventually(checkConfigMap(ctx, coreCMName)).Should(Satisfy(apierrors.IsNotFound))
Eventually(func() error { return checkConfigMap(ctx, coreCMName) }).Should(Satisfy(apierrors.IsNotFound))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/controllers/installer/installer_controller_test.go` at line 179, The test
is calling checkConfigMap(ctx, coreCMName) immediately and passing its error
result to Eventually, so Gomega polls a fixed value; change the Eventually
argument to a closure that calls checkConfigMap each poll (e.g., pass func()
error { return checkConfigMap(ctx, coreCMName) }) and keep the existing
Should(Satisfy(apierrors.IsNotFound)) assertion so Eventually re-evaluates the
checkConfigMap call on each retry.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have reworked this test entirely. Expecting to observe not found is a race.

Comment on lines +203 to +209
if len(clusterAPI.Status.Revisions) == 0 {
if err := writeRelatedObjects(ctx, c.client, staticRelatedObjects()); err != nil {
return opresult.Error(fmt.Errorf("writing relatedObjects: %w", err))
}

return opresult.WaitingOnExternal("ClusterAPI revisions")
}

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

Clear stale tracking-cache watches when revisions disappear.

This branch returns before updateWatches(). If status.revisions goes from non-empty to empty, the old GVK watches stay registered and keep enqueueing reconciles for resources that are no longer part of the desired state.

Proposed fix
 	if len(clusterAPI.Status.Revisions) == 0 {
+		if err := c.updateWatches(ctx, log, clusterAPI, sets.New[schema.GroupVersionKind]()); err != nil {
+			return opresult.Error(err)
+		}
+
 		if err := writeRelatedObjects(ctx, c.client, staticRelatedObjects()); err != nil {
 			return opresult.Error(fmt.Errorf("writing relatedObjects: %w", err))
 		}
 
 		return opresult.WaitingOnExternal("ClusterAPI revisions")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if len(clusterAPI.Status.Revisions) == 0 {
if err := writeRelatedObjects(ctx, c.client, staticRelatedObjects()); err != nil {
return opresult.Error(fmt.Errorf("writing relatedObjects: %w", err))
}
return opresult.WaitingOnExternal("ClusterAPI revisions")
}
if len(clusterAPI.Status.Revisions) == 0 {
if err := c.updateWatches(ctx, log, clusterAPI, sets.New[schema.GroupVersionKind]()); err != nil {
return opresult.Error(err)
}
if err := writeRelatedObjects(ctx, c.client, staticRelatedObjects()); err != nil {
return opresult.Error(fmt.Errorf("writing relatedObjects: %w", err))
}
return opresult.WaitingOnExternal("ClusterAPI revisions")
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/controllers/installer/installer_controller.go` around lines 203 - 209,
The early return when clusterAPI.Status.Revisions is empty skips calling
updateWatches(), leaving stale tracking-cache watches registered; change the
branch so that before returning (after
writeRelatedObjects/staticRelatedObjects), you invoke updateWatches(...) to
clear or reconcile watches for the now-empty revisions set (i.e., call the
controller's updateWatches(ctx, ...) / tracking-cache cleanup function using the
current Cluster API object), then return opresult.WaitingOnExternal("ClusterAPI
revisions"); ensure you reference clusterAPI.Status.Revisions and
updateWatches() so the watches are updated even when revisions go from non-empty
to empty.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Revisions can never become empty after it has been non-empty.

Comment thread pkg/controllers/installer/revision_reconciler.go
Should(HaveField("Data", HaveKeyWithValue("version", "v1")))
}, defaultNodeTimeout)

It("re-creates a deleted managed object", func(ctx context.Context) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same here - event? too noisy?

waitForRelatedObjects(ctx, Not(BeEmpty()))

initial := getRelatedObjects(ctx)
Expect(initial).To(HaveLen(10 + len(staticRelatedObjects())))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

magic number 10 🎉

})

var _ = Describe("InstallerController without ClusterAPI", Serial, func() {
It("reports WaitingOnExternal when ClusterAPI does not exist", func(ctx context.Context) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

probably duplicate test / tests already tested code path?

@mdbooth

mdbooth commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

The e2e-openstack-ovn-techpreview failure is because we missed ORC when upgrading CAPO. I've submitted a PR for it here: openshift/openstack-resource-controller#30

@mdbooth
mdbooth force-pushed the installer-controller branch from 1812b00 to a404b74 Compare March 27, 2026 07:45
@mdbooth

mdbooth commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

For now, I've removed the commit which removes capiinstaller. capiinstaller should continue to run and install the ORC manifests for CAPO.

@openshift-ci-robot

Copy link
Copy Markdown

Scheduling required tests:
/test e2e-openstack-ovn-techpreview

Scheduling tests matching the pipeline_run_if_changed or not excluded by pipeline_skip_if_only_changed parameters:
/test e2e-aws-capi-techpreview
/test e2e-aws-ovn
/test e2e-aws-ovn-serial-1of2
/test e2e-aws-ovn-serial-2of2
/test e2e-aws-ovn-techpreview
/test e2e-aws-ovn-techpreview-upgrade
/test e2e-azure-capi-techpreview
/test e2e-azure-ovn-techpreview
/test e2e-azure-ovn-techpreview-upgrade
/test e2e-gcp-capi-techpreview
/test e2e-gcp-ovn-techpreview
/test e2e-metal3-capi-techpreview
/test e2e-openstack-capi-techpreview
/test e2e-vsphere-capi-techpreview
/test regression-clusterinfra-aws-ipi-techpreview-capi

@mdbooth

mdbooth commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

Infra flake (no lease):
/test e2e-gcp-capi-techpreview

@mdbooth

mdbooth commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

/test e2e-vsphere-capi-techpreview

@mdbooth

mdbooth commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

/test e2e-aws-ovn

}

revision := conv.revision
bcRevision := toBoxcutterRevision(revision)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: naming

waiting := phase.Waiting()

// Logging gone objects is currently problematic due to
// https://github.com/package-operator/boxcutter/issues/497

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TODO? Jira?

@mdbooth

mdbooth commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

/verified by CI and manual testing

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

Copy link
Copy Markdown

@mdbooth: This PR has been marked as verified by CI and manual testing.

Details

In response to this:

/verified by CI and manual testing

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.

@mdbooth

mdbooth commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

Permafailing:

/override pull-ci-openshift-cluster-capi-operator-main-e2e-openstack-ovn-techpreview

@openshift-ci

openshift-ci Bot commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

@mdbooth: Overrode contexts on behalf of mdbooth: ci/prow/e2e-openstack-ovn-techpreview

Details

In response to this:

Permafailing:

/override pull-ci-openshift-cluster-capi-operator-main-e2e-openstack-ovn-techpreview

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.

@theobarberbany

Copy link
Copy Markdown
Contributor

/lgtm

looks good - a few nits /comments but nothing blocking.

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

Copy link
Copy Markdown
Contributor

/approve

Lets ship this, CI is green and we won't work out whats broken if we don't

@openshift-ci

openshift-ci Bot commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: theobarberbany

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 Mar 27, 2026
@openshift-ci

openshift-ci Bot commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

@mdbooth: 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/e2e-azure-ovn-techpreview a404b74 link false /test e2e-azure-ovn-techpreview

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.

@openshift-merge-bot
openshift-merge-bot Bot merged commit ee07af3 into openshift:main Mar 27, 2026
24 of 25 checks passed
@mdbooth
mdbooth deleted the installer-controller branch March 27, 2026 16:20
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. 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. pipeline-auto verified Signifies that the PR passed pre-merge verification criteria

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants