Skip to content

CNTRLPLANE-2777: feat(resource builder): allow to inject tls configuration into annotated config maps#1322

Open
ingvagabund wants to merge 6 commits intoopenshift:mainfrom
ingvagabund:configmap-tls-injection
Open

CNTRLPLANE-2777: feat(resource builder): allow to inject tls configuration into annotated config maps#1322
ingvagabund wants to merge 6 commits intoopenshift:mainfrom
ingvagabund:configmap-tls-injection

Conversation

@ingvagabund
Copy link
Member

@ingvagabund ingvagabund commented Feb 16, 2026

Design notes:

  • any operator built on top of library-go's NewControllerCommandConfig gets --config flag where operatorv1alpha1.GenericOperatorConfig manifest is expected.
  • operatorv1alpha1.GenericOperatorConfig has a .servingInfo.minTLSVersion and .servingInfo.cipherSuites where CVO can inject the centralized TLS configuration.
  • example: https://github.com/openshift/cluster-etcd-operator/blob/main/manifests/0000_20_etcd-operator_03_configmap.yaml#L14-L21
  • CVO's lib/resourcebuilder.builder can be extended to inject the tls configuration the same way as proxy injection is.
  • CVO is expected to reconcile every 2 minutes? So eventually all CM's annotated with config.openshift.io/inject-tls can be injected with the TLS configuration.

Operators with the tls-inject annotation:

How to test it:

  • provision a cluster with the operator PRs included
  • follow https://access.redhat.com/solutions/6955910 to make the CVO unmanaged + increase log level to 4
  • test 1: change the TLS to Moden (oc edit apiserver cluster):
    spec:
      tlsSecurityProfile:
        modern: {}
        type: Modern
  • test 2: dropping the tlsSecurityProfile field
    spec:
      tlsSecurityProfile:
        modern: {}
        type: Modern
  • reading the CMs:
    oc get cm -n openshift-apiserver-operator openshift-apiserver-operator-config -o json | jq -r '.data["config.yaml"]' | yq '.servingInfo'
    oc get cm -n openshift-controller-manager-operator openshift-controller-manager-operator-config -o json | jq -r '.data["config.yaml"]' | yq '.servingInfo'
    oc get cm -n openshift-etcd-operator etcd-operator-config -o json | jq -r '.data["config.yaml"]' | yq '.servingInfo'
    oc get cm -n openshift-image-registry image-registry-operator-config -o json | jq -r '.data["config.yaml"]' | yq '.servingInfo'
    oc get cm -n openshift-kube-apiserver-operator kube-apiserver-operator-config -o json | jq -r '.data["config.yaml"]' | yq '.servingInfo'
    oc get cm -n openshift-kube-controller-manager-operator kube-controller-manager-operator-config -o json | jq -r '.data["config.yaml"]' | yq '.servingInfo'
    oc get cm -n openshift-kube-scheduler-operator openshift-kube-scheduler-operator-config -o json | jq '.data["config.yaml"]' -r | yq '.servingInfo'
  • In both cases I was able to observe the tls configuration getting injected.

Summary by CodeRabbit

  • New Features

    • ConfigMaps annotated with config.openshift.io/inject-tls="true" are automatically injected with cluster TLS settings (min TLS version and cipher suites), preserving other data and YAML structure.
  • Tests

    • Added comprehensive tests covering annotation gating, mixed-data handling, idempotence, and TLS field correctness.
  • Refactor

    • Internal client abstractions updated to support the new ConfigMap TLS injection workflow.
  • Chores

    • Updated dependency declarations to support integrations.

@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 Feb 16, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4f73d44e-1b23-410d-9ada-423628c97beb

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

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds TLS-injection for ConfigMaps annotated config.openshift.io/inject-tls by observing APIServer TLS settings and injecting minTLSVersion and cipherSuites into GenericOperatorConfig YAML entries; changes generator to emit interface-typed clients and updates the builder to use those interfaces; adds tests and dependency updates.

Changes

Cohort / File(s) Summary
Dependency Management
go.mod
Added direct deps sigs.k8s.io/kustomize/kyaml v0.21.1, sigs.k8s.io/yaml v1.6.0; added indirect deps (github.com/go-errors/errors, github.com/imdario/mergo, sigs.k8s.io/kube-storage-version-migrator); removed a duplicate indirect sigs.k8s.io/yaml entry.
Generation Script
hack/generate-lib-resources.py
Generator now emits client property types as pointer or non-pointer depending on an interface flag; marked some scheme mappings as interfaces (e.g., ConfigV1Interface, CoreV1Interface); added modifier mapping for ConfigMap -> b.modifyConfigMap.
Resource Builder: core logic
lib/resourcebuilder/core.go
New TLS-injection implementation: exported ConfigMapInjectTLSAnnotation, modifyConfigMap, TLS observation helpers (observeTLSConfiguration, lister/adapters), RNode/kyaml-based YAML parsing and modification (updateRNodeWithTLSSettings, getSortedCipherSuites), and associated logging/error handling.
Resource Builder: wiring
lib/resourcebuilder/resourcebuilder.go
Builder struct fields changed from concrete clients to interfaces (configclientv1.ConfigV1Interface, coreclientv1.CoreV1Interface); Do() now invokes b.modifyConfigMap(ctx, typedObject) for ConfigMap resources before apply.
Tests
lib/resourcebuilder/core_test.go, lib/resourcebuilder/resourcebuilder_test.go
Added extensive unit tests covering annotation gating, YAML parsing/preservation, idempotency, mixed data handling, missing APIServer behavior, and builder integration using fake clients and helpers.

Sequence Diagram

sequenceDiagram
    participant Builder as Resource Builder
    participant CM as ConfigMap
    participant APIServer as APIServer Lister
    participant Observer as TLS Observer
    participant YAML as kyaml Parser
    participant RNode as RNode Modifier

    Builder->>CM: Read annotations & data
    CM-->>Builder: Return data & annotation
    alt annotation == "true"
        Builder->>APIServer: Get APIServer TLS/securityProfile
        APIServer-->>Observer: Provide servingInfo / securityProfile
        Observer-->>Builder: Return minTLSVersion + cipherSuites
        Builder->>YAML: Parse each data entry
        YAML-->>Builder: Identify GenericOperatorConfig entries
        Builder->>RNode: Inject minTLSVersion & cipherSuites into nodes
        RNode-->>YAML: Return modified YAML text
        Builder->>CM: Update data entries and apply resource
        CM-->>Builder: Persisted
    else annotation missing/false
        Builder-->>CM: Skip modification
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Stable And Deterministic Test Names ❓ Inconclusive Test files lib/resourcebuilder/core_test.go and lib/resourcebuilder/resourcebuilder_test.go mentioned in PR summary could not be located in repository despite comprehensive search. Verify PR has been properly applied and test files are staged or present in working directory before examining test names for dynamic information.
✅ Passed checks (3 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 feature: allowing TLS configuration injection into annotated ConfigMaps in the resource builder.
Test Structure And Quality ✅ Passed Test code demonstrates good quality following Go testing patterns with single responsibility, proper setup/cleanup via fresh fake clients and deep copies, meaningful assertion messages, and consistency with established repository conventions.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

}

// Process each data entry that contains GenericOperatorConfig
for key, value := range cm.Data {

Choose a reason for hiding this comment

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

have you considered going for a single config.yaml entry ?

Copy link
Member Author

Choose a reason for hiding this comment

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

@ingvagabund ingvagabund force-pushed the configmap-tls-injection branch 5 times, most recently from 938cef8 to bea8cc6 Compare February 18, 2026 16:35
Copy link

@ricardomaraschini ricardomaraschini left a comment

Choose a reason for hiding this comment

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

I assume that the hash of the configmap will be automatically added to the deployment for rollouts.

edit: we don't yet, a similar mechanism is still to be implemented

@DavidHurta
Copy link
Contributor

/cc

@openshift-ci openshift-ci bot requested a review from DavidHurta February 19, 2026 13:36
@ingvagabund ingvagabund changed the title wip: feat(resource builder): allow to inject tls configuration into annotated config maps CNTRLPLANE-2777: wip: feat(resource builder): allow to inject tls configuration into annotated config maps Feb 19, 2026
@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 Feb 19, 2026
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Feb 19, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 19, 2026

@ingvagabund: This pull request references CNTRLPLANE-2777 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 epic to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Design notes:

  • any operator built on top of library-go's NewControllerCommandConfig gets --config flag where operatorv1alpha1.GenericOperatorConfig manifest is expected.
  • operatorv1alpha1.GenericOperatorConfig has a .servingInfo.minTLSVersion and .servingInfo.cipherSuites where CVO can inject the centralized TLS configuration.
  • example: https://github.com/openshift/cluster-etcd-operator/blob/main/manifests/0000_20_etcd-operator_03_configmap.yaml#L14-L21
  • CVO's lib/resourcebuilder.builder can be extended to inject the tls configuration the same way as proxy injection is.
  • CVO is expected to reconcile every 2 minutes? So eventually all CM's annotated with config.openshift.io/inject-tls can be injected with the TLS configuration.

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.

@ingvagabund ingvagabund changed the title CNTRLPLANE-2777: wip: feat(resource builder): allow to inject tls configuration into annotated config maps CNTRLPLANE-2777: feat(resource builder): allow to inject tls configuration into annotated config maps Feb 24, 2026
@ingvagabund
Copy link
Member Author

@DavidHurta the high level functionality design is finished. Is the current implementation in the right direction that is aligned with how CVO is expected to reconcile?

@ingvagabund ingvagabund force-pushed the configmap-tls-injection branch from bea8cc6 to 4ae1b14 Compare February 25, 2026 11:41
@ingvagabund
Copy link
Member Author

ingvagabund commented Feb 25, 2026

I assume that the hash of the configmap will be automatically added to the deployment for rollouts.

As you suggested we can either:

  1. use --terminate-on-files so each operator gets terminated and restarted. Or,
  2. the resource builder could be extended with another annotation for saying which CMs/Secrets are dependencies and where a hash is expected to be computed. E.g.:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      namespace: NS
      name: operator
      annotations:
        ...
        config.openshift.io/depends-on-configmap-NAME: ""
        config.openshift.io/depends-on-secret-NAME: ""
    Where configmap/secret are scoped within NS. The resource builder will compute a hash of each listed CM/Secret and updates the corresponding annotation with it. The deployment controller will do the rest if the annotation hash changes.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 25, 2026

@ingvagabund: This pull request references CNTRLPLANE-2777 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 epic to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Design notes:

  • any operator built on top of library-go's NewControllerCommandConfig gets --config flag where operatorv1alpha1.GenericOperatorConfig manifest is expected.
  • operatorv1alpha1.GenericOperatorConfig has a .servingInfo.minTLSVersion and .servingInfo.cipherSuites where CVO can inject the centralized TLS configuration.
  • example: https://github.com/openshift/cluster-etcd-operator/blob/main/manifests/0000_20_etcd-operator_03_configmap.yaml#L14-L21
  • CVO's lib/resourcebuilder.builder can be extended to inject the tls configuration the same way as proxy injection is.
  • CVO is expected to reconcile every 2 minutes? So eventually all CM's annotated with config.openshift.io/inject-tls can be injected with the TLS configuration.

Summary by CodeRabbit

  • New Features

  • ConfigMaps can now be automatically configured with TLS settings via annotation, allowing security profiles to be injected into application configurations while preserving existing data.

  • Tests

  • Added comprehensive test suite for ConfigMap modification behavior, covering annotation states, mixed data formats, and TLS field validation.

  • Chores

  • Updated and added dependencies for Kubernetes support.

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.

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: 3

🧹 Nitpick comments (6)
lib/resourcebuilder/core_test.go (1)

64-84: Ignored error from json.Marshal in test helper.

While marshalling a simple Pod struct should never fail, silently discarding the error at Line 82 is a test smell. Consider using t parameter or must-style helper.

♻️ Suggested fix

Since this is a package-level helper without access to *testing.T, a simple panic is acceptable in test code:

-func makePodJSON() string {
+func makePodJSON(t *testing.T) string {
+	t.Helper()
 	pod := &corev1.Pod{
 		...
 	}
-	data, _ := json.Marshal(pod)
+	data, err := json.Marshal(pod)
+	if err != nil {
+		t.Fatalf("Failed to marshal Pod: %v", err)
+	}
 	return string(data)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/resourcebuilder/core_test.go` around lines 64 - 84, The helper
makePodJSON ignores the error returned by json.Marshal; change it to fail loudly
in tests by checking the error from json.Marshal(pod) and panicking (or using a
must-style behavior) if err != nil so test failures are obvious; update the
function makePodJSON to capture the error from json.Marshal and call panic with
that error (or wrap a descriptive message) instead of discarding it.
hack/generate-lib-resources.py (1)

127-127: Avoid equality comparison to True; use truthiness check instead.

Per Ruff E712, use client['interface'] instead of client['interface'] == True.

♻️ Suggested fix
-            'type': '{}{}.{}'.format("" if 'interface' in client and client['interface'] == True else "*", client_short_name, client['type']),
+            'type': '{}{}.{}'.format("" if client.get('interface') else "*", client_short_name, client['type']),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hack/generate-lib-resources.py` at line 127, Replace the explicit equality
check client['interface'] == True with a truthiness check so the 'type' dict
entry uses if client.get('interface') or client['interface'] (truthy) instead of
comparing to True; update the expression that builds the 'type' value (the dict
entry using '{}{}.{}'.format(...)) to use a truthy test on client['interface']
to decide whether to include the empty string or "*" (reference the 'client'
variable and the 'type' dict entry in the generator).
lib/resourcebuilder/core.go (4)

98-104: Redundant API call: APIServers().Get() is called twice per ConfigMap.

Line 100 fetches the APIServer resource to check existence, and then ObserveTLSSecurityProfile (via the apiServerListerAdapter.Get at Line 212) fetches it again. This doubles the API server load for every annotated ConfigMap on each reconciliation cycle. Consider removing the pre-check and relying solely on the errors returned by ObserveTLSSecurityProfile.

♻️ Suggested simplification
 func (b *builder) observeTLSConfiguration(ctx context.Context, cm *corev1.ConfigMap) (minTLSVersion string, minTLSFound bool, cipherSuites []string, ciphersFound bool) {
-	// First check if the APIServer cluster resource exists
-	_, err := b.configClientv1.APIServers().Get(ctx, "cluster", metav1.GetOptions{})
-	if err != nil {
-		klog.V(2).Infof("ConfigMap %s/%s: APIServer cluster resource not found, skipping TLS injection: %v", cm.Namespace, cm.Name, err)
-		return "", false, nil, false
-	}
-
 	// Create a lister adapter for ObserveTLSSecurityProfile
 	lister := &apiServerListerAdapter{
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/resourcebuilder/core.go` around lines 98 - 104, The initial APIServer
existence check in builder.observeTLSConfiguration that calls
b.configClientv1.APIServers().Get should be removed to avoid duplicate API
calls; instead, rely on ObserveTLSSecurityProfile (and the
apiServerListerAdapter.Get it uses) to return appropriate errors if the
APIServer is missing. Edit observeTLSConfiguration to delete the pre-check block
(the call to b.configClientv1.APIServers().Get and its early return/log) and let
the subsequent call to ObserveTLSSecurityProfile handle not-found or error
cases, preserving existing logging where ObserveTLSSecurityProfile reports
failures.

29-41: Empty (non-nil) Data map silently passes through without logging.

Line 38 checks cm.Data == nil but logs "empty data". If Data is an empty map (map[string]string{}), it won't be logged as "empty" and the loop at Line 52 will simply not iterate. Consider also checking len(cm.Data) == 0 for consistency with the log message.

♻️ Minor fix
 	// Empty data, nothing to inject into
-	if cm.Data == nil {
+	if len(cm.Data) == 0 {
 		klog.V(2).Infof("ConfigMap %s/%s has empty data, skipping", cm.Namespace, cm.Name)
 		return nil
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/resourcebuilder/core.go` around lines 29 - 41, In modifyConfigMap on
builder, the current check only tests cm.Data == nil but the log says "empty
data" while an empty (non-nil) map will silently pass through; change the
condition to treat both nil and zero-length maps as empty (e.g., if cm.Data ==
nil || len(cm.Data) == 0) and keep the klog message, or adjust the message to
reflect nil vs empty; update the early-return branch so the range loop that
follows (the iteration over cm.Data) isn't relied on to detect emptiness.

86-93: Log at Line 91 is misleading: it prints even when no entries were modified.

The "APIServer config available for ConfigMap ... TLS injection" message is emitted unconditionally after the loop, even if no GenericOperatorConfig entries were found or updated. Consider moving it inside the loop (where updates happen) or tracking whether any modification occurred.

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

In `@lib/resourcebuilder/core.go` around lines 86 - 93, The log "APIServer config
available for ConfigMap ... TLS injection" is emitted unconditionally; update
the code in the routine that iterates cm.Data and updates GenericOperatorConfig
entries (the block that sets cm.Data[key] = modifiedYAML and calls
klog.V(2).Infof about updated ciphers/minTLSVersion) to only emit the
APIServer-config-available message when at least one entry was actually
modified—either move that klog.V(2).Infof into the modification branch or
introduce a boolean modified flag (e.g., modified := false; set to true when you
change cm.Data[key]) and emit the message only if modified is true, referencing
cm.Namespace and cm.Name as currently done.

141-166: servingInfo is unconditionally created even when neither TLS field needs updating.

LookupCreate(yaml.MappingNode, "servingInfo") at Line 142 will create a servingInfo mapping if it doesn't exist. If both ciphersFound and minTLSFound are false (shouldn't reach here given the caller check, but defensively), this would inject an empty servingInfo node. The current caller guards against this, so this is a minor defensive-coding note.

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

In `@lib/resourcebuilder/core.go` around lines 141 - 166, The function
updateRNodeWithTLSSettings unconditionally creates servingInfo via
yaml.LookupCreate which can add an empty mapping when neither TLS value needs
updating; change the logic to only obtain/create servingInfo when needed: check
ciphersFound and minTLSFound first, and for cipherSuites call
yaml.Lookup(servingInfoParent, "servingInfo") to see if it exists (and only
create with yaml.SetField("servingInfo", yaml.NewMapRNode(...)) or
yaml.LookupCreate when actually about to set fields), and similarly only create
servingInfo when setting minTLSVersion; update the code paths in
updateRNodeWithTLSSettings so servingInfo is created lazily (use yaml.Lookup
instead of yaml.LookupCreate or defer yaml.LookupCreate until just before PipeE
calls) while keeping the existing comparisons with getSortedCipherSuites and
slices.Equal.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/resourcebuilder/core_test.go`:
- Around line 536-541: The table-driven test includes a shouldCallAPI boolean
that is never asserted in the test loop; either remove the unused shouldCallAPI
field from the tests slice or add an assertion inside the test loop that
inspects the fake client's recorded actions and compares whether an API call
occurred to tests[i].shouldCallAPI (e.g., check
fakeClient.Actions()/RecordedActions length or presence of a specific verb) so
the table entry is validated; update the tests slice entry points and the test
loop where the resource builder is exercised to perform this check.
- Around line 226-235: The test declares expectModified in TestModifyConfigMap
but never asserts it; update the test loop in TestModifyConfigMap to verify
expectModified for each case by comparing the original and returned/modified
*corev1.ConfigMap (or by using a boolean return if ModifyConfigMap returns one):
after calling the function under test, add an assertion that when
tc.expectModified is true the modified ConfigMap differs (use reflect.DeepEqual
or cmp.Diff) from the original, and when false they are equal; reference the
existing test case variable tc.expectModified and the test helper
validateConfigMap to keep behavior checks consistent.

In `@lib/resourcebuilder/core.go`:
- Around line 224-232: configObserverListers currently returns nil from
ResourceSyncer() and PreRunHasSynced(), violating the configobserver.Listers
contract and risking a panic in callers like listersToInformer() or
NewNestedConfigObserver(); fix by returning concrete no-op implementations: have
PreRunHasSynced() return an empty slice (e.g. return []cache.InformerSynced{}),
and implement a small noop ResourceSyncer type that satisfies
resourcesynccontroller.ResourceSyncer (methods are no-ops) and return an
instance from ResourceSyncer(); update core.go's configObserverListers methods
accordingly (referencing configObserverListers, ResourceSyncer(),
PreRunHasSynced(), ObserveTLSSecurityProfile, APIServerLister(),
listersToInformer(), NewNestedConfigObserver()) or, if you choose not to add a
noop, add a clear comment on configObserverListers' type header stating it must
never be passed to code paths that call these methods.

---

Nitpick comments:
In `@hack/generate-lib-resources.py`:
- Line 127: Replace the explicit equality check client['interface'] == True with
a truthiness check so the 'type' dict entry uses if client.get('interface') or
client['interface'] (truthy) instead of comparing to True; update the expression
that builds the 'type' value (the dict entry using '{}{}.{}'.format(...)) to use
a truthy test on client['interface'] to decide whether to include the empty
string or "*" (reference the 'client' variable and the 'type' dict entry in the
generator).

In `@lib/resourcebuilder/core_test.go`:
- Around line 64-84: The helper makePodJSON ignores the error returned by
json.Marshal; change it to fail loudly in tests by checking the error from
json.Marshal(pod) and panicking (or using a must-style behavior) if err != nil
so test failures are obvious; update the function makePodJSON to capture the
error from json.Marshal and call panic with that error (or wrap a descriptive
message) instead of discarding it.

In `@lib/resourcebuilder/core.go`:
- Around line 98-104: The initial APIServer existence check in
builder.observeTLSConfiguration that calls b.configClientv1.APIServers().Get
should be removed to avoid duplicate API calls; instead, rely on
ObserveTLSSecurityProfile (and the apiServerListerAdapter.Get it uses) to return
appropriate errors if the APIServer is missing. Edit observeTLSConfiguration to
delete the pre-check block (the call to b.configClientv1.APIServers().Get and
its early return/log) and let the subsequent call to ObserveTLSSecurityProfile
handle not-found or error cases, preserving existing logging where
ObserveTLSSecurityProfile reports failures.
- Around line 29-41: In modifyConfigMap on builder, the current check only tests
cm.Data == nil but the log says "empty data" while an empty (non-nil) map will
silently pass through; change the condition to treat both nil and zero-length
maps as empty (e.g., if cm.Data == nil || len(cm.Data) == 0) and keep the klog
message, or adjust the message to reflect nil vs empty; update the early-return
branch so the range loop that follows (the iteration over cm.Data) isn't relied
on to detect emptiness.
- Around line 86-93: The log "APIServer config available for ConfigMap ... TLS
injection" is emitted unconditionally; update the code in the routine that
iterates cm.Data and updates GenericOperatorConfig entries (the block that sets
cm.Data[key] = modifiedYAML and calls klog.V(2).Infof about updated
ciphers/minTLSVersion) to only emit the APIServer-config-available message when
at least one entry was actually modified—either move that klog.V(2).Infof into
the modification branch or introduce a boolean modified flag (e.g., modified :=
false; set to true when you change cm.Data[key]) and emit the message only if
modified is true, referencing cm.Namespace and cm.Name as currently done.
- Around line 141-166: The function updateRNodeWithTLSSettings unconditionally
creates servingInfo via yaml.LookupCreate which can add an empty mapping when
neither TLS value needs updating; change the logic to only obtain/create
servingInfo when needed: check ciphersFound and minTLSFound first, and for
cipherSuites call yaml.Lookup(servingInfoParent, "servingInfo") to see if it
exists (and only create with yaml.SetField("servingInfo", yaml.NewMapRNode(...))
or yaml.LookupCreate when actually about to set fields), and similarly only
create servingInfo when setting minTLSVersion; update the code paths in
updateRNodeWithTLSSettings so servingInfo is created lazily (use yaml.Lookup
instead of yaml.LookupCreate or defer yaml.LookupCreate until just before PipeE
calls) while keeping the existing comparisons with getSortedCipherSuites and
slices.Equal.

ℹ️ 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 9a401d5 and 4ae1b14.

⛔ Files ignored due to path filters (294)
  • go.sum is excluded by !**/*.sum
  • vendor/github.com/go-errors/errors/.travis.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-errors/errors/LICENSE.MIT is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-errors/errors/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-errors/errors/error.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-errors/errors/error_1_13.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-errors/errors/error_backward.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-errors/errors/parse_panic.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/go-errors/errors/stackframe.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/imdario/mergo/.deepsource.toml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/imdario/mergo/.gitignore is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/imdario/mergo/.travis.yml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/imdario/mergo/CODE_OF_CONDUCT.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/imdario/mergo/LICENSE is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/imdario/mergo/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/imdario/mergo/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/imdario/mergo/map.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/imdario/mergo/merge.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/imdario/mergo/mergo.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/.ci-operator.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/.coderabbit.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/.gitattributes is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/.gitignore is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/.golangci.go-validated.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/.golangci.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/AGENTS.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/Dockerfile.ocp is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/README.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/annotations/annotations.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apiextensions/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apiextensions/v1alpha1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apiextensions/v1alpha1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apiextensions/v1alpha1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apiextensions/v1alpha1/types_compatibilityrequirement.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apiextensions/v1alpha1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/apiextensions/v1alpha1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/apiextensions/v1alpha1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/apiserver/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apiserver/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apiserver/v1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apiserver/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apiserver/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apiserver/v1/types_apirequestcount.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apiserver/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/apiserver/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/apiserver/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/apps/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apps/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apps/v1/consts.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apps/v1/deprecated_consts.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apps/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apps/v1/generated.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apps/v1/generated.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apps/v1/legacy.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apps/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apps/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/apps/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/apps/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/apps/v1/zz_prerelease_lifecycle_generated.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/authorization/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/authorization/v1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/authorization/v1/codec.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/authorization/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/authorization/v1/generated.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/authorization/v1/generated.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/authorization/v1/legacy.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/authorization/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/authorization/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/authorization/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/authorization/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/authorization/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/build/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/build/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/build/v1/consts.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/build/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/build/v1/generated.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/build/v1/generated.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/build/v1/legacy.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/build/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/build/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/build/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/build/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/cloudnetwork/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/cloudnetwork/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/cloudnetwork/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/cloudnetwork/v1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/cloudnetwork/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/cloudnetwork/v1/generated.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/cloudnetwork/v1/generated.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/cloudnetwork/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/cloudnetwork/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/cloudnetwork/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/cloudnetwork/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/cloudnetwork/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/console/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/types_console_cli_download.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/types_console_external_log_links.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/types_console_link.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/types_console_notification.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/types_console_plugin.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/types_console_quick_start.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/types_console_sample.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/types_console_yaml_sample.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/console/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/console/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/console/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/envtest-releases.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/features.md is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/helm/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/helm/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/helm/v1beta1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/helm/v1beta1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/helm/v1beta1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/helm/v1beta1/types_helm_chart_repository.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/helm/v1beta1/types_project_helm_chart_repository.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/helm/v1beta1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/helm/v1beta1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/helm/v1beta1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/image/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/image/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/image/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/imageregistry/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/imageregistry/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/imageregistry/v1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/imageregistry/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/imageregistry/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/imageregistry/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/imageregistry/v1/types_imagepruner.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/imageregistry/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/imageregistry/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/imageregistry/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/kubecontrolplane/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/kubecontrolplane/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/kubecontrolplane/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/kubecontrolplane/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/kubecontrolplane/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/kubecontrolplane/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/kubecontrolplane/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/legacyconfig/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/legacyconfig/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/legacyconfig/v1/serialization.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/legacyconfig/v1/stringsource.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/legacyconfig/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/legacyconfig/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/legacyconfig/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/machine/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1/common.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1/types_alibabaprovider.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1/types_aws.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1/types_controlplanemachineset.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1/types_nutanixprovider.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1/types_powervsprovider.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/machine/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/machine/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/machine/v1alpha1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1alpha1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1alpha1/types_openstack.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1alpha1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/machine/v1alpha1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/machine/v1beta1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1beta1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1beta1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1beta1/types_awsprovider.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1beta1/types_azureprovider.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1beta1/types_gcpprovider.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1beta1/types_machine.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1beta1/types_machinehealthcheck.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1beta1/types_machineset.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1beta1/types_provider.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1beta1/types_vsphereprovider.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/machine/v1beta1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/machine/v1beta1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/machine/v1beta1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/monitoring/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/monitoring/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/monitoring/v1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/monitoring/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/monitoring/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/monitoring/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/monitoring/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/monitoring/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/monitoring/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/network/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1/constants.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1/generated.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1/generated.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1/legacy.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/network/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/network/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/network/v1alpha1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1alpha1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1alpha1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1alpha1/types_dnsnameresolver.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/network/v1alpha1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/network/v1alpha1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/network/v1alpha1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/networkoperator/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/networkoperator/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/networkoperator/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/networkoperator/v1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/networkoperator/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/networkoperator/v1/generated.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/networkoperator/v1/generated.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/networkoperator/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/networkoperator/v1/types_egressrouter.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/networkoperator/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/networkoperator/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/networkoperator/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/oauth/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/oauth/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/oauth/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/oauth/v1/generated.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/oauth/v1/generated.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/oauth/v1/legacy.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/oauth/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/oauth/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/oauth/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/oauth/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/openshiftcontrolplane/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/openshiftcontrolplane/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/openshiftcontrolplane/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/openshiftcontrolplane/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/openshiftcontrolplane/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/openshiftcontrolplane/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/openshiftcontrolplane/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/operator/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/operator/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/operatorcontrolplane/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/operatorcontrolplane/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/types_conditioncheck.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/operatorcontrolplane/v1alpha1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/osin/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/osin/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/osin/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/osin/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/osin/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/osin/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/pkg/serialization/serialization.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/project/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/project/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/project/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/project/v1/generated.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/project/v1/generated.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/project/v1/legacy.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/project/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/project/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/project/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/project/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/quota/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/quota/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/quota/v1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/quota/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/quota/v1/generated.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/quota/v1/generated.proto is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/quota/v1/legacy.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/quota/v1/register.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/quota/v1/types.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/quota/v1/zz_generated.deepcopy.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/quota/v1/zz_generated.featuregated-crd-manifests.yaml is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/quota/v1/zz_generated.swagger_doc_generated.go is excluded by !**/vendor/**, !vendor/**, !**/zz_generated*
  • vendor/github.com/openshift/api/route/.codegen.yaml is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/route/OWNERS is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/route/install.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/route/v1/Makefile is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/route/v1/doc.go is excluded by !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/route/v1/generated.pb.go is excluded by !**/*.pb.go, !**/vendor/**, !vendor/**
  • vendor/github.com/openshift/api/route/v1/generated.proto is excluded by !**/vendor/**, !vendor/**
📒 Files selected for processing (6)
  • go.mod
  • hack/generate-lib-resources.py
  • lib/resourcebuilder/core.go
  • lib/resourcebuilder/core_test.go
  • lib/resourcebuilder/resourcebuilder.go
  • lib/resourcebuilder/resourcebuilder_test.go

Comment on lines +224 to +232
func (l *configObserverListers) ResourceSyncer() resourcesynccontroller.ResourceSyncer {
// Not needed for TLS observation
return nil
}

func (l *configObserverListers) PreRunHasSynced() []cache.InformerSynced {
// Not needed for TLS observation
return nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's understand the repo structure and find the relevant files
git ls-files | grep -E "(resourcebuilder|configobserver)" | head -20

Repository: openshift/cluster-version-operator

Length of output: 1188


🏁 Script executed:

# Read the core.go file to see the full context
cat -n lib/resourcebuilder/core.go | sed -n '220,240p'

# Also check the interface definition
cat -n lib/resourcebuilder/interface.go | head -50

# Look at the observe_tlssecurityprofile implementation
cat -n vendor/github.com/openshift/library-go/pkg/operator/configobserver/apiserver/observe_tlssecurityprofile.go

Repository: openshift/cluster-version-operator

Length of output: 8005


🏁 Script executed:

# Find the APIServerLister interface definition
rg -n "type APIServerLister" --type go

# Search for all uses of ResourceSyncer in the codebase
rg -n "ResourceSyncer" --type go

# Search for all uses of PreRunHasSynced in the codebase
rg -n "PreRunHasSynced" --type go

# Find where configObserverListers is instantiated
rg -n "configObserverListers{" --type go

Repository: openshift/cluster-version-operator

Length of output: 2179


🏁 Script executed:

# Read the config_observer_controller.go around the lines that call PreRunHasSynced
cat -n vendor/github.com/openshift/library-go/pkg/operator/configobserver/config_observer_controller.go | sed -n '35,45p'

# Read the context around lines 255-257 where PreRunHasSynced is called
cat -n vendor/github.com/openshift/library-go/pkg/operator/configobserver/config_observer_controller.go | sed -n '250,270p'

# Look at the full Listers interface definition
cat -n vendor/github.com/openshift/library-go/pkg/operator/configobserver/config_observer_controller.go | sed -n '33,50p'

# Check the apiserver listers.go to understand the interface
cat -n vendor/github.com/openshift/library-go/pkg/operator/configobserver/apiserver/listers.go

Repository: openshift/cluster-version-operator

Length of output: 3078


🏁 Script executed:

# Find where listersToInformer is called
rg -n "listersToInformer" --type go

# Look for the Run method or startup code that uses this
cat -n vendor/github.com/openshift/library-go/pkg/operator/configobserver/config_observer_controller.go | sed -n '200,250p'

# Check how configObserverListers is initialized and used in lib/resourcebuilder/core.go
cat -n lib/resourcebuilder/core.go | sed -n '1,50p'

# Look for where configObserverListers is created/instantiated
rg -n "New.*configObserverListers\|&configObserverListers" --type go

Repository: openshift/cluster-version-operator

Length of output: 6154


🏁 Script executed:

# Look at the code around line 126 in config_observer_controller.go
cat -n vendor/github.com/openshift/library-go/pkg/operator/configobserver/config_observer_controller.go | sed -n '100,140p'

# Search for any calls to ResourceSyncer() in the codebase
rg -n "\.ResourceSyncer\(\)" --type go -A 2

# Check if the configObserverListers is actually used with the config observer controller
grep -r "configObserverListers\|NewConfigObserverListers" lib/ --include="*.go"

Repository: openshift/cluster-version-operator

Length of output: 2578


🏁 Script executed:

# Find where configObserverListers is used
rg -n "configObserverListers" lib/resourcebuilder/core.go -B 5 -A 10

# Check if it's passed to NewNestedConfigObserver or similar
rg -n "NewNestedConfigObserver\|NewConfigObserver" lib/ --include="*.go" -B 2 -A 2

Repository: openshift/cluster-version-operator

Length of output: 2026


🏁 Script executed:

# Double-check the current usage is safe by seeing what ObserveTLSSecurityProfile actually does
cat -n vendor/github.com/openshift/library-go/pkg/operator/configobserver/apiserver/observe_tlssecurityprofile.go | sed -n '35,80p'

Repository: openshift/cluster-version-operator

Length of output: 2460


Return proper implementations instead of nil for ResourceSyncer() and PreRunHasSynced(), or document that configObserverListers must never be passed to code paths that invoke these methods.

ObserveTLSSecurityProfile currently only calls APIServerLister() and never invokes ResourceSyncer() or PreRunHasSynced(), so the current usage is safe. However, configObserverListers implements the configobserver.Listers interface, which requires both methods. The listersToInformer() function in library-go's config_observer_controller.go (lines 255–257) iterates over the result of PreRunHasSynced() without nil-checking, which would panic if this lister is ever passed to NewNestedConfigObserver() or similar code paths. This pattern violates the interface contract and creates a landmine for future refactoring. Either return empty slices/objects instead of nil, or add a clear comment that this lister is only for TLS observation and must never be used with the config observer controller.

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

In `@lib/resourcebuilder/core.go` around lines 224 - 232, configObserverListers
currently returns nil from ResourceSyncer() and PreRunHasSynced(), violating the
configobserver.Listers contract and risking a panic in callers like
listersToInformer() or NewNestedConfigObserver(); fix by returning concrete
no-op implementations: have PreRunHasSynced() return an empty slice (e.g. return
[]cache.InformerSynced{}), and implement a small noop ResourceSyncer type that
satisfies resourcesynccontroller.ResourceSyncer (methods are no-ops) and return
an instance from ResourceSyncer(); update core.go's configObserverListers
methods accordingly (referencing configObserverListers, ResourceSyncer(),
PreRunHasSynced(), ObserveTLSSecurityProfile, APIServerLister(),
listersToInformer(), NewNestedConfigObserver()) or, if you choose not to add a
noop, add a clear comment on configObserverListers' type header stating it must
never be passed to code paths that call these methods.

@DavidHurta
Copy link
Contributor

@DavidHurta the high level functionality design is finished. Is the current implementation in the right direction that is aligned with how CVO is expected to reconcile?

Hello, @ingvagabund. The high-level design looks good on my end. From quickly going through the code, the implementation looks good.

I have reserved some time for myself to properly review the PR. Thanks for creating some tests, that's always appreciated!

I see the openshift/cluster-image-registry-operator#1297 PR, which is great. We can leverage it to verify the PR before merging, if possible.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Mar 2, 2026

@ingvagabund: This pull request references CNTRLPLANE-2777 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 epic to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Design notes:

  • any operator built on top of library-go's NewControllerCommandConfig gets --config flag where operatorv1alpha1.GenericOperatorConfig manifest is expected.
  • operatorv1alpha1.GenericOperatorConfig has a .servingInfo.minTLSVersion and .servingInfo.cipherSuites where CVO can inject the centralized TLS configuration.
  • example: https://github.com/openshift/cluster-etcd-operator/blob/main/manifests/0000_20_etcd-operator_03_configmap.yaml#L14-L21
  • CVO's lib/resourcebuilder.builder can be extended to inject the tls configuration the same way as proxy injection is.
  • CVO is expected to reconcile every 2 minutes? So eventually all CM's annotated with config.openshift.io/inject-tls can be injected with the TLS configuration.

Summary by CodeRabbit

  • New Features

  • ConfigMaps can be annotated to automatically receive TLS settings (min TLS version and cipher suites) injected from cluster security profile, preserving other data.

  • Tests

  • Added comprehensive tests covering annotation states, mixed data formats, idempotence, and TLS field validation.

  • Chores

  • Updated dependency declarations to support Kubernetes/OpenShift integrations.

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.

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)
lib/resourcebuilder/core.go (2)

163-168: Consider checking if minTLSVersion needs updating.

Unlike cipherSuites (which compares current vs. new values before updating), minTLSVersion is always set without checking the current value. While kyaml preserves YAML structure when setting the same value, adding a comparison would make the idempotency intent explicit and consistent with the cipher handling.

 	// Update minTLSVersion if found
 	if minTLSFound && minTLSVersion != "" {
+		currentMinTLS, err := servingInfo.GetString("minTLSVersion")
+		if err != nil || currentMinTLS != minTLSVersion {
 			if err := servingInfo.PipeE(yaml.SetField("minTLSVersion", yaml.NewStringRNode(minTLSVersion))); err != nil {
 				return err
 			}
+		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/resourcebuilder/core.go` around lines 163 - 168, The code unconditionally
sets minTLSVersion when minTLSFound is true, unlike the cipherSuites logic which
first compares existing vs new value; update the block around
minTLSFound/minTLSVersion and servingInfo.PipeE to read the current
"minTLSVersion" (via yaml.Field/Get or the same helper used for cipherSuites),
compare it to minTLSVersion, and only call
servingInfo.PipeE(yaml.SetField("minTLSVersion",
yaml.NewStringRNode(minTLSVersion))) when they differ so the update is
idempotent and consistent with cipher handling.

104-109: Minor: Redundant APIServer fetch.

The APIServer is fetched twice: once explicitly at Line 105, then again inside ObserveTLSSecurityProfile via the lister adapter. Consider removing the explicit check since ObserveTLSSecurityProfile already handles missing config gracefully.

 func (b *builder) observeTLSConfiguration(ctx context.Context, cm *corev1.ConfigMap) (minTLSVersion string, minTLSFound bool, cipherSuites []string, ciphersFound bool) {
-	// First check if the APIServer cluster resource exists
-	_, err := b.configClientv1.APIServers().Get(ctx, "cluster", metav1.GetOptions{})
-	if err != nil {
-		klog.V(2).Infof("ConfigMap %s/%s: APIServer cluster resource not found, skipping TLS injection: %v", cm.Namespace, cm.Name, err)
-		return "", false, nil, false
-	}
-
 	// Create a lister adapter for ObserveTLSSecurityProfile
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/resourcebuilder/core.go` around lines 104 - 109, Remove the redundant
explicit APIServer fetch block that calls b.configClientv1.APIServers().Get and
its error handling/klog/return; rely on ObserveTLSSecurityProfile (and its
lister adapter) to detect a missing APIServer and handle it gracefully.
Specifically, delete the lines invoking b.configClientv1.APIServers().Get(ctx,
"cluster", metav1.GetOptions{}) and the subsequent if err != nil { ... return
... } block so the function flow continues to ObserveTLSSecurityProfile without
an extra fetch.
lib/resourcebuilder/core_test.go (1)

177-193: Minor: Variable shadowing reduces readability.

The inner found variable at Line 183 shadows the outer found from Line 173. While not a bug, consider renaming to cipherFound for clarity.

-	for _, expectedCipher := range expectedCiphers {
-		found := false
-		for _, cipher := range cipherSuites {
-			if cipher == expectedCipher {
-				found = true
-				break
-			}
-		}
-		if !found {
+	for _, expectedCipher := range expectedCiphers {
+		cipherFound := false
+		for _, cipher := range cipherSuites {
+			if cipher == expectedCipher {
+				cipherFound = true
+				break
+			}
+		}
+		if !cipherFound {

Alternatively, use slices.Contains:

if !slices.Contains(cipherSuites, expectedCipher) {
    return fmt.Errorf("expected cipher %s not found in cipherSuites: %v", expectedCipher, cipherSuites)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/resourcebuilder/core_test.go` around lines 177 - 193, The inner loop uses
a variable named "found" which shadows the outer "found" (from the outer scope)
and reduces readability; rename the inner boolean to "cipherFound" (or similar)
in the loop that iterates over cipherSuites to avoid shadowing, or replace the
whole inner loop by calling slices.Contains(cipherSuites, expectedCipher) to
check presence and simplify the logic in the test that iterates over
expectedCiphers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/resourcebuilder/core.go`:
- Around line 70-72: The log message in the ConfigMap handling branch (where
kind != "GenericOperatorConfig") contains a typo ("skiping"); update the
klog.V(4).Infof call string in the code path that checks kind (the conditional
using variable kind) to correct "skiping" to "skipping" so the message logged by
klog.V(4).Infof accurately reads "skipping this entry".

---

Nitpick comments:
In `@lib/resourcebuilder/core_test.go`:
- Around line 177-193: The inner loop uses a variable named "found" which
shadows the outer "found" (from the outer scope) and reduces readability; rename
the inner boolean to "cipherFound" (or similar) in the loop that iterates over
cipherSuites to avoid shadowing, or replace the whole inner loop by calling
slices.Contains(cipherSuites, expectedCipher) to check presence and simplify the
logic in the test that iterates over expectedCiphers.

In `@lib/resourcebuilder/core.go`:
- Around line 163-168: The code unconditionally sets minTLSVersion when
minTLSFound is true, unlike the cipherSuites logic which first compares existing
vs new value; update the block around minTLSFound/minTLSVersion and
servingInfo.PipeE to read the current "minTLSVersion" (via yaml.Field/Get or the
same helper used for cipherSuites), compare it to minTLSVersion, and only call
servingInfo.PipeE(yaml.SetField("minTLSVersion",
yaml.NewStringRNode(minTLSVersion))) when they differ so the update is
idempotent and consistent with cipher handling.
- Around line 104-109: Remove the redundant explicit APIServer fetch block that
calls b.configClientv1.APIServers().Get and its error handling/klog/return; rely
on ObserveTLSSecurityProfile (and its lister adapter) to detect a missing
APIServer and handle it gracefully. Specifically, delete the lines invoking
b.configClientv1.APIServers().Get(ctx, "cluster", metav1.GetOptions{}) and the
subsequent if err != nil { ... return ... } block so the function flow continues
to ObserveTLSSecurityProfile without an extra fetch.

ℹ️ 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 4ae1b14 and 1718c36.

📒 Files selected for processing (2)
  • lib/resourcebuilder/core.go
  • lib/resourcebuilder/core_test.go

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Mar 2, 2026

@ingvagabund: This pull request references CNTRLPLANE-2777 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 epic to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Design notes:

  • any operator built on top of library-go's NewControllerCommandConfig gets --config flag where operatorv1alpha1.GenericOperatorConfig manifest is expected.
  • operatorv1alpha1.GenericOperatorConfig has a .servingInfo.minTLSVersion and .servingInfo.cipherSuites where CVO can inject the centralized TLS configuration.
  • example: https://github.com/openshift/cluster-etcd-operator/blob/main/manifests/0000_20_etcd-operator_03_configmap.yaml#L14-L21
  • CVO's lib/resourcebuilder.builder can be extended to inject the tls configuration the same way as proxy injection is.
  • CVO is expected to reconcile every 2 minutes? So eventually all CM's annotated with config.openshift.io/inject-tls can be injected with the TLS configuration.

Operators with the tls-inject annotation:

How to test it:

Summary by CodeRabbit

  • New Features

  • ConfigMaps can be annotated to automatically receive TLS settings (min TLS version and cipher suites) injected from cluster security profile, preserving other data.

  • Tests

  • Added comprehensive tests covering annotation states, mixed data formats, idempotence, and TLS field validation.

  • Chores

  • Updated dependency declarations to support Kubernetes/OpenShift integrations.

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-robot
Copy link
Contributor

openshift-ci-robot commented Mar 2, 2026

@ingvagabund: This pull request references CNTRLPLANE-2777 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 epic to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Design notes:

  • any operator built on top of library-go's NewControllerCommandConfig gets --config flag where operatorv1alpha1.GenericOperatorConfig manifest is expected.
  • operatorv1alpha1.GenericOperatorConfig has a .servingInfo.minTLSVersion and .servingInfo.cipherSuites where CVO can inject the centralized TLS configuration.
  • example: https://github.com/openshift/cluster-etcd-operator/blob/main/manifests/0000_20_etcd-operator_03_configmap.yaml#L14-L21
  • CVO's lib/resourcebuilder.builder can be extended to inject the tls configuration the same way as proxy injection is.
  • CVO is expected to reconcile every 2 minutes? So eventually all CM's annotated with config.openshift.io/inject-tls can be injected with the TLS configuration.

Operators with the tls-inject annotation:

How to test it:

spec:
audit:
  profile: Default
tlsSecurityProfile:
  modern: {}
  type: Modern

Summary by CodeRabbit

  • New Features

  • ConfigMaps can be annotated to automatically receive TLS settings (min TLS version and cipher suites) injected from cluster security profile, preserving other data.

  • Tests

  • Added comprehensive tests covering annotation states, mixed data formats, idempotence, and TLS field validation.

  • Chores

  • Updated dependency declarations to support Kubernetes/OpenShift integrations.

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-robot
Copy link
Contributor

openshift-ci-robot commented Mar 2, 2026

@ingvagabund: This pull request references CNTRLPLANE-2777 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 epic to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Design notes:

  • any operator built on top of library-go's NewControllerCommandConfig gets --config flag where operatorv1alpha1.GenericOperatorConfig manifest is expected.
  • operatorv1alpha1.GenericOperatorConfig has a .servingInfo.minTLSVersion and .servingInfo.cipherSuites where CVO can inject the centralized TLS configuration.
  • example: https://github.com/openshift/cluster-etcd-operator/blob/main/manifests/0000_20_etcd-operator_03_configmap.yaml#L14-L21
  • CVO's lib/resourcebuilder.builder can be extended to inject the tls configuration the same way as proxy injection is.
  • CVO is expected to reconcile every 2 minutes? So eventually all CM's annotated with config.openshift.io/inject-tls can be injected with the TLS configuration.

Operators with the tls-inject annotation:

How to test it:

  • provision a cluster with the operator PRs included
  • follow https://access.redhat.com/solutions/6955910 to make the CVO unmanaged + increase log level to 4
  • test 1: change the TLS to Moden (oc edit apiserver cluster):
spec:
tlsSecurityProfile:
  modern: {}
  type: Modern

Summary by CodeRabbit

  • New Features

  • ConfigMaps can be annotated to automatically receive TLS settings (min TLS version and cipher suites) injected from cluster security profile, preserving other data.

  • Tests

  • Added comprehensive tests covering annotation states, mixed data formats, idempotence, and TLS field validation.

  • Chores

  • Updated dependency declarations to support Kubernetes/OpenShift integrations.

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-robot
Copy link
Contributor

openshift-ci-robot commented Mar 2, 2026

@ingvagabund: This pull request references CNTRLPLANE-2777 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 epic to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Design notes:

  • any operator built on top of library-go's NewControllerCommandConfig gets --config flag where operatorv1alpha1.GenericOperatorConfig manifest is expected.
  • operatorv1alpha1.GenericOperatorConfig has a .servingInfo.minTLSVersion and .servingInfo.cipherSuites where CVO can inject the centralized TLS configuration.
  • example: https://github.com/openshift/cluster-etcd-operator/blob/main/manifests/0000_20_etcd-operator_03_configmap.yaml#L14-L21
  • CVO's lib/resourcebuilder.builder can be extended to inject the tls configuration the same way as proxy injection is.
  • CVO is expected to reconcile every 2 minutes? So eventually all CM's annotated with config.openshift.io/inject-tls can be injected with the TLS configuration.

Operators with the tls-inject annotation:

How to test it:

  • provision a cluster with the operator PRs included
  • follow https://access.redhat.com/solutions/6955910 to make the CVO unmanaged + increase log level to 4
  • test 1: change the TLS to Moden (oc edit apiserver cluster):
spec:
  tlsSecurityProfile:
    modern: {}
    type: Modern

Summary by CodeRabbit

  • New Features

  • ConfigMaps can be annotated to automatically receive TLS settings (min TLS version and cipher suites) injected from cluster security profile, preserving other data.

  • Tests

  • Added comprehensive tests covering annotation states, mixed data formats, idempotence, and TLS field validation.

  • Chores

  • Updated dependency declarations to support Kubernetes/OpenShift integrations.

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.

@ingvagabund ingvagabund force-pushed the configmap-tls-injection branch from a298b49 to 3e06bb6 Compare March 24, 2026 12:08
@openshift-ci openshift-ci bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Mar 24, 2026
@ingvagabund
Copy link
Member Author

I have incorporated your changes from #1350. These looks good.

@ingvagabund
Copy link
Member Author

This is me being thorough. I need to check whether we have missed a corner case somewhere. If you believe there is a missing test case, feel free to add it. In case I find a missing test case for a necessary functionality, I will let you know. However, I would rather be defensive with testing around such an area.

Gotcha. I will give it another round of microlook to see if there's more that needs to be testing in case the underlying library-go's functionality changes.

So what is left:

  • include v1.GenericControllerConfig in the list of valid manifests
  • add more defensive testing

@ingvagabund
Copy link
Member Author

Adding GenericOperatorConfig into the list of supported configs. With openshift/cluster-image-registry-operator#1297 merged it should be sufficient to check the e2e logs to the right injection.

// updateRNodeWithTLSSettings injects TLS settings into a GenericOperatorConfig RNode while preserving structure.
// If a field in tlsConf is not found, the corresponding field will be deleted from the RNode.
func updateRNodeWithTLSSettings(rnode *yaml.RNode, tlsConf *tlsConfig) error {
servingInfo, err := rnode.Pipe(yaml.LookupCreate(yaml.MappingNode, "servingInfo"))
Copy link
Contributor

@DavidHurta DavidHurta Mar 24, 2026

Choose a reason for hiding this comment

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

I have seen your comment.

Feel free to add such logic. Something like this should hopefully work:

Suggested change
servingInfo, err := rnode.Pipe(yaml.LookupCreate(yaml.MappingNode, "servingInfo"))
// If neither field is found, check if servingInfo exists
if !tlsConf.cipherSuites.found && !tlsConf.minTLSVersion.found {
// Only proceed if servingInfo already exists (to clear fields)
servingInfo, err := rnode.Pipe(yaml.Lookup("servingInfo"))
if err != nil {
return err
}
// servingInfo doesn't exist, nothing to do
if servingInfo == nil {
return nil
}
// servingInfo exists, clear both fields
if err := servingInfo.PipeE(yaml.Clear("cipherSuites")); err != nil {
return err
}
if err := servingInfo.PipeE(yaml.Clear("minTLSVersion")); err != nil {
return err
}
return nil
}
// At least one field is found, so create servingInfo if needed
servingInfo, err := rnode.Pipe(yaml.LookupCreate(yaml.MappingNode, "servingInfo"))

with the updated unit test and a new test case/s for this new logic.

Copy link
Contributor

Choose a reason for hiding this comment

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

I am not aware of the impact of the existing logic. If it's a no-op, we can leave it as is due to time constraints. If we are not certain, we can implement the logic.

Copy link
Member Author

Choose a reason for hiding this comment

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

Minor issue. Given both fields are always present this is no-op. Please ignore.

@@ -0,0 +1,223 @@
package resourcebuilder
Copy link
Contributor

Choose a reason for hiding this comment

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

For the existing logic, I am proposing to add small tests for:

TestModifyConfigMap

  • ConfigMap with invalid YAML - no modification
    • to check explicitly YAML parsing errors
  • ConfigMap with wrong apiVersion - no modification

Copy link
Member Author

Choose a reason for hiding this comment

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

I will update the PR tomorrow

@DavidHurta
Copy link
Contributor

Will you be able to verify the PR after the approval?

@ingvagabund
Copy link
Member Author

ingvagabund commented Mar 24, 2026

From https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/pr-logs/pull/openshift_cluster-version-operator/1322/pull-ci-openshift-cluster-version-operator-main-e2e-agnostic-operator/2036441175245721600/artifacts/e2e-agnostic-operator/gather-extra/artifacts/configmaps.json

{
    "apiVersion": "v1",
    "data": {
        "config.yaml": "apiVersion: config.openshift.io/v1\nkind: GenericControllerConfig\nservingInfo:\n  bindAddress: \":60000\"\n  cipherSuites:\n  - TLS_AES_128_GCM_SHA256\n  - TLS_AES_256_GCM_SHA384\n  - TLS_CHACHA20_POLY1305_SHA256\n  - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\n  - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\n  - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\n  - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n  - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n  - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n  minTLSVersion: VersionTLS12\n"
    },
    "kind": "ConfigMap",
    "metadata": { 
        ...
        "name": "image-registry-operator-config",
        "namespace": "openshift-image-registry",

@ingvagabund
Copy link
Member Author

Will you be able to verify the PR after the approval?

Lemme first introduce the new unit tests and squash the commits.

@ingvagabund
Copy link
Member Author

ingvagabund commented Mar 25, 2026

@DavidHurta I kept the original commits in #1359 for the transparency. Also adding another change to return error when APIServer CR is not found (not respecting the library-go's ObserveTLSSecurityProfile defaults) to avoid unexpected flips to TLS configuration that does not adhere to the centralized one.

@DavidHurta
Copy link
Contributor

I prefer the original approach of using good defaults and depending on the library-go logic, which was crafted by library and security folks in collaboration. A missing TLS profile or a missing APIServer object will result in the default TLS profile.

This removes any maintenance from the CVO and provides increased availability for the cluster upgrade and reconciliation.

The proposed approach also potentially creates a bug. In case there is no Validating Webhook ensuring users cannot delete the APIServer object, a user may delete the object. If a ConfigMap then would like to have the TLS injected, and its runlevel is lower than the APIServer CR manifest file, the cluster administrator will have to create the APIServer object manually to resolve the issue, as the CVO may get hung up, as the CVO will not move forward to its creation of the APIServer object. This potentially creates a permanent issue, not a transient one.

Checking quay.io/openshift-release-dev/ocp-release:4.21.2-x86_64:

$ ls -la release-manifests/ | grep apiserver.cr
-rw-r-----.  1 dhurta dhurta     469 Feb  9 16:28 0000_05_config-operator_02_apiserver.cr.yaml

Checking potentially stale documentation:

Assigned runlevels

00-04 - CVO
05 - cluster-config-operator
07 - Network operator
08 - DNS operator
09 - Service certificate authority and machine approver
10-29 - Kubernetes operators (master team)
30-39 - Machine API
50-59 - Operator-lifecycle manager
60-69 - OpenShift core operators (master team)

Thus, if the cluster config operator would like to inject the TLS into its ConfigMap while the APIServer is missing, and the runlevel would be such that it would not allow it, cluster administrators would have to reach out to Red Hat to manually solve the issue and apply the manifest. This is a corner case and may not even be possible if there is a Validating Webhook to prevent this. I do not know. For these reasons, I would prefer to depend on the library-go for only transient issues and a lower maintenance cost for the CVO team.

This can also result in additional errors during installation where the runlevels are more relaxed. Nothing we would get blocked on, though. Although a further investigation would be required to check whether erroring out or redundantly restarting components increases the installation time.

However, yes, this may result in the dependent operators even restarting twice on blips if configured to restart to pick a new configuration.


to avoid unexpected flips to TLS configuration that does not adhere to the centralized one.

Technically, if there is no centralized configuration, then there is nothing to adhere to, and it is up to the components to decide whether to fall back to defaults or fail connections, IMO.


Thank you for squashing the commits; however, the attribution to me (Lines changed: 64 additions & 94 deletions) and, primarily, Claude (Lines changed: 230 additions & 2 deletions) was lost during the process.

@ingvagabund
Copy link
Member Author

If a ConfigMap then would like to have the TLS injected, and its runlevel is lower than the APIServer CR manifest file, the cluster administrator will have to create the APIServer object manually to resolve the issue,

Good point. In that case the code has to default. I will revert the change.

@ingvagabund ingvagabund force-pushed the configmap-tls-injection branch from befe25e to 11a9781 Compare March 25, 2026 20:25
@ingvagabund
Copy link
Member Author

@DavidHurta adding back your two commits (one from you, one co-authored by Claude).

@ingvagabund
Copy link
Member Author

Once you approve the changes I will ask our QEs for validation in a testing cluster 🤞

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Mar 25, 2026

@ingvagabund: 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-agnostic-ovn-techpreview-serial 0e3d09e link true /test e2e-agnostic-ovn-techpreview-serial

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.

@ingvagabund
Copy link
Member Author

/retest-required

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Mar 26, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Mar 26, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: DavidHurta, ingvagabund

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 26, 2026
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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants