Merge pull request #137343 from gnufied/prevent-podscheduling-optin

Add API changes to prevent pod scheduling via CSIDriver object
This commit is contained in:
Kubernetes Prow Robot 2026-03-11 03:53:17 +05:30 committed by GitHub
commit d47f3f253b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 894 additions and 191 deletions

View file

@ -19732,6 +19732,10 @@
"description": "podInfoOnMount indicates this CSI volume driver requires additional pod information (like podName, podUID, etc.) during mount operations, if set to true. If set to false, pod information will not be passed on mount. Default is false.\n\nThe CSI driver specifies podInfoOnMount as part of driver deployment. If true, Kubelet will pass pod information as VolumeContext in the CSI NodePublishVolume() calls. The CSI driver is responsible for parsing and validating the information passed in as VolumeContext.\n\nThe following VolumeContext will be passed if podInfoOnMount is set to true. This list might grow, but the prefix will be used. \"csi.storage.k8s.io/pod.name\": pod.Name \"csi.storage.k8s.io/pod.namespace\": pod.Namespace \"csi.storage.k8s.io/pod.uid\": string(pod.UID) \"csi.storage.k8s.io/ephemeral\": \"true\" if the volume is an ephemeral inline volume\n defined by a CSIVolumeSource, otherwise \"false\"\n\n\"csi.storage.k8s.io/ephemeral\" is a new feature in Kubernetes 1.16. It is only required for drivers which support both the \"Persistent\" and \"Ephemeral\" VolumeLifecycleMode. Other drivers can leave pod info disabled and/or ignore this field. As Kubernetes 1.15 doesn't support this field, drivers can only support one mode when deployed on such a cluster and the deployment determines which mode that is, for example via a command line parameter of the driver.\n\nThis field was immutable in Kubernetes < 1.29 and now is mutable.",
"type": "boolean"
},
"preventPodSchedulingIfMissing": {
"description": "PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod scheduling if the CSI driver on the node is missing.\n\nEnabling this option will prevent the scheduler (or any other component which embeds default scheduler such as cluster-autoscaler) from scheduling pods to nodes where CSI driver is not installed.\n\nFor components(such as cluster-autoscaler) that embed the scheduler and run pod placement simulations using scheduler plugins, they MUST be aware of CSI driver registration information via CSINode object. They must create simulated CSINode objects in addition to Node objects during scheduling simulation, otherwise if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any newly created node may be rejected by the scheduler because of missing CSI driver information from the node.\n\nThis is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled. Default is \"false\".",
"type": "boolean"
},
"requiresRepublish": {
"description": "requiresRepublish indicates the CSI driver wants `NodePublishVolume` being periodically called to reflect any possible change in the mounted volume. This field defaults to false.\n\nNote: After a successful initial NodePublishVolume call, subsequent calls to NodePublishVolume should only update the contents of the volume. New mount points will not be seen by a running container.",
"type": "boolean"

View file

@ -1303,6 +1303,10 @@
"description": "podInfoOnMount indicates this CSI volume driver requires additional pod information (like podName, podUID, etc.) during mount operations, if set to true. If set to false, pod information will not be passed on mount. Default is false.\n\nThe CSI driver specifies podInfoOnMount as part of driver deployment. If true, Kubelet will pass pod information as VolumeContext in the CSI NodePublishVolume() calls. The CSI driver is responsible for parsing and validating the information passed in as VolumeContext.\n\nThe following VolumeContext will be passed if podInfoOnMount is set to true. This list might grow, but the prefix will be used. \"csi.storage.k8s.io/pod.name\": pod.Name \"csi.storage.k8s.io/pod.namespace\": pod.Namespace \"csi.storage.k8s.io/pod.uid\": string(pod.UID) \"csi.storage.k8s.io/ephemeral\": \"true\" if the volume is an ephemeral inline volume\n defined by a CSIVolumeSource, otherwise \"false\"\n\n\"csi.storage.k8s.io/ephemeral\" is a new feature in Kubernetes 1.16. It is only required for drivers which support both the \"Persistent\" and \"Ephemeral\" VolumeLifecycleMode. Other drivers can leave pod info disabled and/or ignore this field. As Kubernetes 1.15 doesn't support this field, drivers can only support one mode when deployed on such a cluster and the deployment determines which mode that is, for example via a command line parameter of the driver.\n\nThis field was immutable in Kubernetes < 1.29 and now is mutable.",
"type": "boolean"
},
"preventPodSchedulingIfMissing": {
"description": "PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod scheduling if the CSI driver on the node is missing.\n\nEnabling this option will prevent the scheduler (or any other component which embeds default scheduler such as cluster-autoscaler) from scheduling pods to nodes where CSI driver is not installed.\n\nFor components(such as cluster-autoscaler) that embed the scheduler and run pod placement simulations using scheduler plugins, they MUST be aware of CSI driver registration information via CSINode object. They must create simulated CSINode objects in addition to Node objects during scheduling simulation, otherwise if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any newly created node may be rejected by the scheduler because of missing CSI driver information from the node.\n\nThis is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled. Default is \"false\".",
"type": "boolean"
},
"requiresRepublish": {
"description": "requiresRepublish indicates the CSI driver wants `NodePublishVolume` being periodically called to reflect any possible change in the mounted volume. This field defaults to false.\n\nNote: After a successful initial NodePublishVolume call, subsequent calls to NodePublishVolume should only update the contents of the volume. New mount points will not be seen by a running container.",
"type": "boolean"

View file

@ -458,6 +458,27 @@ type CSIDriverSpec struct {
// +featureGate=CSIServiceAccountTokenSecrets
// +optional
ServiceAccountTokenInSecrets *bool
// PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod
// scheduling if the CSI driver on the node is missing.
//
// Enabling this option will prevent the scheduler (or any other
// component which embeds default scheduler such as cluster-autoscaler) from
// scheduling pods to nodes where CSI driver is not installed.
//
// For components(such as cluster-autoscaler) that embed the scheduler and run
// pod placement simulations using scheduler plugins, they MUST be aware of
// CSI driver registration information via CSINode object. They must create simulated
// CSINode objects in addition to Node objects during scheduling simulation, otherwise
// if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any
// newly created node may be rejected by the scheduler because of missing CSI driver
// information from the node.
//
// This is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled.
// Default is "false".
// +featureGate=VolumeLimitScaling
// +optional
PreventPodSchedulingIfMissing *bool
}
// FSGroupPolicy specifies if a CSI Driver supports modifying

View file

@ -68,4 +68,9 @@ func SetDefaults_CSIDriver(obj *storagev1.CSIDriver) {
obj.Spec.SELinuxMount = new(bool)
*(obj.Spec.SELinuxMount) = false
}
if obj.Spec.PreventPodSchedulingIfMissing == nil && utilfeature.DefaultFeatureGate.Enabled(features.VolumeLimitScaling) {
obj.Spec.PreventPodSchedulingIfMissing = new(bool)
*(obj.Spec.PreventPodSchedulingIfMissing) = false
}
}

View file

@ -148,3 +148,30 @@ func TestSetDefaultSELinuxMountReadWriteOncePodDisabled(t *testing.T) {
t.Errorf("Expected SELinuxMount to remain nil, got: %+v", outSELinuxMount)
}
}
func TestSetDefaultPreventPodSchedulingIfMissingVolumeLimitScalingEnabled(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeLimitScaling, true)
driver := &storagev1.CSIDriver{}
// field should be defaulted
defaultPreventPodSchedulingIfMissing := false
output := roundTrip(t, runtime.Object(driver)).(*storagev1.CSIDriver)
outPreventPodSchedulingIfMissing := output.Spec.PreventPodSchedulingIfMissing
if outPreventPodSchedulingIfMissing == nil {
t.Errorf("Expected PreventPodSchedulingIfMissing to be defaulted to: %+v, got: nil", defaultPreventPodSchedulingIfMissing)
} else if *outPreventPodSchedulingIfMissing != defaultPreventPodSchedulingIfMissing {
t.Errorf("Expected PreventPodSchedulingIfMissing to be defaulted to: %+v, got: %+v", defaultPreventPodSchedulingIfMissing, *outPreventPodSchedulingIfMissing)
}
}
func TestSetDefaultPreventPodSchedulingIfMissingVolumeLimitScalingDisabled(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeLimitScaling, false)
driver := &storagev1.CSIDriver{}
// field should not be defaulted
output := roundTrip(t, runtime.Object(driver)).(*storagev1.CSIDriver)
outPreventPodSchedulingIfMissing := output.Spec.PreventPodSchedulingIfMissing
if outPreventPodSchedulingIfMissing != nil {
t.Errorf("Expected PreventPodSchedulingIfMissing to remain nil, got: %+v", *outPreventPodSchedulingIfMissing)
}
}

View file

@ -334,6 +334,7 @@ func autoConvert_v1_CSIDriverSpec_To_storage_CSIDriverSpec(in *storagev1.CSIDriv
out.SELinuxMount = (*bool)(unsafe.Pointer(in.SELinuxMount))
out.NodeAllocatableUpdatePeriodSeconds = (*int64)(unsafe.Pointer(in.NodeAllocatableUpdatePeriodSeconds))
out.ServiceAccountTokenInSecrets = (*bool)(unsafe.Pointer(in.ServiceAccountTokenInSecrets))
out.PreventPodSchedulingIfMissing = (*bool)(unsafe.Pointer(in.PreventPodSchedulingIfMissing))
return nil
}
@ -353,6 +354,7 @@ func autoConvert_storage_CSIDriverSpec_To_v1_CSIDriverSpec(in *storage.CSIDriver
out.SELinuxMount = (*bool)(unsafe.Pointer(in.SELinuxMount))
out.NodeAllocatableUpdatePeriodSeconds = (*int64)(unsafe.Pointer(in.NodeAllocatableUpdatePeriodSeconds))
out.ServiceAccountTokenInSecrets = (*bool)(unsafe.Pointer(in.ServiceAccountTokenInSecrets))
out.PreventPodSchedulingIfMissing = (*bool)(unsafe.Pointer(in.PreventPodSchedulingIfMissing))
return nil
}

View file

@ -334,6 +334,7 @@ func autoConvert_v1beta1_CSIDriverSpec_To_storage_CSIDriverSpec(in *storagev1bet
out.SELinuxMount = (*bool)(unsafe.Pointer(in.SELinuxMount))
out.NodeAllocatableUpdatePeriodSeconds = (*int64)(unsafe.Pointer(in.NodeAllocatableUpdatePeriodSeconds))
out.ServiceAccountTokenInSecrets = (*bool)(unsafe.Pointer(in.ServiceAccountTokenInSecrets))
out.PreventPodSchedulingIfMissing = (*bool)(unsafe.Pointer(in.PreventPodSchedulingIfMissing))
return nil
}
@ -353,6 +354,7 @@ func autoConvert_storage_CSIDriverSpec_To_v1beta1_CSIDriverSpec(in *storage.CSID
out.SELinuxMount = (*bool)(unsafe.Pointer(in.SELinuxMount))
out.NodeAllocatableUpdatePeriodSeconds = (*int64)(unsafe.Pointer(in.NodeAllocatableUpdatePeriodSeconds))
out.ServiceAccountTokenInSecrets = (*bool)(unsafe.Pointer(in.ServiceAccountTokenInSecrets))
out.PreventPodSchedulingIfMissing = (*bool)(unsafe.Pointer(in.PreventPodSchedulingIfMissing))
return nil
}

View file

@ -420,6 +420,7 @@ func validateCSIDriverSpec(
allErrs = append(allErrs, validateSELinuxMount(spec.SELinuxMount, fldPath.Child("seLinuxMount"))...)
allErrs = append(allErrs, validateNodeAllocatableUpdatePeriodSeconds(spec.NodeAllocatableUpdatePeriodSeconds, fldPath.Child("nodeAllocatableUpdatePeriodSeconds"))...)
allErrs = append(allErrs, validateServiceAccountTokenInSecrets(spec.ServiceAccountTokenInSecrets, spec.TokenRequests, fldPath.Child("serviceAccountTokenInSecrets"))...)
allErrs = append(allErrs, validatePreventPodSchedulingIfMissing(spec.PreventPodSchedulingIfMissing, fldPath.Child("preventPodSchedulingIfMissing"))...)
return allErrs
}
@ -546,6 +547,16 @@ func validateServiceAccountTokenInSecrets(serviceAccountTokenInSecrets *bool, to
return allErrs
}
// validatePreventPodSchedulingIfMissing validates that preventPodSchedulingIfMissing is not set when the VolumeLimitScaling feature gate is disabled.
func validatePreventPodSchedulingIfMissing(preventPodSchedulingIfMissing *bool, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if preventPodSchedulingIfMissing == nil && utilfeature.DefaultFeatureGate.Enabled(features.VolumeLimitScaling) {
allErrs = append(allErrs, field.Required(fldPath, ""))
}
return allErrs
}
// ValidateStorageCapacityName checks that a name is appropriate for a
// CSIStorageCapacity object.
var ValidateStorageCapacityName = apimachineryvalidation.NameIsDNSSubdomain

View file

@ -1506,6 +1506,8 @@ func TestCSINodeUpdateValidation(t *testing.T) {
func TestCSIDriverValidation(t *testing.T) {
// assume this feature is on for this test, detailed enabled/disabled tests in TestMutableCSINodeAllocatableCountEnabledDisabled
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MutableCSINodeAllocatableCount, true)
// assume this feature is on for this test, detailed enabled/disabled tests in TestCSIDriverValidationPreventPodSchedulingIfMissingEnabledDisabled
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeLimitScaling, true)
driverName := "test-driver"
longName := "my-a-b-c-d-c-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-ABCDEFGHIJKLMNOPQRSTUVWXYZ-driver"
@ -1521,6 +1523,7 @@ func TestCSIDriverValidation(t *testing.T) {
notSELinuxMount := false
serviceAccountTokenInSecrets := true
notServiceAccountTokenInSecrets := false
preventPodSchedulingIfMissing := false
supportedFSGroupPolicy := storage.FileFSGroupPolicy
invalidFSGroupPolicy := storage.FSGroupPolicy("invalid-mode")
validNodeAllocatableUpdatePeriodSeconds := int64(10)
@ -1529,78 +1532,86 @@ func TestCSIDriverValidation(t *testing.T) {
successCases := []storage.CSIDriver{{
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
// driver name: dot only
ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi.driver"},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &notStorageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &notStorageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
// driver name: dash only
ObjectMeta: metav1.ObjectMeta{Name: "io-kubernetes-storage-csi-driver"},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
// driver name: numbers
ObjectMeta: metav1.ObjectMeta{Name: "1csi2driver3"},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
// driver name: dot and dash
ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi-driver"},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
ObjectMeta: metav1.ObjectMeta{Name: driverName},
@ -1612,7 +1623,8 @@ func TestCSIDriverValidation(t *testing.T) {
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
storage.VolumeLifecyclePersistent,
},
SELinuxMount: &seLinuxMount,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
ObjectMeta: metav1.ObjectMeta{Name: driverName},
@ -1624,7 +1636,8 @@ func TestCSIDriverValidation(t *testing.T) {
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
storage.VolumeLifecycleEphemeral,
},
SELinuxMount: &seLinuxMount,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
ObjectMeta: metav1.ObjectMeta{Name: driverName},
@ -1637,7 +1650,8 @@ func TestCSIDriverValidation(t *testing.T) {
storage.VolumeLifecycleEphemeral,
storage.VolumeLifecyclePersistent,
},
SELinuxMount: &seLinuxMount,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
ObjectMeta: metav1.ObjectMeta{Name: driverName},
@ -1651,27 +1665,30 @@ func TestCSIDriverValidation(t *testing.T) {
storage.VolumeLifecyclePersistent,
storage.VolumeLifecycleEphemeral,
},
SELinuxMount: &seLinuxMount,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
FSGroupPolicy: &supportedFSGroupPolicy,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
FSGroupPolicy: &supportedFSGroupPolicy,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
// SELinuxMount: false
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &notSELinuxMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &notSELinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
// With NodeAllocatableUpdatePeriodSeconds set to nil (valid)
@ -1682,6 +1699,7 @@ func TestCSIDriverValidation(t *testing.T) {
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
NodeAllocatableUpdatePeriodSeconds: nil,
},
}, {
@ -1693,42 +1711,46 @@ func TestCSIDriverValidation(t *testing.T) {
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
NodeAllocatableUpdatePeriodSeconds: &validNodeAllocatableUpdatePeriodSeconds,
},
}, {
// With ServiceAccountTokenInSecrets set to true with TokenRequests
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
ServiceAccountTokenInSecrets: &serviceAccountTokenInSecrets,
TokenRequests: tokenRequests,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
ServiceAccountTokenInSecrets: &serviceAccountTokenInSecrets,
TokenRequests: tokenRequests,
},
}, {
// With ServiceAccountTokenInSecrets set to false with TokenRequests
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
ServiceAccountTokenInSecrets: &notServiceAccountTokenInSecrets,
TokenRequests: tokenRequests,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
ServiceAccountTokenInSecrets: &notServiceAccountTokenInSecrets,
TokenRequests: tokenRequests,
},
}, {
// With ServiceAccountTokenInSecrets set to nil (not set)
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
ServiceAccountTokenInSecrets: nil,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
RequiresRepublish: &notRequiresRepublish,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
ServiceAccountTokenInSecrets: nil,
},
}}
@ -1740,45 +1762,50 @@ func TestCSIDriverValidation(t *testing.T) {
errorCases := []storage.CSIDriver{{
ObjectMeta: metav1.ObjectMeta{Name: invalidName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachRequired,
PodInfoOnMount: &podInfoOnMount,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
ObjectMeta: metav1.ObjectMeta{Name: longName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
// AttachRequired not set
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: nil,
PodInfoOnMount: &podInfoOnMount,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: nil,
PodInfoOnMount: &podInfoOnMount,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
// PodInfoOnMount not set
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: nil,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: nil,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
// StorageCapacity not set
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &podInfoOnMount,
StorageCapacity: nil,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &podInfoOnMount,
StorageCapacity: nil,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
// invalid mode
@ -1790,17 +1817,19 @@ func TestCSIDriverValidation(t *testing.T) {
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
"no-such-mode",
},
SELinuxMount: &seLinuxMount,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
// invalid fsGroupPolicy
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
FSGroupPolicy: &invalidFSGroupPolicy,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
FSGroupPolicy: &invalidFSGroupPolicy,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}, {
// no SELinuxMount
@ -1818,17 +1847,19 @@ func TestCSIDriverValidation(t *testing.T) {
PodInfoOnMount: &notPodInfoOnMount,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
NodeAllocatableUpdatePeriodSeconds: &invalidNodeAllocatableUpdatePeriodSeconds,
},
}, {
// ServiceAccountTokenInSecrets set without TokenRequests (invalid)
ObjectMeta: metav1.ObjectMeta{Name: driverName},
Spec: storage.CSIDriverSpec{
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
ServiceAccountTokenInSecrets: &serviceAccountTokenInSecrets,
AttachRequired: &attachNotRequired,
PodInfoOnMount: &notPodInfoOnMount,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
ServiceAccountTokenInSecrets: &serviceAccountTokenInSecrets,
},
}}
@ -1842,6 +1873,8 @@ func TestCSIDriverValidation(t *testing.T) {
func TestCSIDriverValidationUpdate(t *testing.T) {
// assume this feature is on for this test, detailed enabled/disabled tests in TestMutableCSINodeAllocatableCountEnabledDisabled
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MutableCSINodeAllocatableCount, true)
// assume this feature is on for this test, detailed enabled/disabled tests in TestCSIDriverValidationPreventPodSchedulingIfMissingEnabledDisabled
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeLimitScaling, true)
driverName := "test-driver"
longName := "my-a-b-c-d-c-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-ABCDEFGHIJKLMNOPQRSTUVWXYZ-driver"
@ -1859,6 +1892,7 @@ func TestCSIDriverValidationUpdate(t *testing.T) {
notSELinuxMount := false
serviceAccountTokenInSecrets := true
notServiceAccountTokenInSecrets := false
preventPodSchedulingIfMissing := false
validNodeAllocatableUpdatePeriodSeconds := int64(10)
invalidNodeAllocatableUpdatePeriodSeconds := int64(9)
tokenRequests := []storage.TokenRequest{{Audience: "test-audience"}}
@ -1873,8 +1907,9 @@ func TestCSIDriverValidationUpdate(t *testing.T) {
storage.VolumeLifecycleEphemeral,
storage.VolumeLifecyclePersistent,
},
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
StorageCapacity: &storageCapacity,
SELinuxMount: &seLinuxMount,
PreventPodSchedulingIfMissing: &preventPodSchedulingIfMissing,
},
}
@ -2394,6 +2429,140 @@ func TestCSIDriverValidationSELinuxMountEnabledDisabled(t *testing.T) {
}
}
func TestCSIDriverValidationPreventPodSchedulingIfMissingEnabledDisabled(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, true)
tests := []struct {
name string
featureEnabled bool
preventPodSchedulingIfMissingValue *bool
expectError bool
}{{
name: "feature enabled, nil value",
featureEnabled: true,
preventPodSchedulingIfMissingValue: nil,
expectError: true,
}, {
name: "feature enabled, non-nil value",
featureEnabled: true,
preventPodSchedulingIfMissingValue: new(true),
expectError: false,
}, {
name: "feature disabled, nil value",
featureEnabled: false,
preventPodSchedulingIfMissingValue: nil,
expectError: false,
}, {
name: "feature disabled, non-nil value",
featureEnabled: false,
preventPodSchedulingIfMissingValue: new(true),
expectError: false,
}}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeLimitScaling, test.featureEnabled)
csiDriver := &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: storage.CSIDriverSpec{
AttachRequired: new(true),
PodInfoOnMount: new(true),
RequiresRepublish: new(true),
StorageCapacity: new(true),
SELinuxMount: new(true),
PreventPodSchedulingIfMissing: test.preventPodSchedulingIfMissingValue,
},
}
err := ValidateCSIDriver(csiDriver)
if test.expectError && err == nil {
t.Error("Expected validation error, got nil")
}
if !test.expectError && err != nil {
t.Errorf("Validation returned error: %s", err)
}
})
}
updateTests := []struct {
name string
featureEnabled bool
oldValue *bool
newValue *bool
expectError bool
}{{
name: "feature enabled, nil->nil",
featureEnabled: true,
oldValue: nil,
newValue: nil,
expectError: true, // populated by defaulting and required when feature is enabled
}, {
name: "feature enabled, nil->set",
featureEnabled: true,
oldValue: nil,
newValue: new(true),
expectError: false,
}, {
name: "feature enabled, set->set",
featureEnabled: true,
oldValue: new(true),
newValue: new(true),
expectError: false,
}, {
name: "feature enabled, set->nil",
featureEnabled: true,
oldValue: new(true),
newValue: nil,
expectError: true, // populated by defaulting and required when feature is enabled
}, {
name: "feature disabled, nil->nil",
featureEnabled: false,
oldValue: nil,
newValue: nil,
expectError: false,
}, {
name: "feature disabled, nil->set",
featureEnabled: false,
oldValue: nil,
newValue: new(true),
expectError: false,
}, {
name: "feature disabled, set->set",
featureEnabled: false,
oldValue: new(true),
newValue: new(true),
expectError: false,
}, {
name: "feature disabled, set->nil",
featureEnabled: false,
oldValue: new(true),
newValue: nil,
expectError: false,
}}
for _, test := range updateTests {
t.Run(test.name, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.VolumeLimitScaling, test.featureEnabled)
oldCSIDriver := &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "1"},
Spec: storage.CSIDriverSpec{
AttachRequired: new(true),
PodInfoOnMount: new(true),
RequiresRepublish: new(true),
StorageCapacity: new(true),
SELinuxMount: new(true),
PreventPodSchedulingIfMissing: test.oldValue,
},
}
newCSIDriver := oldCSIDriver.DeepCopy()
newCSIDriver.Spec.PreventPodSchedulingIfMissing = test.newValue
err := ValidateCSIDriverUpdate(newCSIDriver, oldCSIDriver)
if test.expectError && err == nil {
t.Error("Expected validation error, got nil")
}
if !test.expectError && err != nil {
t.Errorf("Validation returned error: %s", err)
}
})
}
}
func TestValidateVolumeAttributesClass(t *testing.T) {
successCases := []storage.VolumeAttributesClass{
{

View file

@ -142,6 +142,11 @@ func (in *CSIDriverSpec) DeepCopyInto(out *CSIDriverSpec) {
*out = new(bool)
**out = **in
}
if in.PreventPodSchedulingIfMissing != nil {
in, out := &in.PreventPodSchedulingIfMissing, &out.PreventPodSchedulingIfMissing
*out = new(bool)
**out = **in
}
return
}

View file

@ -54529,6 +54529,13 @@ func schema_k8sio_api_storage_v1_CSIDriverSpec(ref common.ReferenceCallback) com
Format: "",
},
},
"preventPodSchedulingIfMissing": {
SchemaProps: spec.SchemaProps{
Description: "PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod scheduling if the CSI driver on the node is missing.\n\nEnabling this option will prevent the scheduler (or any other component which embeds default scheduler such as cluster-autoscaler) from scheduling pods to nodes where CSI driver is not installed.\n\nFor components(such as cluster-autoscaler) that embed the scheduler and run pod placement simulations using scheduler plugins, they MUST be aware of CSI driver registration information via CSINode object. They must create simulated CSINode objects in addition to Node objects during scheduling simulation, otherwise if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any newly created node may be rejected by the scheduler because of missing CSI driver information from the node.\n\nThis is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled. Default is \"false\".",
Type: []string{"boolean"},
Format: "",
},
},
},
},
},
@ -56114,6 +56121,13 @@ func schema_k8sio_api_storage_v1beta1_CSIDriverSpec(ref common.ReferenceCallback
Format: "",
},
},
"preventPodSchedulingIfMissing": {
SchemaProps: spec.SchemaProps{
Description: "PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod scheduling if the CSI driver on the node is missing.\n\nEnabling this option will prevent the scheduler (or any other component which embeds default scheduler such as cluster-autoscaler) from scheduling pods to nodes where CSI driver is not installed.\n\nFor components(such as cluster-autoscaler) that embed the scheduler and run pod placement simulations using scheduler plugins, they MUST be aware of CSI driver registration information via CSINode object. They must create simulated CSINode objects in addition to Node objects during scheduling simulation, otherwise if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any newly created node may be rejected by the scheduler because of missing CSI driver information from the node.\n\nThis is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled. Default is \"false\".",
Type: []string{"boolean"},
Format: "",
},
},
},
},
},

View file

@ -60,6 +60,9 @@ func (csiDriverStrategy) PrepareForCreate(ctx context.Context, obj runtime.Objec
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIServiceAccountTokenSecrets) {
csiDriver.Spec.ServiceAccountTokenInSecrets = nil
}
if !utilfeature.DefaultFeatureGate.Enabled(features.VolumeLimitScaling) {
csiDriver.Spec.PreventPodSchedulingIfMissing = nil
}
}
func (csiDriverStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
@ -113,6 +116,11 @@ func (csiDriverStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.
newCSIDriver.Spec.NodeAllocatableUpdatePeriodSeconds = nil
}
if oldCSIDriver.Spec.PreventPodSchedulingIfMissing == nil &&
!utilfeature.DefaultFeatureGate.Enabled(features.VolumeLimitScaling) {
newCSIDriver.Spec.PreventPodSchedulingIfMissing = nil
}
// Any changes to the spec increment the generation number.
if !apiequality.Semantic.DeepEqual(oldCSIDriver.Spec, newCSIDriver.Spec) {
newCSIDriver.Generation = oldCSIDriver.Generation + 1

View file

@ -39,11 +39,12 @@ func getValidCSIDriver(name string) *storage.CSIDriver {
Name: name,
},
Spec: storage.CSIDriverSpec{
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
},
}
}
@ -213,6 +214,23 @@ func TestCSIDriverPrepareForUpdate(t *testing.T) {
},
}
driverWithPreventPodSchedulingIfMissingEnabled := &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: storage.CSIDriverSpec{
PreventPodSchedulingIfMissing: &enabled,
},
}
driverWithPreventPodSchedulingIfMissingDisabled := &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: storage.CSIDriverSpec{
PreventPodSchedulingIfMissing: &disabled,
},
}
thirty := int64(30)
sixty := int64(60)
driverWithNodeAllocatableUpdatePeriodSeconds30 := &storage.CSIDriver{
@ -248,6 +266,8 @@ func TestCSIDriverPrepareForUpdate(t *testing.T) {
wantSELinuxMount *bool
wantNodeAllocatableUpdatePeriodSeconds *int64
wantServiceAccountTokenInSecrets *bool
volumeLimitScalingEnabled bool
wantPreventPodSchedulingIfMissing *bool
}{
{
name: "podInfoOnMount feature enabled, before: none, update: enabled",
@ -452,6 +472,38 @@ func TestCSIDriverPrepareForUpdate(t *testing.T) {
wantTokenRequests: []storage.TokenRequest{{Audience: gcp}},
wantGeneration: 0,
},
{
name: "VolumeLimitScaling feature enabled, before: nil, update: enabled",
volumeLimitScalingEnabled: true,
old: driverWithNothing,
update: driverWithPreventPodSchedulingIfMissingEnabled,
wantPreventPodSchedulingIfMissing: &enabled,
wantGeneration: 1,
},
{
name: "VolumeLimitScaling feature enabled, before: enabled, update: disabled",
volumeLimitScalingEnabled: true,
old: driverWithPreventPodSchedulingIfMissingEnabled,
update: driverWithPreventPodSchedulingIfMissingDisabled,
wantPreventPodSchedulingIfMissing: &disabled,
wantGeneration: 1,
},
{
name: "VolumeLimitScaling feature disabled, before: nil, update: enabled",
volumeLimitScalingEnabled: false,
old: driverWithNothing,
update: driverWithPreventPodSchedulingIfMissingEnabled,
wantPreventPodSchedulingIfMissing: nil,
wantGeneration: 0,
},
{
name: "VolumeLimitScaling feature disabled, before: enabled, update: enabled",
volumeLimitScalingEnabled: false,
old: driverWithPreventPodSchedulingIfMissingEnabled,
update: driverWithPreventPodSchedulingIfMissingEnabled,
wantPreventPodSchedulingIfMissing: &enabled,
wantGeneration: 0,
},
}
for _, test := range tests {
@ -466,6 +518,7 @@ func TestCSIDriverPrepareForUpdate(t *testing.T) {
features.SELinuxMountReadWriteOncePod: test.seLinuxMountReadWriteOncePodEnabled,
features.MutableCSINodeAllocatableCount: test.mutableCSINodeAllocatableCountEnabled,
features.CSIServiceAccountTokenSecrets: test.csiServiceAccountTokenSecretsEnabled,
features.VolumeLimitScaling: test.volumeLimitScalingEnabled,
})
csiDriver := test.update.DeepCopy()
@ -478,6 +531,7 @@ func TestCSIDriverPrepareForUpdate(t *testing.T) {
require.Equal(t, test.wantSELinuxMount, csiDriver.Spec.SELinuxMount)
require.Equal(t, test.wantNodeAllocatableUpdatePeriodSeconds, csiDriver.Spec.NodeAllocatableUpdatePeriodSeconds)
require.Equal(t, test.wantServiceAccountTokenInSecrets, csiDriver.Spec.ServiceAccountTokenInSecrets)
require.Equal(t, test.wantPreventPodSchedulingIfMissing, csiDriver.Spec.PreventPodSchedulingIfMissing)
})
}
}
@ -507,11 +561,12 @@ func TestCSIDriverValidation(t *testing.T) {
Name: "foo",
},
Spec: storage.CSIDriverSpec{
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
},
},
false,
@ -523,12 +578,12 @@ func TestCSIDriverValidation(t *testing.T) {
Name: "foo",
},
Spec: storage.CSIDriverSpec{
AttachRequired: &disabled,
PodInfoOnMount: &disabled,
StorageCapacity: &disabled,
RequiresRepublish: &disabled,
SELinuxMount: &disabled,
AttachRequired: &disabled,
PodInfoOnMount: &disabled,
StorageCapacity: &disabled,
RequiresRepublish: &disabled,
SELinuxMount: &disabled,
PreventPodSchedulingIfMissing: &disabled,
},
},
false,
@ -540,11 +595,12 @@ func TestCSIDriverValidation(t *testing.T) {
Name: "*foo#",
},
Spec: storage.CSIDriverSpec{
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
},
},
true,
@ -562,8 +618,9 @@ func TestCSIDriverValidation(t *testing.T) {
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
storage.VolumeLifecycleMode("no-such-mode"),
},
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
},
},
true,
@ -581,8 +638,9 @@ func TestCSIDriverValidation(t *testing.T) {
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
storage.VolumeLifecyclePersistent,
},
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
},
},
false,
@ -600,8 +658,9 @@ func TestCSIDriverValidation(t *testing.T) {
VolumeLifecycleModes: []storage.VolumeLifecycleMode{
storage.VolumeLifecycleEphemeral,
},
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
},
},
false,
@ -620,8 +679,9 @@ func TestCSIDriverValidation(t *testing.T) {
storage.VolumeLifecyclePersistent,
storage.VolumeLifecycleEphemeral,
},
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
},
},
false,
@ -633,12 +693,13 @@ func TestCSIDriverValidation(t *testing.T) {
Name: "foo",
},
Spec: storage.CSIDriverSpec{
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
TokenRequests: []storage.TokenRequest{{Audience: gcp}},
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
TokenRequests: []storage.TokenRequest{{Audience: gcp}},
RequiresRepublish: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
},
},
false,
@ -650,10 +711,11 @@ func TestCSIDriverValidation(t *testing.T) {
Name: "foo",
},
Spec: storage.CSIDriverSpec{
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
SELinuxMount: nil,
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
SELinuxMount: nil,
PreventPodSchedulingIfMissing: &enabled,
},
},
true,
@ -669,6 +731,7 @@ func TestCSIDriverValidation(t *testing.T) {
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
NodeAllocatableUpdatePeriodSeconds: &validNodeAllocatableUpdatePeriodSeconds,
},
},
@ -685,6 +748,7 @@ func TestCSIDriverValidation(t *testing.T) {
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
NodeAllocatableUpdatePeriodSeconds: &invalidNodeAllocatableUpdatePeriodSeconds,
},
},
@ -697,12 +761,13 @@ func TestCSIDriverValidation(t *testing.T) {
Name: "foo",
},
Spec: storage.CSIDriverSpec{
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
SELinuxMount: &enabled,
ServiceAccountTokenInSecrets: &enabled,
TokenRequests: tokenRequests,
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
ServiceAccountTokenInSecrets: &enabled,
TokenRequests: tokenRequests,
},
},
false,
@ -714,11 +779,59 @@ func TestCSIDriverValidation(t *testing.T) {
Name: "foo",
},
Spec: storage.CSIDriverSpec{
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
SELinuxMount: &enabled,
ServiceAccountTokenInSecrets: &enabled,
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
ServiceAccountTokenInSecrets: &enabled,
},
},
true,
},
{
"valid PreventPodSchedulingIfMissing set to true",
&storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: storage.CSIDriverSpec{
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &enabled,
},
},
false,
},
{
"valid PreventPodSchedulingIfMissing set to false",
&storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: storage.CSIDriverSpec{
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
SELinuxMount: &enabled,
PreventPodSchedulingIfMissing: &disabled,
},
},
false,
},
{
"invalid PreventPodSchedulingIfMissing not set (nil)",
&storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: storage.CSIDriverSpec{
AttachRequired: &enabled,
PodInfoOnMount: &enabled,
StorageCapacity: &enabled,
SELinuxMount: &enabled,
},
},
true,
@ -729,10 +842,12 @@ func TestCSIDriverValidation(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
// assume this feature is on for this test, detailed enabled/disabled tests in TestCSIDriverValidationSELinuxMountEnabledDisabled
// and TestCSIDriverValidationServiceAccountTokenInSecretsEnabledDisabled
// and TestCSIDriverValidationPreventPodSchedulingIfMissingEnabledDisabled
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
features.SELinuxMountReadWriteOncePod: true,
features.MutableCSINodeAllocatableCount: true,
features.CSIServiceAccountTokenSecrets: true,
features.VolumeLimitScaling: true,
})
testValidation := func(csiDriver *storage.CSIDriver, apiVersion string) field.ErrorList {
@ -1018,6 +1133,8 @@ func TestCSIDriverPrepareForCreate(t *testing.T) {
csiDriver *storage.CSIDriver
csiServiceAccountTokenSecretsEnabled bool
wantServiceAccountTokenInSecrets *bool
volumeLimitScalingEnabled bool
wantPreventPodSchedulingIfMissing *bool
}{
{
name: "ServiceAccountTokenInSecrets feature enabled, field set to true",
@ -1073,6 +1190,54 @@ func TestCSIDriverPrepareForCreate(t *testing.T) {
csiServiceAccountTokenSecretsEnabled: false,
wantServiceAccountTokenInSecrets: nil,
},
{
name: "VolumeLimitScaling feature enabled, field set to true",
csiDriver: &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: storage.CSIDriverSpec{
PreventPodSchedulingIfMissing: &enabled,
},
},
volumeLimitScalingEnabled: true,
wantPreventPodSchedulingIfMissing: &enabled,
},
{
name: "VolumeLimitScaling feature disabled, field set to true should be cleared",
csiDriver: &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: storage.CSIDriverSpec{
PreventPodSchedulingIfMissing: &enabled,
},
},
volumeLimitScalingEnabled: false,
wantPreventPodSchedulingIfMissing: nil,
},
{
name: "VolumeLimitScaling feature enabled, field not set",
csiDriver: &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: storage.CSIDriverSpec{},
},
volumeLimitScalingEnabled: true,
wantPreventPodSchedulingIfMissing: nil,
},
{
name: "VolumeLimitScaling feature disabled, field not set",
csiDriver: &storage.CSIDriver{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: storage.CSIDriverSpec{},
},
volumeLimitScalingEnabled: false,
wantPreventPodSchedulingIfMissing: nil,
},
}
for _, test := range tests {
@ -1082,11 +1247,13 @@ func TestCSIDriverPrepareForCreate(t *testing.T) {
}
featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{
features.CSIServiceAccountTokenSecrets: test.csiServiceAccountTokenSecretsEnabled,
features.VolumeLimitScaling: test.volumeLimitScalingEnabled,
})
csiDriver := test.csiDriver.DeepCopy()
Strategy.PrepareForCreate(ctx, csiDriver)
require.Equal(t, test.wantServiceAccountTokenInSecrets, csiDriver.Spec.ServiceAccountTokenInSecrets)
require.Equal(t, test.wantPreventPodSchedulingIfMissing, csiDriver.Spec.PreventPodSchedulingIfMissing)
})
}
}

View file

@ -355,7 +355,7 @@ func (pl *CSILimits) Filter(ctx context.Context, _ fwk.CycleState, pod *v1.Pod,
func (pl *CSILimits) checkCSIDriverOnNode(pluginName string, csiNode *storagev1.CSINode) (bool, error) {
// the registered driver must be a CSI driver to enforce this limit, if we can't find the driver,
// we assume the driver may not be a CSI driver and allow the pod to be scheduled.
_, err := pl.csiDriverLister.Get(pluginName)
csiDriver, err := pl.csiDriverLister.Get(pluginName)
if err != nil {
if apierrors.IsNotFound(err) {
return true, nil
@ -363,6 +363,12 @@ func (pl *CSILimits) checkCSIDriverOnNode(pluginName string, csiNode *storagev1.
return false, fmt.Errorf("error getting CSIDriver for provider %s: %w", pluginName, err)
}
driverOptin := csiDriver.Spec.PreventPodSchedulingIfMissing
if driverOptin == nil || !*driverOptin {
return true, nil
}
if csiNode == nil {
return false, nil
}

View file

@ -1275,49 +1275,76 @@ func getFakeCSIDriverLister(driverNames ...string) fakeCSIDriverLister {
return list
}
func getFakeCSIDriverListerWithPreventPodSchedulingIfMissing(driverNames ...string) fakeCSIDriverLister {
var list fakeCSIDriverLister
for _, name := range driverNames {
list = append(list, storagev1.CSIDriver{
ObjectMeta: metav1.ObjectMeta{Name: name},
Spec: storagev1.CSIDriverSpec{
PreventPodSchedulingIfMissing: ptr.To(true),
},
})
}
return list
}
func TestVolumeLimitScalingGate(t *testing.T) {
// Pod uses a PVC that resolves to the EBS CSI driver via PV
newPod := st.MakePod().PVC("csi-ebs.csi.aws.com-0").Obj()
cases := []struct {
name string
enableVolumeLimitScaling bool
limitSource string
limit int32
csiDriverPresent bool
wantStatus *fwk.Status
name string
enableVolumeLimitScaling bool
limitSource string
limit int32
csiDriverPresent bool
preventPodSchedulingIfMissing bool
wantStatus *fwk.Status
}{
{
name: "gate enabled - fail when driver not installed and CSIDriver exists",
enableVolumeLimitScaling: true,
limitSource: "no-csi-driver",
limit: 0,
csiDriverPresent: true,
wantStatus: fwk.NewStatus(fwk.Unschedulable, fmt.Sprintf("%s CSI driver is not installed on the node", ebsCSIDriverName)),
name: "gate enabled - allow scheduling when CSIDriver exists but PreventPodSchedulingIfMissing is not set",
enableVolumeLimitScaling: true,
limitSource: "no-csi-driver",
limit: 0,
csiDriverPresent: true,
preventPodSchedulingIfMissing: false,
wantStatus: nil,
},
{
name: "gate disabled - skip driver presence check (regardless of CSIDriver presence)",
enableVolumeLimitScaling: false,
limitSource: "no-csi-driver",
limit: 0,
csiDriverPresent: true,
wantStatus: nil,
name: "gate enabled - fail when driver not installed and PreventPodSchedulingIfMissing is true",
enableVolumeLimitScaling: true,
limitSource: "no-csi-driver",
limit: 0,
csiDriverPresent: true,
preventPodSchedulingIfMissing: true,
wantStatus: fwk.NewStatus(fwk.Unschedulable, fmt.Sprintf("%s CSI driver is not installed on the node", ebsCSIDriverName)),
},
{
name: "gate enabled - driver installed within limit",
enableVolumeLimitScaling: true,
limitSource: "csinode",
limit: 2,
csiDriverPresent: true,
wantStatus: nil,
name: "gate disabled - skip driver presence check (regardless of CSIDriver presence)",
enableVolumeLimitScaling: false,
limitSource: "no-csi-driver",
limit: 0,
csiDriverPresent: true,
preventPodSchedulingIfMissing: true,
wantStatus: nil,
},
{
name: "gate enabled - allow scheduling when CSIDriver object missing",
enableVolumeLimitScaling: true,
limitSource: "no-csi-driver",
limit: 0,
csiDriverPresent: false,
wantStatus: nil,
name: "gate enabled - driver installed within limit",
enableVolumeLimitScaling: true,
limitSource: "csinode",
limit: 2,
csiDriverPresent: true,
preventPodSchedulingIfMissing: true,
wantStatus: nil,
},
{
name: "gate enabled - allow scheduling when CSIDriver object missing",
enableVolumeLimitScaling: true,
limitSource: "no-csi-driver",
limit: 0,
csiDriverPresent: false,
preventPodSchedulingIfMissing: false,
wantStatus: nil,
},
}
@ -1334,6 +1361,9 @@ func TestVolumeLimitScalingGate(t *testing.T) {
vaLister: getFakeVolumeAttachmentLister(0, ebsCSIDriverName),
csiDriverLister: func() fakeCSIDriverLister {
if tt.csiDriverPresent {
if tt.preventPodSchedulingIfMissing {
return getFakeCSIDriverListerWithPreventPodSchedulingIfMissing(ebsCSIDriverName)
}
return getFakeCSIDriverLister(ebsCSIDriverName)
}
return getFakeCSIDriverLister()

View file

@ -187,6 +187,16 @@ func (m *CSIDriverSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.PreventPodSchedulingIfMissing != nil {
i--
if *m.PreventPodSchedulingIfMissing {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x58
}
if m.ServiceAccountTokenInSecrets != nil {
i--
if *m.ServiceAccountTokenInSecrets {
@ -1304,6 +1314,9 @@ func (m *CSIDriverSpec) Size() (n int) {
if m.ServiceAccountTokenInSecrets != nil {
n += 2
}
if m.PreventPodSchedulingIfMissing != nil {
n += 2
}
return n
}
@ -1701,6 +1714,7 @@ func (this *CSIDriverSpec) String() string {
`SELinuxMount:` + valueToStringGenerated(this.SELinuxMount) + `,`,
`NodeAllocatableUpdatePeriodSeconds:` + valueToStringGenerated(this.NodeAllocatableUpdatePeriodSeconds) + `,`,
`ServiceAccountTokenInSecrets:` + valueToStringGenerated(this.ServiceAccountTokenInSecrets) + `,`,
`PreventPodSchedulingIfMissing:` + valueToStringGenerated(this.PreventPodSchedulingIfMissing) + `,`,
`}`,
}, "")
return s
@ -2498,6 +2512,27 @@ func (m *CSIDriverSpec) Unmarshal(dAtA []byte) error {
}
b := bool(v != 0)
m.ServiceAccountTokenInSecrets = &b
case 11:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field PreventPodSchedulingIfMissing", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
b := bool(v != 0)
m.PreventPodSchedulingIfMissing = &b
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])

View file

@ -249,6 +249,27 @@ message CSIDriverSpec {
// +featureGate=CSIServiceAccountTokenSecrets
// +optional
optional bool serviceAccountTokenInSecrets = 10;
// PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod
// scheduling if the CSI driver on the node is missing.
//
// Enabling this option will prevent the scheduler (or any other
// component which embeds default scheduler such as cluster-autoscaler) from
// scheduling pods to nodes where CSI driver is not installed.
//
// For components(such as cluster-autoscaler) that embed the scheduler and run
// pod placement simulations using scheduler plugins, they MUST be aware of
// CSI driver registration information via CSINode object. They must create simulated
// CSINode objects in addition to Node objects during scheduling simulation, otherwise
// if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any
// newly created node may be rejected by the scheduler because of missing CSI driver
// information from the node.
//
// This is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled.
// Default is "false".
// +featureGate=VolumeLimitScaling
// +optional
optional bool preventPodSchedulingIfMissing = 11;
}
// CSINode holds information about all CSI drivers installed on a node.

View file

@ -483,6 +483,27 @@ type CSIDriverSpec struct {
// +featureGate=CSIServiceAccountTokenSecrets
// +optional
ServiceAccountTokenInSecrets *bool `json:"serviceAccountTokenInSecrets,omitempty" protobuf:"varint,10,opt,name=serviceAccountTokenInSecrets"`
// PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod
// scheduling if the CSI driver on the node is missing.
//
// Enabling this option will prevent the scheduler (or any other
// component which embeds default scheduler such as cluster-autoscaler) from
// scheduling pods to nodes where CSI driver is not installed.
//
// For components(such as cluster-autoscaler) that embed the scheduler and run
// pod placement simulations using scheduler plugins, they MUST be aware of
// CSI driver registration information via CSINode object. They must create simulated
// CSINode objects in addition to Node objects during scheduling simulation, otherwise
// if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any
// newly created node may be rejected by the scheduler because of missing CSI driver
// information from the node.
//
// This is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled.
// Default is "false".
// +featureGate=VolumeLimitScaling
// +optional
PreventPodSchedulingIfMissing *bool `json:"preventPodSchedulingIfMissing,omitempty" protobuf:"varint,11,opt,name=preventPodSchedulingIfMissing"`
}
// FSGroupPolicy specifies if a CSI Driver supports modifying

View file

@ -59,6 +59,7 @@ var map_CSIDriverSpec = map[string]string{
"seLinuxMount": "seLinuxMount specifies if the CSI driver supports \"-o context\" mount option.\n\nWhen \"true\", the CSI driver must ensure that all volumes provided by this CSI driver can be mounted separately with different `-o context` options. This is typical for storage backends that provide volumes as filesystems on block devices or as independent shared volumes. Kubernetes will call NodeStage / NodePublish with \"-o context=xyz\" mount option when mounting a ReadWriteOncePod volume used in Pod that has explicitly set SELinux context. In the future, it may be expanded to other volume AccessModes. In any case, Kubernetes will ensure that the volume is mounted only with a single SELinux context.\n\nWhen \"false\", Kubernetes won't pass any special SELinux mount options to the driver. This is typical for volumes that represent subdirectories of a bigger shared filesystem.\n\nDefault is \"false\".",
"nodeAllocatableUpdatePeriodSeconds": "nodeAllocatableUpdatePeriodSeconds specifies the interval between periodic updates of the CSINode allocatable capacity for this driver. When set, both periodic updates and updates triggered by capacity-related failures are enabled. If not set, no updates occur (neither periodic nor upon detecting capacity-related failures), and the allocatable.count remains static. The minimum allowed value for this field is 10 seconds.\n\nThis feature requires the MutableCSINodeAllocatableCount feature gate to be enabled.\n\nThis field is mutable.",
"serviceAccountTokenInSecrets": "serviceAccountTokenInSecrets is an opt-in for CSI drivers to indicate that service account tokens should be passed via the Secrets field in NodePublishVolumeRequest instead of the VolumeContext field. The CSI specification provides a dedicated Secrets field for sensitive information like tokens, which is the appropriate mechanism for handling credentials. This addresses security concerns where sensitive tokens were being logged as part of volume context.\n\nWhen \"true\", kubelet will pass the tokens only in the Secrets field with the key \"csi.storage.k8s.io/serviceAccount.tokens\". The CSI driver must be updated to read tokens from the Secrets field instead of VolumeContext.\n\nWhen \"false\" or not set, kubelet will pass the tokens in VolumeContext with the key \"csi.storage.k8s.io/serviceAccount.tokens\" (existing behavior). This maintains backward compatibility with existing CSI drivers.\n\nThis field can only be set when TokenRequests is configured. The API server will reject CSIDriver specs that set this field without TokenRequests.\n\nDefault behavior if unset is to pass tokens in the VolumeContext field.",
"preventPodSchedulingIfMissing": "PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod scheduling if the CSI driver on the node is missing.\n\nEnabling this option will prevent the scheduler (or any other component which embeds default scheduler such as cluster-autoscaler) from scheduling pods to nodes where CSI driver is not installed.\n\nFor components(such as cluster-autoscaler) that embed the scheduler and run pod placement simulations using scheduler plugins, they MUST be aware of CSI driver registration information via CSINode object. They must create simulated CSINode objects in addition to Node objects during scheduling simulation, otherwise if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any newly created node may be rejected by the scheduler because of missing CSI driver information from the node.\n\nThis is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled. Default is \"false\".",
}
func (CSIDriverSpec) SwaggerDoc() map[string]string {

View file

@ -142,6 +142,11 @@ func (in *CSIDriverSpec) DeepCopyInto(out *CSIDriverSpec) {
*out = new(bool)
**out = **in
}
if in.PreventPodSchedulingIfMissing != nil {
in, out := &in.PreventPodSchedulingIfMissing, &out.PreventPodSchedulingIfMissing
*out = new(bool)
**out = **in
}
return
}

View file

@ -187,6 +187,16 @@ func (m *CSIDriverSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.PreventPodSchedulingIfMissing != nil {
i--
if *m.PreventPodSchedulingIfMissing {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x58
}
if m.ServiceAccountTokenInSecrets != nil {
i--
if *m.ServiceAccountTokenInSecrets {
@ -1304,6 +1314,9 @@ func (m *CSIDriverSpec) Size() (n int) {
if m.ServiceAccountTokenInSecrets != nil {
n += 2
}
if m.PreventPodSchedulingIfMissing != nil {
n += 2
}
return n
}
@ -1701,6 +1714,7 @@ func (this *CSIDriverSpec) String() string {
`SELinuxMount:` + valueToStringGenerated(this.SELinuxMount) + `,`,
`NodeAllocatableUpdatePeriodSeconds:` + valueToStringGenerated(this.NodeAllocatableUpdatePeriodSeconds) + `,`,
`ServiceAccountTokenInSecrets:` + valueToStringGenerated(this.ServiceAccountTokenInSecrets) + `,`,
`PreventPodSchedulingIfMissing:` + valueToStringGenerated(this.PreventPodSchedulingIfMissing) + `,`,
`}`,
}, "")
return s
@ -2498,6 +2512,27 @@ func (m *CSIDriverSpec) Unmarshal(dAtA []byte) error {
}
b := bool(v != 0)
m.ServiceAccountTokenInSecrets = &b
case 11:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field PreventPodSchedulingIfMissing", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
b := bool(v != 0)
m.PreventPodSchedulingIfMissing = &b
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])

View file

@ -251,6 +251,27 @@ message CSIDriverSpec {
// +featureGate=CSIServiceAccountTokenSecrets
// +optional
optional bool serviceAccountTokenInSecrets = 10;
// PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod
// scheduling if the CSI driver on the node is missing.
//
// Enabling this option will prevent the scheduler (or any other
// component which embeds default scheduler such as cluster-autoscaler) from
// scheduling pods to nodes where CSI driver is not installed.
//
// For components(such as cluster-autoscaler) that embed the scheduler and run
// pod placement simulations using scheduler plugins, they MUST be aware of
// CSI driver registration information via CSINode object. They must create simulated
// CSINode objects in addition to Node objects during scheduling simulation, otherwise
// if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any
// newly created node may be rejected by the scheduler because of missing CSI driver
// information from the node.
//
// This is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled.
// Default is "false".
// +featureGate=VolumeLimitScaling
// +optional
optional bool preventPodSchedulingIfMissing = 11;
}
// DEPRECATED - This group version of CSINode is deprecated by storage/v1/CSINode.

View file

@ -496,6 +496,27 @@ type CSIDriverSpec struct {
// +featureGate=CSIServiceAccountTokenSecrets
// +optional
ServiceAccountTokenInSecrets *bool `json:"serviceAccountTokenInSecrets,omitempty" protobuf:"varint,10,opt,name=serviceAccountTokenInSecrets"`
// PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod
// scheduling if the CSI driver on the node is missing.
//
// Enabling this option will prevent the scheduler (or any other
// component which embeds default scheduler such as cluster-autoscaler) from
// scheduling pods to nodes where CSI driver is not installed.
//
// For components(such as cluster-autoscaler) that embed the scheduler and run
// pod placement simulations using scheduler plugins, they MUST be aware of
// CSI driver registration information via CSINode object. They must create simulated
// CSINode objects in addition to Node objects during scheduling simulation, otherwise
// if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any
// newly created node may be rejected by the scheduler because of missing CSI driver
// information from the node.
//
// This is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled.
// Default is "false".
// +featureGate=VolumeLimitScaling
// +optional
PreventPodSchedulingIfMissing *bool `json:"preventPodSchedulingIfMissing,omitempty" protobuf:"varint,11,opt,name=preventPodSchedulingIfMissing"`
}
// FSGroupPolicy specifies if a CSI Driver supports modifying

View file

@ -59,6 +59,7 @@ var map_CSIDriverSpec = map[string]string{
"seLinuxMount": "seLinuxMount specifies if the CSI driver supports \"-o context\" mount option.\n\nWhen \"true\", the CSI driver must ensure that all volumes provided by this CSI driver can be mounted separately with different `-o context` options. This is typical for storage backends that provide volumes as filesystems on block devices or as independent shared volumes. Kubernetes will call NodeStage / NodePublish with \"-o context=xyz\" mount option when mounting a ReadWriteOncePod volume used in Pod that has explicitly set SELinux context. In the future, it may be expanded to other volume AccessModes. In any case, Kubernetes will ensure that the volume is mounted only with a single SELinux context.\n\nWhen \"false\", Kubernetes won't pass any special SELinux mount options to the driver. This is typical for volumes that represent subdirectories of a bigger shared filesystem.\n\nDefault is \"false\".",
"nodeAllocatableUpdatePeriodSeconds": "nodeAllocatableUpdatePeriodSeconds specifies the interval between periodic updates of the CSINode allocatable capacity for this driver. When set, both periodic updates and updates triggered by capacity-related failures are enabled. If not set, no updates occur (neither periodic nor upon detecting capacity-related failures), and the allocatable.count remains static. The minimum allowed value for this field is 10 seconds.\n\nThis is a beta feature and requires the MutableCSINodeAllocatableCount feature gate to be enabled.\n\nThis field is mutable.",
"serviceAccountTokenInSecrets": "serviceAccountTokenInSecrets is an opt-in for CSI drivers to indicate that service account tokens should be passed via the Secrets field in NodePublishVolumeRequest instead of the VolumeContext field. The CSI specification provides a dedicated Secrets field for sensitive information like tokens, which is the appropriate mechanism for handling credentials. This addresses security concerns where sensitive tokens were being logged as part of volume context.\n\nWhen \"true\", kubelet will pass the tokens only in the Secrets field with the key \"csi.storage.k8s.io/serviceAccount.tokens\". The CSI driver must be updated to read tokens from the Secrets field instead of VolumeContext.\n\nWhen \"false\" or not set, kubelet will pass the tokens in VolumeContext with the key \"csi.storage.k8s.io/serviceAccount.tokens\" (existing behavior). This maintains backward compatibility with existing CSI drivers.\n\nThis field can only be set when TokenRequests is configured. The API server will reject CSIDriver specs that set this field without TokenRequests.\n\nDefault behavior if unset is to pass tokens in the VolumeContext field.",
"preventPodSchedulingIfMissing": "PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod scheduling if the CSI driver on the node is missing.\n\nEnabling this option will prevent the scheduler (or any other component which embeds default scheduler such as cluster-autoscaler) from scheduling pods to nodes where CSI driver is not installed.\n\nFor components(such as cluster-autoscaler) that embed the scheduler and run pod placement simulations using scheduler plugins, they MUST be aware of CSI driver registration information via CSINode object. They must create simulated CSINode objects in addition to Node objects during scheduling simulation, otherwise if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any newly created node may be rejected by the scheduler because of missing CSI driver information from the node.\n\nThis is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled. Default is \"false\".",
}
func (CSIDriverSpec) SwaggerDoc() map[string]string {

View file

@ -142,6 +142,11 @@ func (in *CSIDriverSpec) DeepCopyInto(out *CSIDriverSpec) {
*out = new(bool)
**out = **in
}
if in.PreventPodSchedulingIfMissing != nil {
in, out := &in.PreventPodSchedulingIfMissing, &out.PreventPodSchedulingIfMissing
*out = new(bool)
**out = **in
}
return
}

View file

@ -60,6 +60,7 @@
"requiresRepublish": true,
"seLinuxMount": true,
"nodeAllocatableUpdatePeriodSeconds": 9,
"serviceAccountTokenInSecrets": true
"serviceAccountTokenInSecrets": true,
"preventPodSchedulingIfMissing": true
}
}

View file

@ -37,6 +37,7 @@ spec:
fsGroupPolicy: fsGroupPolicyValue
nodeAllocatableUpdatePeriodSeconds: 9
podInfoOnMount: true
preventPodSchedulingIfMissing: true
requiresRepublish: true
seLinuxMount: true
serviceAccountTokenInSecrets: true

View file

@ -60,6 +60,7 @@
"requiresRepublish": true,
"seLinuxMount": true,
"nodeAllocatableUpdatePeriodSeconds": 9,
"serviceAccountTokenInSecrets": true
"serviceAccountTokenInSecrets": true,
"preventPodSchedulingIfMissing": true
}
}

View file

@ -37,6 +37,7 @@ spec:
fsGroupPolicy: fsGroupPolicyValue
nodeAllocatableUpdatePeriodSeconds: 9
podInfoOnMount: true
preventPodSchedulingIfMissing: true
requiresRepublish: true
seLinuxMount: true
serviceAccountTokenInSecrets: true

View file

@ -14643,6 +14643,9 @@ var schemaYAML = typed.YAMLObject(`types:
- name: podInfoOnMount
type:
scalar: boolean
- name: preventPodSchedulingIfMissing
type:
scalar: boolean
- name: requiresRepublish
type:
scalar: boolean
@ -15052,6 +15055,9 @@ var schemaYAML = typed.YAMLObject(`types:
- name: podInfoOnMount
type:
scalar: boolean
- name: preventPodSchedulingIfMissing
type:
scalar: boolean
- name: requiresRepublish
type:
scalar: boolean

View file

@ -178,6 +178,24 @@ type CSIDriverSpecApplyConfiguration struct {
//
// Default behavior if unset is to pass tokens in the VolumeContext field.
ServiceAccountTokenInSecrets *bool `json:"serviceAccountTokenInSecrets,omitempty"`
// PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod
// scheduling if the CSI driver on the node is missing.
//
// Enabling this option will prevent the scheduler (or any other
// component which embeds default scheduler such as cluster-autoscaler) from
// scheduling pods to nodes where CSI driver is not installed.
//
// For components(such as cluster-autoscaler) that embed the scheduler and run
// pod placement simulations using scheduler plugins, they MUST be aware of
// CSI driver registration information via CSINode object. They must create simulated
// CSINode objects in addition to Node objects during scheduling simulation, otherwise
// if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any
// newly created node may be rejected by the scheduler because of missing CSI driver
// information from the node.
//
// This is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled.
// Default is "false".
PreventPodSchedulingIfMissing *bool `json:"preventPodSchedulingIfMissing,omitempty"`
}
// CSIDriverSpecApplyConfiguration constructs a declarative configuration of the CSIDriverSpec type for use with
@ -272,3 +290,11 @@ func (b *CSIDriverSpecApplyConfiguration) WithServiceAccountTokenInSecrets(value
b.ServiceAccountTokenInSecrets = &value
return b
}
// WithPreventPodSchedulingIfMissing sets the PreventPodSchedulingIfMissing field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the PreventPodSchedulingIfMissing field is set to the value of the last call.
func (b *CSIDriverSpecApplyConfiguration) WithPreventPodSchedulingIfMissing(value bool) *CSIDriverSpecApplyConfiguration {
b.PreventPodSchedulingIfMissing = &value
return b
}

View file

@ -178,6 +178,24 @@ type CSIDriverSpecApplyConfiguration struct {
//
// Default behavior if unset is to pass tokens in the VolumeContext field.
ServiceAccountTokenInSecrets *bool `json:"serviceAccountTokenInSecrets,omitempty"`
// PreventPodSchedulingIfMissing indicates that the CSI driver wants to prevent pod
// scheduling if the CSI driver on the node is missing.
//
// Enabling this option will prevent the scheduler (or any other
// component which embeds default scheduler such as cluster-autoscaler) from
// scheduling pods to nodes where CSI driver is not installed.
//
// For components(such as cluster-autoscaler) that embed the scheduler and run
// pod placement simulations using scheduler plugins, they MUST be aware of
// CSI driver registration information via CSINode object. They must create simulated
// CSINode objects in addition to Node objects during scheduling simulation, otherwise
// if PreventPodSchedulingIfMissing is enabled globally for CSIDriver object, any
// newly created node may be rejected by the scheduler because of missing CSI driver
// information from the node.
//
// This is an alpha feature and requires the VolumeLimitScaling feature gate to be enabled.
// Default is "false".
PreventPodSchedulingIfMissing *bool `json:"preventPodSchedulingIfMissing,omitempty"`
}
// CSIDriverSpecApplyConfiguration constructs a declarative configuration of the CSIDriverSpec type for use with
@ -272,3 +290,11 @@ func (b *CSIDriverSpecApplyConfiguration) WithServiceAccountTokenInSecrets(value
b.ServiceAccountTokenInSecrets = &value
return b
}
// WithPreventPodSchedulingIfMissing sets the PreventPodSchedulingIfMissing field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the PreventPodSchedulingIfMissing field is set to the value of the last call.
func (b *CSIDriverSpecApplyConfiguration) WithPreventPodSchedulingIfMissing(value bool) *CSIDriverSpecApplyConfiguration {
b.PreventPodSchedulingIfMissing = &value
return b
}