diff --git a/pkg/apis/storage/types.go b/pkg/apis/storage/types.go index 6c6fd11d804..bde08724a71 100644 --- a/pkg/apis/storage/types.go +++ b/pkg/apis/storage/types.go @@ -390,6 +390,27 @@ type CSIDriverSpec struct { // // +optional RequiresRepublish *bool + + // SELinuxMount specifies if the CSI driver supports "-o context" + // mount option. + // + // When "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. + // + // When "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. + // + // Default is "false". + // + // +optional + SELinuxMount *bool } // FSGroupPolicy specifies if a CSI Driver supports modifying diff --git a/pkg/apis/storage/v1/defaults.go b/pkg/apis/storage/v1/defaults.go index 5a6d40dd229..1f2dfc0f2ee 100644 --- a/pkg/apis/storage/v1/defaults.go +++ b/pkg/apis/storage/v1/defaults.go @@ -64,4 +64,8 @@ func SetDefaults_CSIDriver(obj *storagev1.CSIDriver) { obj.Spec.RequiresRepublish = new(bool) *(obj.Spec.RequiresRepublish) = false } + if obj.Spec.SELinuxMount == nil && utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { + obj.Spec.SELinuxMount = new(bool) + *(obj.Spec.SELinuxMount) = false + } } diff --git a/pkg/apis/storage/v1/defaults_test.go b/pkg/apis/storage/v1/defaults_test.go index 3c482e086b7..0d5b744c77f 100644 --- a/pkg/apis/storage/v1/defaults_test.go +++ b/pkg/apis/storage/v1/defaults_test.go @@ -122,3 +122,30 @@ func TestSetDefaultCSIDriver(t *testing.T) { }) } } + +func TestSetDefaultSELinuxMountReadWriteOncePodEnabled(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, true)() + driver := &storagev1.CSIDriver{} + + // field should be defaulted + defaultSELinuxMount := false + output := roundTrip(t, runtime.Object(driver)).(*storagev1.CSIDriver) + outSELinuxMount := output.Spec.SELinuxMount + if outSELinuxMount == nil { + t.Errorf("Expected SELinuxMount to be defaulted to: %+v, got: nil", defaultSELinuxMount) + } else if *outSELinuxMount != defaultSELinuxMount { + t.Errorf("Expected SELinuxMount to be defaulted to: %+v, got: %+v", defaultSELinuxMount, outSELinuxMount) + } +} + +func TestSetDefaultSELinuxMountReadWriteOncePodDisabled(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, false)() + driver := &storagev1.CSIDriver{} + + // field should not be defaulted + output := roundTrip(t, runtime.Object(driver)).(*storagev1.CSIDriver) + outSELinuxMount := output.Spec.SELinuxMount + if outSELinuxMount != nil { + t.Errorf("Expected SELinuxMount to remain nil, got: %+v", outSELinuxMount) + } +} diff --git a/pkg/apis/storage/v1beta1/defaults.go b/pkg/apis/storage/v1beta1/defaults.go index c6d853203af..7897a29f1a5 100644 --- a/pkg/apis/storage/v1beta1/defaults.go +++ b/pkg/apis/storage/v1beta1/defaults.go @@ -64,4 +64,8 @@ func SetDefaults_CSIDriver(obj *storagev1beta1.CSIDriver) { obj.Spec.RequiresRepublish = new(bool) *(obj.Spec.RequiresRepublish) = false } + if obj.Spec.SELinuxMount == nil && utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { + obj.Spec.SELinuxMount = new(bool) + *(obj.Spec.SELinuxMount) = false + } } diff --git a/pkg/apis/storage/v1beta1/defaults_test.go b/pkg/apis/storage/v1beta1/defaults_test.go index 317c8a78a96..3dcb0229373 100644 --- a/pkg/apis/storage/v1beta1/defaults_test.go +++ b/pkg/apis/storage/v1beta1/defaults_test.go @@ -165,3 +165,30 @@ func TestSetDefaultCSIDriver(t *testing.T) { }) } } + +func TestSetDefaultSELinuxMountReadWriteOncePodEnabled(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, true)() + driver := &storagev1beta1.CSIDriver{} + + // field should be defaulted + defaultSELinuxMount := false + output := roundTrip(t, runtime.Object(driver)).(*storagev1beta1.CSIDriver) + outSELinuxMount := output.Spec.SELinuxMount + if outSELinuxMount == nil { + t.Errorf("Expected SELinuxMount to be defaulted to: %+v, got: nil", defaultSELinuxMount) + } else if *outSELinuxMount != defaultSELinuxMount { + t.Errorf("Expected SELinuxMount to be defaulted to: %+v, got: %+v", defaultSELinuxMount, outSELinuxMount) + } +} + +func TestSetDefaultSELinuxMountReadWriteOncePodDisabled(t *testing.T) { + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, false)() + driver := &storagev1beta1.CSIDriver{} + + // field should not be defaulted + output := roundTrip(t, runtime.Object(driver)).(*storagev1beta1.CSIDriver) + outSELinuxMount := output.Spec.SELinuxMount + if outSELinuxMount != nil { + t.Errorf("Expected SELinuxMount remain nil, got: %+v", outSELinuxMount) + } +} diff --git a/pkg/registry/storage/csidriver/strategy.go b/pkg/registry/storage/csidriver/strategy.go index d068cefdc5e..d266dfa0431 100644 --- a/pkg/registry/storage/csidriver/strategy.go +++ b/pkg/registry/storage/csidriver/strategy.go @@ -50,6 +50,9 @@ func (csiDriverStrategy) PrepareForCreate(ctx context.Context, obj runtime.Objec if !utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) { csiDriver.Spec.VolumeLifecycleModes = nil } + if !utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { + csiDriver.Spec.SELinuxMount = nil + } } func (csiDriverStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { @@ -87,6 +90,11 @@ func (csiDriverStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime. if !apiequality.Semantic.DeepEqual(oldCSIDriver.Spec.TokenRequests, newCSIDriver.Spec.TokenRequests) || !apiequality.Semantic.DeepEqual(oldCSIDriver.Spec.RequiresRepublish, newCSIDriver.Spec.RequiresRepublish) { newCSIDriver.Generation = oldCSIDriver.Generation + 1 } + + if oldCSIDriver.Spec.SELinuxMount == nil && + !utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) { + newCSIDriver.Spec.SELinuxMount = nil + } } func (csiDriverStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { diff --git a/pkg/registry/storage/csidriver/strategy_test.go b/pkg/registry/storage/csidriver/strategy_test.go index 59c25bdb92f..e0172823188 100644 --- a/pkg/registry/storage/csidriver/strategy_test.go +++ b/pkg/registry/storage/csidriver/strategy_test.go @@ -211,18 +211,36 @@ func TestCSIDriverPrepareForUpdate(t *testing.T) { RequiresRepublish: &enabled, }, } + driverWithSELinuxMountEnabled := &storage.CSIDriver{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: storage.CSIDriverSpec{ + SELinuxMount: &enabled, + }, + } + driverWithSELinuxMountDisabled := &storage.CSIDriver{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Spec: storage.CSIDriverSpec{ + SELinuxMount: &disabled, + }, + } resultPersistent := []storage.VolumeLifecycleMode{storage.VolumeLifecyclePersistent} tests := []struct { - name string - old, update *storage.CSIDriver - csiInlineVolumeEnabled bool - wantCapacity *bool - wantModes []storage.VolumeLifecycleMode - wantTokenRequests []storage.TokenRequest - wantRequiresRepublish *bool - wantGeneration int64 + name string + old, update *storage.CSIDriver + csiInlineVolumeEnabled bool + seLinuxMountReadWriteOncePodEnabled bool + wantCapacity *bool + wantModes []storage.VolumeLifecycleMode + wantTokenRequests []storage.TokenRequest + wantRequiresRepublish *bool + wantGeneration int64 + wantSELinuxMount *bool }{ { name: "capacity feature enabled, before: none, update: enabled", @@ -237,20 +255,20 @@ func TestCSIDriverPrepareForUpdate(t *testing.T) { wantCapacity: &disabled, }, { - name: "inline feature enabled, before: none, update: persitent", + name: "inline feature enabled, before: none, update: persistent", csiInlineVolumeEnabled: true, old: driverWithNothing, update: driverWithPersistent, wantModes: resultPersistent, }, { - name: "inline feature disabled, before: none, update: persitent", + name: "inline feature disabled, before: none, update: persistent", old: driverWithNothing, update: driverWithPersistent, wantModes: nil, }, { - name: "inline feature disabled, before: ephemeral, update: persitent", + name: "inline feature disabled, before: ephemeral, update: persistent", old: driverWithEphemeral, update: driverWithPersistent, wantModes: resultPersistent, @@ -263,11 +281,54 @@ func TestCSIDriverPrepareForUpdate(t *testing.T) { wantRequiresRepublish: &enabled, wantGeneration: 1, }, + { + name: "SELinux mount support feature enabled, before: nil, update: on", + seLinuxMountReadWriteOncePodEnabled: true, + old: driverWithNothing, + update: driverWithSELinuxMountEnabled, + wantSELinuxMount: &enabled, + }, + { + name: "SELinux mount support feature enabled, before: off, update: on", + seLinuxMountReadWriteOncePodEnabled: true, + old: driverWithSELinuxMountDisabled, + update: driverWithSELinuxMountEnabled, + wantSELinuxMount: &enabled, + }, + { + name: "SELinux mount support feature enabled, before: on, update: off", + seLinuxMountReadWriteOncePodEnabled: true, + old: driverWithSELinuxMountEnabled, + update: driverWithSELinuxMountDisabled, + wantSELinuxMount: &disabled, + }, + { + name: "SELinux mount support feature disabled, before: nil, update: on", + seLinuxMountReadWriteOncePodEnabled: false, + old: driverWithNothing, + update: driverWithSELinuxMountEnabled, + wantSELinuxMount: nil, + }, + { + name: "SELinux mount support feature disabled, before: off, update: on", + seLinuxMountReadWriteOncePodEnabled: false, + old: driverWithSELinuxMountDisabled, + update: driverWithSELinuxMountEnabled, + wantSELinuxMount: &enabled, + }, + { + name: "SELinux mount support feature enabled, before: on, update: off", + seLinuxMountReadWriteOncePodEnabled: false, + old: driverWithSELinuxMountEnabled, + update: driverWithSELinuxMountDisabled, + wantSELinuxMount: &disabled, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, test.csiInlineVolumeEnabled)() + defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, test.seLinuxMountReadWriteOncePodEnabled)() csiDriver := test.update.DeepCopy() Strategy.PrepareForUpdate(ctx, csiDriver, test.old) @@ -276,9 +337,9 @@ func TestCSIDriverPrepareForUpdate(t *testing.T) { require.Equal(t, test.wantModes, csiDriver.Spec.VolumeLifecycleModes) require.Equal(t, test.wantTokenRequests, csiDriver.Spec.TokenRequests) require.Equal(t, test.wantRequiresRepublish, csiDriver.Spec.RequiresRepublish) + require.Equal(t, test.wantSELinuxMount, csiDriver.Spec.SELinuxMounted) }) } - } func TestCSIDriverValidation(t *testing.T) { diff --git a/staging/src/k8s.io/api/storage/v1/types.go b/staging/src/k8s.io/api/storage/v1/types.go index 4812287abf6..f57099df6dc 100644 --- a/staging/src/k8s.io/api/storage/v1/types.go +++ b/staging/src/k8s.io/api/storage/v1/types.go @@ -392,6 +392,27 @@ type CSIDriverSpec struct { // // +optional RequiresRepublish *bool `json:"requiresRepublish,omitempty" protobuf:"varint,7,opt,name=requiresRepublish"` + + // SELinuxMount specifies if the CSI driver supports "-o context" + // mount option. + // + // When "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. + // + // When "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. + // + // Default is "false". + // + // +optional + SELinuxMount *bool `json:"seLinuxMount,omitempty" protobuf:"varint,8,opt,name=seLinuxMount"` } // FSGroupPolicy specifies if a CSI Driver supports modifying diff --git a/staging/src/k8s.io/api/storage/v1beta1/types.go b/staging/src/k8s.io/api/storage/v1beta1/types.go index b39414b9605..f4d09b641a9 100644 --- a/staging/src/k8s.io/api/storage/v1beta1/types.go +++ b/staging/src/k8s.io/api/storage/v1beta1/types.go @@ -410,6 +410,27 @@ type CSIDriverSpec struct { // // +optional RequiresRepublish *bool `json:"requiresRepublish,omitempty" protobuf:"varint,7,opt,name=requiresRepublish"` + + // SELinuxMount specifies if the CSI driver supports "-o context" + // mount option. + // + // When "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. + // + // When "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. + // + // Default is "false". + // + // +optional + SELinuxMount *bool `json:"seLinuxMount,omitempty" protobuf:"varint,8,opt,name=seLinuxMount"` } // FSGroupPolicy specifies if a CSI Driver supports modifying