Skip to content

UPSTREAM: 140013: KEP-5855: Add bind mount options to VolumeMount - #2744

Draft
amritansh1502 wants to merge 10 commits into
openshift:masterfrom
amritansh1502:carry-bind-mount-options
Draft

UPSTREAM: 140013: KEP-5855: Add bind mount options to VolumeMount#2744
amritansh1502 wants to merge 10 commits into
openshift:masterfrom
amritansh1502:carry-bind-mount-options

Conversation

@amritansh1502

@amritansh1502 amritansh1502 commented Aug 12, 2026

Copy link
Copy Markdown
  • Add bindMountOptions field to VolumeMount
  • Run codegen for VolumeMount bindMountOptions field
  • Add VolumeBindMountOptions feature gate
  • Add validation for volumeMount bindMountOptions
  • Add feature gate drop logic for volumeMount bindMountOptions
  • Add mount_options to CRI Mount message
  • Implement kubelet support for bindMountOptions
  • Add mount_options to CRI RuntimeFeatures
  • Add Node Declared Features integration for VolumeBindMountOptions

Summary by CodeRabbit

  • New Features

    • Added optional Linux bind-mount options for volume mounts: noexec, nodev, and nosuid.
    • Propagated bind-mount options through container runtimes and declarative configuration.
    • Added the alpha VolumeBindMountOptions feature gate in Kubernetes 1.37.
    • Options have no effect on Windows and are unavailable for image volumes.
  • Bug Fixes

    • Added validation for unsupported, duplicate, and image-volume bind-mount options.
  • Tests

    • Added coverage for bind-mount option validation and runtime propagation.

@openshift-merge-bot

Copy link
Copy Markdown

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: LGTM mode

@openshift-ci-robot

Copy link
Copy Markdown

@amritansh1502: No Jira issue with key KEP-5855 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:

  • Add bindMountOptions field to VolumeMount
  • Run codegen for VolumeMount bindMountOptions field
  • Add VolumeBindMountOptions feature gate
  • Add validation for volumeMount bindMountOptions
  • Add feature gate drop logic for volumeMount bindMountOptions
  • Add mount_options to CRI Mount message
  • Implement kubelet support for bindMountOptions
  • Add mount_options to CRI RuntimeFeatures
  • Add Node Declared Features integration for VolumeBindMountOptions

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 Aug 12, 2026
@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 Aug 12, 2026
@openshift-ci

openshift-ci Bot commented Aug 12, 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

@amritansh1502: 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 Aug 12, 2026

Copy link
Copy Markdown

Walkthrough

The change adds alpha-gated Linux bind-mount options to VolumeMount, validates and preserves them through pod updates, propagates them through the kubelet and CRI, registers node feature discovery, and updates schemas, fixtures, tests, and feature-lifecycle metadata.

Changes

Volume bind-mount options

Layer / File(s) Summary
VolumeMount API contract and validation
pkg/apis/core/*, staging/src/k8s.io/api/core/v1/*, api/openapi-spec/*, staging/src/k8s.io/client-go/applyconfigurations/*
Adds the optional set-valued BindMountOptions field and the noexec, nodev, and nosuid constants. Validation rejects unsupported, duplicate, and image-volume options.
Pod compatibility handling
pkg/api/pod/*
Preserves bind-mount options used by old pods and clears newly introduced options when the feature is disabled. Adds related compatibility tests.
Runtime propagation and node capability
pkg/kubelet/*, staging/src/k8s.io/cri-api/*, staging/src/k8s.io/component-helpers/nodedeclaredfeatures/*
Propagates bind-mount options from pod mounts to CRI mounts. Reports runtime support through MountOptions and registers node feature discovery.
Fixtures and lifecycle metadata
staging/src/k8s.io/api/testdata/HEAD/*, test/compatibility_lifecycle/reference/*
Adds bind-mount options to versioned API fixtures and registers VolumeBindMountOptions as an alpha feature beginning in version 1.37. Related kubelet tests are updated.

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

Sequence Diagram(s)

sequenceDiagram
  participant PodSpec
  participant Kubelet
  participant ContainerRuntime
  participant NodeFeatureDiscovery
  PodSpec->>Kubelet: BindMountOptions
  Kubelet->>ContainerRuntime: Mount.mount_options
  ContainerRuntime-->>Kubelet: RuntimeFeatures.mount_options
  Kubelet->>NodeFeatureDiscovery: RuntimeFeatures.MountOptions
  NodeFeatureDiscovery-->>Kubelet: VolumeBindMountOptions availability
Loading
🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
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.
✅ 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 clearly and concisely identifies the main change: adding bind mount options to VolumeMount.
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 The cumulative PR diff changes five Go test files, but scans found no Ginkgo APIs or It/Describe/Context/When titles. Added t.Run names use deterministic table values.
Test Structure And Quality ✅ Passed The changed tests use Go testing Test/t.Run only; no Ginkgo/Gomega imports or APIs and no It blocks are present, so this Ginkgo-specific check is not applicable.
Microshift Test Compatibility ✅ Passed The PR adds only Go unit tests; the diff contains no Ginkgo DSL/imports or e2e test paths, so the MicroShift compatibility check is not applicable.
Single Node Openshift (Sno) Test Compatibility ✅ Passed The feature-range diff adds no e2e paths or Ginkgo declarations; its new tests use Go testing.T and t.Run, so SNO multi-node compatibility rules do not apply.
Topology-Aware Scheduling Compatibility ✅ Passed The PR adds VolumeMount/CRI/kubelet plumbing and fixture fields only; no deployment manifests, operators, controllers, or topology constraints such as affinity, selectors, spreads, replicas, tolera...
Ote Binary Stdout Contract ✅ Passed The feature series leaves openshift-hack OTE binaries and e2e wrappers unchanged; added Go code has no process-level stdout writes or suite entrypoint additions.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed Changed tests use standard Go testing and contain no Ginkgo imports or It/Describe/Context/When declarations; no new Ginkgo e2e test or external connectivity requirement was introduced.
No-Weak-Crypto ✅ Passed The PR adds mount-option API, validation, and propagation only; a scan of 1,475 added source/schema lines found no weak algorithms, crypto APIs, or secret/token comparisons.
Container-Privileges ✅ Passed The PR diff adds no privileged, hostPID, hostNetwork, hostIPC, SYS_ADMIN, allowPrivilegeEscalation, or root-execution settings; fixture additions contain no security settings.
No-Sensitive-Data-In-Logs ✅ Passed Diff inspection found no new production logging calls; added outputs only format bind-mount options in tests/protobuf strings, and the values are mount flags, not sensitive data.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@openshift-ci openshift-ci Bot added the vendor-update Touching vendor dir or related files label Aug 12, 2026
@openshift-ci

openshift-ci Bot commented Aug 12, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

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

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

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

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

@openshift-ci-robot

Copy link
Copy Markdown

@amritansh1502: No Jira issue with key KEP-5855 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:

  • Add bindMountOptions field to VolumeMount
  • Run codegen for VolumeMount bindMountOptions field
  • Add VolumeBindMountOptions feature gate
  • Add validation for volumeMount bindMountOptions
  • Add feature gate drop logic for volumeMount bindMountOptions
  • Add mount_options to CRI Mount message
  • Implement kubelet support for bindMountOptions
  • Add mount_options to CRI RuntimeFeatures
  • Add Node Declared Features integration for VolumeBindMountOptions

Summary by CodeRabbit

  • New Features

  • Added optional Linux bind-mount options for volume mounts: noexec, nodev, and nosuid.

  • Added support for propagating these options through container runtimes and declarative configuration.

  • Added the alpha VolumeBindMountOptions feature gate in Kubernetes 1.37.

  • Bug Fixes

  • Added validation to reject unsupported, duplicate, or image-volume bind-mount options.

  • Improved pod resource defaulting and validation compatibility for newer Kubernetes fields.

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

Caution

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

⚠️ Outside diff range comments (1)
pkg/kubelet/kubelet_pods.go (1)

403-413: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Gate BindMountOptions in the kubelet mount path.

makeMounts copies mount.BindMountOptions without checking the VolumeBindMountOptions feature gate. The same function guards the analogous alpha field: lines 399-401 return an error when RecursiveReadOnlyMounts is disabled but the field is set. Two paths reach makeMounts without apiserver validation:

  • Static pods, which the kubelet admits directly.
  • Pods created while the apiserver gate was enabled and later served to a kubelet with the gate disabled.

In both cases the kubelet forwards the options to CRI even though the feature is off on this node.

A second gap: the options are also forwarded when the runtime does not advertise the mount_options capability. The runtime then ignores unknown fields, so a pod that asks for noexec or nosuid starts without them. Silently dropping a hardening option is worse than failing the mount. Consider plumbing the runtime capability into makeMounts the same way supportsRRO is plumbed, and returning an error when options are requested but unsupported.

🛡️ Proposed gate check
 		if rro && !utilfeature.DefaultFeatureGate.Enabled(features.RecursiveReadOnlyMounts) {
 			return nil, cleanupAction, fmt.Errorf("recursive read-only mount needs feature gate %q to be enabled", features.RecursiveReadOnlyMounts)
 		}
+
+		bindMountOptions := mount.BindMountOptions
+		if len(bindMountOptions) > 0 && !utilfeature.DefaultFeatureGate.Enabled(features.VolumeBindMountOptions) {
+			return nil, cleanupAction, fmt.Errorf("bind mount options need feature gate %q to be enabled", features.VolumeBindMountOptions)
+		}
 
 		mounts = append(mounts, kubecontainer.Mount{
 			Name:              mount.Name,
@@
 			Propagation:       propagation,
-			BindMountOptions:  mount.BindMountOptions,
+			BindMountOptions:  bindMountOptions,
 		})
🤖 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/kubelet_pods.go` around lines 403 - 413, Update makeMounts to
gate BindMountOptions on the VolumeBindMountOptions feature gate, rejecting
requested options when the gate is disabled, including for static pods and
previously validated pods. Plumb the runtime’s mount_options capability into
makeMounts alongside supportsRRO, and return an error when bind-mount options
are requested but unsupported instead of forwarding them or silently dropping
them.
🧹 Nitpick comments (5)
staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go (2)

55-78: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reduce the duplicated scan over container lists.

The three loops differ only in the list they read. A small helper keeps the intent in one place.

♻️ Proposed refactor
+func hasBindMountOptions(mounts []v1.VolumeMount) bool {
+	for i := range mounts {
+		if len(mounts[i].BindMountOptions) > 0 {
+			return true
+		}
+	}
+	return false
+}
+
 func (f *volumeBindMountOptionsFeature) InferForScheduling(podInfo *types.PodInfo) bool {
-	for i := range podInfo.Spec.Containers {
-		for j := range podInfo.Spec.Containers[i].VolumeMounts {
-			if len(podInfo.Spec.Containers[i].VolumeMounts[j].BindMountOptions) > 0 {
-				return true
-			}
-		}
-	}
-	for i := range podInfo.Spec.InitContainers {
-		for j := range podInfo.Spec.InitContainers[i].VolumeMounts {
-			if len(podInfo.Spec.InitContainers[i].VolumeMounts[j].BindMountOptions) > 0 {
-				return true
-			}
-		}
-	}
-	for i := range podInfo.Spec.EphemeralContainers {
-		for j := range podInfo.Spec.EphemeralContainers[i].VolumeMounts {
-			if len(podInfo.Spec.EphemeralContainers[i].VolumeMounts[j].BindMountOptions) > 0 {
-				return true
-			}
-		}
-	}
-	return false
+	for i := range podInfo.Spec.Containers {
+		if hasBindMountOptions(podInfo.Spec.Containers[i].VolumeMounts) {
+			return true
+		}
+	}
+	for i := range podInfo.Spec.InitContainers {
+		if hasBindMountOptions(podInfo.Spec.InitContainers[i].VolumeMounts) {
+			return true
+		}
+	}
+	for i := range podInfo.Spec.EphemeralContainers {
+		if hasBindMountOptions(podInfo.Spec.EphemeralContainers[i].VolumeMounts) {
+			return true
+		}
+	}
+	return false
 }

The helper needs the k8s.io/api/core/v1 import.

🤖 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
`@staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go`
around lines 55 - 78, Refactor volumeBindMountOptionsFeature.InferForScheduling
to use a small helper accepting a core/v1 container slice and scanning
VolumeMounts for non-empty BindMountOptions. Invoke the helper for Containers,
InitContainers, and EphemeralContainers, returning true if any scan succeeds and
false otherwise; add the required core/v1 import.

48-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add unit tests for volumebindmountoptions.

Cover Discover with disabled gates and unsupported runtime features. Cover InferForScheduling with init and ephemeral containers.

🤖 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
`@staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go`
around lines 48 - 53, Add unit tests for the volumebindmountoptions feature,
covering volumeBindMountOptionsFeature.Discover when the feature gate is
disabled and when runtime MountOptions support is unavailable, and covering
InferForScheduling for both init-container and ephemeral-container scenarios.
Use the existing feature-test patterns and assert the expected discovery and
scheduling results.
pkg/kubelet/container/runtime_test.go (1)

690-705: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a case where MountOptions is true.

Both cases keep MountOptions at the zero value. The true rendering of the new flag stays untested.

♻️ Proposed additional case
 		{
 			name: "features with both flags true",
 			features: &RuntimeFeatures{
 				SupplementalGroupsPolicy:  true,
 				UserNamespacesHostNetwork: true,
 			},
 			expected: "SupplementalGroupsPolicy: true UserNamespacesHostNetwork: true MountOptions: false",
 		},
+		{
+			name: "features with mount options true",
+			features: &RuntimeFeatures{
+				SupplementalGroupsPolicy:  true,
+				UserNamespacesHostNetwork: true,
+				MountOptions:              true,
+			},
+			expected: "SupplementalGroupsPolicy: true UserNamespacesHostNetwork: true MountOptions: true",
+		},
 		{
🤖 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/container/runtime_test.go` around lines 690 - 705, Add a test
case to the RuntimeFeatures string-formatting table with MountOptions set to
true, and update the expected output to include “MountOptions: true” while
preserving coverage of the existing feature flags.
pkg/kubelet/kubelet_pods_test.go (1)

9485-9512: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Assert the mount identity, not only the index.

makeMounts appends the /etc/hosts mount after the volume mounts, so the index assumptions hold today. They break silently if mount ordering changes. Assert on Name or ContainerPath to keep the failure message specific.

♻️ Proposed refactor
 	mounts, _, err := makeMounts(logger, &pod, podDir, &container, "fakepod", "", []string{""}, podVolumes, fhu, &trackingSubpath{}, nil, false, nil)
 	require.NoError(t, err)
 	require.Len(t, mounts, 3) // 2 volume mounts + /etc/hosts
+	require.Equal(t, "/mnt/a", mounts[0].ContainerPath)
+	require.Equal(t, "/mnt/b", mounts[1].ContainerPath)
 	assert.Equal(t, []string{"noexec", "nosuid"}, mounts[0].BindMountOptions)
 	assert.Empty(t, mounts[1].BindMountOptions)
🤖 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/kubelet_pods_test.go` around lines 9485 - 9512, Update
TestMakeMountsBindMountOptions to locate mounts by their identifying Name or
ContainerPath values instead of relying on mounts[0] and mounts[1]. Assert the
expected BindMountOptions for the /mnt/a and /mnt/b volume mounts, while
preserving the existing mount count assertion.
pkg/apis/core/validation/validation_test.go (1)

7611-7621: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the error type and field path, not only the error count.

The current check only verifies that some error exists. A test case can pass for an unrelated reason. For example, the image-volume case would still pass if the error came from another rule. Compare the expected field path and error type per case.

♻️ Suggested stronger assertions
 	tests := []struct {
 		name        string
 		mount       core.VolumeMount
-		expectError bool
+		expectErrs  field.ErrorList
 	}{

Then compare with the expected list, for example:

errs := ValidateVolumeMounts([]core.VolumeMount{test.mount}, volDevices, volumes, nil, field.NewPath("field"), PodValidationOptions{})
if diff := cmp.Diff(test.expectErrs, errs, cmpopts.IgnoreFields(field.Error{}, "Detail", "Origin")); diff != "" {
    t.Errorf("unexpected errors (-want +got):\n%s", diff)
}
🤖 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_test.go` around lines 7611 - 7621,
Strengthen the table-driven assertions around ValidateVolumeMounts so each case
specifies its expected field.Error values, including error type and field path,
rather than only an expectError boolean. Compare test.expectErrs with errs using
cmp.Diff while ignoring Detail and Origin, and update the test cases to provide
the expected errors.
🤖 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/api/pod/util_test.go`:
- Around line 3470-3479: Deep-copy the shared volume fixture for each subtest in
the test loop before assigning it to the pod specs, ensuring newPod.Volumes and
wantPod.Volumes are independent. Update the fixture setup around
dropDisabledAtomicWriteVolumeUserFields so mutation in one case cannot affect
subsequent cases, especially the feature-gate-enabled scenarios.

In `@pkg/features/kube_features.go`:
- Line 2741: Update the VolumeBindMountOptions dependency declaration to include
NodeDeclaredFeatures, changing the empty dependency set to the required
NodeDeclaredFeatures entry while preserving the existing feature registration.

In `@pkg/kubelet/kubelet_pods_test.go`:
- Around line 5748-5762: Replace the invalid new(resource.MustParse(...))
expressions in NodeAllocatableResourceClaimStatuses with the existing ptr
helper, passing each parsed resource.Quantity value to ptr.To. Preserve the CPU
and memory quantities and use the same approach already established elsewhere in
kubelet_pods_test.go.
- Around line 9276-9386: Fix the invalid resource quantity pointer construction
by replacing new(resource.MustParse(...)) usages with
ptr.To(resource.MustParse(...)). Update makeMounts to accumulate every
successful subpath cleanup callback instead of overwriting cleanupAction, and
return a non-nil aggregate cleanup callback even when mount processing errors;
invoke all callbacks created before the error.

In `@staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml`:
- Around line 445-447: Replace every bindMountOptionsValue placeholder in
staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml lines 445-447, 676-678,
and 908-910; staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json
lines 702-705, 1019-1022, and 1336-1339;
staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml lines 481-483,
712-714, and 944-946; and staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json
lines 560-563, 877-880, and 1194-1197 with an allowed bind-mount option: noexec,
nodev, or nosuid.

In
`@staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go`:
- Around line 80-82: Update volumeBindMountOptionsFeature.InferForUpdate to
return true when newPodInfo requires VolumeBindMountOptions but oldPodInfo does
not, including additions of BindMountOptions through ephemeral container
updates. Ensure the related admission handling includes the ephemeralcontainers
subresource if needed to enforce the node MountOptions requirement.

In `@staging/src/k8s.io/cri-api/pkg/apis/runtime/v1/api.proto`:
- Around line 359-362: Update the container mount creation path around
makeMounts to check RuntimeFeatures.MountOptions before propagating
BindMountOptions into runtimeapi.Mount.MountOptions. Reject the pod or container
request when bind mount options are specified but runtime support is
unavailable, before container creation; preserve existing behavior for supported
runtimes and mounts without options.

---

Outside diff comments:
In `@pkg/kubelet/kubelet_pods.go`:
- Around line 403-413: Update makeMounts to gate BindMountOptions on the
VolumeBindMountOptions feature gate, rejecting requested options when the gate
is disabled, including for static pods and previously validated pods. Plumb the
runtime’s mount_options capability into makeMounts alongside supportsRRO, and
return an error when bind-mount options are requested but unsupported instead of
forwarding them or silently dropping them.

---

Nitpick comments:
In `@pkg/apis/core/validation/validation_test.go`:
- Around line 7611-7621: Strengthen the table-driven assertions around
ValidateVolumeMounts so each case specifies its expected field.Error values,
including error type and field path, rather than only an expectError boolean.
Compare test.expectErrs with errs using cmp.Diff while ignoring Detail and
Origin, and update the test cases to provide the expected errors.

In `@pkg/kubelet/container/runtime_test.go`:
- Around line 690-705: Add a test case to the RuntimeFeatures string-formatting
table with MountOptions set to true, and update the expected output to include
“MountOptions: true” while preserving coverage of the existing feature flags.

In `@pkg/kubelet/kubelet_pods_test.go`:
- Around line 9485-9512: Update TestMakeMountsBindMountOptions to locate mounts
by their identifying Name or ContainerPath values instead of relying on
mounts[0] and mounts[1]. Assert the expected BindMountOptions for the /mnt/a and
/mnt/b volume mounts, while preserving the existing mount count assertion.

In
`@staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go`:
- Around line 55-78: Refactor volumeBindMountOptionsFeature.InferForScheduling
to use a small helper accepting a core/v1 container slice and scanning
VolumeMounts for non-empty BindMountOptions. Invoke the helper for Containers,
InitContainers, and EphemeralContainers, returning true if any scan succeeds and
false otherwise; add the required core/v1 import.
- Around line 48-53: Add unit tests for the volumebindmountoptions feature,
covering volumeBindMountOptionsFeature.Discover when the feature gate is
disabled and when runtime MountOptions support is unavailable, and covering
InferForScheduling for both init-container and ephemeral-container scenarios.
Use the existing feature-test patterns and assert the expected discovery and
scheduling results.
🪄 Autofix

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: Pro Plus

Run ID: a5494c3c-54d7-42a9-85dc-1df22d13a0de

📥 Commits

Reviewing files that changed from the base of the PR and between e63ab41 and f5f636d.

⛔ Files ignored due to path filters (5)
  • pkg/apis/core/zz_generated.deepcopy.go is excluded by !**/zz_generated*
  • pkg/generated/openapi/zz_generated.openapi.go is excluded by !**/generated/**, !**/zz_generated*
  • staging/src/k8s.io/api/core/v1/generated.pb.go is excluded by !**/*.pb.go
  • staging/src/k8s.io/api/core/v1/zz_generated.deepcopy.go is excluded by !**/zz_generated*
  • staging/src/k8s.io/cri-api/pkg/apis/runtime/v1/api.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (86)
  • api/openapi-spec/swagger.json
  • api/openapi-spec/v3/api__v1_openapi.json
  • api/openapi-spec/v3/apis__apps__v1_openapi.json
  • api/openapi-spec/v3/apis__batch__v1_openapi.json
  • pkg/api/pod/util.go
  • pkg/api/pod/util_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/container/runtime.go
  • pkg/kubelet/container/runtime_test.go
  • pkg/kubelet/kubelet_node_declared_features.go
  • pkg/kubelet/kubelet_pods.go
  • pkg/kubelet/kubelet_pods_test.go
  • pkg/kubelet/kuberuntime/helpers.go
  • pkg/kubelet/kuberuntime/kuberuntime_container.go
  • pkg/kubelet/kuberuntime/kuberuntime_container_test.go
  • staging/src/k8s.io/api/core/v1/generated.proto
  • staging/src/k8s.io/api/core/v1/types.go
  • staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.json
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.pb
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1.DaemonSet.yaml
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.json
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.pb
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1.Deployment.yaml
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.json
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.pb
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1.ReplicaSet.yaml
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.json
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.pb
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1.StatefulSet.yaml
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.json
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.pb
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.Deployment.yaml
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.json
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.pb
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta1.StatefulSet.yaml
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.json
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.pb
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.DaemonSet.yaml
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.json
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.pb
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.Deployment.yaml
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.json
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.pb
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.ReplicaSet.yaml
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.json
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.pb
  • staging/src/k8s.io/api/testdata/HEAD/apps.v1beta2.StatefulSet.yaml
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.json
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.pb
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1.CronJob.yaml
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.json
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.pb
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.pb
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.pb
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.yaml
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.json
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.pb
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.PodTemplate.yaml
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.json
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.pb
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.ReplicationController.yaml
  • staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.json
  • staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.pb
  • staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.DaemonSet.yaml
  • staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.json
  • staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.pb
  • staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.Deployment.yaml
  • staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.json
  • staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.pb
  • staging/src/k8s.io/api/testdata/HEAD/extensions.v1beta1.ReplicaSet.yaml
  • staging/src/k8s.io/client-go/applyconfigurations/core/v1/volumemount.go
  • staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go
  • staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/registry.go
  • staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go
  • staging/src/k8s.io/component-helpers/nodedeclaredfeatures/types/types.go
  • staging/src/k8s.io/cri-api/pkg/apis/runtime/v1/api.proto
  • test/compatibility_lifecycle/reference/feature_list.md
  • test/compatibility_lifecycle/reference/versioned_feature_list.yaml

Comment thread pkg/api/pod/util_test.go Outdated
Comment on lines +3470 to +3479
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AtomicWriteVolumeUserFields, tc.atomicWriteVolumeUserFieldsEnabled)

dropDisabledAtomicWriteVolumeUserFields(tc.newPod, tc.oldPod)
if diff := cmp.Diff(tc.newPod, tc.wantPod); diff != "" {
t.Fatalf("Unexpected modification to new pod; diff (-got +want)\n%s", diff)
}
})
}

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

Deep-copy the shared volume fixture per test case.

volumesWithUserFields is a single shared slice. The first case passes it as newPod.Volumes, and dropDisabledAtomicWriteVolumeUserFields clears the User and DefaultUser fields in place. The later "feature gate enabled" cases then use the already-stripped slice for both newPod and wantPod, so those comparisons succeed without testing anything.

Copy the pod specs inside the subtest.

💚 Proposed fix
 	for _, tc := range testCases {
 		t.Run(tc.description, func(t *testing.T) {
 			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AtomicWriteVolumeUserFields, tc.atomicWriteVolumeUserFieldsEnabled)
 
-			dropDisabledAtomicWriteVolumeUserFields(tc.newPod, tc.oldPod)
-			if diff := cmp.Diff(tc.newPod, tc.wantPod); diff != "" {
+			newPod := tc.newPod.DeepCopy()
+			wantPod := tc.wantPod.DeepCopy()
+			dropDisabledAtomicWriteVolumeUserFields(newPod, tc.oldPod.DeepCopy())
+			if diff := cmp.Diff(newPod, wantPod); diff != "" {
 				t.Fatalf("Unexpected modification to new pod; diff (-got +want)\n%s", diff)
 			}
 		})
 	}
📝 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
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AtomicWriteVolumeUserFields, tc.atomicWriteVolumeUserFieldsEnabled)
dropDisabledAtomicWriteVolumeUserFields(tc.newPod, tc.oldPod)
if diff := cmp.Diff(tc.newPod, tc.wantPod); diff != "" {
t.Fatalf("Unexpected modification to new pod; diff (-got +want)\n%s", diff)
}
})
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AtomicWriteVolumeUserFields, tc.atomicWriteVolumeUserFieldsEnabled)
newPod := tc.newPod.DeepCopy()
wantPod := tc.wantPod.DeepCopy()
dropDisabledAtomicWriteVolumeUserFields(newPod, tc.oldPod.DeepCopy())
if diff := cmp.Diff(newPod, wantPod); diff != "" {
t.Fatalf("Unexpected modification to new pod; diff (-got +want)\n%s", diff)
}
})
}
🤖 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/api/pod/util_test.go` around lines 3470 - 3479, Deep-copy the shared
volume fixture for each subtest in the test loop before assigning it to the pod
specs, ensuring newPod.Volumes and wantPod.Volumes are independent. Update the
fixture setup around dropDisabledAtomicWriteVolumeUserFields so mutation in one
case cannot affect subsequent cases, especially the feature-gate-enabled
scenarios.


VolumeAttributesClass: {},

VolumeBindMountOptions: {},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the node declared feature registration for bind mount options.
fd -t f 'feature.go' -p 'nodedeclaredfeatures' --exec cat -n

# Show the registry entries.
fd -t f 'registry.go' -p 'nodedeclaredfeatures' --exec cat -n

# Show how the kubelet gates the node declared feature.
rg -n -C 5 'VolumeBindMountOptions' --type=go

Repository: openshift/kubernetes

Length of output: 374


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' 'Candidate node-declared-feature files:'
git ls-files | rg 'nodedeclaredfeatures|kube_features\.go$' | head -200

printf '%s\n' 'Bind-mount feature references:'
rg -n -C 8 'VolumeBindMountOptions|NodeDeclaredFeatures|ExtendWebSocketsToKubelet|InPlacePodVerticalScalingInitContainers|UserNamespacesHostNetworkSupport' --glob '*.go' . | head -400

Repository: openshift/kubernetes

Length of output: 42972


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' 'Bind-mount feature registration:'
cat -n staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go

printf '%s\n' 'Node-declared feature registry:'
cat -n staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/registry.go

printf '%s\n' 'Feature-set and dependency definitions:'
cat -n staging/src/k8s.io/component-helpers/nodedeclaredfeatures/featureset.go
rg -n -C 12 'NodeDeclaredFeatures|VolumeBindMountOptions|ExtendWebSocketsToKubelet|InPlacePodVerticalScalingInitContainers|UserNamespacesHostNetworkSupport' pkg/features/kube_features.go staging/src/k8s.io/component-helpers/nodedeclaredfeatures --glob '*.go'

Repository: openshift/kubernetes

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' 'Consumers of the node-declared feature registry:'
rg -n -C 10 'AllFeatures|FeatureRequirements|Requirements\(\)|InferForScheduling|Discover\(' \
  staging/src/k8s.io/component-helpers/nodedeclaredfeatures \
  pkg/scheduler plugin pkg/kubelet --glob '*.go' | head -500

printf '%s\n' 'Feature-gate dependency validation and resolution:'
rg -n -C 12 'FeatureGateDependencies|featureDependencies|Dependencies|NodeDeclaredFeatures' \
  pkg staging/src/k8s.io --glob '*.go' | rg -v 'validation_test.go|kube_features.go:.*feature declaration' | head -500

Repository: openshift/kubernetes

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' 'GetFeatureRequirements call sites:'
rg -n -C 15 'GetFeatureRequirements\(' --glob '*.go' .

printf '%s\n' 'Feature-gate dependency map type and validation:'
rg -n -C 20 'FeatureDependencies|featureDependencies|DependencyMap|Dependencies map|FeatureGate.*depend|dependency.*feature' \
  pkg staging --glob '*.go' | head -500

printf '%s\n' 'Node-declared feature gate checks around framework initialization:'
rg -n -C 20 'NodeDeclaredFeatures' \
  pkg/kubelet staging/src/k8s.io/component-helpers/nodedeclaredfeatures \
  --glob '*.go' | rg -v 'kube_features.go|_test.go' | head -500

Repository: openshift/kubernetes

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' 'Scheduling and admission paths for declared feature requirements:'
rg -n -C 18 'InferForPodScheduling|NewDeclaredFeaturesAdmitHandler|nodeDeclaredFeaturesFramework|MatchNodeFeatureSet' \
  pkg/scheduler pkg/kubelet plugin staging/src/k8s.io --glob '*.go' | head -600

printf '%s\n' 'VolumeBindMountOptions runtime and kubelet paths:'
rg -n -C 15 'VolumeBindMountOptions|BindMountOptions|MountOptions' \
  pkg staging/src/k8s.io --glob '*.go' | head -600

printf '%s\n' 'Relevant feature-gate dependency entries:'
sed -n '2480,2495p;2510,2528p;2580,2588p;2730,2745p' pkg/features/kube_features.go

Repository: openshift/kubernetes

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
from pathlib import Path
import re

features = Path("pkg/features/kube_features.go").read_text()
volume = Path("staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go").read_text()
kubelet = Path("pkg/kubelet/kubelet.go").read_text()
scheduler = Path("pkg/scheduler/framework/plugins/nodedeclaredfeatures/nodedeclaredfeatures.go").read_text()

dep = re.search(
    r"VolumeBindMountOptions:\s*\{([^}]*)\}",
    features,
)
req = re.search(
    r"func \(f \*volumeBindMountOptionsFeature\) Requirements\(\).*?"
    r"EnabledFeatureGates:\s*\[\]string\{([^}]*)\}",
    volume,
    re.S,
)

print("VolumeBindMountOptions dependency entry:",
      dep.group(1).strip() if dep else "<missing>")
print("VolumeBindMountOptions registered gate requirements:",
      req.group(1).strip() if req else "<missing>")
print("VolumeBindMountOptions infers scheduling requirements:",
      "BindMountOptions" in volume and "InferForScheduling" in volume)
print("Kubelet creates declared-feature admission only under NodeDeclaredFeatures:",
      bool(re.search(
          r'if utilfeature\.DefaultFeatureGate\.Enabled\(features\.NodeDeclaredFeatures\)\s*\{'
          r'.{0,300}NewDeclaredFeaturesAdmitHandler',
          kubelet, re.S)))
print("Scheduler declared-feature plugin is gated by NodeDeclaredFeatures:",
      "features.NodeDeclaredFeatures" in scheduler)
PY

Repository: openshift/kubernetes

Length of output: 584


🏁 Script executed:

#!/bin/bash
set -eu

cat -n pkg/scheduler/framework/plugins/nodedeclaredfeatures/nodedeclaredfeatures.go | sed -n '45,100p'

rg -n -C 12 'NewNodeDeclaredFeatures|NodeDeclaredFeatures\(' \
  pkg/scheduler --glob '*.go'

rg -n -C 12 'NodeDeclaredFeatures' \
  pkg/scheduler/framework/runtime pkg/scheduler/apis pkg/scheduler --glob '*.go' | head -300

Repository: openshift/kubernetes

Length of output: 39202


Add NodeDeclaredFeatures to the VolumeBindMountOptions dependency list. VolumeBindMountOptions infers node requirements from BindMountOptions, but scheduler filtering and kubelet admission run only when NodeDeclaredFeatures is enabled. Use VolumeBindMountOptions: {NodeDeclaredFeatures}.

🤖 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/features/kube_features.go` at line 2741, Update the
VolumeBindMountOptions dependency declaration to include NodeDeclaredFeatures,
changing the empty dependency set to the required NodeDeclaredFeatures entry
while preserving the existing feature registration.

Comment on lines +5748 to +5762
NodeAllocatableResourceClaimStatuses: []v1.NodeAllocatableResourceClaimStatus{
{
ResourceClaimName: "dra-claim",
Mapping: []v1.NodeAllocatableMappedResources{
{
Name: v1.ResourceCPU,
Quantity: new(resource.MustParse("50m")),
},
{
Name: v1.ResourceMemory,
Quantity: new(resource.MustParse("50Mi")),
},
},
},
},

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

new(resource.MustParse(...)) does not compile.

The builtin new accepts a type, not a value. new(resource.MustParse("50m")) passes an expression, so the package fails to build. This breaks every test in pkg/kubelet.

ptr is already imported in this file and used at line 9156.

🐛 Proposed fix
 							Mapping: []v1.NodeAllocatableMappedResources{
 								{
 									Name:     v1.ResourceCPU,
-									Quantity: new(resource.MustParse("50m")),
+									Quantity: ptr.To(resource.MustParse("50m")),
 								},
 								{
 									Name:     v1.ResourceMemory,
-									Quantity: new(resource.MustParse("50Mi")),
+									Quantity: ptr.To(resource.MustParse("50Mi")),
 								},
 							},
📝 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
NodeAllocatableResourceClaimStatuses: []v1.NodeAllocatableResourceClaimStatus{
{
ResourceClaimName: "dra-claim",
Mapping: []v1.NodeAllocatableMappedResources{
{
Name: v1.ResourceCPU,
Quantity: new(resource.MustParse("50m")),
},
{
Name: v1.ResourceMemory,
Quantity: new(resource.MustParse("50Mi")),
},
},
},
},
NodeAllocatableResourceClaimStatuses: []v1.NodeAllocatableResourceClaimStatus{
{
ResourceClaimName: "dra-claim",
Mapping: []v1.NodeAllocatableMappedResources{
{
Name: v1.ResourceCPU,
Quantity: ptr.To(resource.MustParse("50m")),
},
{
Name: v1.ResourceMemory,
Quantity: ptr.To(resource.MustParse("50Mi")),
},
},
},
},
🤖 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/kubelet_pods_test.go` around lines 5748 - 5762, Replace the
invalid new(resource.MustParse(...)) expressions in
NodeAllocatableResourceClaimStatuses with the existing ptr helper, passing each
parsed resource.Quantity value to ptr.To. Preserve the CPU and memory quantities
and use the same approach already established elsewhere in kubelet_pods_test.go.

Comment on lines +9276 to +9386
func TestMakemountsSubpathCleanupAccumulation(t *testing.T) {
logger, _ := ktesting.NewTestContext(t)

podDir := t.TempDir()
volPath := filepath.Join(podDir, "volumes", "disk")
require.NoError(t, os.MkdirAll(volPath, 0755))

// Create the subpath directories on disk so PathExists returns true
sub1 := filepath.Join(volPath, "sub1")
sub2 := filepath.Join(volPath, "sub2")
sub3 := filepath.Join(volPath, "sub3")
require.NoError(t, os.MkdirAll(sub1, 0755))
require.NoError(t, os.MkdirAll(sub2, 0755))
require.NoError(t, os.MkdirAll(sub3, 0755))

tests := []struct {
name string
volumeMounts []v1.VolumeMount
expectedCleanedSubPaths []string
expectError bool
errorOnSubPath string
}{
{
name: "multiple subpath mounts all get cleanup called",
volumeMounts: []v1.VolumeMount{
{MountPath: "/mnt/a", Name: "disk", SubPath: "sub1"},
{MountPath: "/mnt/b", Name: "disk", SubPath: "sub2"},
{MountPath: "/mnt/c", Name: "disk", SubPath: "sub3"},
},
expectedCleanedSubPaths: []string{"sub1", "sub2", "sub3"},
},
{
name: "single subpath mount cleanup called once",
volumeMounts: []v1.VolumeMount{
{MountPath: "/mnt/a", Name: "disk", SubPath: "sub1"},
},
expectedCleanedSubPaths: []string{"sub1"},
},
{
name: "no subpath mounts no cleanup needed",
volumeMounts: []v1.VolumeMount{
{MountPath: "/mnt/a", Name: "disk"},
},
},
{
name: "error on third mount still cleans up first two",
volumeMounts: []v1.VolumeMount{
{MountPath: "/mnt/a", Name: "disk", SubPath: "sub1"},
{MountPath: "/mnt/b", Name: "disk", SubPath: "sub2"},
{MountPath: "/mnt/c", Name: "disk", SubPath: "sub3"},
},
expectedCleanedSubPaths: []string{"sub1", "sub2"},
expectError: true,
errorOnSubPath: "sub3",
},
{
name: "error on first mount returns zero cleanups",
volumeMounts: []v1.VolumeMount{
{MountPath: "/mnt/a", Name: "disk", SubPath: "sub1"},
{MountPath: "/mnt/b", Name: "disk", SubPath: "sub2"},
},
expectError: true,
errorOnSubPath: "sub1",
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
tracker := &trackingSubpath{
errorOnSubPath: tc.errorOnSubPath,
}

container := v1.Container{
VolumeMounts: tc.volumeMounts,
}

// FakeHostUtil with the subpath directories registered so PathExists returns true
fhu := hostutil.NewFakeHostUtil(map[string]hostutil.FileType{
sub1: hostutil.FileTypeDirectory,
sub2: hostutil.FileTypeDirectory,
sub3: hostutil.FileTypeDirectory,
})

podVolumes := kubecontainer.VolumeMap{
"disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: volPath}},
}

pod := v1.Pod{
ObjectMeta: metav1.ObjectMeta{
UID: "test-pod-uid",
},
Spec: v1.PodSpec{},
}

_, cleanupAction, err := makeMounts(logger, &pod, podDir, &container, "fakepod", "", []string{""}, podVolumes, fhu, tracker, nil, false, nil)

if tc.expectError {
require.Error(t, err)
} else {
require.NoError(t, err)
}

// Call the cleanup action returned by makeMounts
require.NotNil(t, cleanupAction, "cleanupAction should never be nil")
cleanupAction()

require.ElementsMatch(t, tc.expectedCleanedSubPaths, tracker.cleanedSubPaths,
"expected cleaned subpaths %v but got %v", tc.expectedCleanedSubPaths, tracker.cleanedSubPaths)
})
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect cleanupAction handling in makeMounts.
set -uo pipefail

rg -nP -C4 'cleanupAction' pkg/kubelet/kubelet_pods.go

ast-grep run --pattern 'func makeMounts($$$) ([]kubecontainer.Mount, func(), error) { $$$ }' \
  --lang go pkg/kubelet/kubelet_pods.go

Repository: openshift/kubernetes

Length of output: 17538


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- resource quantity expressions ---'
rg -n -C4 'new\(resource\.MustParse|MustParse\("50m"\)|enableDRANodeAllocatableResources' pkg/kubelet/kubelet_pods_test.go

printf '%s\n' '--- cleanup test and tracker ---'
rg -n -C6 'trackingSubpath|TestMakemountsSubpathCleanupAccumulation|error on first mount|cleanedSubPaths|PrepareSafeSubpath' pkg/kubelet/kubelet_pods_test.go pkg/kubelet/kubelet_pods.go

Repository: openshift/kubernetes

Length of output: 9761


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

test = Path("pkg/kubelet/kubelet_pods_test.go").read_text()
impl = Path("pkg/kubelet/kubelet_pods.go").read_text()

quantity_calls = re.findall(r'Quantity:\s+new\(resource\.MustParse\("([^"]+)"\)\)', test)
assert quantity_calls == ["50m", "50Mi"], quantity_calls

assert "var cleanupAction func()" in impl
assert "hostPath, cleanupAction, err = subpather.PrepareSafeSubpath(" in impl

# Model the production assignment semantics for the test's cleanup callbacks.
def returned_cleanups(outcomes):
    cleanup = None
    for name, succeeds in outcomes:
        cleanup = (name,) if succeeds else None
    return [] if cleanup is None else list(cleanup)

assert returned_cleanups([("sub1", True), ("sub2", True), ("sub3", True)]) == ["sub3"]
assert returned_cleanups([("sub1", False), ("sub2", True)]) == []
assert returned_cleanups([("sub1", True), ("sub2", True), ("sub3", False)]) == []

print("invalid Quantity pointer expressions: 50m, 50Mi")
print("successful subpath cleanup callbacks returned: sub3 only")
print("first/third error cleanup callbacks returned: none")
PY

Repository: openshift/kubernetes

Length of output: 247


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

test = Path("pkg/kubelet/kubelet_pods_test.go").read_text()
impl = Path("pkg/kubelet/kubelet_pods.go").read_text()

quantity_calls = re.findall(r'Quantity:\s+new\(resource\.MustParse\("([^"]+)"\)\)', test)
assert quantity_calls == ["50m", "50Mi"], quantity_calls

assert "var cleanupAction func()" in impl
assert "hostPath, cleanupAction, err = subpather.PrepareSafeSubpath(" in impl

def returned_cleanups(outcomes):
    cleanup = None
    for name, succeeds in outcomes:
        cleanup = (name,) if succeeds else None
    return [] if cleanup is None else list(cleanup)

assert returned_cleanups([("sub1", True), ("sub2", True), ("sub3", True)]) == ["sub3"]
assert returned_cleanups([("sub1", False), ("sub2", True)]) == []
assert returned_cleanups([("sub1", True), ("sub2", True), ("sub3", False)]) == []

print("invalid Quantity pointer expressions: 50m, 50Mi")
print("successful subpath cleanup callbacks returned: sub3 only")
print("first/third error cleanup callbacks returned: none")
PY

Repository: openshift/kubernetes

Length of output: 247


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

test = Path("pkg/kubelet/kubelet_pods_test.go").read_text()
impl = Path("pkg/kubelet/kubelet_pods.go").read_text()

print("quantity:", re.findall(r'Quantity:\s+new\(resource\.MustParse\("([^"]+)"\)\)', test))
print("has var cleanupAction:", "var cleanupAction func()" in impl)
print("has direct assignment:", "hostPath, cleanupAction, err = subpather.PrepareSafeSubpath(" in impl)

def returned_cleanups(outcomes):
    cleanup = None
    for name, succeeds in outcomes:
        cleanup = (name,) if succeeds else None
    return [] if cleanup is None else list(cleanup)

for outcomes in (
    [("sub1", True), ("sub2", True), ("sub3", True)],
    [("sub1", False), ("sub2", True)],
    [("sub1", True), ("sub2", True), ("sub3", False)],
):
    print(outcomes, "=>", returned_cleanups(outcomes))
PY

Repository: openshift/kubernetes

Length of output: 403


Fix the invalid quantity pointers and accumulate subpath cleanups.

  • new(resource.MustParse("50m")) and new(resource.MustParse("50Mi")) do not compile. Use ptr.To(resource.MustParse(...)).
  • makeMounts overwrites cleanupAction at pkg/kubelet/kubelet_pods.go:361. Only the last successful subpath cleanup is returned. An error also returns nil, so the test's require.NotNil assertion fails. Accumulate all cleanup callbacks and invoke previously created callbacks on errors.
🤖 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/kubelet_pods_test.go` around lines 9276 - 9386, Fix the invalid
resource quantity pointer construction by replacing new(resource.MustParse(...))
usages with ptr.To(resource.MustParse(...)). Update makeMounts to accumulate
every successful subpath cleanup callback instead of overwriting cleanupAction,
and return a non-nil aggregate cleanup callback even when mount processing
errors; invoke all callbacks created before the error.

Comment on lines +445 to +447
- bindMountOptions:
- bindMountOptionsValue
mountPath: mountPathValue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Use an allowed value in every bind-mount fixture.

The API contract allows only noexec, nodev, and nosuid. Replace bindMountOptionsValue with a supported option in every affected fixture entry.

  • staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml#L445-L447: Replace the placeholder value.
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml#L676-L678: Replace the placeholder value.
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml#L908-L910: Replace the placeholder value.
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json#L702-L705: Replace the placeholder value.
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json#L1019-L1022: Replace the placeholder value.
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json#L1336-L1339: Replace the placeholder value.
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml#L481-L483: Replace the placeholder value.
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml#L712-L714: Replace the placeholder value.
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml#L944-L946: Replace the placeholder value.
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json#L560-L563: Replace the placeholder value.
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json#L877-L880: Replace the placeholder value.
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json#L1194-L1197: Replace the placeholder value.
📍 Affects 4 files
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml#L445-L447 (this comment)
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml#L676-L678
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml#L908-L910
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json#L702-L705
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json#L1019-L1022
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json#L1336-L1339
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml#L481-L483
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml#L712-L714
  • staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml#L944-L946
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json#L560-L563
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json#L877-L880
  • staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json#L1194-L1197
🤖 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 `@staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml` around lines 445 -
447, Replace every bindMountOptionsValue placeholder in
staging/src/k8s.io/api/testdata/HEAD/batch.v1.Job.yaml lines 445-447, 676-678,
and 908-910; staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.json
lines 702-705, 1019-1022, and 1336-1339;
staging/src/k8s.io/api/testdata/HEAD/batch.v1beta1.CronJob.yaml lines 481-483,
712-714, and 944-946; and staging/src/k8s.io/api/testdata/HEAD/core.v1.Pod.json
lines 560-563, 877-880, and 1194-1197 with an allowed bind-mount option: noexec,
nodev, or nosuid.

Comment on lines +80 to +82
func (f *volumeBindMountOptionsFeature) InferForUpdate(oldPodInfo, newPodInfo *types.PodInfo) bool {
return 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 | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Compare InferForUpdate implementations across node-declared features.
set -uo pipefail

ast-grep run --pattern 'func ($_ $_) InferForUpdate($$$) bool { $$$ }' \
  --lang go staging/src/k8s.io/component-helpers/nodedeclaredfeatures

# Find where the framework calls InferForUpdate to confirm the contract.
rg -nP -C6 '\bInferForUpdate\s*\(' --type=go

Repository: openshift/kubernetes

Length of output: 6267


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- feature implementation and tests ---'
sed -n '1,180p' staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go
fd -i '.*' staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions
echo '--- framework update contract and callers ---'
rg -n -C8 'InferForUpdate|PodInfo|RequiredRuntimeFeatures' staging/src/k8s.io/component-helpers/nodedeclaredfeatures \
  --glob '*.go'
echo '--- ephemeral-container update handling ---'
rg -n -C5 'EphemeralContainers|ephemeral container|EphemeralContainer' staging/src/k8s.io --glob '*.go' | head -n 300

Repository: openshift/kubernetes

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- update inference callers ---'
rg -n -C12 'InferForPodUpdate\(' --glob '*.go' . | head -n 240

echo '--- ephemeral container API validation and update strategy ---'
rg -n -C8 'EphemeralContainers|EphemeralContainer' pkg staging/src/k8s.io --glob '*.go' \
  | rg -n 'validation|Validate|Strategy|PrepareForUpdate|ValidateUpdate|EphemeralContainers|VolumeMounts|BindMountOptions' \
  | head -n 320

echo '--- bind mount option definitions and feature tests ---'
rg -n -C8 'BindMountOptions|VolumeBindMountOptions|MountOptions' staging/src/k8s.io pkg --glob '*.go' \
  | head -n 360

echo '--- relevant API type definitions ---'
rg -n -C12 'type EphemeralContainer(Common)? struct|type VolumeMount struct' staging/src/k8s.io/api pkg --glob '*.go' \
  | head -n 220

Repository: openshift/kubernetes

Length of output: 47169


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- ephemeral-container allowed fields ---'
sed -n '88,145p' pkg/apis/core/validation/validation.go
sed -n '3749,3810p' pkg/apis/core/validation/validation.go

echo '--- pod update validation and ephemeralcontainers subresource ---'
rg -n -C12 'ValidatePodUpdate|ephemeralcontainers|Subresource|validatePodUpdate' pkg/apis/core plugin/pkg/admission/nodedeclaredfeatures --glob '*.go' \
  | head -n 320

echo '--- node-declared-features admission dispatch ---'
sed -n '70,190p' plugin/pkg/admission/nodedeclaredfeatures/admission.go
sed -n '2935,2990p' pkg/kubelet/kubelet.go

echo '--- bind-mount option API and kubelet consumers ---'
rg -n -C10 'BindMountOptions|MountOptions' --glob '*.go' . \
  | head -n 420

Repository: openshift/kubernetes

Length of output: 50376


🌐 Web query:

Kubernetes volumeBindMountOptionsFeature InferForUpdate EphemeralContainers feature.go

💡 Result:

The terms you queried represent distinct components and concepts within the Kubernetes codebase and feature management framework: 1. VolumeBindMountOptions: This is a specific Kubernetes feature [1] introduced via KEP-5855 [2] that allows users to apply security-related bind mount flags (such as noexec, nodev, and nosuid) to volume mounts in a Pod specification [2]. It requires the VolumeBindMountOptions feature gate to be enabled [1][3]. 2. InferForUpdate: This is a method associated with the Node Declared Features framework (KEP-5328) [4][5]. The framework uses this method to determine if a pod update requires a specific feature [4][6]. By inspecting the old and new pod specifications, InferForUpdate helps the system decide whether a feature needs to be validated or processed during an update, allowing for more efficient processing by avoiding expensive operations for irrelevant subresources [7][5][6]. 3. EphemeralContainers: This is a feature that allows the addition of temporary containers to running pods for debugging or other purposes [8]. It has its own dedicated logic in the Kubernetes storage registry (e.g., EphemeralContainersREST) to handle updates specifically for these containers [8]. 4. feature.go: This typically refers to files such as pkg/features/kube_features.go [9][10], which manage the registration and definition of feature gates across the Kubernetes codebase. Features like VolumeBindMountOptions or EphemeralContainers are defined and tracked within these configuration files [9]. In summary, VolumeBindMountOptions is a functional feature for mount security [2], InferForUpdate is an architectural mechanism for efficient feature detection during updates [4][6], and EphemeralContainers is a specific pod resource type [8], all of which interact with the broader Kubernetes feature management system often centered around files like feature.go [9][10].

Citations:


🌐 Web query:

site:github.com/kubernetes/kubernetes "func (f *volumeBindMountOptionsFeature) InferForUpdate"

💡 Result:

The function signature func (f *volumeBindMountOptionsFeature) InferForUpdate is not a standard function within the core Kubernetes codebase as of August 2026. Based on research, this appears to be a hallucinated or non-standard identifier. While there are ongoing efforts in Kubernetes related to "bind mount options" (such as KEP-5855, which introduces a bindMountOptions field to VolumeMount) [1][2] and "InferForUpdate" methods (used, for example, in the NodeDeclaredFeatures testing infrastructure to determine if an update to a pod object requires reconciliation) [3][4][5], there is no existing struct named volumeBindMountOptionsFeature that implements an InferForUpdate method in the main Kubernetes repository. If you encountered this in a specific codebase, it is likely a custom internal implementation or a feature-gated utility used for managing volume-related feature flags or reconciliation logic. [1][3]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- bindMountOptions production consumers ---'
rg -n -C10 'BindMountOptions|bindMountOptions' --glob '*.go' \
  --glob '!**/*_test.go' \
  --glob '!staging/src/k8s.io/mount-utils/**' . | head -n 360

echo '--- ephemeral containers REST update path ---'
rg -n -C12 'EphemeralContainersREST|Update\(.*ephemeral|ephemeralcontainers' pkg/registry pkg/kubelet --glob '*.go' \
  | head -n 300

echo '--- pod update framework tests and feature registration ---'
sed -n '333,490p' staging/src/k8s.io/component-helpers/nodedeclaredfeatures/framework_test.go
rg -n -C8 'volumebindmountoptions|VolumeBindMountOptions' staging/src/k8s.io/component-helpers/nodedeclaredfeatures --glob '*.go'

Repository: openshift/kubernetes

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- all non-generated bind option references ---'
rg -l -i 'bindmountoptions|bind_mount_options' --glob '*.go' . \
  | rg -v '(_test\.go|generated|zz_generated|mount-utils)' \
  | sort

echo '--- pod feature-gate drop behavior ---'
sed -n '780,835p' pkg/api/pod/util.go
sed -n '1555,1590p' pkg/api/pod/util.go

echo '--- kubelet mount construction ---'
sed -n '360,430p' pkg/kubelet/kubelet_pods.go
rg -n -C12 'MountFlags|MountSensitive|VolumeMounts' pkg/kubelet pkg/volume staging/src/k8s.io --glob '*.go' \
  | rg -i 'bind|mountflags|volumeMount' | head -n 260

Repository: openshift/kubernetes

Length of output: 36012


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- runtime bind option handling ---'
rg -n -C14 'BindMountOptions' pkg/kubelet/container/runtime.go pkg/kubelet/kuberuntime/kuberuntime_container.go pkg/kubelet/kubelet_pods.go

echo '--- feature usage in pod update processing ---'
rg -n -C10 'dropDisabled|Drop.*Disabled|Prepare.*Pod|VolumeBindMountOptions' pkg/kubelet pkg/api --glob '*.go' \
  | head -n 260

echo '--- exact ephemeral subresource strategy behavior ---'
rg -n -C15 'EphemeralContainersStrategy|PrepareForUpdate|ValidateUpdate' pkg/registry/core/pod --glob '*.go' \
  | head -n 260

Repository: openshift/kubernetes

Length of output: 43435


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

feature = Path("staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go").read_text()
framework = Path("staging/src/k8s.io/component-helpers/nodedeclaredfeatures/framework.go").read_text()
admission = Path("plugin/pkg/admission/nodedeclaredfeatures/admission.go").read_text()
kubelet = Path("pkg/kubelet/kubelet.go").read_text()
strategy = Path("pkg/registry/core/pod/strategy.go").read_text()
runtime = Path("pkg/kubelet/kuberuntime/kuberuntime_container.go").read_text()

infer = re.search(
    r"func \(f \*volumeBindMountOptionsFeature\) InferForUpdate\(.*?\n\}",
    feature, re.S
)
assert infer and "return false" in infer.group(0), "InferForUpdate is not unconditional false"

assert "f.InferForUpdate(oldPodInfo, newPodInfo)" in framework
assert "InferForPodUpdate(oldPodInfo, newPodInfo" in kubelet
assert "newPod.Spec.EphemeralContainers" in strategy
assert 'subresource != "" && subresource != "resize"' in admission
assert "BindMountOptions:      mount.BindMountOptions" in Path("pkg/kubelet/kubelet_pods.go").read_text()
assert "MountOptions:      v.BindMountOptions" in runtime

print("InferForUpdate returns false; the framework evaluates it for kubelet pod updates.")
print("Ephemeral containers are persisted through the ephemeralcontainers subresource.")
print("The node-declared-features admission plugin excludes that subresource.")
print("BindMountOptions is propagated to the kubelet runtime mount request.")
PY

Repository: openshift/kubernetes

Length of output: 247


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

checks = {
    "InferForUpdate returns false": (
        re.search(
            r"func \(f \*volumeBindMountOptionsFeature\) InferForUpdate\(.*?\n\}",
            Path("staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go").read_text(),
            re.S,
        )
        and "return false" in re.search(
            r"func \(f \*volumeBindMountOptionsFeature\) InferForUpdate\(.*?\n\}",
            Path("staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go").read_text(),
            re.S,
        ).group(0)
    ),
    "framework calls InferForUpdate": "f.InferForUpdate(oldPodInfo, newPodInfo)" in Path("staging/src/k8s.io/component-helpers/nodedeclaredfeatures/framework.go").read_text(),
    "kubelet calls InferForPodUpdate": "InferForPodUpdate(oldPodInfo, newPodInfo" in Path("pkg/kubelet/kubelet.go").read_text(),
    "ephemeral strategy persists new list": "newPod.Spec.EphemeralContainers" in Path("pkg/registry/core/pod/strategy.go").read_text(),
    "admission excludes non-main and resize subresources": 'subresource != "" && subresource != "resize"' in Path("plugin/pkg/admission/nodedeclaredfeatures/admission.go").read_text(),
    "kubelet propagates bind options": "BindMountOptions" in Path("pkg/kubelet/kubelet_pods.go").read_text(),
    "runtime propagates mount options": "MountOptions" in Path("pkg/kubelet/kuberuntime/kuberuntime_container.go").read_text(),
}
for name, result in checks.items():
    print(f"{'PASS' if result else 'FAIL'}: {name}")
assert all(checks.values())
PY

Repository: openshift/kubernetes

Length of output: 446


Infer update requirements for ephemeral containers.

InferForUpdate always returns false, although InferForPodUpdate evaluates it for kubelet pod updates. Return true when the new pod requires VolumeBindMountOptions and the old pod did not, including when an ephemeralcontainers update adds BindMountOptions. Otherwise the update bypasses the node's MountOptions requirement. The admission plugin also skips the ephemeralcontainers subresource, so include that subresource if admission-time rejection is required.

🤖 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
`@staging/src/k8s.io/component-helpers/nodedeclaredfeatures/features/volumebindmountoptions/feature.go`
around lines 80 - 82, Update volumeBindMountOptionsFeature.InferForUpdate to
return true when newPodInfo requires VolumeBindMountOptions but oldPodInfo does
not, including additions of BindMountOptions through ephemeral container
updates. Ensure the related admission handling includes the ephemeralcontainers
subresource if needed to enforce the node MountOptions requirement.

Comment on lines +359 to +362
// mount_options specifies additional bind mount options (e.g., noexec,
// nodev, nosuid) that the runtime must apply when mounting this volume
// into the container. These are passed as OCI mount options.
repeated string mount_options = 11;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the generated CRI Go bindings expose the new fields.
set -uo pipefail

fd -t f 'api.pb.go' staging/src/k8s.io/cri-api || true

fd -t f 'api.pb.go' staging/src/k8s.io/cri-api --exec rg -n 'MountOptions' {} \;

# Confirm the field tags used in the generated struct tags.
fd -t f 'api.pb.go' staging/src/k8s.io/cri-api --exec rg -n 'mount_options,(3|11)' {} \;

Repository: openshift/kubernetes

Length of output: 635


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- proto fields ---'
sed -n '340,370p' staging/src/k8s.io/cri-api/pkg/apis/runtime/v1/api.proto
sed -n '1870,1890p' staging/src/k8s.io/cri-api/pkg/apis/runtime/v1/api.proto

printf '%s\n' '--- feature and mount option usage ---'
rg -n -C 6 'MountOptions|RuntimeFeatures' \
  pkg/kubelet/kuberuntime \
  staging/src/k8s.io/cri-api/pkg/apis/runtime/v1/api.pb.go

Repository: openshift/kubernetes

Length of output: 28587


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- BindMountOptions assignments and feature checks ---'
rg -n -C 5 'BindMountOptions|MountOptions' \
  pkg staging/src/k8s.io/cri-api/pkg/apis/runtime/v1 \
  -g '*.go' -g '*.proto'

printf '%s\n' '--- RuntimeFeatures.mount_options checks ---'
rg -n -C 8 'MountOptions' pkg/kubelet -g '*.go'

Repository: openshift/kubernetes

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- kubelet MountOptions references ---'
rg -n 'MountOptions|BindMountOptions' pkg/kubelet/kuberuntime pkg/kubelet/container -g '*.go'

printf '%s\n' '--- runtime feature propagation and status consumers ---'
rg -n -C 4 'RuntimeFeatures|RuntimeStatus|Features:' pkg/kubelet -g '*.go' | head -n 220

printf '%s\n' '--- makeMounts implementation ---'
sed -n '460,510p' pkg/kubelet/kuberuntime/kuberuntime_container.go

Repository: openshift/kubernetes

Length of output: 22693


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '430,455p' pkg/kubelet/nodestatus/setters.go
rg -n -C 5 'NodeFeatures\\(|Features\\(.*Runtime|MountOptions' pkg -g '*.go' | head -n 160

Repository: openshift/kubernetes

Length of output: 1299


Gate bind mount options on runtime support. makeMounts always copies BindMountOptions into runtimeapi.Mount.MountOptions, but the kubelet does not check RuntimeFeatures.MountOptions. A runtime that does not support the field can ignore noexec, nodev, or nosuid. Reject unsupported requests before creating the container.

🤖 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 `@staging/src/k8s.io/cri-api/pkg/apis/runtime/v1/api.proto` around lines 359 -
362, Update the container mount creation path around makeMounts to check
RuntimeFeatures.MountOptions before propagating BindMountOptions into
runtimeapi.Mount.MountOptions. Reject the pod or container request when bind
mount options are specified but runtime support is unavailable, before container
creation; preserve existing behavior for supported runtimes and mounts without
options.

@openshift-ci-robot

Copy link
Copy Markdown

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

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

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

@openshift-ci-robot

Copy link
Copy Markdown

@amritansh1502: No Jira issue with key KEP-5855 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:

  • Add bindMountOptions field to VolumeMount
  • Run codegen for VolumeMount bindMountOptions field
  • Add VolumeBindMountOptions feature gate
  • Add validation for volumeMount bindMountOptions
  • Add feature gate drop logic for volumeMount bindMountOptions
  • Add mount_options to CRI Mount message
  • Implement kubelet support for bindMountOptions
  • Add mount_options to CRI RuntimeFeatures
  • Add Node Declared Features integration for VolumeBindMountOptions

Summary by CodeRabbit

  • New Features

  • Added optional Linux bind-mount options for volume mounts: noexec, nodev, and nosuid.

  • Added support for propagating these options through container runtimes and declarative configuration.

  • Added the alpha VolumeBindMountOptions feature gate in Kubernetes 1.37.

  • Options have no effect on Windows and are unavailable for image volumes.

  • Bug Fixes

  • Added validation to reject unsupported, duplicate, or image-volume bind-mount options.

  • Improved pod resource defaulting and validation compatibility for newer Kubernetes fields.

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.

…patibility

Restore util.go, util_test.go, and api.pb.go from openshift/master base,
then surgically add only VolumeBindMountOptions code. Remove upstream-only
references (dranodeallocatableresources, EmptyDirVolumeMode, Signal enums)
that do not exist in openshift/master.
@amritansh1502
amritansh1502 force-pushed the carry-bind-mount-options branch from 619147d to baf96dc Compare August 12, 2026 16:05
@openshift-ci-robot

Copy link
Copy Markdown

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

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

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

@openshift-ci-robot

Copy link
Copy Markdown

@amritansh1502: No Jira issue with key KEP-5855 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:

  • Add bindMountOptions field to VolumeMount
  • Run codegen for VolumeMount bindMountOptions field
  • Add VolumeBindMountOptions feature gate
  • Add validation for volumeMount bindMountOptions
  • Add feature gate drop logic for volumeMount bindMountOptions
  • Add mount_options to CRI Mount message
  • Implement kubelet support for bindMountOptions
  • Add mount_options to CRI RuntimeFeatures
  • Add Node Declared Features integration for VolumeBindMountOptions

Summary by CodeRabbit

  • New Features

  • Added optional Linux bind-mount options for volume mounts: noexec, nodev, and nosuid.

  • Propagated bind-mount options through container runtimes and declarative configuration.

  • Added the alpha VolumeBindMountOptions feature gate in Kubernetes 1.37.

  • Options have no effect on Windows and are unavailable for image volumes.

  • Bug Fixes

  • Added validation for unsupported, duplicate, and image-volume bind-mount options.

  • Tests

  • Added coverage for bind-mount option validation and runtime propagation.

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.

🧹 Nitpick comments (1)
pkg/api/pod/util.go (1)

805-821: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for init and ephemeral containers.

TestDropVolumeBindMountOptions only creates Spec.Containers. It does not verify the init-container and ephemeral-container loops in this block. Add old and new pod cases with BindMountOptions in both container types for enabled and disabled feature-gate states.

🤖 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/api/pod/util.go` around lines 805 - 821, Extend
TestDropVolumeBindMountOptions to include old and new pod fixtures containing
BindMountOptions in InitContainers and EphemeralContainers. Cover both enabled
and disabled VolumeBindMountOptions feature-gate states, asserting options are
preserved when enabled and removed when disabled.
🤖 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.

Nitpick comments:
In `@pkg/api/pod/util.go`:
- Around line 805-821: Extend TestDropVolumeBindMountOptions to include old and
new pod fixtures containing BindMountOptions in InitContainers and
EphemeralContainers. Cover both enabled and disabled VolumeBindMountOptions
feature-gate states, asserting options are preserved when enabled and removed
when disabled.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: 0ac09b0f-88b9-4c2a-b4f6-22175b636f77

📥 Commits

Reviewing files that changed from the base of the PR and between 619147d and baf96dc.

⛔ Files ignored due to path filters (1)
  • staging/src/k8s.io/cri-api/pkg/apis/runtime/v1/api.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (3)
  • pkg/api/pod/util.go
  • pkg/api/pod/util_test.go
  • staging/src/k8s.io/cri-api/pkg/apis/runtime/v1/api.proto
🚧 Files skipped from review as they are similar to previous changes (2)
  • staging/src/k8s.io/cri-api/pkg/apis/runtime/v1/api.proto
  • pkg/api/pod/util_test.go

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. vendor-update Touching vendor dir or related files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants