@@ -33,6 +33,7 @@ import (
3333 "google.golang.org/grpc/status"
3434 v1 "k8s.io/api/core/v1"
3535 storagev1 "k8s.io/api/storage/v1"
36+ storagev1alpha1 "k8s.io/api/storage/v1alpha1"
3637 "k8s.io/apimachinery/pkg/api/resource"
3738 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3839 "k8s.io/apimachinery/pkg/runtime"
@@ -42,6 +43,7 @@ import (
4243 "k8s.io/client-go/kubernetes"
4344 fakeclientset "k8s.io/client-go/kubernetes/fake"
4445 k8stesting "k8s.io/client-go/testing"
46+ "k8s.io/component-base/featuregate"
4547 utilfeaturetesting "k8s.io/component-base/featuregate/testing"
4648 csitrans "k8s.io/csi-translation-lib"
4749 "k8s.io/klog/v2"
@@ -504,6 +506,15 @@ func provisionFromPVCCapabilities() (rpc.PluginCapabilitySet, rpc.ControllerCapa
504506 }
505507}
506508
509+ func provisionWithVACCapabilities () (rpc.PluginCapabilitySet , rpc.ControllerCapabilitySet ) {
510+ return rpc.PluginCapabilitySet {
511+ csi .PluginCapability_Service_CONTROLLER_SERVICE : true ,
512+ }, rpc.ControllerCapabilitySet {
513+ csi .ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME : true ,
514+ csi .ControllerServiceCapability_RPC_MODIFY_VOLUME : true ,
515+ }
516+ }
517+
507518var fakeSCName = "fake-test-sc"
508519
509520func createFakeNamedPVC (requestBytes int64 , name string , userAnnotations map [string ]string ) * v1.PersistentVolumeClaim {
@@ -543,6 +554,13 @@ func createFakePVCWithVolumeMode(requestBytes int64, volumeMode v1.PersistentVol
543554 return claim
544555}
545556
557+ // createFakePVCWithVAC returns PVC with VolumeAttributesClassName
558+ func createFakePVCWithVAC (requestBytes int64 , vacName string ) * v1.PersistentVolumeClaim {
559+ claim := createFakePVC (requestBytes )
560+ claim .Spec .VolumeAttributesClassName = & vacName
561+ return claim
562+ }
563+
546564// fakeClaim returns a valid PVC with the requested settings
547565func fakeClaim (name , namespace , claimUID string , capacity int64 , boundToVolume string , phase v1.PersistentVolumeClaimPhase , class * string , mode string ) * v1.PersistentVolumeClaim {
548566 claim := v1.PersistentVolumeClaim {
@@ -820,28 +838,29 @@ func TestGetSecretReference(t *testing.T) {
820838}
821839
822840type provisioningTestcase struct {
823- capacity int64 // if zero, default capacity, otherwise available bytes
824- volOpts controller.ProvisionOptions
825- notNilSelector bool
826- makeVolumeNameErr bool
827- getSecretRefErr bool
828- getCredentialsErr bool
829- volWithLessCap bool
830- volWithZeroCap bool
831- expectedPVSpec * pvSpec
832- clientSetObjects []runtime.Object
833- createVolumeError error
834- expectErr bool
835- expectState controller.ProvisioningState
836- expectCreateVolDo func (t * testing.T , ctx context.Context , req * csi.CreateVolumeRequest )
837- withExtraMetadata bool
838- skipCreateVolume bool
839- deploymentNode string // fake distributed provisioning with this node as host
840- immediateBinding bool // enable immediate binding support for distributed provisioning
841- expectSelectedNode string // a specific selected-node of the PVC in the apiserver after the test, same as before if empty
842- expectNoProvision bool // if true, then ShouldProvision should return false
843- supportsSingleNodeMultiWriter bool // if true, then provision with single node multi writer capabilities
844- controllerPublishReadOnly bool
841+ capacity int64 // if zero, default capacity, otherwise available bytes
842+ volOpts controller.ProvisionOptions
843+ notNilSelector bool
844+ makeVolumeNameErr bool
845+ getSecretRefErr bool
846+ getCredentialsErr bool
847+ volWithLessCap bool
848+ volWithZeroCap bool
849+ expectedPVSpec * pvSpec
850+ clientSetObjects []runtime.Object
851+ createVolumeError error
852+ expectErr bool
853+ expectState controller.ProvisioningState
854+ expectCreateVolDo func (t * testing.T , ctx context.Context , req * csi.CreateVolumeRequest )
855+ withExtraMetadata bool
856+ skipCreateVolume bool
857+ deploymentNode string // fake distributed provisioning with this node as host
858+ immediateBinding bool // enable immediate binding support for distributed provisioning
859+ expectSelectedNode string // a specific selected-node of the PVC in the apiserver after the test, same as before if empty
860+ expectNoProvision bool // if true, then ShouldProvision should return false
861+ controllerPublishReadOnly bool
862+ featureGates map [featuregate.Feature ]bool
863+ pluginCapabilities func () (rpc.PluginCapabilitySet , rpc.ControllerCapabilitySet )
845864}
846865
847866type provisioningFSTypeTestcase struct {
@@ -857,14 +876,15 @@ type provisioningFSTypeTestcase struct {
857876}
858877
859878type pvSpec struct {
860- Name string
861- Annotations map [string ]string
862- ReclaimPolicy v1.PersistentVolumeReclaimPolicy
863- AccessModes []v1.PersistentVolumeAccessMode
864- MountOptions []string
865- VolumeMode * v1.PersistentVolumeMode
866- Capacity v1.ResourceList
867- CSIPVS * v1.CSIPersistentVolumeSource
879+ Name string
880+ Annotations map [string ]string
881+ ReclaimPolicy v1.PersistentVolumeReclaimPolicy
882+ AccessModes []v1.PersistentVolumeAccessMode
883+ MountOptions []string
884+ VolumeMode * v1.PersistentVolumeMode
885+ Capacity v1.ResourceList
886+ CSIPVS * v1.CSIPersistentVolumeSource
887+ VolumeAttributesClassName * string
868888}
869889
870890const defaultSecretNsName = "default"
@@ -1068,6 +1088,7 @@ func provisionTestcases() (int64, map[string]provisioningTestcase) {
10681088 Name : "bar" ,
10691089 },
10701090 }
1091+ vacName := "test-vac"
10711092 return requestedBytes , map [string ]provisioningTestcase {
10721093 "normal provision" : {
10731094 volOpts : controller.ProvisionOptions {
@@ -1403,7 +1424,7 @@ func provisionTestcases() (int64, map[string]provisioningTestcase) {
14031424 expectState : controller .ProvisioningFinished ,
14041425 },
14051426 "provision with access mode single node writer and single node multi writer capability" : {
1406- supportsSingleNodeMultiWriter : true ,
1427+ pluginCapabilities : provisionWithSingleNodeMultiWriterCapabilities ,
14071428 volOpts : controller.ProvisionOptions {
14081429 StorageClass : & storagev1.StorageClass {
14091430 ReclaimPolicy : & deletePolicy ,
@@ -1456,7 +1477,7 @@ func provisionTestcases() (int64, map[string]provisioningTestcase) {
14561477 expectState : controller .ProvisioningFinished ,
14571478 },
14581479 "provision with access mode single node single writer and single node multi writer capability" : {
1459- supportsSingleNodeMultiWriter : true ,
1480+ pluginCapabilities : provisionWithSingleNodeMultiWriterCapabilities ,
14601481 volOpts : controller.ProvisionOptions {
14611482 StorageClass : & storagev1.StorageClass {
14621483 ReclaimPolicy : & deletePolicy ,
@@ -2282,6 +2303,130 @@ func provisionTestcases() (int64, map[string]provisioningTestcase) {
22822303 expectNoProvision : true , // not owner and will not change that either
22832304 expectSelectedNode : "" , // not changed by ShouldProvision
22842305 },
2306+ "normal provision with VolumeAttributesClass" : {
2307+ featureGates : map [featuregate.Feature ]bool {
2308+ features .VolumeAttributesClass : true ,
2309+ },
2310+ pluginCapabilities : provisionWithVACCapabilities ,
2311+ clientSetObjects : []runtime.Object {& storagev1alpha1.VolumeAttributesClass {
2312+ ObjectMeta : metav1.ObjectMeta {
2313+ Name : vacName ,
2314+ },
2315+ DriverName : driverName ,
2316+ Parameters : map [string ]string {
2317+ "test-param" : "from-vac" ,
2318+ },
2319+ }},
2320+ expectCreateVolDo : func (t * testing.T , ctx context.Context , req * csi.CreateVolumeRequest ) {
2321+ // TODO: define a constant for this? kind of ugly
2322+ if ! reflect .DeepEqual (req .MutableParameters , map [string ]string {"test-param" : "from-vac" }) {
2323+ t .Errorf ("Missing or incorrect VolumeAttributesClass (mutable) parameters" )
2324+ }
2325+ },
2326+ volOpts : controller.ProvisionOptions {
2327+ StorageClass : & storagev1.StorageClass {
2328+ ReclaimPolicy : & deletePolicy ,
2329+ Parameters : map [string ]string {
2330+ "fstype" : "ext3" ,
2331+ "test-param" : "from-sc" ,
2332+ },
2333+ },
2334+ PVName : "test-name" ,
2335+ PVC : createFakePVCWithVAC (requestedBytes , vacName ),
2336+ },
2337+ expectedPVSpec : & pvSpec {
2338+ Name : "test-testi" ,
2339+ Annotations : map [string ]string {
2340+ annDeletionProvisionerSecretRefName : "" ,
2341+ annDeletionProvisionerSecretRefNamespace : "" ,
2342+ },
2343+ ReclaimPolicy : v1 .PersistentVolumeReclaimDelete ,
2344+ Capacity : v1.ResourceList {
2345+ v1 .ResourceName (v1 .ResourceStorage ): bytesToQuantity (requestedBytes ),
2346+ },
2347+ CSIPVS : & v1.CSIPersistentVolumeSource {
2348+ Driver : "test-driver" ,
2349+ VolumeHandle : "test-volume-id" ,
2350+ FSType : "ext3" ,
2351+ VolumeAttributes : map [string ]string {
2352+ "storage.kubernetes.io/csiProvisionerIdentity" : "test-provisioner" ,
2353+ },
2354+ },
2355+ VolumeAttributesClassName : & vacName ,
2356+ },
2357+ expectState : controller .ProvisioningFinished ,
2358+ },
2359+ "fail with VolumeAttributesClass but driver does not support MODIFY_VOLUME" : {
2360+ featureGates : map [featuregate.Feature ]bool {
2361+ features .VolumeAttributesClass : true ,
2362+ },
2363+ volOpts : controller.ProvisionOptions {
2364+ StorageClass : & storagev1.StorageClass {
2365+ ReclaimPolicy : & deletePolicy ,
2366+ Parameters : map [string ]string {
2367+ "fstype" : "ext3" ,
2368+ },
2369+ },
2370+ PVName : "test-name" ,
2371+ PVC : createFakePVCWithVAC (requestedBytes , vacName ),
2372+ },
2373+ expectErr : true ,
2374+ expectState : controller .ProvisioningFinished ,
2375+ },
2376+ "fail with VolumeAttributesClass but VAC does not exist" : {
2377+ featureGates : map [featuregate.Feature ]bool {
2378+ features .VolumeAttributesClass : true ,
2379+ },
2380+ pluginCapabilities : provisionWithVACCapabilities ,
2381+ clientSetObjects : []runtime.Object {& storagev1alpha1.VolumeAttributesClass {
2382+ ObjectMeta : metav1.ObjectMeta {
2383+ Name : vacName ,
2384+ },
2385+ DriverName : driverName ,
2386+ Parameters : map [string ]string {
2387+ "test-param" : "from-vac" ,
2388+ },
2389+ }},
2390+ volOpts : controller.ProvisionOptions {
2391+ StorageClass : & storagev1.StorageClass {
2392+ ReclaimPolicy : & deletePolicy ,
2393+ Parameters : map [string ]string {
2394+ "fstype" : "ext3" ,
2395+ },
2396+ },
2397+ PVName : "test-name" ,
2398+ PVC : createFakePVCWithVAC (requestedBytes , "does-not-exist" ),
2399+ },
2400+ expectErr : true ,
2401+ expectState : controller .ProvisioningNoChange ,
2402+ },
2403+ "fail with VolumeAttributesClass but driver name does not match" : {
2404+ featureGates : map [featuregate.Feature ]bool {
2405+ features .VolumeAttributesClass : true ,
2406+ },
2407+ pluginCapabilities : provisionWithVACCapabilities ,
2408+ clientSetObjects : []runtime.Object {& storagev1alpha1.VolumeAttributesClass {
2409+ ObjectMeta : metav1.ObjectMeta {
2410+ Name : vacName ,
2411+ },
2412+ DriverName : "not-" + driverName ,
2413+ Parameters : map [string ]string {
2414+ "test-param" : "from-vac" ,
2415+ },
2416+ }},
2417+ volOpts : controller.ProvisionOptions {
2418+ StorageClass : & storagev1.StorageClass {
2419+ ReclaimPolicy : & deletePolicy ,
2420+ Parameters : map [string ]string {
2421+ "fstype" : "ext3" ,
2422+ },
2423+ },
2424+ PVName : "test-name" ,
2425+ PVC : createFakePVCWithVAC (requestedBytes , vacName ),
2426+ },
2427+ expectErr : true ,
2428+ expectState : controller .ProvisioningFinished ,
2429+ },
22852430 }
22862431}
22872432
@@ -2410,10 +2555,17 @@ func runFSTypeProvisionTest(t *testing.T, k string, tc provisioningFSTypeTestcas
24102555 }
24112556 }
24122557
2558+ if ! reflect .DeepEqual (pv .Spec .VolumeAttributesClassName , tc .expectedPVSpec .VolumeAttributesClassName ) {
2559+ t .Errorf ("test %q: expected VAC: %v, got: %v" , k , tc .expectedPVSpec .VolumeAttributesClassName , pv .Spec .VolumeAttributesClassName )
2560+ }
24132561 }
24142562}
24152563
24162564func runProvisionTest (t * testing.T , tc provisioningTestcase , requestedBytes int64 , provisionDriverName , supportsMigrationFromInTreePluginName string , testProvision bool ) {
2565+ for featureName , featureValue := range tc .featureGates {
2566+ defer utilfeaturetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , featureName , featureValue )()
2567+ }
2568+
24172569 tmpdir := tempDir (t )
24182570 defer os .RemoveAll (tmpdir )
24192571 mockController , driver , _ , controllerServer , csiConn , err := createMockServer (t , tmpdir )
@@ -2524,8 +2676,8 @@ func runProvisionTest(t *testing.T, tc provisioningTestcase, requestedBytes int6
25242676
25252677 var pluginCaps rpc.PluginCapabilitySet
25262678 var controllerCaps rpc.ControllerCapabilitySet
2527- if tc .supportsSingleNodeMultiWriter {
2528- pluginCaps , controllerCaps = provisionWithSingleNodeMultiWriterCapabilities ()
2679+ if tc .pluginCapabilities != nil {
2680+ pluginCaps , controllerCaps = tc . pluginCapabilities ()
25292681 } else {
25302682 pluginCaps , controllerCaps = provisionCapabilities ()
25312683 }
0 commit comments