Skip to content

UPSTREAM: KEP-6063: Implement per-pod PID limit (Alpha)#2710

Draft
BhargaviGudi wants to merge 1 commit into
openshift:masterfrom
BhargaviGudi:per-pod-pid-limit-carry
Draft

UPSTREAM: KEP-6063: Implement per-pod PID limit (Alpha)#2710
BhargaviGudi wants to merge 1 commit into
openshift:masterfrom
BhargaviGudi:per-pod-pid-limit-carry

Conversation

@BhargaviGudi

@BhargaviGudi BhargaviGudi commented Jul 1, 2026

Copy link
Copy Markdown

What type of PR is this?

/kind feature

What this PR does / why we need it:

Ports the KEP-6063 per-pod PID limit implementation to openshift/kubernetes as an UPSTREAM carry.

This adds support for per-pod PID limits via spec.resources.limits.pid, gated behind the PerPodPIDLimit feature gate (Alpha, disabled by default). The kubelet enforces the effective limit as min(node podPidsLimit, pod pid limit) on cgroupsv2 nodes, falling back to the node-level limit on cgroupsv1 with a warning.

Changes:

  • Add PerPodPIDLimit feature gate (Alpha, v1.37) with PodLevelResources dependency
  • Register pid as a standard, integer, non-overcommittable resource
  • Validate pid values in the range 1024–16384
  • Forbid requests.pid (only limits.pid is valid)
  • Strip pid fields when the gate is disabled; preserve on existing pods
  • Add Prometheus metric kubelet_pod_pid_limit_applied_total
  • Add perpodpidlimit node declared feature for scheduling
  • Add unit tests for validation, helpers, field stripping, kubelet logic
  • Add e2e node tests for per-pod PID limit enforcement and eviction

Which issue(s) this PR is related to:

Upstream PR: kubernetes#139277
KEP: kubernetes/enhancements#6063

Special notes for your reviewer:

This is an UPSTREAM carry since the upstream PR (kubernetes#139277) has not merged yet. The perpodpidlimit node declared feature was adapted to use OCP's existing nodedeclaredfeatures.Feature interface (upstream refactored this into a separate types package which OCP hasn't picked up yet). Once the upstream PR merges, this carry should be replaced with the merged commit.

Does this PR introduce a user-facing change?

Add per-pod PID limit support (Alpha, off by default). When the PerPodPIDLimit feature gate is enabled, users can set spec.resources.limits.pid on pods to enforce a PID limit lower than the node-level podPidsLimit. Requires cgroupsv2 and the PodLevelResources feature gate.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Added support for setting per-pod PID limits, letting pods request a specific process limit that can be capped by the node default.
  * Expanded pod status handling to preserve new resource health information when available.

* **Bug Fixes**
  * Improved handling of PID-related fields so invalid request values are removed and existing pod settings are preserved during updates.
  * Ensured PID pressure eviction behavior remains based on node-wide availability, not individual pod PID limits.

* **Tests**
  * Added broad validation and end-to-end coverage for PID limits, feature-gated behavior, and pod compatibility scenarios.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Adds support for per-pod PID limits via resources.limits.pid, gated
behind the PerPodPIDLimit feature gate (Alpha, disabled by default).
The kubelet enforces the effective limit as min(node podPidsLimit,
pod pid limit) on cgroupsv2 nodes, falling back to the node-level
limit on cgroupsv1 with a warning.

- Add PerPodPIDLimit feature gate (Alpha, v1.37)
- Register pid as a standard, integer, non-overcommittable resource
- Validate pid values in the range 1024-16384
- Strip pid fields when the gate is disabled, preserve on existing pods
- Add Prometheus metric kubelet_pod_pid_limit_applied_total
- Add unit tests for validation, helpers, field stripping, kubelet logic
- Add e2e node tests for per-pod PID limit enforcement

Signed-off-by: Bhargavi Gudi <bgudi@redhat.com>
@openshift-ci-robot

Copy link
Copy Markdown

@BhargaviGudi: No Jira issue with key KEP-6063 exists in the tracker at https://redhat.atlassian.net.
Once a valid jira issue is referenced in the title of this pull request, request a refresh with /jira refresh.

Details

In response to this:

What type of PR is this?

/kind feature

What this PR does / why we need it:

Ports the KEP-6063 per-pod PID limit implementation to openshift/kubernetes as an UPSTREAM carry.

This adds support for per-pod PID limits via spec.resources.limits.pid, gated behind the PerPodPIDLimit feature gate (Alpha, disabled by default). The kubelet enforces the effective limit as min(node podPidsLimit, pod pid limit) on cgroupsv2 nodes, falling back to the node-level limit on cgroupsv1 with a warning.

Changes:

  • Add PerPodPIDLimit feature gate (Alpha, v1.37) with PodLevelResources dependency
  • Register pid as a standard, integer, non-overcommittable resource
  • Validate pid values in the range 1024–16384
  • Forbid requests.pid (only limits.pid is valid)
  • Strip pid fields when the gate is disabled; preserve on existing pods
  • Add Prometheus metric kubelet_pod_pid_limit_applied_total
  • Add perpodpidlimit node declared feature for scheduling
  • Add unit tests for validation, helpers, field stripping, kubelet logic
  • Add e2e node tests for per-pod PID limit enforcement and eviction

Which issue(s) this PR is related to:

Upstream PR: kubernetes#139277
KEP: kubernetes/enhancements#6063

Special notes for your reviewer:

This is an UPSTREAM carry since the upstream PR (kubernetes#139277) has not merged yet. The perpodpidlimit node declared feature was adapted to use OCP's existing nodedeclaredfeatures.Feature interface (upstream refactored this into a separate types package which OCP hasn't picked up yet). Once the upstream PR merges, this carry should be replaced with the merged commit.

Does this PR introduce a user-facing change?

Add per-pod PID limit support (Alpha, off by default). When the PerPodPIDLimit feature gate is enabled, users can set spec.resources.limits.pid on pods to enforce a PID limit lower than the node-level podPidsLimit. Requires cgroupsv2 and the PodLevelResources feature gate.

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

@openshift-ci-robot openshift-ci-robot added the backports/unvalidated-commits Indicates that not all commits come to merged upstream PRs. label Jul 1, 2026
@openshift-ci openshift-ci Bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. kind/feature Categorizes issue or PR as related to a new feature. labels Jul 1, 2026
@openshift-ci

openshift-ci Bot commented Jul 1, 2026

Copy link
Copy Markdown

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

@openshift-ci-robot

Copy link
Copy Markdown

@BhargaviGudi: 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 openshift-ci Bot added the vendor-update Touching vendor dir or related files label Jul 1, 2026
@openshift-ci

openshift-ci Bot commented Jul 1, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: BhargaviGudi
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

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Walkthrough

This PR adds a new alpha PerPodPIDLimit feature gate enabling per-pod PID limits via spec.resources.limits.pid. Changes span API type constants, validation rules, feature-drop logic, kubelet cgroup enforcement with a new metric and event, node-declared-features registration, and extensive unit/e2e tests. Unrelated test additions for ResourceHealthStatus are included in the same test file.

Changes

Per-Pod PID Limit Feature

Layer / File(s) Summary
API constant and feature gate
pkg/apis/core/types.go, staging/.../v1/types.go, pkg/features/kube_features.go, test/compatibility_lifecycle/reference/versioned_feature_list.yaml
Adds ResourcePID resource name constant and the PerPodPIDLimit alpha feature gate (v1.37, default false, depends on PodLevelResources).
Resource classification and validation
pkg/apis/core/helper/helpers.go, pkg/apis/core/helper/helpers_test.go, pkg/apis/core/validation/validation.go, pkg/apis/core/validation/validation_test.go
Classifies pid as standard/integer/non-overcommittable resource; validation requires the feature gate, forbids pid in requests, and enforces a 1024–16384 range for limits.
Dropping disabled pid fields
pkg/api/pod/util.go, pkg/api/pod/util_test.go
Adds dropDisabledPodPIDLimitFields/podPIDLimitInUse to strip pid requests always and limits when the gate is disabled and not previously used; includes new tests for this and unrelated ResourceHealthStatus behaviors.
Kubelet cgroup enforcement and metrics
pkg/kubelet/cm/pod_container_manager_linux.go, pkg/kubelet/cm/container_manager_linux.go, pkg/kubelet/cm/pod_container_manager_linux_test.go, pkg/kubelet/metrics/metrics.go, pkg/kubelet/eviction/eviction_manager_test.go, test/instrumentation/documentation/documentation-list.yaml
Computes an effective PID limit combining node default and per-pod request, validates cgroups v2, caps and emits a PIDLimitCapped event, increments PodPIDLimitApplied metric; verifies eviction is unaffected by per-pod limits.
Node declared features registration
staging/.../nodedeclaredfeatures/features/perpodpidlimit/*, staging/.../nodedeclaredfeatures/features/registry.go
Adds a perpodpidlimit declared feature implementing scheduling inference and registers it in AllFeatures.
End-to-end tests
test/e2e_node/pids_test.go, test/e2e_node/eviction_test.go
Adds e2e coverage for pids.max enforcement, capping, multi-container sharing, hostPID, PSA compatibility, and priority-based PID eviction.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
    participant Kubelet as Kubelet PodContainerManager
    participant PodSpec as Pod Spec
    participant Metrics as Metrics Registry
    participant Recorder as Event Recorder
    participant Cgroup as Pod Cgroup

    Kubelet->>PodSpec: getPodPIDLimit(pod)
    PodSpec-->>Kubelet: requested pid limit (or 0)
    Kubelet->>Kubelet: compute effectivePidLimit from node default + request
    alt PerPodPIDLimit gate enabled and request set
        Kubelet->>Kubelet: validate cgroups v2 support
        alt request exceeds node limit
            Kubelet->>Recorder: emit PIDLimitCapped warning event
            Kubelet->>Metrics: increment PodPIDLimitApplied
        end
    end
    Kubelet->>Cgroup: set ResourceParameters.PidsLimit = effectivePidLimit
Loading

Estimated code review effort: 4 (Complex) | ~60 minutes


Important

Pre-merge checks failed

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

❌ Failed checks (1 error, 2 warnings)

Check name Status Explanation Resolution
Container-Privileges ❌ Error New e2e pods in test/e2e_node/pids_test.go set HostPID: true, which the check flags as a privileged container setting. Remove hostPID from the added pod specs, or move this coverage to a separately justified privileged test if hostPID is intentional.
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% 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 FAIL: New Ginkgo tests have compile-breaking new(true) initializers, reuse a HostPath verifier in PSA Baseline/Restricted tests, and bundle multiple behaviors in one It block. Use typed pointer helpers instead of new(...), split the hostPID and fork checks into separate tests, and replace the HostPath verifier with a PSA-safe approach (and fix the eviction resource assertion).
✅ Passed checks (12 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding alpha per-pod PID limit support.
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 Checked changed Ginkgo titles: they’re static or built from fixed case constants; no generated names, timestamps, node/namespace names, or IPs appear.
Microshift Test Compatibility ✅ Passed The new e2e tests use only Kubernetes pods, PriorityClasses, PSA, and feature-gate tags; no MicroShift-unsupported OpenShift APIs/resources or guards were added.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The new e2e tests are node-local only: no node-count, rescheduling, anti-affinity, or topology-spread assumptions, and no HA-specific behavior was found.
Topology-Aware Scheduling Compatibility ✅ Passed No deployment manifests, operator code, or controllers were modified; the diff is kubelet/API/feature-gate logic and tests, with no nodeSelector/anti-affinity/topology-spread assumptions.
Ote Binary Stdout Contract ✅ Passed No stdout writes were added in main/init/suite setup; the only logging found is inside test helpers/It blocks, and tempSetCurrentKubeletConfig is silent.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed The new e2e tests use no hardcoded IPv4/external endpoints; they rely on standard image helpers and IPv6-safe net.JoinHostPort.
No-Weak-Crypto ✅ Passed No touched source file adds weak crypto, custom crypto, or unsafe secret comparisons; only unrelated doc text mentions SHA1.
No-Sensitive-Data-In-Logs ✅ Passed New PID-limit logs/events only include pod refs and numeric limits; no passwords, tokens, PII, hostnames, or customer data are logged.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.44.0)
pkg/apis/core/validation/validation_test.go

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@BhargaviGudi BhargaviGudi changed the title UPSTREAM: <carry>: KEP-6063: Implement per-pod PID limit (Alpha) UPSTREAM: KEP-6063: Implement per-pod PID limit (Alpha) Jul 1, 2026
@openshift-ci-robot

Copy link
Copy Markdown

@BhargaviGudi: No Jira issue with key KEP-6063 exists in the tracker at https://redhat.atlassian.net.
Once a valid jira issue is referenced in the title of this pull request, request a refresh with /jira refresh.

Details

In response to this:

What type of PR is this?

/kind feature

What this PR does / why we need it:

Ports the KEP-6063 per-pod PID limit implementation to openshift/kubernetes as an UPSTREAM carry.

This adds support for per-pod PID limits via spec.resources.limits.pid, gated behind the PerPodPIDLimit feature gate (Alpha, disabled by default). The kubelet enforces the effective limit as min(node podPidsLimit, pod pid limit) on cgroupsv2 nodes, falling back to the node-level limit on cgroupsv1 with a warning.

Changes:

  • Add PerPodPIDLimit feature gate (Alpha, v1.37) with PodLevelResources dependency
  • Register pid as a standard, integer, non-overcommittable resource
  • Validate pid values in the range 1024–16384
  • Forbid requests.pid (only limits.pid is valid)
  • Strip pid fields when the gate is disabled; preserve on existing pods
  • Add Prometheus metric kubelet_pod_pid_limit_applied_total
  • Add perpodpidlimit node declared feature for scheduling
  • Add unit tests for validation, helpers, field stripping, kubelet logic
  • Add e2e node tests for per-pod PID limit enforcement and eviction

Which issue(s) this PR is related to:

Upstream PR: kubernetes#139277
KEP: kubernetes/enhancements#6063

Special notes for your reviewer:

This is an UPSTREAM carry since the upstream PR (kubernetes#139277) has not merged yet. The perpodpidlimit node declared feature was adapted to use OCP's existing nodedeclaredfeatures.Feature interface (upstream refactored this into a separate types package which OCP hasn't picked up yet). Once the upstream PR merges, this carry should be replaced with the merged commit.

Does this PR introduce a user-facing change?

Add per-pod PID limit support (Alpha, off by default). When the PerPodPIDLimit feature gate is enabled, users can set spec.resources.limits.pid on pods to enforce a PID limit lower than the node-level podPidsLimit. Requires cgroupsv2 and the PodLevelResources feature gate.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
 * Added support for setting per-pod PID limits, letting pods request a specific process limit that can be capped by the node default.
 * Expanded pod status handling to preserve new resource health information when available.

* **Bug Fixes**
 * Improved handling of PID-related fields so invalid request values are removed and existing pod settings are preserved during updates.
 * Ensured PID pressure eviction behavior remains based on node-wide availability, not individual pod PID limits.

* **Tests**
 * Added broad validation and end-to-end coverage for PID limits, feature-gated behavior, and pod compatibility scenarios.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/apis/core/validation/validation.go (1)

7457-7475: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve existing pid values when PerPodPIDLimit is disabled.

ValidatePodUpdate re-runs pod-level resource validation on the full new spec, and validatePodResourceName has no old-object context. A pod created with limits.pid will fail any later update once the gate is turned off, even though dropDisabledPodPIDLimitFields explicitly preserves that field on update. Thread the old pod through this validator or add a ratchet for pid here.

🤖 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/apis/core/validation/validation.go` around lines 7457 - 7475,
validatePodResourceName currently rejects core.ResourcePID whenever
PerPodPIDLimit is disabled, which breaks updates for pods that already had
limits.pid set. Update the validation path used by ValidatePodUpdate so it can
see the old pod state, and either thread the old object into
validatePodResourceName or add a ratchet there to allow existing pid values to
pass when they were already present before the feature gate was turned off. Keep
the fix anchored around validatePodResourceName and the pod update validation
flow.
🧹 Nitpick comments (2)
pkg/kubelet/cm/pod_container_manager_linux_test.go (1)

442-509: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tests reimplement production logic instead of exercising it — no regression protection for the actual code paths.

TestGetPodPIDLimitCgroupsV1ErrorMessage builds its own fmt.Errorf with the same literal string as the source instead of calling into the code that produces it, and TestPIDLimitCappedEvent/TestNoPIDLimitCappedEventWhenNotCapped reimplement the podPid > effectivePidLimit condition inline rather than calling into EnsureExists's actual capping logic. As written, a regression in the real cgroupsv1-handling or capping logic in pod_container_manager_linux.go would not be caught by these tests. Consider extracting the decision logic (as suggested on the source file) into a small function these tests can call directly.

🤖 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/kubelet/cm/pod_container_manager_linux_test.go` around lines 442 - 509,
These tests are duplicating production behavior instead of verifying it, so they
won’t catch regressions in the real PID-limit and capping paths. Move the
cgroupsv1 error decision and PID-limit capping decision into a small helper used
by EnsureExists in pod_container_manager_linux.go, then update
TestGetPodPIDLimitCgroupsV1ErrorMessage and the PIDLimitCapped tests to call
that helper (or the real path) rather than constructing the same fmt.Errorf or
inline if-check themselves.
pkg/kubelet/cm/pod_container_manager_linux.go (1)

98-119: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Testability concern: PID-limit computation is embedded in EnsureExists, making it hard to unit test directly.

The effective-PID-limit computation, capping, warning-event emission, and cgroupsv1 handling are all inlined in EnsureExists, which also depends on libcontainercgroups.IsCgroup2UnifiedMode() (a real system check) and cgroup creation. This makes it impossible to unit test the actual capping/error logic without relying on the host's cgroup mode — as evidenced by the new tests in pod_container_manager_linux_test.go, which reimplement the logic inline rather than calling into this code (see comment there). Extracting this into a small, injectable helper (e.g. computeEffectivePIDLimit(pod, nodePidsLimit int64, isCgroup2 func() bool, recorder, logger) (int64, error)) would let tests exercise the real decision logic directly.

Also applies to: 388-399

🤖 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/kubelet/cm/pod_container_manager_linux.go` around lines 98 - 119, The
PID-limit decision logic inside EnsureExists is too tightly coupled to cgroup
checks and side effects to unit test cleanly. Extract the effective PID limit
computation, cgroupsv1/v2 validation, capping, metrics, and warning-event
emission into a small helper (for example around getPodPIDLimit and the
podPidsLimit handling) that takes an injectable cgroups-mode check and
recorder/logger dependencies. Keep EnsureExists focused on wiring the helper’s
result into containerConfig.ResourceParameters.PidsLimit so tests can call the
real logic directly.
🤖 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/kubelet/cm/pod_container_manager_linux.go`:
- Around line 98-119: The cgroupsv1 branch in the pod PID limit handling is
hard-failing in the pod container manager instead of falling back as intended.
Update the logic in pod_container_manager_linux.go around the per-pod PID limit
check so that, when PerPodPIDLimit is enabled and getPodPIDLimit(pod) is set on
cgroupsv1, it logs a warning and continues using m.podPidsLimit rather than
returning an error from EnsureExists. Keep the existing cgroupsv2 min(node
limit, pod limit) behavior in the same Pod PID limit path and preserve the
metrics/event reporting symbols (getPodPIDLimit, m.podPidsLimit, EnsureExists,
containerConfig.ResourceParameters.PidsLimit).

In `@test/e2e_node/eviction_test.go`:
- Line 743: The PID-pressure eviction test is expecting the wrong starved
resource because SignalPIDAvailable maps to the internal pids resource, not
none. Update the expected value in verifyEvictionEvents for this PID eviction
case so it uses the PID starved resource constant instead of noStarvedResource,
and keep the change localized around the expectedStarvedResource assignment in
eviction_test.go.

In `@test/e2e_node/pids_test.go`:
- Around line 423-425: The PID verifier path in makePodToVerifyPids is using a
HostPath mount that will be rejected in PSA Baseline/Restricted namespaces, so
don’t create or wait for that verifier pod in those namespaces. Update the e2e
flow around the verifyPod creation and WaitForPodSuccessInNamespace calls to
either run the verifier in a privileged namespace or only execute it when the
test is validating admission success, and apply the same fix in the other
matching verify block.
- Around line 178-180: The per-pod PID enforcement checks in the pids E2E flow
are assuming cgroups v2 behavior, but cgroups v1 should fall back to the node
PodPidsLimit instead. Update the assertions in pidsTest (including the
makePodToVerifyPids/Create checks and the fork-failure verification paths) to
either skip on cgroups v1 or choose expected PID limits based on cgroup mode, so
the 2048/1024 expectations only run when cgroups v2 is enabled.
- Around line 467-470: The SecurityContext initializers use invalid new(value)
calls, which do not compile. Update the pids test fixture to use valid pointer
creation for the RunAsNonRoot, RunAsUser, and AllowPrivilegeEscalation fields in
the SecurityContext literal, keeping the changes localized around the existing
SecurityContext setup.

---

Outside diff comments:
In `@pkg/apis/core/validation/validation.go`:
- Around line 7457-7475: validatePodResourceName currently rejects
core.ResourcePID whenever PerPodPIDLimit is disabled, which breaks updates for
pods that already had limits.pid set. Update the validation path used by
ValidatePodUpdate so it can see the old pod state, and either thread the old
object into validatePodResourceName or add a ratchet there to allow existing pid
values to pass when they were already present before the feature gate was turned
off. Keep the fix anchored around validatePodResourceName and the pod update
validation flow.

---

Nitpick comments:
In `@pkg/kubelet/cm/pod_container_manager_linux_test.go`:
- Around line 442-509: These tests are duplicating production behavior instead
of verifying it, so they won’t catch regressions in the real PID-limit and
capping paths. Move the cgroupsv1 error decision and PID-limit capping decision
into a small helper used by EnsureExists in pod_container_manager_linux.go, then
update TestGetPodPIDLimitCgroupsV1ErrorMessage and the PIDLimitCapped tests to
call that helper (or the real path) rather than constructing the same fmt.Errorf
or inline if-check themselves.

In `@pkg/kubelet/cm/pod_container_manager_linux.go`:
- Around line 98-119: The PID-limit decision logic inside EnsureExists is too
tightly coupled to cgroup checks and side effects to unit test cleanly. Extract
the effective PID limit computation, cgroupsv1/v2 validation, capping, metrics,
and warning-event emission into a small helper (for example around
getPodPIDLimit and the podPidsLimit handling) that takes an injectable
cgroups-mode check and recorder/logger dependencies. Keep EnsureExists focused
on wiring the helper’s result into containerConfig.ResourceParameters.PidsLimit
so tests can call the real logic directly.
🪄 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: 1d6772d8-82b9-44cd-8a17-a19a43ba32a8

📥 Commits

Reviewing files that changed from the base of the PR and between a466682 and f768e33.

📒 Files selected for processing (21)
  • pkg/api/pod/util.go
  • pkg/api/pod/util_test.go
  • pkg/apis/core/helper/helpers.go
  • pkg/apis/core/helper/helpers_test.go
  • pkg/apis/core/types.go
  • pkg/apis/core/validation/validation.go
  • pkg/apis/core/validation/validation_test.go
  • pkg/features/kube_features.go
  • pkg/kubelet/cm/container_manager_linux.go
  • pkg/kubelet/cm/pod_container_manager_linux.go
  • pkg/kubelet/cm/pod_container_manager_linux_test.go
  • pkg/kubelet/eviction/eviction_manager_test.go
  • pkg/kubelet/metrics/metrics.go
  • staging/src/k8s.io/api/core/v1/types.go
  • staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/perpodpidlimit/feature.go
  • staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/perpodpidlimit/feature_test.go
  • staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/registry.go
  • test/compatibility_lifecycle/reference/versioned_feature_list.yaml
  • test/e2e_node/eviction_test.go
  • test/e2e_node/pids_test.go
  • test/instrumentation/documentation/documentation-list.yaml

Comment on lines +98 to +119
effectivePidLimit := m.podPidsLimit
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PerPodPIDLimit) {
if podPid := getPodPIDLimit(pod); podPid > 0 {
if !libcontainercgroups.IsCgroup2UnifiedMode() {
return fmt.Errorf("per-pod PID limit requires cgroupsv2, but this node is running cgroupsv1; pod %s specifies spec.resources.limits.pid", pod.Name)
} else {
if podPid < effectivePidLimit || effectivePidLimit <= 0 {
effectivePidLimit = podPid
} else if podPid > effectivePidLimit {
logger.Info("Pod PID limit capped by node podPidsLimit", "pod", klog.KObj(pod), "requested", podPid, "effective", effectivePidLimit)
if m.recorder != nil {
m.recorder.Eventf(pod, v1.EventTypeWarning, "PIDLimitCapped",
"Requested PID limit %d exceeds node podPidsLimit %d; effective limit capped to %d",
podPid, effectivePidLimit, effectivePidLimit)
}
}
metrics.PodPIDLimitApplied.Inc()
}
}
}
if effectivePidLimit > 0 {
containerConfig.ResourceParameters.PidsLimit = &effectivePidLimit

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Cgroupsv1 path hard-errors instead of falling back, contradicting the PR's stated design.

Per the PR objectives: "on cgroupsv2 nodes, kubelet enforces the effective PID limit as min(node podPidsLimit, pod pid limit); on cgroupsv1, it falls back to the node-level limit and emits a warning." The implementation instead returns an error, which propagates out of EnsureExists and fails pod cgroup/sandbox creation entirely — the pod will never start on a cgroupsv1 node once PerPodPIDLimit is enabled and a pod specifies spec.resources.limits.pid, rather than gracefully degrading to the node-level limit as documented.

🛡️ Proposed fix: fall back to node-level limit with a warning instead of erroring
 		effectivePidLimit := m.podPidsLimit
 		if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PerPodPIDLimit) {
 			if podPid := getPodPIDLimit(pod); podPid > 0 {
 				if !libcontainercgroups.IsCgroup2UnifiedMode() {
-					return fmt.Errorf("per-pod PID limit requires cgroupsv2, but this node is running cgroupsv1; pod %s specifies spec.resources.limits.pid", pod.Name)
+					logger.Info("Per-pod PID limit requires cgroupsv2; falling back to node-level podPidsLimit", "pod", klog.KObj(pod))
+					if m.recorder != nil {
+						m.recorder.Eventf(pod, v1.EventTypeWarning, "PIDLimitUnsupported",
+							"Per-pod PID limit requires cgroupsv2, but this node is running cgroupsv1; falling back to node podPidsLimit %d", effectivePidLimit)
+					}
 				} else {
 					if podPid < effectivePidLimit || effectivePidLimit <= 0 {
 						effectivePidLimit = podPid

Please confirm against the KEP-6063 design whether hard-failing or falling back is intended.

📝 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
effectivePidLimit := m.podPidsLimit
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PerPodPIDLimit) {
if podPid := getPodPIDLimit(pod); podPid > 0 {
if !libcontainercgroups.IsCgroup2UnifiedMode() {
return fmt.Errorf("per-pod PID limit requires cgroupsv2, but this node is running cgroupsv1; pod %s specifies spec.resources.limits.pid", pod.Name)
} else {
if podPid < effectivePidLimit || effectivePidLimit <= 0 {
effectivePidLimit = podPid
} else if podPid > effectivePidLimit {
logger.Info("Pod PID limit capped by node podPidsLimit", "pod", klog.KObj(pod), "requested", podPid, "effective", effectivePidLimit)
if m.recorder != nil {
m.recorder.Eventf(pod, v1.EventTypeWarning, "PIDLimitCapped",
"Requested PID limit %d exceeds node podPidsLimit %d; effective limit capped to %d",
podPid, effectivePidLimit, effectivePidLimit)
}
}
metrics.PodPIDLimitApplied.Inc()
}
}
}
if effectivePidLimit > 0 {
containerConfig.ResourceParameters.PidsLimit = &effectivePidLimit
effectivePidLimit := m.podPidsLimit
if utilfeature.DefaultFeatureGate.Enabled(kubefeatures.PerPodPIDLimit) {
if podPid := getPodPIDLimit(pod); podPid > 0 {
if !libcontainercgroups.IsCgroup2UnifiedMode() {
logger.Info("Per-pod PID limit requires cgroupsv2; falling back to node-level podPidsLimit", "pod", klog.KObj(pod))
if m.recorder != nil {
m.recorder.Eventf(pod, v1.EventTypeWarning, "PIDLimitUnsupported",
"Per-pod PID limit requires cgroupsv2, but this node is running cgroupsv1; falling back to node podPidsLimit %d", effectivePidLimit)
}
} else {
if podPid < effectivePidLimit || effectivePidLimit <= 0 {
effectivePidLimit = podPid
} else if podPid > effectivePidLimit {
logger.Info("Pod PID limit capped by node podPidsLimit", "pod", klog.KObj(pod), "requested", podPid, "effective", effectivePidLimit)
if m.recorder != nil {
m.recorder.Eventf(pod, v1.EventTypeWarning, "PIDLimitCapped",
"Requested PID limit %d exceeds node podPidsLimit %d; effective limit capped to %d",
podPid, effectivePidLimit, effectivePidLimit)
}
}
metrics.PodPIDLimitApplied.Inc()
}
}
}
if effectivePidLimit > 0 {
containerConfig.ResourceParameters.PidsLimit = &effectivePidLimit
🤖 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/kubelet/cm/pod_container_manager_linux.go` around lines 98 - 119, The
cgroupsv1 branch in the pod PID limit handling is hard-failing in the pod
container manager instead of falling back as intended. Update the logic in
pod_container_manager_linux.go around the per-pod PID limit check so that, when
PerPodPIDLimit is enabled and getPodPIDLimit(pod) is set on cgroupsv1, it logs a
warning and continues using m.podPidsLimit rather than returning an error from
EnsureExists. Keep the existing cgroupsv2 min(node limit, pod limit) behavior in
the same Pod PID limit path and preserve the metrics/event reporting symbols
(getPodPIDLimit, m.podPidsLimit, EnsureExists,
containerConfig.ResourceParameters.PidsLimit).

f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
const pressureTimeout = 10 * time.Minute
const expectedNodeCondition = v1.NodePIDPressure
const expectedStarvedResource = noStarvedResource

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Expect the PID starved resource in eviction events.

SignalPIDAvailable maps to the internal "pids" resource, so passing none here makes verifyEvictionEvents expect no starved resource for a PID-pressure eviction.

Proposed fix
-	const expectedStarvedResource = noStarvedResource
+	const expectedStarvedResource = v1.ResourceName("pids")
📝 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
const expectedStarvedResource = noStarvedResource
const expectedStarvedResource = v1.ResourceName("pids")
🤖 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 `@test/e2e_node/eviction_test.go` at line 743, The PID-pressure eviction test
is expecting the wrong starved resource because SignalPIDAvailable maps to the
internal pids resource, not none. Update the expected value in
verifyEvictionEvents for this PID eviction case so it uses the PID starved
resource constant instead of noStarvedResource, and keep the change localized
around the expectedStarvedResource assignment in eviction_test.go.

Comment on lines +178 to +180
ginkgo.By("checking if the per-pod PID limit was applied (min of node 4096, pod 2048 = 2048)")
verifyPod := makePodToVerifyPids("pod"+podUID, resource.MustParse("2048"))
e2epod.NewPodClient(f).Create(ctx, verifyPod)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Gate per-pod enforcement assertions to cgroups v2.

These checks expect 2048/1024 and fork failure unconditionally, but the PR contract says cgroups v1 falls back to the node PodPidsLimit (4096). Skip these enforcement-only checks on cgroups v1 or branch expected values by cgroup mode.

Example adjustment
 ginkgo.It("should apply per-pod PID limit lower than node default", func(ctx context.Context) {
+	if !IsCgroup2UnifiedMode() {
+		ginkgo.Skip("per-pod PID limits are enforced only on cgroup v2")
+	}
 	ginkgo.By("creating a pod with pid limit below the node default")

Also applies to: 258-260, 324-327, 354-359

🤖 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 `@test/e2e_node/pids_test.go` around lines 178 - 180, The per-pod PID
enforcement checks in the pids E2E flow are assuming cgroups v2 behavior, but
cgroups v1 should fall back to the node PodPidsLimit instead. Update the
assertions in pidsTest (including the makePodToVerifyPids/Create checks and the
fork-failure verification paths) to either skip on cgroups v1 or choose expected
PID limits based on cgroup mode, so the 2048/1024 expectations only run when
cgroups v2 is enabled.

Comment on lines +423 to +425
verifyPod := makePodToVerifyPids("pod"+podUID, resource.MustParse("2048"))
e2epod.NewPodClient(f).Create(ctx, verifyPod)
err := e2epod.WaitForPodSuccessInNamespace(ctx, f.ClientSet, verifyPod.Name, f.Namespace.Name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don’t run the HostPath verifier inside PSA namespaces.

makePodToVerifyPids mounts /sys/fs/cgroup via HostPath, so the verifier pod will be rejected under Baseline/Restricted PSA even if the PID-limited workload pod is admitted. Move verification to a privileged namespace or limit these cases to admission success.

Also applies to: 484-486

🤖 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 `@test/e2e_node/pids_test.go` around lines 423 - 425, The PID verifier path in
makePodToVerifyPids is using a HostPath mount that will be rejected in PSA
Baseline/Restricted namespaces, so don’t create or wait for that verifier pod in
those namespaces. Update the e2e flow around the verifyPod creation and
WaitForPodSuccessInNamespace calls to either run the verifier in a privileged
namespace or only execute it when the test is validating admission success, and
apply the same fix in the other matching verify block.

Comment on lines +467 to +470
SecurityContext: &v1.SecurityContext{
RunAsNonRoot: new(true),
RunAsUser: new(int64(1000)),
AllowPrivilegeEscalation: new(false),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify no invalid new(value) bool/int64 pointer initializers remain.
rg -nP 'new\((true|false|int64\()' --type=go .

Repository: openshift/kubernetes

Length of output: 400


Replace invalid new(value) initializers. new takes a type, so new(true), new(int64(1000)), and new(false) won’t compile.

🤖 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 `@test/e2e_node/pids_test.go` around lines 467 - 470, The SecurityContext
initializers use invalid new(value) calls, which do not compile. Update the pids
test fixture to use valid pointer creation for the RunAsNonRoot, RunAsUser, and
AllowPrivilegeEscalation fields in the SecurityContext literal, keeping the
changes localized around the existing SecurityContext setup.

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. kind/feature Categorizes issue or PR as related to a new feature. vendor-update Touching vendor dir or related files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants