Skip to content

WIP: STOR-2962: Save SELinuxWarningController upgradeability to a ConfigMap#2671

Draft
jsafrane wants to merge 2 commits into
openshift:masterfrom
jsafrane:kcm-selinux-configmap
Draft

WIP: STOR-2962: Save SELinuxWarningController upgradeability to a ConfigMap#2671
jsafrane wants to merge 2 commits into
openshift:masterfrom
jsafrane:kcm-selinux-configmap

Conversation

@jsafrane
Copy link
Copy Markdown

@jsafrane jsafrane commented May 19, 2026

KCM's SELinuxWarningController knows how many Pods could get broken by upgrade to Kubernetes 1.37 / a version where SELinuxMount feature gate is enabled.

Add a carry patch to KCM to store the information into a ConfigMap openshift-config/selinux-conflicts.
cluster-storage-operator can read it from there and mark itself Upgradeable: false.

See openshift/enhancements#2010 for details.

  • The first commit is a clean upstream cherry-pick of few PRs that are / will be available in 1.36.something.
  • The second patch is the actual <carry>. Written in a way that minimizes a possibility of a conflict with future Kubernetes patches.

Summary by CodeRabbit

  • New Features

    • Adds upgrade readiness reporter that records whether SELinux-conflicting pods exist to a cluster ConfigMap.
    • Exposes a way to query the total number of active SELinux conflicts.
  • Improvements

    • Parses and caches SELinux labels once for faster conflict detection.
    • Cache now returns aggregated conflicts (deduplicated) and maintains pod↔volume indexing.
  • Tests

    • Expanded tests for multi-volume conflicts and pod-deletion cleanup.
  • Chores

    • RBAC updated to allow the controller to create/patch the ConfigMap.

@openshift-ci-robot openshift-ci-robot added jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. backports/unvalidated-commits Indicates that not all commits come to merged upstream PRs. labels May 19, 2026
@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented May 19, 2026

@jsafrane: This pull request references STOR-2962 which is a valid jira issue.

Details

In response to this:

KCM's SELinuxWarningController knows how many Pods could get broken by upgrade to Kubernetes 1.37 / a version where SELinuxMount feature gate is enabled.

Add a carry patch to KCM to store the information into a ConfigMap openshift-config/selinux-warning-assessment.
cluster-storage-operator can read it from there and mark itself Upgradeable: false.

See openshift/enhancements#2010 for details.

  • The first commit is a clean upstream cherry-pick of few PRs that are / will be available in 1.36.something.
  • The second patch is the actual <carry>. Written in a way that minimizes a possibility of a conflict with future Kubernetes patches.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label May 19, 2026
@jsafrane jsafrane marked this pull request as draft May 19, 2026 13:48
@openshift-ci-robot
Copy link
Copy Markdown

@jsafrane: the contents of this pull request could not be automatically validated.

The following commits could not be validated and must be approved by a top-level approver:

Comment /validate-backports to re-evaluate validity of the upstream PRs, for example when they are merged upstream.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

Walkthrough

Refactors the SELinux volume conflict cache to a query-based API with parsed-label detection and reverse indexing, exposes a conflict-count interface, updates metrics and tests, and adds a periodic OpenShift reporter that patches/creates a ConfigMap indicating whether conflicts are present.

Changes

SELinux Cache Refactoring and OpenShift Reporter

Layer / File(s) Summary
SELinux label parsing optimization
pkg/controller/volume/selinuxwarning/translator/selinux_translator.go
Introduces ConflictsParsed(partsA,partsB) and refactors Conflicts to parse labels once and delegate to the parsed comparison.
Volume cache API and internal state refactoring
pkg/controller/volume/selinuxwarning/cache/volumecache.go, pkg/controller/volume/selinuxwarning/cache/volumecache_test.go
Replaces streaming SendConflicts with GetConflicts(logger), adds podToVolumes reverse index and per-volume conflicts cache, stores parsed seLinuxParts in pod info, updates AddVolume/DeletePod/dump to maintain indexes and prune conflicts, and extends tests (reverse-index consistency, multi-volume and delete scenarios).
Conflict counter interface
pkg/controller/volume/selinuxwarning/cache/openshift_patch.go
Adds ConflictCounter and implements GetConflictCount() on volumeCache that read-locks and sums per-volume conflict lengths; includes compile-time conformance assertion.
Metrics integration with new cache API
pkg/controller/volume/selinuxwarning/metrics.go
CollectWithStability now iterates over c.cache.GetConflicts(c.logger) instead of consuming a streamed channel.
Controller wiring and test support
pkg/controller/volume/selinuxwarning/selinux_warning_controller.go, pkg/controller/volume/selinuxwarning/selinux_warning_controller_test.go
Controller.Run launches the OpenShift reporter goroutine; tests update fakeVolumeCache to return aggregated conflicts via GetConflicts.
OpenShift upgrade reporter implementation
pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go
New periodic reporter maps conflict count to a metav1.ConditionStatus, patches or creates openshift-config/selinux-conflicts ConfigMap /data/conflictsPresent, and suppresses redundant writes by tracking previous status.
RBAC permissions for upgrade controller
plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go, plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml
Grants the selinux-warning-controller role create and patch on the selinux-warning-assessment/selinux-conflicts ConfigMap (resourceNames-scoped).

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 11 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main purpose: saving SELinux warning controller upgradeability to a ConfigMap, which aligns with the core changes across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed This PR contains no Ginkgo tests—only standard Go tests with static, deterministic test names. The custom check is not applicable.
Test Structure And Quality ✅ Passed PR uses standard Go tests, not Ginkgo. Check targets Ginkgo patterns; tests meet equivalent quality standards for single responsibility, setup, assertions, and no cluster timeouts needed.
Microshift Test Compatibility ✅ Passed No new Ginkgo e2e tests are added in this PR. All new tests are standard Go unit tests using *testing.T, not Ginkgo-based e2e tests. The check does not apply.
Single Node Openshift (Sno) Test Compatibility ✅ Passed This PR adds only standard Go unit tests (using testing.T) to cache logic in pkg/controller/volume/selinuxwarning/, not Ginkgo e2e tests. The check does not apply.
Topology-Aware Scheduling Compatibility ✅ Passed PR introduces no Pod scheduling constraints, deployments, or topology assumptions. Changes are controller loop and ConfigMap API operations only—topology-agnostic.
Ote Binary Stdout Contract ✅ Passed Check is not applicable; PR modifies only Kubernetes controller code without OTE binary entry points or process-level stdout writes.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e tests added. Only standard Go unit tests (func Test*(t *testing.T)) were added, which are not subject to this IPv6/disconnected network check.

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

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

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

@openshift-ci openshift-ci Bot requested review from bertinatto and jubittajohn May 19, 2026 13:49
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 19, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: jsafrane
Once this PR has been reviewed and has the lgtm label, please assign p0lyn0mial for approval. For more information see the Code Review Process.

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

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

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

@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented May 19, 2026

@jsafrane: This pull request references STOR-2962 which is a valid jira issue.

Details

In response to this:

KCM's SELinuxWarningController knows how many Pods could get broken by upgrade to Kubernetes 1.37 / a version where SELinuxMount feature gate is enabled.

Add a carry patch to KCM to store the information into a ConfigMap openshift-config/selinux-warning-assessment.
cluster-storage-operator can read it from there and mark itself Upgradeable: false.

See openshift/enhancements#2010 for details.

  • The first commit is a clean upstream cherry-pick of few PRs that are / will be available in 1.36.something.
  • The second patch is the actual <carry>. Written in a way that minimizes a possibility of a conflict with future Kubernetes patches.

Summary by CodeRabbit

Release Notes

  • New Features

  • Added upgrade readiness assessment that monitors SELinux conflicts and updates status in a ConfigMap.

  • Added conflict counter interface for querying total active conflicts.

  • Improvements

  • Optimized conflict detection by parsing SELinux labels once and caching results.

  • Refactored cache to return conflicts directly instead of streaming via channels.

  • Enhanced cache with pod-to-volume reverse indexing for improved consistency.

  • Tests

  • Expanded test coverage for multi-volume conflict scenarios and pod deletion workflows.

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
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go`:
- Around line 23-25: The package-global variable previousUnogradeable should be
moved onto the Controller type to avoid shared state across controller instances
and tests: remove the package-level previousUnogradeable declaration and add a
field (e.g., previousUnogradeable metav1.ConditionStatus) to the Controller
struct, initialize it in the controller constructor/New function, and update all
references in openshift_upgrade_controller.go (including the usages around lines
43-55) to use c.previousUnogradeable instead of the global name; ensure any
concurrent access follows the Controller's existing synchronization model if
needed.
- Around line 57-59: In getUpgradeability, avoid the unchecked assertion
c.labelCache.(cache.ConflictCounter); instead use a safe type assertion (e.g.,
cc, ok := c.labelCache.(cache.ConflictCounter)) and handle the !ok case by
logging via the provided logger and returning a safe failure status (for example
metav1.ConditionFalse) so the controller won't panic; when ok is true continue
using cc.GetConflictCount() as before.
- Around line 68-81: The current JSON patch built with json.Marshal uses an op
"replace" on path "/data" which fails if the ConfigMap exists without a data
field; change the patch to perform a merge-patch or use a JSON Patch "add" op
for the specific key instead of replacing /data. Update the code that builds
patch (the json.Marshal call that creates patch) and the subsequent call to
c.kubeClient.CoreV1().ConfigMaps(configMapNamespace).Patch(...) (currently using
types.JSONPatchType) so it either constructs a strategic/merge JSON object that
upserts the "upgradeable" key under data, or uses a JSON Patch with op "add" and
path "/data/upgradeable"; ensure configMapName and configMapNamespace are left
intact and error handling remains the same.

In `@plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go`:
- Line 567: The rule created by rbacv1helpers.NewRule("create",
"patch").Groups(legacyGroup).Resources("configmaps").RuleOrDie() is too broad;
restrict it to the specific assessment ConfigMap by adding
ResourceNames("selinux-warning-assessment") (i.e., change the chain to include
.ResourceNames("selinux-warning-assessment")) so the generated role matches the
golden least-privilege scope.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: 8e9778de-4b6b-4bc0-b002-00c6cdb7e155

📥 Commits

Reviewing files that changed from the base of the PR and between 73359c5 and cde26e2.

📒 Files selected for processing (10)
  • pkg/controller/volume/selinuxwarning/cache/openshift_patch.go
  • pkg/controller/volume/selinuxwarning/cache/volumecache.go
  • pkg/controller/volume/selinuxwarning/cache/volumecache_test.go
  • pkg/controller/volume/selinuxwarning/metrics.go
  • pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go
  • pkg/controller/volume/selinuxwarning/selinux_warning_controller.go
  • pkg/controller/volume/selinuxwarning/selinux_warning_controller_test.go
  • pkg/controller/volume/selinuxwarning/translator/selinux_translator.go
  • plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go
  • plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml

Comment on lines +23 to +25
var (
previousUnogradeable metav1.ConditionStatus = metav1.ConditionUnknown
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Move upgradeability state off package-global storage.

previousUnogradeable being package-global makes state shared across controller instances and test cases. Keep this on Controller to avoid cross-instance leakage and concurrency hazards.

Suggested fix
- var (
- 	previousUnogradeable metav1.ConditionStatus = metav1.ConditionUnknown
- )
+ // in Controller struct (pkg/controller/volume/selinuxwarning/selinux_warning_controller.go):
+ // previousUpgradeable metav1.ConditionStatus

 func (c *Controller) checkForOpenShiftUpgrade(ctx context.Context) {
@@
-	if currentUpgradeable == previousUnogradeable {
+	if currentUpgradeable == c.previousUpgradeable {
@@
-	previousUnogradeable = currentUpgradeable
+	c.previousUpgradeable = currentUpgradeable
 }

Also applies to: 43-55

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go` around
lines 23 - 25, The package-global variable previousUnogradeable should be moved
onto the Controller type to avoid shared state across controller instances and
tests: remove the package-level previousUnogradeable declaration and add a field
(e.g., previousUnogradeable metav1.ConditionStatus) to the Controller struct,
initialize it in the controller constructor/New function, and update all
references in openshift_upgrade_controller.go (including the usages around lines
43-55) to use c.previousUnogradeable instead of the global name; ensure any
concurrent access follows the Controller's existing synchronization model if
needed.

Comment on lines +57 to +59
func (c *Controller) getUpgradeability(logger klog.Logger) metav1.ConditionStatus {
conflictsCount := c.labelCache.(cache.ConflictCounter).GetConflictCount()
if conflictsCount > 0 {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid unchecked type assertion on labelCache.

c.labelCache.(cache.ConflictCounter) can panic if labelCache is swapped with an implementation that doesn’t satisfy ConflictCounter. Guard the assertion and fail safely.

Suggested fix
 func (c *Controller) getUpgradeability(logger klog.Logger) metav1.ConditionStatus {
-	conflictsCount := c.labelCache.(cache.ConflictCounter).GetConflictCount()
+	conflictCounter, ok := c.labelCache.(cache.ConflictCounter)
+	if !ok {
+		logger.Error(nil, "labelCache does not implement ConflictCounter")
+		return metav1.ConditionUnknown
+	}
+	conflictsCount := conflictCounter.GetConflictCount()
 	if conflictsCount > 0 {
📝 Committable suggestion

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

Suggested change
func (c *Controller) getUpgradeability(logger klog.Logger) metav1.ConditionStatus {
conflictsCount := c.labelCache.(cache.ConflictCounter).GetConflictCount()
if conflictsCount > 0 {
func (c *Controller) getUpgradeability(logger klog.Logger) metav1.ConditionStatus {
conflictCounter, ok := c.labelCache.(cache.ConflictCounter)
if !ok {
logger.Error(nil, "labelCache does not implement ConflictCounter")
return metav1.ConditionUnknown
}
conflictsCount := conflictCounter.GetConflictCount()
if conflictsCount > 0 {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go` around
lines 57 - 59, In getUpgradeability, avoid the unchecked assertion
c.labelCache.(cache.ConflictCounter); instead use a safe type assertion (e.g.,
cc, ok := c.labelCache.(cache.ConflictCounter)) and handle the !ok case by
logging via the provided logger and returning a safe failure status (for example
metav1.ConditionFalse) so the controller won't panic; when ok is true continue
using cc.GetConflictCount() as before.

Comment on lines +68 to +81
patch, err := json.Marshal([]map[string]any{
{
"op": "replace",
"path": "/data",
"value": map[string]string{
"upgradeable": string(upgradeable),
},
},
})
if err != nil {
return fmt.Errorf("error building config map %s patch: %w", configMapName, err)
}
_, err = c.kubeClient.CoreV1().ConfigMaps(configMapNamespace).Patch(ctx, configMapName, types.JSONPatchType, patch, metav1.PatchOptions{})
if err != nil {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use merge-patch (or add op) instead of replace /data.

replace on /data fails when the target ConfigMap exists without data. This makes updates unnecessarily brittle. Use merge-patch for idempotent upsert behavior of the key.

Suggested fix
-	patch, err := json.Marshal([]map[string]any{
-		{
-			"op":   "replace",
-			"path": "/data",
-			"value": map[string]string{
-				"upgradeable": string(upgradeable),
-			},
-		},
-	})
+	patch, err := json.Marshal(map[string]any{
+		"data": map[string]string{
+			"upgradeable": string(upgradeable),
+		},
+	})
@@
-	_, err = c.kubeClient.CoreV1().ConfigMaps(configMapNamespace).Patch(ctx, configMapName, types.JSONPatchType, patch, metav1.PatchOptions{})
+	_, err = c.kubeClient.CoreV1().ConfigMaps(configMapNamespace).Patch(ctx, configMapName, types.MergePatchType, patch, metav1.PatchOptions{})
📝 Committable suggestion

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

Suggested change
patch, err := json.Marshal([]map[string]any{
{
"op": "replace",
"path": "/data",
"value": map[string]string{
"upgradeable": string(upgradeable),
},
},
})
if err != nil {
return fmt.Errorf("error building config map %s patch: %w", configMapName, err)
}
_, err = c.kubeClient.CoreV1().ConfigMaps(configMapNamespace).Patch(ctx, configMapName, types.JSONPatchType, patch, metav1.PatchOptions{})
if err != nil {
patch, err := json.Marshal(map[string]any{
"data": map[string]string{
"upgradeable": string(upgradeable),
},
})
if err != nil {
return fmt.Errorf("error building config map %s patch: %w", configMapName, err)
}
_, err = c.kubeClient.CoreV1().ConfigMaps(configMapNamespace).Patch(ctx, configMapName, types.MergePatchType, patch, metav1.PatchOptions{})
if err != nil {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go` around
lines 68 - 81, The current JSON patch built with json.Marshal uses an op
"replace" on path "/data" which fails if the ConfigMap exists without a data
field; change the patch to perform a merge-patch or use a JSON Patch "add" op
for the specific key instead of replacing /data. Update the code that builds
patch (the json.Marshal call that creates patch) and the subsequent call to
c.kubeClient.CoreV1().ConfigMaps(configMapNamespace).Patch(...) (currently using
types.JSONPatchType) so it either constructs a strategic/merge JSON object that
upserts the "upgradeable" key under data, or uses a JSON Patch with op "add" and
path "/data/upgradeable"; ensure configMapName and configMapNamespace are left
intact and error handling remains the same.

rbacv1helpers.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("persistentvolumeclaims").RuleOrDie(),
rbacv1helpers.NewRule("get", "list", "watch").Groups(legacyGroup).Resources("pods").RuleOrDie(),
rbacv1helpers.NewRule("get", "list", "watch").Groups(storageGroup).Resources("csidrivers").RuleOrDie(),
rbacv1helpers.NewRule("create", "patch").Groups(legacyGroup).Resources("configmaps").RuleOrDie(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope ConfigMap permissions to the assessment object (and align with golden).

This rule grants create/patch on all ConfigMaps, while the paired golden role scopes to selinux-warning-assessment. Please align policy generation with the intended least-privilege scope.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go` at line
567, The rule created by rbacv1helpers.NewRule("create",
"patch").Groups(legacyGroup).Resources("configmaps").RuleOrDie() is too broad;
restrict it to the specific assessment ConfigMap by adding
ResourceNames("selinux-warning-assessment") (i.e., change the chain to include
.ResourceNames("selinux-warning-assessment")) so the generated role matches the
golden least-privilege scope.

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 19, 2026

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

Test name Commit Details Required Rerun command
ci/prow/integration cde26e2 link true /test integration
ci/prow/e2e-aws-ovn-techpreview-serial-2of2 cde26e2 link false /test e2e-aws-ovn-techpreview-serial-2of2
ci/prow/e2e-aws-ovn-techpreview cde26e2 link false /test e2e-aws-ovn-techpreview
ci/prow/images cde26e2 link true /test images
ci/prow/e2e-metal-ipi-ovn-ipv6 cde26e2 link true /test e2e-metal-ipi-ovn-ipv6
ci/prow/e2e-aws-ovn-serial-1of2 cde26e2 link true /test e2e-aws-ovn-serial-1of2
ci/prow/e2e-gcp cde26e2 link true /test e2e-gcp

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.

…ConfigMap

To upgrade OCP to Kubernetes 1.37 with SELinuxMount enabled, we need to
ensure there are no user workloads that could get broken by the feature
gate. SELinuxWarningController in KCP has the information and emits it as a
metric.

To mark the cluster un-upgradeable easily using API objects, store the
information as a ConfigMap too. Reading metrics in an operator is too
complicated.
@jsafrane jsafrane force-pushed the kcm-selinux-configmap branch from cde26e2 to 1432d8b Compare May 20, 2026 13:55
@openshift-ci-robot
Copy link
Copy Markdown

@jsafrane: the contents of this pull request could not be automatically validated.

The following commits could not be validated and must be approved by a top-level approver:

Comment /validate-backports to re-evaluate validity of the upstream PRs, for example when they are merged upstream.

@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented May 20, 2026

@jsafrane: This pull request references STOR-2962 which is a valid jira issue.

Details

In response to this:

KCM's SELinuxWarningController knows how many Pods could get broken by upgrade to Kubernetes 1.37 / a version where SELinuxMount feature gate is enabled.

Add a carry patch to KCM to store the information into a ConfigMap openshift-config/selinux-conflicts.
cluster-storage-operator can read it from there and mark itself Upgradeable: false.

See openshift/enhancements#2010 for details.

  • The first commit is a clean upstream cherry-pick of few PRs that are / will be available in 1.36.something.
  • The second patch is the actual <carry>. Written in a way that minimizes a possibility of a conflict with future Kubernetes patches.

Summary by CodeRabbit

Release Notes

  • New Features

  • Added upgrade readiness assessment that monitors SELinux conflicts and updates status in a ConfigMap.

  • Added conflict counter interface for querying total active conflicts.

  • Improvements

  • Optimized conflict detection by parsing SELinux labels once and caching results.

  • Refactored cache to return conflicts directly instead of streaming via channels.

  • Enhanced cache with pod-to-volume reverse indexing for improved consistency.

  • Tests

  • Expanded test coverage for multi-volume conflict scenarios and pod deletion workflows.

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
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go (1)

567-567: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope ConfigMap permissions to the single assessment object.

This grants create/patch on all configmaps. Restrict with .ResourceNames("selinux-warning-assessment") to enforce least privilege and align policy output.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go` at line
567, The rule currently grants create/patch on all configmaps via
rbacv1helpers.NewRule("create",
"patch").Groups(legacyGroup).Resources("configmaps").RuleOrDie(); restrict it to
only the single assessment object by adding
.ResourceNames("selinux-warning-assessment") to that rule so it becomes
rbacv1helpers.NewRule(...).Groups(legacyGroup).Resources("configmaps").ResourceNames("selinux-warning-assessment").RuleOrDie(),
thereby limiting permissions to the specific ConfigMap.
pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go (3)

23-25: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid package-global status state for controller instance logic.

previousConflicts as package-global shares state across controller instances/tests. Keep it on Controller instead.

Also applies to: 43-55

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go` around
lines 23 - 25, The package-global variable previousConflicts
(metav1.ConditionStatus) creates shared state; move it into the Controller
struct as a field (e.g., Controller.previousConflicts) and remove the global
declaration; update all references (previousConflicts -> c.previousConflicts)
inside methods such as those changed around lines 43-55 and initialize it via
the Controller constructor (NewController) or rely on the zero-value
metav1.ConditionUnknown; also update any tests to construct Controller with the
desired initial previousConflicts value instead of relying on package-global
state.

82-95: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Patch strategy is brittle for ConfigMaps missing .data.

replace on /data can fail when the field is absent. Use merge-patch (or JSON Patch add on the key) for idempotent updates.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go` around
lines 82 - 95, The JSON Patch uses a "replace" on "/data" which fails if .data
is missing; instead build a merge patch that sets data.conflictsPresent (e.g.
marshal map[string]any{"data": map[string]string{"conflictsPresent":
string(conflictsPresent)}}) and call Patch with types.MergePatchType, or switch
to a JSON Patch "add" op targeting "/data/conflictsPresent" so the update is
idempotent; update the code around the json.Marshal call and the Patch
invocation that uses c.kubeClient.CoreV1().ConfigMaps(configMapNamespace).Patch
to use the merge patch payload and types.MergePatchType (or use the "add" JSON
Patch op) referencing configMapName, configMapNamespace and conflictsPresent.

57-59: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unchecked type assertion can panic.

Use a safe assertion before calling GetConflictCount() to avoid runtime panic when labelCache is swapped with a non-ConflictCounter implementation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go` around
lines 57 - 59, In getConflicts, replace the unchecked type assertion on
c.labelCache to cache.ConflictCounter with a safe comma-ok assertion (e.g., cc,
ok := c.labelCache.(cache.ConflictCounter)); if ok use cc.GetConflictCount(),
otherwise log the mismatch via the provided logger and treat conflictsCount as 0
(returning metav1.ConditionFalse) to avoid a panic; reference getConflicts,
c.labelCache, cache.ConflictCounter, and GetConflictCount when making the
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go`:
- Around line 19-21: The ConfigMap name constant configMapName is set to
"selinux-conflicts" but the RBAC resourceNames scope targets
"selinux-warning-assessment", causing create/patch calls to be denied; update
the constant configMapName to "selinux-warning-assessment" (or alternatively
adjust the RBAC resourceNames to match the current constant) so the value used
in the controller (configMapName and any code that references it) matches the
RBAC-scoped object name.

---

Duplicate comments:
In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go`:
- Around line 23-25: The package-global variable previousConflicts
(metav1.ConditionStatus) creates shared state; move it into the Controller
struct as a field (e.g., Controller.previousConflicts) and remove the global
declaration; update all references (previousConflicts -> c.previousConflicts)
inside methods such as those changed around lines 43-55 and initialize it via
the Controller constructor (NewController) or rely on the zero-value
metav1.ConditionUnknown; also update any tests to construct Controller with the
desired initial previousConflicts value instead of relying on package-global
state.
- Around line 82-95: The JSON Patch uses a "replace" on "/data" which fails if
.data is missing; instead build a merge patch that sets data.conflictsPresent
(e.g. marshal map[string]any{"data": map[string]string{"conflictsPresent":
string(conflictsPresent)}}) and call Patch with types.MergePatchType, or switch
to a JSON Patch "add" op targeting "/data/conflictsPresent" so the update is
idempotent; update the code around the json.Marshal call and the Patch
invocation that uses c.kubeClient.CoreV1().ConfigMaps(configMapNamespace).Patch
to use the merge patch payload and types.MergePatchType (or use the "add" JSON
Patch op) referencing configMapName, configMapNamespace and conflictsPresent.
- Around line 57-59: In getConflicts, replace the unchecked type assertion on
c.labelCache to cache.ConflictCounter with a safe comma-ok assertion (e.g., cc,
ok := c.labelCache.(cache.ConflictCounter)); if ok use cc.GetConflictCount(),
otherwise log the mismatch via the provided logger and treat conflictsCount as 0
(returning metav1.ConditionFalse) to avoid a panic; reference getConflicts,
c.labelCache, cache.ConflictCounter, and GetConflictCount when making the
change.

In `@plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go`:
- Line 567: The rule currently grants create/patch on all configmaps via
rbacv1helpers.NewRule("create",
"patch").Groups(legacyGroup).Resources("configmaps").RuleOrDie(); restrict it to
only the single assessment object by adding
.ResourceNames("selinux-warning-assessment") to that rule so it becomes
rbacv1helpers.NewRule(...).Groups(legacyGroup).Resources("configmaps").ResourceNames("selinux-warning-assessment").RuleOrDie(),
thereby limiting permissions to the specific ConfigMap.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Enterprise

Run ID: 3b898633-0b8e-48a5-a9cc-d00e6e0c9b42

📥 Commits

Reviewing files that changed from the base of the PR and between cde26e2 and 1432d8b.

📒 Files selected for processing (5)
  • pkg/controller/volume/selinuxwarning/cache/openshift_patch.go
  • pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go
  • pkg/controller/volume/selinuxwarning/selinux_warning_controller.go
  • plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go
  • plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml

Comment on lines +19 to +21
configMapNamespace = "openshift-config"
configMapName = "selinux-conflicts"
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

ConfigMap name does not match the RBAC-scoped assessment object.

configMapName is selinux-conflicts, but this PR’s RBAC scope targets selinux-warning-assessment. With resourceNames scoping, patch/create will fail for the current name.

Suggested fix
 const (
 	checkInterval      = 30 * time.Second
 	configMapNamespace = "openshift-config"
-	configMapName      = "selinux-conflicts"
+	configMapName      = "selinux-warning-assessment"
 )
📝 Committable suggestion

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

Suggested change
configMapNamespace = "openshift-config"
configMapName = "selinux-conflicts"
)
const (
checkInterval = 30 * time.Second
configMapNamespace = "openshift-config"
configMapName = "selinux-warning-assessment"
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go` around
lines 19 - 21, The ConfigMap name constant configMapName is set to
"selinux-conflicts" but the RBAC resourceNames scope targets
"selinux-warning-assessment", causing create/patch calls to be denied; update
the constant configMapName to "selinux-warning-assessment" (or alternatively
adjust the RBAC resourceNames to match the current constant) so the value used
in the controller (configMapName and any code that references it) matches the
RBAC-scoped object name.

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

Labels

backports/unvalidated-commits Indicates that not all commits come to merged upstream PRs. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants