Skip to content

STOR-2963: Save SELinuxWarningController upgradeability to a ConfigMap#2671

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

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

Conversation

@jsafrane

@jsafrane jsafrane commented May 19, 2026

Copy link
Copy Markdown

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

    • SELinux conflict monitoring and reporting now available in volume controller, reporting status every 30 seconds.
    • New SELinuxMountGAReadiness feature gate added.
  • Chores

    • Updated RBAC permissions to support SELinux conflict reporting.
  • Tests

    • Added comprehensive test coverage for SELinux conflict reporting functionality.

@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

openshift-ci-robot commented May 19, 2026

Copy link
Copy Markdown

@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

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown

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

This PR adds a feature-gated SELinux conflict reporter that periodically reads cached conflicts, computes a present/absent condition, and writes conflictsPresent to the selinux-conflicts ConfigMap in openshift-config, with controller wiring, RBAC permissions, and targeted unit tests.

Changes

SELinux Conflicts Reporter

Layer / File(s) Summary
Cache conflict counting contract
pkg/controller/volume/selinuxwarning/cache/openshift_patch.go
Adds ConflictCounter and volumeCache.GetConflictCount() to expose aggregated conflict totals from cached entries.
Reporter loop and state persistence
pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go
Introduces periodic reporting, conflict status derivation, previous-status change detection, and ConfigMap persistence using patch-first with create-on-NotFound fallback.
Controller run-path integration
pkg/controller/volume/selinuxwarning/selinux_warning_controller.go
Launches the new reporter as an additional goroutine in Controller.Run.
Feature gate enablement path
pkg/features/openshift_features.go
Adds and registers SELinuxMountGAReadiness as an OpenShift feature gate with its versioned default spec and dependency list entry.
Access control updates
plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go, plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml
Adds cluster-role rules granting create and patch on the target ConfigMap and updates role testdata accordingly.
Behavioral test coverage
pkg/controller/volume/selinuxwarning/openshift_upgrade_controller_test.go
Adds table-driven tests for reporting transitions, no-op behavior when unchanged, patch/create application behavior, and conflict-to-condition mapping.

Sequence Diagram(s)

sequenceDiagram
  participant Reporter as runOpenShiftSELinuxConflictsReporter
  participant Cache as labelCache.ConflictCounter
  participant Cond as getConflicts
  participant Apply as applySELinuxConflictsConfigMap
  participant PatchCM as patchSELinuxConflictsConfigMap
  participant CreateCM as createSELinuxConflictsConfigMap

  loop every 30s
    Reporter->>Cache: GetConflictCount()
    Cache-->>Reporter: count
    Reporter->>Cond: getConflicts(count)
    Cond-->>Reporter: condition status
    Reporter->>Apply: applySELinuxConflictsConfigMap(status)
    Apply->>PatchCM: JSONPatch /data
    alt patch success
      PatchCM-->>Apply: success
    else patch NotFound
      Apply->>CreateCM: create ConfigMap with conflictsPresent
      CreateCM-->>Apply: success
    end
    Apply-->>Reporter: result
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • openshift/kubernetes#2668: Both changes operate on SELinux conflict cache behavior in volumeCache, and this PR’s reporting count logic depends on that cache structure.

Suggested reviewers

  • jerpeter1
🚥 Pre-merge checks | ✅ 13 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Structure And Quality ⚠️ Warning Global previousConflicts variable modified without cleanup causes test pollution. TestApplySELinuxConflictsConfigMap inconsistently uses context.Background() instead of ktesting.NewTestContext(t). Add cleanup resetting previousConflicts. Use ktesting.NewTestContext(t) consistently in all tests matching selinux_warning_controller_test.go patterns.
✅ Passed checks (13 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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 No Ginkgo tests found in PR. New tests use standard Go testing with static, descriptive test names (no dynamic values, timestamps, UUIDs, or generated identifiers).
Microshift Test Compatibility ✅ Passed No new Ginkgo e2e tests found. All tests added are standard Go unit tests using the testing package, not Ginkgo e2e tests.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No Ginkgo e2e tests are added in this PR. The only tests added are standard Go unit tests in openshift_upgrade_controller_test.go using the testing.T framework, not Ginkgo's test syntax.
Topology-Aware Scheduling Compatibility ✅ Passed PR adds no deployment manifests, pod specs, or scheduling constraints. Changes are controller reporting logic, RBAC permissions, and feature gates with no topology-aware scheduling assumptions.
Ote Binary Stdout Contract ✅ Passed PR contains KCM controller code, not OTE test binaries. Comprehensive scan found zero stdout violations: no fmt.Print*, log.Print*, or os.Stdout writes in modified files.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed PR contains no Ginkgo e2e tests. Only test file is a standard Go unit test (openshift_upgrade_controller_test.go) using testing.T, not Ginkgo framework.
No-Weak-Crypto ✅ Passed No weak crypto (MD5, SHA1, DES, RC4, 3DES, Blowfish, ECB), no custom crypto implementations, and no non-constant-time secret/token comparisons found in the PR changes.
Container-Privileges ✅ Passed PR contains only Go code and RBAC rule changes with no container specs or privileged security settings like privileged: true, hostPID, hostNetwork, hostIPC, SYS_ADMIN, or allowPrivilegeEscalation.
No-Sensitive-Data-In-Logs ✅ Passed All logging statements in the PR are safe. No passwords, tokens, API keys, PII, or sensitive data are logged. ConfigMap data contains only status values (True/False/Unknown).
Title check ✅ Passed The title accurately summarizes the main change: adding functionality to persist SELinuxWarningController's upgradeability assessment to a ConfigMap, which is reflected across all modified files including the cache interface, reporter loop, and RBAC permissions.

✏️ 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

openshift-ci Bot commented May 19, 2026

Copy link
Copy Markdown

[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

openshift-ci-robot commented May 19, 2026

Copy link
Copy Markdown

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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 thread pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go Outdated
Comment thread pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go Outdated
Comment thread pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go Outdated
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

openshift-ci Bot commented May 19, 2026

Copy link
Copy Markdown

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

@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

openshift-ci-robot commented May 20, 2026

Copy link
Copy Markdown

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 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.

@jsafrane jsafrane force-pushed the kcm-selinux-configmap branch from 1432d8b to 8e71471 Compare June 8, 2026 13:20
@openshift-ci-robot

Copy link
Copy Markdown

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

The following commits are valid:

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

openshift-ci-robot commented Jun 8, 2026

Copy link
Copy Markdown

@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

  • New Features

  • Adds a periodic upgrade readiness reporter that records whether SELinux-conflicting pods exist to a cluster ConfigMap.

  • Exposes a query for the total number of active SELinux conflicts.

  • Improvements

  • Parse-and-cache SELinux labels once for faster, more accurate conflict detection.

  • Cache now maintains pod↔volume indexing and returns deduplicated aggregated conflicts.

  • Tests

  • Expanded coverage for multi-volume conflicts, pod-deletion cleanup, and label parsing.

  • Chores

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

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (4)
pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go (4)

23-25: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Move conflict state off package-global storage.

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

🤖 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 should be removed
and made a field on the Controller struct to avoid shared state across
controller instances and tests; add a field like previousConflicts
metav1.ConditionStatus to the Controller type, initialize it (default
metav1.ConditionUnknown) in the controller constructor/new function, and update
all references from previousConflicts to c.previousConflicts inside methods
(e.g., reconcile loop and any helpers) so each Controller instance has its own
state and you avoid cross-instance leakage and concurrency hazards.

20-20: ⚠️ 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.

🤖 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` at line
20, The configured constant configMapName is set to "selinux-conflicts" but the
PR's RBAC resourceNames scope targets "selinux-warning-assessment", causing
create/patch to be denied; update the configMapName constant to
"selinux-warning-assessment" (and adjust any code that references configMapName
if it assumes the old name) so the ConfigMap name matches the RBAC-scoped
assessment object used by the controller.

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

Use merge-patch 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.

♻️ Proposed fix
 func (c *Controller) patchSELinuxConflictsConfigMap(ctx context.Context, conflictsPresent metav1.ConditionStatus) error {
-	patch, err := json.Marshal([]map[string]any{
-		{
-			"op":   "replace",
-			"path": "/data",
-			"value": map[string]string{
-				"conflictsPresent": string(conflictsPresent),
-			},
-		},
+	patch, err := json.Marshal(map[string]any{
+		"data": map[string]string{
+			"conflictsPresent": string(conflictsPresent),
+		},
 	})
 	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{})
+	_, err = c.kubeClient.CoreV1().ConfigMaps(configMapNamespace).Patch(ctx, configMapName, types.MergePatchType, patch, metav1.PatchOptions{})
 	return err
 }
🤖 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 81 - 96, The current patchSELinuxConflictsConfigMap builds a JSON Patch
that uses an op "replace" on "/data", which fails when the ConfigMap exists
without a data field; change the logic to send a JSON merge patch that upserts
the key idempotently by marshaling {"data":{"conflictsPresent": "<value>"}} and
call c.kubeClient.CoreV1().ConfigMaps(...).Patch with types.MergePatchType
(keeping ctx, configMapName, configMapNamespace and metav1.PatchOptions as
before) so the conflictsPresent key is merged/created rather than replaced.

57-59: ⚠️ 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.

🛡️ Proposed fix
 func (c *Controller) getConflicts(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 {
🤖 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, The getConflicts method currently does an unchecked type
assertion on c.labelCache to cache.ConflictCounter which can panic; change it to
a safe assertion (e.g., cc, ok := c.labelCache.(cache.ConflictCounter)) and
handle the non-matching case by returning a safe default (such as
metav1.ConditionFalse) and optionally logging the mismatch via the provided
logger; update references to GetConflictCount to use cc.GetConflictCount() only
when ok. Ensure you modify the Controller.getConflicts function to perform this
guarded check and fallback rather than using a direct assertion.
🧹 Nitpick comments (1)
pkg/controller/volume/selinuxwarning/openshift_upgrade_controller_test.go (1)

316-316: 💤 Low value

Remove unused context assignment.

The assigned ctx on line 316 is never used. Remove the assignment to keep the code clean.

♻️ Proposed fix
 		t.Run(tt.name, func(t *testing.T) {
-			_, ctx := ktesting.NewTestContext(t)
 			logger := ktesting.NewLogger(t, ktesting.NewConfig())
-			_ = ctx
🤖 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_test.go` at
line 316, The line `_ = ctx` in the openshift_upgrade_controller_test.go file is
an unused assignment that serves no purpose. Remove the entire line that
contains the unused context assignment to keep the code clean and remove
unnecessary code.
🤖 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_test.go`:
- Line 151: The tests mutate a package-global previousConflicts which breaks
isolation; move previousConflicts into the Controller struct and update callers
and tests to use the instance field. Add a field (e.g., previousConflicts
map[string]bool) to the Controller type in openshift_upgrade_controller.go,
initialize it in the controller constructor/new function, replace all references
to the package-level previousConflicts with c.previousConflicts (or the chosen
field name) throughout the controller code, remove the package-global variable,
and change the test in openshift_upgrade_controller_test.go to set the per-test
controller instance's previousConflicts (e.g., controller.previousConflicts =
tt.initialConflict) instead of mutating a global.

In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go`:
- Around line 27-36: The loop in runOpenShiftSELinuxConflictsReporter uses
time.After which leaks timers; replace it by creating a
time.NewTicker(checkInterval) (ticker := time.NewTicker(checkInterval)) with
defer ticker.Stop(), then change the select to listen on ctx.Done() and
<-ticker.C and call c.reportSELinuxConflicts(ctx) when the ticker fires,
preserving existing behavior and ensuring the ticker is stopped on exit.

---

Duplicate comments:
In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go`:
- Around line 23-25: The package-global variable previousConflicts should be
removed and made a field on the Controller struct to avoid shared state across
controller instances and tests; add a field like previousConflicts
metav1.ConditionStatus to the Controller type, initialize it (default
metav1.ConditionUnknown) in the controller constructor/new function, and update
all references from previousConflicts to c.previousConflicts inside methods
(e.g., reconcile loop and any helpers) so each Controller instance has its own
state and you avoid cross-instance leakage and concurrency hazards.
- Line 20: The configured constant configMapName is set to "selinux-conflicts"
but the PR's RBAC resourceNames scope targets "selinux-warning-assessment",
causing create/patch to be denied; update the configMapName constant to
"selinux-warning-assessment" (and adjust any code that references configMapName
if it assumes the old name) so the ConfigMap name matches the RBAC-scoped
assessment object used by the controller.
- Around line 81-96: The current patchSELinuxConflictsConfigMap builds a JSON
Patch that uses an op "replace" on "/data", which fails when the ConfigMap
exists without a data field; change the logic to send a JSON merge patch that
upserts the key idempotently by marshaling {"data":{"conflictsPresent":
"<value>"}} and call c.kubeClient.CoreV1().ConfigMaps(...).Patch with
types.MergePatchType (keeping ctx, configMapName, configMapNamespace and
metav1.PatchOptions as before) so the conflictsPresent key is merged/created
rather than replaced.
- Around line 57-59: The getConflicts method currently does an unchecked type
assertion on c.labelCache to cache.ConflictCounter which can panic; change it to
a safe assertion (e.g., cc, ok := c.labelCache.(cache.ConflictCounter)) and
handle the non-matching case by returning a safe default (such as
metav1.ConditionFalse) and optionally logging the mismatch via the provided
logger; update references to GetConflictCount to use cc.GetConflictCount() only
when ok. Ensure you modify the Controller.getConflicts function to perform this
guarded check and fallback rather than using a direct assertion.

---

Nitpick comments:
In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller_test.go`:
- Line 316: The line `_ = ctx` in the openshift_upgrade_controller_test.go file
is an unused assignment that serves no purpose. Remove the entire line that
contains the unused context assignment to keep the code clean and remove
unnecessary code.
🪄 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: c9e22094-20bf-463c-8f29-0698e2fdf568

📥 Commits

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

📒 Files selected for processing (13)
  • 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/internal/parse/selinux_label.go
  • pkg/controller/volume/selinuxwarning/internal/parse/selinux_label_test.go
  • pkg/controller/volume/selinuxwarning/metrics.go
  • pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go
  • pkg/controller/volume/selinuxwarning/openshift_upgrade_controller_test.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
🚧 Files skipped from review as they are similar to previous changes (6)
  • plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go
  • pkg/controller/volume/selinuxwarning/selinux_warning_controller.go
  • plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml
  • pkg/controller/volume/selinuxwarning/translator/selinux_translator.go
  • pkg/controller/volume/selinuxwarning/metrics.go
  • pkg/controller/volume/selinuxwarning/cache/volumecache.go

Comment thread pkg/controller/volume/selinuxwarning/openshift_upgrade_controller_test.go Outdated
Comment thread pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go Outdated
@jsafrane jsafrane force-pushed the kcm-selinux-configmap branch from 8e71471 to c19aaca Compare June 8, 2026 13:46
@openshift-ci-robot

Copy link
Copy Markdown

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

The following commits are valid:

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

openshift-ci-robot commented Jun 8, 2026

Copy link
Copy Markdown

@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

  • New Features

  • Adds a periodic reporter that records whether SELinux-conflicting pods exist to a cluster ConfigMap.

  • Exposes queries for active conflicts: GetConflicts (list) and GetConflictCount (total).

  • Improvements

  • Parse-and-cache SELinux labels once for faster, more accurate conflict detection.

  • Cache maintains pod↔volume indexing and returns deduplicated aggregated conflicts.

  • Metrics collection now reads aggregated conflicts directly.

  • Tests

  • Expanded coverage for label parsing, multi-volume conflicts, and pod-deletion cleanup.

  • Chores

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

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (4)
pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go (4)

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

Replace time.After loop with a ticker.

Line 32 allocates a new timer every iteration; cancellation before fire can leave pending timers. Use time.NewTicker with defer ticker.Stop().

Suggested fix
 func (c *Controller) runOpenShiftSELinuxConflictsReporter(ctx context.Context) {
+	ticker := time.NewTicker(checkInterval)
+	defer ticker.Stop()
+
 	for {
 		select {
 		case <-ctx.Done():
 			return
-		case <-time.After(checkInterval):
+		case <-ticker.C:
 			c.reportSELinuxConflicts(ctx)
 		}
 	}
 }
🤖 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 27 - 35, The loop in runOpenShiftSELinuxConflictsReporter uses time.After
which allocates a timer each iteration; replace it with a time.NewTicker to
avoid leaked timers: create ticker := time.NewTicker(checkInterval), defer
ticker.Stop(), and change the select to wait on <-ctx.Done() and <-ticker.C then
call c.reportSELinuxConflicts(ctx); keep using the existing checkInterval and
reportSELinuxConflicts symbols.

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

Move conflict state off package-global storage.

Line 23 and Line 54 keep mutable state in previousConflicts at package scope, which is shared across controller instances and tests. Store it on Controller instead to avoid cross-instance leakage/races.

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

 func (c *Controller) reportSELinuxConflicts(ctx context.Context) {
@@
-	if currentConflicts == previousConflicts {
+	if currentConflicts == c.previousConflicts {
@@
-	previousConflicts = currentConflicts
+	c.previousConflicts = currentConflicts
 }

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 mutable variable previousConflicts should be
removed and made an instance field on the Controller struct to avoid
cross-instance/shared-state races; add a field (e.g., previousConflicts
metav1.ConditionStatus) to the Controller type, initialize it in the controller
constructor (NewController or equivalent) to metav1.ConditionUnknown, and update
all references that used the package-level previousConflicts variable to use
c.previousConflicts (or the receiver name used on Controller) instead; also
remove the package-scoped declaration and update any tests to construct a
Controller and assert against its instance field rather than the package
variable.

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

Guard the ConflictCounter type assertion.

Line 58 can panic if labelCache is replaced with an implementation that does not satisfy cache.ConflictCounter. Handle the assertion safely and return a fallback condition.

Suggested fix
 func (c *Controller) getConflicts(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 {
🤖 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, avoid the unchecked type assertion on
c.labelCache to cache.ConflictCounter: perform a safe assertion
(conflictCounter, ok := c.labelCache.(cache.ConflictCounter)), and if ok is
false log a warning via the provided logger and return a fallback
metav1.ConditionStatus (e.g., metav1.ConditionFalse); otherwise call
conflictCounter.GetConflictCount() and proceed as before. Ensure you reference
getConflicts, c.labelCache, cache.ConflictCounter, and logger in the change.

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

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

Line 84 uses JSON Patch replace on /data; this fails when the ConfigMap exists without a data field. Use merge patch to make updates idempotent.

Suggested fix
-	patch, err := json.Marshal([]map[string]any{
-		{
-			"op":   "replace",
-			"path": "/data",
-			"value": map[string]string{
-				"conflictsPresent": string(conflictsPresent),
-			},
-		},
-	})
+	patch, err := json.Marshal(map[string]any{
+		"data": map[string]string{
+			"conflictsPresent": string(conflictsPresent),
+		},
+	})
@@
-	_, 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{})
 	return err
 }
🤖 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, Current code builds a JSON Patch with op "replace" on "/data" and
calls Patch with types.JSONPatchType which fails if the ConfigMap has no data
field; instead build a merge-style patch (e.g. a JSON object like
{"data":{"conflictsPresent": "<value>"}}), marshal that, and call Patch with
types.MergePatchType (or types.StrategicMergePatchType if preferred) so the data
key is added/updated idempotently; update the json.Marshal target and change
types.JSONPatchType to types.MergePatchType in the
c.kubeClient.CoreV1().ConfigMaps(...).Patch call, keeping configMapName,
configMapNamespace and the conflictsPresent value logic the same.
🤖 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
`@plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml`:
- Around line 1409-1417: The selinux-warning-controller RBAC rule is misaligned:
controller_policy.go grants system:controller:selinux-warning-controller
create,patch on configmaps without resourceNames, while controller-roles.yaml
restricts it to resourceNames: selinux-conflicts; update one to match the other.
Either remove the resourceNames: selinux-conflicts entry from the rule in
plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml
so the testdata allows create,patch on all configmaps like controller_policy.go,
or conversely add the same resourceNames restriction in
plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go for the
role system:controller:selinux-warning-controller (the rule affecting resources:
configmaps, verbs: create,patch) so both generator and golden testdata are
identical.

---

Duplicate comments:
In `@pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go`:
- Around line 27-35: The loop in runOpenShiftSELinuxConflictsReporter uses
time.After which allocates a timer each iteration; replace it with a
time.NewTicker to avoid leaked timers: create ticker :=
time.NewTicker(checkInterval), defer ticker.Stop(), and change the select to
wait on <-ctx.Done() and <-ticker.C then call c.reportSELinuxConflicts(ctx);
keep using the existing checkInterval and reportSELinuxConflicts symbols.
- Around line 23-25: The package-global mutable variable previousConflicts
should be removed and made an instance field on the Controller struct to avoid
cross-instance/shared-state races; add a field (e.g., previousConflicts
metav1.ConditionStatus) to the Controller type, initialize it in the controller
constructor (NewController or equivalent) to metav1.ConditionUnknown, and update
all references that used the package-level previousConflicts variable to use
c.previousConflicts (or the receiver name used on Controller) instead; also
remove the package-scoped declaration and update any tests to construct a
Controller and assert against its instance field rather than the package
variable.
- Around line 57-59: In getConflicts, avoid the unchecked type assertion on
c.labelCache to cache.ConflictCounter: perform a safe assertion
(conflictCounter, ok := c.labelCache.(cache.ConflictCounter)), and if ok is
false log a warning via the provided logger and return a fallback
metav1.ConditionStatus (e.g., metav1.ConditionFalse); otherwise call
conflictCounter.GetConflictCount() and proceed as before. Ensure you reference
getConflicts, c.labelCache, cache.ConflictCounter, and logger in the change.
- Around line 82-95: Current code builds a JSON Patch with op "replace" on
"/data" and calls Patch with types.JSONPatchType which fails if the ConfigMap
has no data field; instead build a merge-style patch (e.g. a JSON object like
{"data":{"conflictsPresent": "<value>"}}), marshal that, and call Patch with
types.MergePatchType (or types.StrategicMergePatchType if preferred) so the data
key is added/updated idempotently; update the json.Marshal target and change
types.JSONPatchType to types.MergePatchType in the
c.kubeClient.CoreV1().ConfigMaps(...).Patch call, keeping configMapName,
configMapNamespace and the conflictsPresent value logic the same.
🪄 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: 12d5ec43-5dcb-4e6e-ba06-06f1b6b34632

📥 Commits

Reviewing files that changed from the base of the PR and between 8e71471 and c19aaca.

📒 Files selected for processing (6)
  • pkg/controller/volume/selinuxwarning/cache/openshift_patch.go
  • pkg/controller/volume/selinuxwarning/openshift_upgrade_controller.go
  • pkg/controller/volume/selinuxwarning/openshift_upgrade_controller_test.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
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/controller/volume/selinuxwarning/selinux_warning_controller.go
  • plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go

tchap and others added 2 commits June 9, 2026 15:52
When calling ControllerSELinuxTranslator.Conflicts(), the SELinux label
is repeatedly split into []string to detect conflicts. This causes a huge
number of allocations when there are many comparisons.

This is now made more efficient by pre-parsing the SELinux label and
storing it in podInfo as [4]string for fast comparison when needed.
@jsafrane jsafrane force-pushed the kcm-selinux-configmap branch from c19aaca to 36fec1b Compare June 9, 2026 13:57
@openshift-ci-robot

Copy link
Copy Markdown

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

The following commits are valid:

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

openshift-ci-robot commented Jun 9, 2026

Copy link
Copy Markdown

@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

  • New Features

  • SELinux conflict monitoring and reporting now available in volume controller, reporting status every 30 seconds.

  • New SELinuxMountGAReadiness feature gate added.

  • Chores

  • Updated RBAC permissions to support SELinux conflict reporting.

  • Tests

  • Added comprehensive test coverage for SELinux conflict reporting functionality.

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.

@jsafrane jsafrane force-pushed the kcm-selinux-configmap branch from 36fec1b to 2a71e03 Compare June 9, 2026 15:54
@openshift-ci-robot

Copy link
Copy Markdown

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

The following commits are valid:

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.

@jsafrane jsafrane changed the title WIP: STOR-2962: Save SELinuxWarningController upgradeability to a ConfigMap WIP: STOR-2963: Save SELinuxWarningController upgradeability to a ConfigMap Jun 10, 2026
@openshift-ci-robot

openshift-ci-robot commented Jun 10, 2026

Copy link
Copy Markdown

@jsafrane: This pull request references STOR-2963 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

  • New Features

  • SELinux conflict monitoring and reporting now available in volume controller, reporting status every 30 seconds.

  • New SELinuxMountGAReadiness feature gate added.

  • Chores

  • Updated RBAC permissions to support SELinux conflict reporting.

  • Tests

  • Added comprehensive test coverage for SELinux conflict reporting functionality.

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.

@jsafrane jsafrane force-pushed the kcm-selinux-configmap branch from 2a71e03 to b343f24 Compare June 10, 2026 12:56
@openshift-ci-robot

Copy link
Copy Markdown

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

The following commits are valid:

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.

@jsafrane jsafrane force-pushed the kcm-selinux-configmap branch from b343f24 to 0f20dc8 Compare June 10, 2026 13:03
@openshift-ci-robot

Copy link
Copy Markdown

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

The following commits are valid:

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.

@jsafrane jsafrane changed the title WIP: STOR-2963: Save SELinuxWarningController upgradeability to a ConfigMap STOR-2963: Save SELinuxWarningController upgradeability to a ConfigMap Jun 10, 2026
…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 0f20dc8 to 4ef4ffb Compare June 11, 2026 11:17
@openshift-ci-robot

Copy link
Copy Markdown

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

The following commits are valid:

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.

func (c *SELinuxConflictsReporterController) patchSELinuxConflictsConfigMap(ctx context.Context, conflictsPresent metav1.ConditionStatus) error {
patch, err := json.Marshal(map[string]any{
"data": map[string]string{
"conflictsPresent": string(conflictsPresent),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The storage operator will block the upgrade (e.g. via a condition) when this is true, right? Is a single boolean enough information for the user to understand the scope of the issue?

return
}
logger.V(2).Info("Starting OpenShift SELinux conflicts reporter")
timer := time.NewTimer(checkInterval)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If this were a permanent feature, I would suggest using a queue for better error handling/backoff and metrics. For a temporary feature, this should be fine.

cm := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: configMapName,
Namespace: configMapNamespace,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This ConfigMap is expected to be removed in the next (5.1?) release. Also, this controller is behind a feature gate.

Can we add some metadata here (annotation, ownerReference, ect.) to document the purpose and lifecycle of this config map?

}
// Introduced in 5.0
defaultVersionedKubernetesFeatureGates[SELinuxMountGAReadiness] = featuregate.VersionedSpecs{
{Version: version.MustParse("1.35"), Default: false, PreRelease: featuregate.Alpha},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

openshift/api#288 adds this FG to TP. When is the feature going to be enabled? I thought, we were targeting this to be enabled by default in 5.0?

"conflictsPresent": string(conflictsPresent),
},
}
_, err := c.kubeClient.CoreV1().ConfigMaps(configMapNamespace).Create(ctx, cm, metav1.CreateOptions{})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: we could use SSA instead, but the current implementation should suffice for a temporary feature as well

if cm.Data["conflictsPresent"] != tt.existingConfigMap.Data["conflictsPresent"] {
t.Errorf("ConfigMap data changed unexpectedly: got %v, want %v", cm.Data, tt.existingConfigMap.Data)
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

else fail?

Shouldn't we ensure that there is an existingConfigMap field set if we expect no write?

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.

4 participants