Skip to content

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

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

WIP: STOR-2962: 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

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

@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

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 and tests
pkg/controller/volume/selinuxwarning/internal/parse/selinux_label.go, pkg/controller/volume/selinuxwarning/internal/parse/selinux_label_test.go
Adds ParseSELinuxLabel and unit tests to split labels into four components.
Translator: parsed-label conflict check
pkg/controller/volume/selinuxwarning/translator/selinux_translator.go
Refactors Conflicts to parse labels and adds ConflictsParsed(partsA, partsB) for component-wise comparison.
Volume cache core: API and internal state refactor
pkg/controller/volume/selinuxwarning/cache/volumecache.go
Replaces streaming SendConflicts with GetConflicts(logger), adds podToVolumes reverse index and per-volume conflicts cache, stores parsed seLinuxParts in pod info, and updates AddVolume/DeletePod/dump to maintain and prune conflicts.
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.
Volume cache tests: reverse-index and conflict cases
pkg/controller/volume/selinuxwarning/cache/volumecache_test.go
Adds reverse-index consistency checks, renames/updates main conflicts test to GetConflicts style, and adds multi-volume and DeletePod conflict table tests.
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.
OpenShift 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.
OpenShift reporter tests
pkg/controller/volume/selinuxwarning/openshift_upgrade_controller_test.go
Adds fakeVolumeCache implementing ConflictCounter and table-driven tests for report/apply/getConflicts behaviors using client-go fakes.
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; controller tests update fakeVolumeCache to return aggregated conflicts via GetConflicts.
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-conflicts ConfigMap (resourceNames-scoped).

Sequence Diagram — OpenShift reporter flow:

sequenceDiagram
  participant Controller
  participant VolumeCache
  participant KubeAPI
  Controller->>VolumeCache: GetConflictCount()
  VolumeCache-->>Controller: conflictCount (int)
  Controller->>KubeAPI: Patch ConfigMap selinux-conflicts /data {"conflictsPresent": "<True|False>"}
  alt Patch NotFound
    Controller->>KubeAPI: Create ConfigMap selinux-conflicts with data.conflictsPresent
  end
  KubeAPI-->>Controller: API response
Loading

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs:

  • openshift/kubernetes#2668: Related prior changeset switching from streamed SendConflicts to GetConflicts and introducing reverse-index/parsed-label conflict detection.
🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (14 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is marked as WIP and references a JIRA ticket (STOR-2962), but the actual change content focuses on implementing SELinux conflict detection and ConfigMap persistence rather than being work-in-progress.
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 PR contains no Ginkgo tests; all test functions use standard Go testing framework. Check is not applicable.
Test Structure And Quality ✅ Passed PR contains standard Go tests (func Test* pattern), not Ginkgo tests (Describe/Context/It blocks). Custom check requirement for Ginkgo test review is not applicable.
Microshift Test Compatibility ✅ Passed No Ginkgo e2e tests are present in this PR. All test files are standard Go unit tests using the testing package, not Ginkgo framework.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No Ginkgo e2e tests added. All new tests are standard Go unit tests in pkg/controller/volume/selinuxwarning/ with testing.T signature, no ginkgo imports. SNO check applies only to Ginkgo e2e tests.
Topology-Aware Scheduling Compatibility ✅ Passed PR modifies only KCM controller code with no deployment manifests, Pod specs, or scheduling constraints (no affinity, topology spread, nodeSelector, or topology assumptions).
Ote Binary Stdout Contract ✅ Passed No process-level stdout writes detected. Code uses proper klog patterns (klog.FromContext) and ktesting for test logging, both of which route output away from stdout.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed PR adds only standard Go unit tests (testing.T), not Ginkgo e2e tests. Check is not applicable since custom check requires Ginkgo e2e tests with Describe/It/Context patterns.
No-Weak-Crypto ✅ Passed No weak crypto algorithms, custom crypto implementations, or non-constant-time secret comparisons. PR handles SELinux label parsing and conflict detection.
Container-Privileges ✅ Passed No privileged container configurations found. PR contains Go source code and RBAC policy testdata only, no deployment manifests.
No-Sensitive-Data-In-Logs ✅ Passed Logging in the PR exposes only SELinux security labels and conflict status at high verbosity levels. These are not passwords, tokens, API keys, PII, credentials, or sensitive customer data.

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

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.

tchap and others added 2 commits June 8, 2026 15:05
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 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

}

// Set the global previousConflicts to the initial value for this test.
previousConflicts = tt.initialConflict

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Package-global state mutation breaks test isolation.

Directly mutating previousConflicts in each test case prevents parallel execution and can cause test pollution. This confirms the need to move previousConflicts to the Controller struct (flagged in openshift_upgrade_controller.go).

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

Comment on lines +27 to +36
func (c *Controller) runOpenShiftSELinuxConflictsReporter(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
case <-time.After(checkInterval):
c.reportSELinuxConflicts(ctx)
}
}
}

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

Replace time.After with time.NewTicker to avoid timer leaks.

Using time.After in a loop creates a new timer on every iteration. If the context is canceled before the timer fires, the timer leaks until it expires. Use time.NewTicker with defer ticker.Stop() to ensure cleanup.

♻️ Proposed 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)
 		}
 	}
 }
📝 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) runOpenShiftSELinuxConflictsReporter(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
case <-time.After(checkInterval):
c.reportSELinuxConflicts(ctx)
}
}
}
func (c *Controller) runOpenShiftSELinuxConflictsReporter(ctx context.Context) {
ticker := time.NewTicker(checkInterval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
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 - 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.

…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 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

Comment on lines +1409 to +1417
- apiGroups:
- ""
resourceNames:
- selinux-conflicts
resources:
- configmaps
verbs:
- create
- patch

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify selinux-warning-controller RBAC rule in generator vs golden testdata.
set -euo pipefail

echo "== controller_policy.go selinux-warning-controller block =="
rg -n -C6 'selinux-warning-controller|Resources\("configmaps"\)|ResourceNames|Names\(' plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go

echo
echo "== testdata selinux-warning-controller block =="
rg -n -C8 'name: system:controller:selinux-warning-controller|resourceNames|configmaps|verbs:' plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml

Repository: openshift/kubernetes

Length of output: 42132


Align selinux-warning-controller RBAC rule between controller policy generator and golden testdata.

plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go grants system:controller:selinux-warning-controller permissions for configmaps create, patch without resourceNames, but plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/controller-roles.yaml scopes the same rule to resourceNames: selinux-conflicts, creating a policy contract drift.

🤖 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/testdata/controller-roles.yaml`
around lines 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.

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.

3 participants