...

Source file src/k8s.io/kubernetes/pkg/apis/storage/validation/validation_test.go

Documentation: k8s.io/kubernetes/pkg/apis/storage/validation

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package validation
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  	"testing"
    23  
    24  	"k8s.io/apimachinery/pkg/api/resource"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    27  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    28  	api "k8s.io/kubernetes/pkg/apis/core"
    29  	"k8s.io/kubernetes/pkg/apis/storage"
    30  	"k8s.io/kubernetes/pkg/features"
    31  	utilpointer "k8s.io/utils/pointer"
    32  )
    33  
    34  var (
    35  	deleteReclaimPolicy = api.PersistentVolumeReclaimDelete
    36  	immediateMode1      = storage.VolumeBindingImmediate
    37  	immediateMode2      = storage.VolumeBindingImmediate
    38  	waitingMode         = storage.VolumeBindingWaitForFirstConsumer
    39  	invalidMode         = storage.VolumeBindingMode("foo")
    40  	inlineSpec          = api.PersistentVolumeSpec{
    41  		AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
    42  		PersistentVolumeSource: api.PersistentVolumeSource{
    43  			CSI: &api.CSIPersistentVolumeSource{
    44  				Driver:       "com.test.foo",
    45  				VolumeHandle: "foobar",
    46  			},
    47  		},
    48  	}
    49  	longerIDValidateOption = CSINodeValidationOptions{
    50  		AllowLongNodeID: true,
    51  	}
    52  	shorterIDValidationOption = CSINodeValidationOptions{
    53  		AllowLongNodeID: false,
    54  	}
    55  )
    56  
    57  func TestValidateStorageClass(t *testing.T) {
    58  	deleteReclaimPolicy := api.PersistentVolumeReclaimPolicy("Delete")
    59  	retainReclaimPolicy := api.PersistentVolumeReclaimPolicy("Retain")
    60  	recycleReclaimPolicy := api.PersistentVolumeReclaimPolicy("Recycle")
    61  	successCases := []storage.StorageClass{{
    62  		// empty parameters
    63  		ObjectMeta:        metav1.ObjectMeta{Name: "foo"},
    64  		Provisioner:       "kubernetes.io/foo-provisioner",
    65  		Parameters:        map[string]string{},
    66  		ReclaimPolicy:     &deleteReclaimPolicy,
    67  		VolumeBindingMode: &immediateMode1,
    68  	}, {
    69  		// nil parameters
    70  		ObjectMeta:        metav1.ObjectMeta{Name: "foo"},
    71  		Provisioner:       "kubernetes.io/foo-provisioner",
    72  		ReclaimPolicy:     &deleteReclaimPolicy,
    73  		VolumeBindingMode: &immediateMode1,
    74  	}, {
    75  		// some parameters
    76  		ObjectMeta:  metav1.ObjectMeta{Name: "foo"},
    77  		Provisioner: "kubernetes.io/foo-provisioner",
    78  		Parameters: map[string]string{
    79  			"kubernetes.io/foo-parameter": "free/form/string",
    80  			"foo-parameter":               "free-form-string",
    81  			"foo-parameter2":              "{\"embedded\": \"json\", \"with\": {\"structures\":\"inside\"}}",
    82  		},
    83  		ReclaimPolicy:     &deleteReclaimPolicy,
    84  		VolumeBindingMode: &immediateMode1,
    85  	}, {
    86  		// retain reclaimPolicy
    87  		ObjectMeta:        metav1.ObjectMeta{Name: "foo"},
    88  		Provisioner:       "kubernetes.io/foo-provisioner",
    89  		ReclaimPolicy:     &retainReclaimPolicy,
    90  		VolumeBindingMode: &immediateMode1,
    91  	}}
    92  
    93  	// Success cases are expected to pass validation.
    94  	for k, v := range successCases {
    95  		if errs := ValidateStorageClass(&v); len(errs) != 0 {
    96  			t.Errorf("Expected success for %d, got %v", k, errs)
    97  		}
    98  	}
    99  
   100  	// generate a map longer than maxProvisionerParameterSize
   101  	longParameters := make(map[string]string)
   102  	totalSize := 0
   103  	for totalSize < maxProvisionerParameterSize {
   104  		k := fmt.Sprintf("param/%d", totalSize)
   105  		v := fmt.Sprintf("value-%d", totalSize)
   106  		longParameters[k] = v
   107  		totalSize = totalSize + len(k) + len(v)
   108  	}
   109  
   110  	errorCases := map[string]storage.StorageClass{
   111  		"namespace is present": {
   112  			ObjectMeta:    metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
   113  			Provisioner:   "kubernetes.io/foo-provisioner",
   114  			ReclaimPolicy: &deleteReclaimPolicy,
   115  		},
   116  		"invalid provisioner": {
   117  			ObjectMeta:    metav1.ObjectMeta{Name: "foo"},
   118  			Provisioner:   "kubernetes.io/invalid/provisioner",
   119  			ReclaimPolicy: &deleteReclaimPolicy,
   120  		},
   121  		"invalid empty parameter name": {
   122  			ObjectMeta:  metav1.ObjectMeta{Name: "foo"},
   123  			Provisioner: "kubernetes.io/foo",
   124  			Parameters: map[string]string{
   125  				"": "value",
   126  			},
   127  			ReclaimPolicy: &deleteReclaimPolicy,
   128  		},
   129  		"provisioner: Required value": {
   130  			ObjectMeta:    metav1.ObjectMeta{Name: "foo"},
   131  			Provisioner:   "",
   132  			ReclaimPolicy: &deleteReclaimPolicy,
   133  		},
   134  		"too long parameters": {
   135  			ObjectMeta:    metav1.ObjectMeta{Name: "foo"},
   136  			Provisioner:   "kubernetes.io/foo",
   137  			Parameters:    longParameters,
   138  			ReclaimPolicy: &deleteReclaimPolicy,
   139  		},
   140  		"invalid reclaimpolicy": {
   141  			ObjectMeta:    metav1.ObjectMeta{Name: "foo"},
   142  			Provisioner:   "kubernetes.io/foo",
   143  			ReclaimPolicy: &recycleReclaimPolicy,
   144  		},
   145  	}
   146  
   147  	// Error cases are not expected to pass validation.
   148  	for testName, storageClass := range errorCases {
   149  		if errs := ValidateStorageClass(&storageClass); len(errs) == 0 {
   150  			t.Errorf("Expected failure for test: %s", testName)
   151  		}
   152  	}
   153  }
   154  
   155  func TestVolumeAttachmentValidation(t *testing.T) {
   156  	volumeName := "pv-name"
   157  	empty := ""
   158  	migrationEnabledSuccessCases := []storage.VolumeAttachment{{
   159  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   160  		Spec: storage.VolumeAttachmentSpec{
   161  			Attacher: "myattacher",
   162  			Source: storage.VolumeAttachmentSource{
   163  				PersistentVolumeName: &volumeName,
   164  			},
   165  			NodeName: "mynode",
   166  		},
   167  	}, {
   168  		ObjectMeta: metav1.ObjectMeta{Name: "foo-with-inlinespec"},
   169  		Spec: storage.VolumeAttachmentSpec{
   170  			Attacher: "myattacher",
   171  			Source: storage.VolumeAttachmentSource{
   172  				InlineVolumeSpec: &inlineSpec,
   173  			},
   174  			NodeName: "mynode",
   175  		},
   176  	}, {
   177  		ObjectMeta: metav1.ObjectMeta{Name: "foo-with-status"},
   178  		Spec: storage.VolumeAttachmentSpec{
   179  			Attacher: "myattacher",
   180  			Source: storage.VolumeAttachmentSource{
   181  				PersistentVolumeName: &volumeName,
   182  			},
   183  			NodeName: "mynode",
   184  		},
   185  		Status: storage.VolumeAttachmentStatus{
   186  			Attached: true,
   187  			AttachmentMetadata: map[string]string{
   188  				"foo": "bar",
   189  			},
   190  			AttachError: &storage.VolumeError{
   191  				Time:    metav1.Time{},
   192  				Message: "hello world",
   193  			},
   194  			DetachError: &storage.VolumeError{
   195  				Time:    metav1.Time{},
   196  				Message: "hello world",
   197  			},
   198  		},
   199  	}, {
   200  		ObjectMeta: metav1.ObjectMeta{Name: "foo-with-inlinespec-and-status"},
   201  		Spec: storage.VolumeAttachmentSpec{
   202  			Attacher: "myattacher",
   203  			Source: storage.VolumeAttachmentSource{
   204  				InlineVolumeSpec: &inlineSpec,
   205  			},
   206  			NodeName: "mynode",
   207  		},
   208  		Status: storage.VolumeAttachmentStatus{
   209  			Attached: true,
   210  			AttachmentMetadata: map[string]string{
   211  				"foo": "bar",
   212  			},
   213  			AttachError: &storage.VolumeError{
   214  				Time:    metav1.Time{},
   215  				Message: "hello world",
   216  			},
   217  			DetachError: &storage.VolumeError{
   218  				Time:    metav1.Time{},
   219  				Message: "hello world",
   220  			},
   221  		},
   222  	}}
   223  
   224  	for _, volumeAttachment := range migrationEnabledSuccessCases {
   225  		if errs := ValidateVolumeAttachment(&volumeAttachment); len(errs) != 0 {
   226  			t.Errorf("expected success: %v %v", volumeAttachment, errs)
   227  		}
   228  	}
   229  	migrationEnabledErrorCases := []storage.VolumeAttachment{{
   230  		// Empty attacher name
   231  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   232  		Spec: storage.VolumeAttachmentSpec{
   233  			Attacher: "",
   234  			NodeName: "mynode",
   235  			Source: storage.VolumeAttachmentSource{
   236  				PersistentVolumeName: &volumeName,
   237  			},
   238  		},
   239  	}, {
   240  		// Empty node name
   241  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   242  		Spec: storage.VolumeAttachmentSpec{
   243  			Attacher: "myattacher",
   244  			NodeName: "",
   245  			Source: storage.VolumeAttachmentSource{
   246  				PersistentVolumeName: &volumeName,
   247  			},
   248  		},
   249  	}, {
   250  		// No volume name
   251  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   252  		Spec: storage.VolumeAttachmentSpec{
   253  			Attacher: "myattacher",
   254  			NodeName: "node",
   255  			Source: storage.VolumeAttachmentSource{
   256  				PersistentVolumeName: nil,
   257  			},
   258  		},
   259  	}, {
   260  		// Empty volume name
   261  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   262  		Spec: storage.VolumeAttachmentSpec{
   263  			Attacher: "myattacher",
   264  			NodeName: "node",
   265  			Source: storage.VolumeAttachmentSource{
   266  				PersistentVolumeName: &empty,
   267  			},
   268  		},
   269  	}, {
   270  		// Too long error message
   271  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   272  		Spec: storage.VolumeAttachmentSpec{
   273  			Attacher: "myattacher",
   274  			NodeName: "node",
   275  			Source: storage.VolumeAttachmentSource{
   276  				PersistentVolumeName: &volumeName,
   277  			},
   278  		},
   279  		Status: storage.VolumeAttachmentStatus{
   280  			Attached: true,
   281  			AttachmentMetadata: map[string]string{
   282  				"foo": "bar",
   283  			},
   284  			AttachError: &storage.VolumeError{
   285  				Time:    metav1.Time{},
   286  				Message: "hello world",
   287  			},
   288  			DetachError: &storage.VolumeError{
   289  				Time:    metav1.Time{},
   290  				Message: strings.Repeat("a", maxVolumeErrorMessageSize+1),
   291  			},
   292  		},
   293  	}, {
   294  		// Too long metadata
   295  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   296  		Spec: storage.VolumeAttachmentSpec{
   297  			Attacher: "myattacher",
   298  			NodeName: "node",
   299  			Source: storage.VolumeAttachmentSource{
   300  				PersistentVolumeName: &volumeName,
   301  			},
   302  		},
   303  		Status: storage.VolumeAttachmentStatus{
   304  			Attached: true,
   305  			AttachmentMetadata: map[string]string{
   306  				"foo": strings.Repeat("a", maxAttachedVolumeMetadataSize),
   307  			},
   308  			AttachError: &storage.VolumeError{
   309  				Time:    metav1.Time{},
   310  				Message: "hello world",
   311  			},
   312  			DetachError: &storage.VolumeError{
   313  				Time:    metav1.Time{},
   314  				Message: "hello world",
   315  			},
   316  		},
   317  	}, {
   318  		// VolumeAttachmentSource with no PersistentVolumeName nor InlineSpec
   319  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   320  		Spec: storage.VolumeAttachmentSpec{
   321  			Attacher: "myattacher",
   322  			NodeName: "node",
   323  			Source:   storage.VolumeAttachmentSource{},
   324  		},
   325  	}, {
   326  		// VolumeAttachmentSource with PersistentVolumeName and InlineSpec
   327  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   328  		Spec: storage.VolumeAttachmentSpec{
   329  			Attacher: "myattacher",
   330  			NodeName: "node",
   331  			Source: storage.VolumeAttachmentSource{
   332  				PersistentVolumeName: &volumeName,
   333  				InlineVolumeSpec:     &inlineSpec,
   334  			},
   335  		},
   336  	}, {
   337  		// VolumeAttachmentSource with InlineSpec without CSI PV Source
   338  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   339  		Spec: storage.VolumeAttachmentSpec{
   340  			Attacher: "myattacher",
   341  			NodeName: "node",
   342  			Source: storage.VolumeAttachmentSource{
   343  				PersistentVolumeName: &volumeName,
   344  				InlineVolumeSpec: &api.PersistentVolumeSpec{
   345  					Capacity: api.ResourceList{
   346  						api.ResourceName(api.ResourceStorage): resource.MustParse("10G"),
   347  					},
   348  					AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
   349  					PersistentVolumeSource: api.PersistentVolumeSource{
   350  						FlexVolume: &api.FlexPersistentVolumeSource{
   351  							Driver: "kubernetes.io/blue",
   352  							FSType: "ext4",
   353  						},
   354  					},
   355  					StorageClassName: "test-storage-class",
   356  				},
   357  			},
   358  		},
   359  	}}
   360  
   361  	for _, volumeAttachment := range migrationEnabledErrorCases {
   362  		if errs := ValidateVolumeAttachment(&volumeAttachment); len(errs) == 0 {
   363  			t.Errorf("expected failure for test: %v", volumeAttachment)
   364  		}
   365  	}
   366  }
   367  
   368  func TestVolumeAttachmentUpdateValidation(t *testing.T) {
   369  	volumeName := "foo"
   370  	newVolumeName := "bar"
   371  
   372  	old := storage.VolumeAttachment{
   373  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   374  		Spec: storage.VolumeAttachmentSpec{
   375  			Attacher: "myattacher",
   376  			Source:   storage.VolumeAttachmentSource{},
   377  			NodeName: "mynode",
   378  		},
   379  	}
   380  
   381  	successCases := []storage.VolumeAttachment{{
   382  		// no change
   383  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   384  		Spec: storage.VolumeAttachmentSpec{
   385  			Attacher: "myattacher",
   386  			Source:   storage.VolumeAttachmentSource{},
   387  			NodeName: "mynode",
   388  		},
   389  	}, {
   390  		// modify status
   391  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   392  		Spec: storage.VolumeAttachmentSpec{
   393  			Attacher: "myattacher",
   394  			Source:   storage.VolumeAttachmentSource{},
   395  			NodeName: "mynode",
   396  		},
   397  		Status: storage.VolumeAttachmentStatus{
   398  			Attached: true,
   399  			AttachmentMetadata: map[string]string{
   400  				"foo": "bar",
   401  			},
   402  			AttachError: &storage.VolumeError{
   403  				Time:    metav1.Time{},
   404  				Message: "hello world",
   405  			},
   406  			DetachError: &storage.VolumeError{
   407  				Time:    metav1.Time{},
   408  				Message: "hello world",
   409  			},
   410  		},
   411  	}}
   412  
   413  	for _, volumeAttachment := range successCases {
   414  		volumeAttachment.Spec.Source = storage.VolumeAttachmentSource{}
   415  		old.Spec.Source = storage.VolumeAttachmentSource{}
   416  		// test scenarios with PersistentVolumeName set
   417  		volumeAttachment.Spec.Source.PersistentVolumeName = &volumeName
   418  		old.Spec.Source.PersistentVolumeName = &volumeName
   419  		if errs := ValidateVolumeAttachmentUpdate(&volumeAttachment, &old); len(errs) != 0 {
   420  			t.Errorf("expected success: %+v", errs)
   421  		}
   422  
   423  		volumeAttachment.Spec.Source = storage.VolumeAttachmentSource{}
   424  		old.Spec.Source = storage.VolumeAttachmentSource{}
   425  		// test scenarios with InlineVolumeSpec set
   426  		volumeAttachment.Spec.Source.InlineVolumeSpec = &inlineSpec
   427  		old.Spec.Source.InlineVolumeSpec = &inlineSpec
   428  		if errs := ValidateVolumeAttachmentUpdate(&volumeAttachment, &old); len(errs) != 0 {
   429  			t.Errorf("expected success: %+v", errs)
   430  		}
   431  	}
   432  
   433  	// reset old's source with volumeName in case it was left with something else by earlier tests
   434  	old.Spec.Source = storage.VolumeAttachmentSource{}
   435  	old.Spec.Source.PersistentVolumeName = &volumeName
   436  
   437  	errorCases := []storage.VolumeAttachment{{
   438  		// change attacher
   439  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   440  		Spec: storage.VolumeAttachmentSpec{
   441  			Attacher: "another-attacher",
   442  			Source: storage.VolumeAttachmentSource{
   443  				PersistentVolumeName: &volumeName,
   444  			},
   445  			NodeName: "mynode",
   446  		},
   447  	}, {
   448  		// change source volume name
   449  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   450  		Spec: storage.VolumeAttachmentSpec{
   451  			Attacher: "myattacher",
   452  			Source: storage.VolumeAttachmentSource{
   453  				PersistentVolumeName: &newVolumeName,
   454  			},
   455  			NodeName: "mynode",
   456  		},
   457  	}, {
   458  		// change node
   459  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   460  		Spec: storage.VolumeAttachmentSpec{
   461  			Attacher: "myattacher",
   462  			Source: storage.VolumeAttachmentSource{
   463  				PersistentVolumeName: &volumeName,
   464  			},
   465  			NodeName: "anothernode",
   466  		},
   467  	}, {
   468  		// change source
   469  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   470  		Spec: storage.VolumeAttachmentSpec{
   471  			Attacher: "myattacher",
   472  			Source: storage.VolumeAttachmentSource{
   473  				InlineVolumeSpec: &inlineSpec,
   474  			},
   475  			NodeName: "mynode",
   476  		},
   477  	}, {
   478  		// add invalid status
   479  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   480  		Spec: storage.VolumeAttachmentSpec{
   481  			Attacher: "myattacher",
   482  			Source: storage.VolumeAttachmentSource{
   483  				PersistentVolumeName: &volumeName,
   484  			},
   485  			NodeName: "mynode",
   486  		},
   487  		Status: storage.VolumeAttachmentStatus{
   488  			Attached: true,
   489  			AttachmentMetadata: map[string]string{
   490  				"foo": "bar",
   491  			},
   492  			AttachError: &storage.VolumeError{
   493  				Time:    metav1.Time{},
   494  				Message: strings.Repeat("a", maxAttachedVolumeMetadataSize),
   495  			},
   496  			DetachError: &storage.VolumeError{
   497  				Time:    metav1.Time{},
   498  				Message: "hello world",
   499  			},
   500  		},
   501  	}}
   502  
   503  	for _, volumeAttachment := range errorCases {
   504  		if errs := ValidateVolumeAttachmentUpdate(&volumeAttachment, &old); len(errs) == 0 {
   505  			t.Errorf("Expected failure for test: %+v", volumeAttachment)
   506  		}
   507  	}
   508  }
   509  
   510  func TestVolumeAttachmentValidationV1(t *testing.T) {
   511  	volumeName := "pv-name"
   512  	invalidVolumeName := "-invalid-@#$%^&*()-"
   513  	successCases := []storage.VolumeAttachment{{
   514  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   515  		Spec: storage.VolumeAttachmentSpec{
   516  			Attacher: "myattacher",
   517  			Source: storage.VolumeAttachmentSource{
   518  				PersistentVolumeName: &volumeName,
   519  			},
   520  			NodeName: "mynode",
   521  		},
   522  	}}
   523  
   524  	for _, volumeAttachment := range successCases {
   525  		if errs := ValidateVolumeAttachmentV1(&volumeAttachment); len(errs) != 0 {
   526  			t.Errorf("expected success: %+v", errs)
   527  		}
   528  	}
   529  
   530  	errorCases := []storage.VolumeAttachment{{
   531  		// Invalid attacher name
   532  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   533  		Spec: storage.VolumeAttachmentSpec{
   534  			Attacher: "invalid-@#$%^&*()",
   535  			NodeName: "mynode",
   536  			Source: storage.VolumeAttachmentSource{
   537  				PersistentVolumeName: &volumeName,
   538  			},
   539  		},
   540  	}, {
   541  		// Invalid PV name
   542  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
   543  		Spec: storage.VolumeAttachmentSpec{
   544  			Attacher: "myattacher",
   545  			NodeName: "mynode",
   546  			Source: storage.VolumeAttachmentSource{
   547  				PersistentVolumeName: &invalidVolumeName,
   548  			},
   549  		},
   550  	}}
   551  
   552  	for _, volumeAttachment := range errorCases {
   553  		if errs := ValidateVolumeAttachmentV1(&volumeAttachment); len(errs) == 0 {
   554  			t.Errorf("Expected failure for test: %+v", volumeAttachment)
   555  		}
   556  	}
   557  }
   558  
   559  func makeClass(mode *storage.VolumeBindingMode, topologies []api.TopologySelectorTerm) *storage.StorageClass {
   560  	return &storage.StorageClass{
   561  		ObjectMeta:        metav1.ObjectMeta{Name: "foo", ResourceVersion: "foo"},
   562  		Provisioner:       "kubernetes.io/foo-provisioner",
   563  		ReclaimPolicy:     &deleteReclaimPolicy,
   564  		VolumeBindingMode: mode,
   565  		AllowedTopologies: topologies,
   566  	}
   567  }
   568  
   569  type bindingTest struct {
   570  	class         *storage.StorageClass
   571  	shouldSucceed bool
   572  }
   573  
   574  func TestValidateVolumeBindingMode(t *testing.T) {
   575  	cases := map[string]bindingTest{
   576  		"no mode": {
   577  			class:         makeClass(nil, nil),
   578  			shouldSucceed: false,
   579  		},
   580  		"immediate mode": {
   581  			class:         makeClass(&immediateMode1, nil),
   582  			shouldSucceed: true,
   583  		},
   584  		"waiting mode": {
   585  			class:         makeClass(&waitingMode, nil),
   586  			shouldSucceed: true,
   587  		},
   588  		"invalid mode": {
   589  			class:         makeClass(&invalidMode, nil),
   590  			shouldSucceed: false,
   591  		},
   592  	}
   593  
   594  	for testName, testCase := range cases {
   595  		errs := ValidateStorageClass(testCase.class)
   596  		if testCase.shouldSucceed && len(errs) != 0 {
   597  			t.Errorf("Expected success for test %q, got %v", testName, errs)
   598  		}
   599  		if !testCase.shouldSucceed && len(errs) == 0 {
   600  			t.Errorf("Expected failure for test %q, got success", testName)
   601  		}
   602  	}
   603  }
   604  
   605  type updateTest struct {
   606  	oldClass      *storage.StorageClass
   607  	newClass      *storage.StorageClass
   608  	shouldSucceed bool
   609  }
   610  
   611  func TestValidateUpdateVolumeBindingMode(t *testing.T) {
   612  	noBinding := makeClass(nil, nil)
   613  	immediateBinding1 := makeClass(&immediateMode1, nil)
   614  	immediateBinding2 := makeClass(&immediateMode2, nil)
   615  	waitBinding := makeClass(&waitingMode, nil)
   616  
   617  	cases := map[string]updateTest{
   618  		"old and new no mode": {
   619  			oldClass:      noBinding,
   620  			newClass:      noBinding,
   621  			shouldSucceed: true,
   622  		},
   623  		"old and new same mode ptr": {
   624  			oldClass:      immediateBinding1,
   625  			newClass:      immediateBinding1,
   626  			shouldSucceed: true,
   627  		},
   628  		"old and new same mode value": {
   629  			oldClass:      immediateBinding1,
   630  			newClass:      immediateBinding2,
   631  			shouldSucceed: true,
   632  		},
   633  		"old no mode, new mode": {
   634  			oldClass:      noBinding,
   635  			newClass:      waitBinding,
   636  			shouldSucceed: false,
   637  		},
   638  		"old mode, new no mode": {
   639  			oldClass:      waitBinding,
   640  			newClass:      noBinding,
   641  			shouldSucceed: false,
   642  		},
   643  		"old and new different modes": {
   644  			oldClass:      waitBinding,
   645  			newClass:      immediateBinding1,
   646  			shouldSucceed: false,
   647  		},
   648  	}
   649  
   650  	for testName, testCase := range cases {
   651  		errs := ValidateStorageClassUpdate(testCase.newClass, testCase.oldClass)
   652  		if testCase.shouldSucceed && len(errs) != 0 {
   653  			t.Errorf("Expected success for %v, got %v", testName, errs)
   654  		}
   655  		if !testCase.shouldSucceed && len(errs) == 0 {
   656  			t.Errorf("Expected failure for %v, got success", testName)
   657  		}
   658  	}
   659  }
   660  
   661  func TestValidateAllowedTopologies(t *testing.T) {
   662  
   663  	validTopology := []api.TopologySelectorTerm{{
   664  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   665  			Key:    "failure-domain.beta.kubernetes.io/zone",
   666  			Values: []string{"zone1"},
   667  		}, {
   668  			Key:    "kubernetes.io/hostname",
   669  			Values: []string{"node1"},
   670  		}},
   671  	}, {
   672  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   673  			Key:    "failure-domain.beta.kubernetes.io/zone",
   674  			Values: []string{"zone2"},
   675  		}, {
   676  			Key:    "kubernetes.io/hostname",
   677  			Values: []string{"node2"},
   678  		}},
   679  	}}
   680  
   681  	topologyInvalidKey := []api.TopologySelectorTerm{{
   682  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   683  			Key:    "/invalidkey",
   684  			Values: []string{"zone1"},
   685  		}},
   686  	}}
   687  
   688  	topologyLackOfValues := []api.TopologySelectorTerm{{
   689  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   690  			Key:    "kubernetes.io/hostname",
   691  			Values: []string{},
   692  		}},
   693  	}}
   694  
   695  	topologyDupValues := []api.TopologySelectorTerm{{
   696  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   697  			Key:    "kubernetes.io/hostname",
   698  			Values: []string{"node1", "node1"},
   699  		}},
   700  	}}
   701  
   702  	topologyMultiValues := []api.TopologySelectorTerm{{
   703  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   704  			Key:    "kubernetes.io/hostname",
   705  			Values: []string{"node1", "node2"},
   706  		}},
   707  	}}
   708  
   709  	topologyEmptyMatchLabelExpressions := []api.TopologySelectorTerm{{
   710  		MatchLabelExpressions: nil,
   711  	}}
   712  
   713  	topologyDupKeys := []api.TopologySelectorTerm{{
   714  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   715  			Key:    "kubernetes.io/hostname",
   716  			Values: []string{"node1"},
   717  		}, {
   718  			Key:    "kubernetes.io/hostname",
   719  			Values: []string{"node2"},
   720  		}},
   721  	}}
   722  
   723  	topologyMultiTerm := []api.TopologySelectorTerm{{
   724  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   725  			Key:    "kubernetes.io/hostname",
   726  			Values: []string{"node1"},
   727  		}},
   728  	}, {
   729  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   730  			Key:    "kubernetes.io/hostname",
   731  			Values: []string{"node2"},
   732  		}},
   733  	}}
   734  
   735  	topologyDupTermsIdentical := []api.TopologySelectorTerm{{
   736  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   737  			Key:    "failure-domain.beta.kubernetes.io/zone",
   738  			Values: []string{"zone1"},
   739  		}, {
   740  			Key:    "kubernetes.io/hostname",
   741  			Values: []string{"node1"},
   742  		}},
   743  	}, {
   744  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   745  			Key:    "failure-domain.beta.kubernetes.io/zone",
   746  			Values: []string{"zone1"},
   747  		}, {
   748  			Key:    "kubernetes.io/hostname",
   749  			Values: []string{"node1"},
   750  		}},
   751  	}}
   752  
   753  	topologyExprsOneSameOneDiff := []api.TopologySelectorTerm{{
   754  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   755  			Key:    "failure-domain.beta.kubernetes.io/zone",
   756  			Values: []string{"zone1"},
   757  		}, {
   758  			Key:    "kubernetes.io/hostname",
   759  			Values: []string{"node1"},
   760  		}},
   761  	}, {
   762  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   763  			Key:    "failure-domain.beta.kubernetes.io/zone",
   764  			Values: []string{"zone1"},
   765  		}, {
   766  			Key:    "kubernetes.io/hostname",
   767  			Values: []string{"node2"},
   768  		}},
   769  	}}
   770  
   771  	topologyValuesOneSameOneDiff := []api.TopologySelectorTerm{{
   772  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   773  			Key:    "kubernetes.io/hostname",
   774  			Values: []string{"node1", "node2"},
   775  		}},
   776  	}, {
   777  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   778  			Key:    "kubernetes.io/hostname",
   779  			Values: []string{"node1", "node3"},
   780  		}},
   781  	}}
   782  
   783  	topologyDupTermsDiffExprOrder := []api.TopologySelectorTerm{{
   784  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   785  			Key:    "kubernetes.io/hostname",
   786  			Values: []string{"node1"},
   787  		}, {
   788  			Key:    "failure-domain.beta.kubernetes.io/zone",
   789  			Values: []string{"zone1"},
   790  		}},
   791  	}, {
   792  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   793  			Key:    "failure-domain.beta.kubernetes.io/zone",
   794  			Values: []string{"zone1"},
   795  		}, {
   796  			Key:    "kubernetes.io/hostname",
   797  			Values: []string{"node1"},
   798  		}},
   799  	}}
   800  
   801  	topologyDupTermsDiffValueOrder := []api.TopologySelectorTerm{{
   802  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   803  			Key:    "failure-domain.beta.kubernetes.io/zone",
   804  			Values: []string{"zone1", "zone2"},
   805  		}},
   806  	}, {
   807  		MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
   808  			Key:    "failure-domain.beta.kubernetes.io/zone",
   809  			Values: []string{"zone2", "zone1"},
   810  		}},
   811  	}}
   812  
   813  	cases := map[string]bindingTest{
   814  		"no topology": {
   815  			class:         makeClass(&waitingMode, nil),
   816  			shouldSucceed: true,
   817  		},
   818  		"valid topology": {
   819  			class:         makeClass(&waitingMode, validTopology),
   820  			shouldSucceed: true,
   821  		},
   822  		"topology invalid key": {
   823  			class:         makeClass(&waitingMode, topologyInvalidKey),
   824  			shouldSucceed: false,
   825  		},
   826  		"topology lack of values": {
   827  			class:         makeClass(&waitingMode, topologyLackOfValues),
   828  			shouldSucceed: false,
   829  		},
   830  		"duplicate TopologySelectorRequirement values": {
   831  			class:         makeClass(&waitingMode, topologyDupValues),
   832  			shouldSucceed: false,
   833  		},
   834  		"multiple TopologySelectorRequirement values": {
   835  			class:         makeClass(&waitingMode, topologyMultiValues),
   836  			shouldSucceed: true,
   837  		},
   838  		"empty MatchLabelExpressions": {
   839  			class:         makeClass(&waitingMode, topologyEmptyMatchLabelExpressions),
   840  			shouldSucceed: false,
   841  		},
   842  		"duplicate MatchLabelExpression keys": {
   843  			class:         makeClass(&waitingMode, topologyDupKeys),
   844  			shouldSucceed: false,
   845  		},
   846  		"duplicate MatchLabelExpression keys but across separate terms": {
   847  			class:         makeClass(&waitingMode, topologyMultiTerm),
   848  			shouldSucceed: true,
   849  		},
   850  		"duplicate AllowedTopologies terms - identical": {
   851  			class:         makeClass(&waitingMode, topologyDupTermsIdentical),
   852  			shouldSucceed: false,
   853  		},
   854  		"two AllowedTopologies terms, with a pair of the same MatchLabelExpressions and a pair of different ones": {
   855  			class:         makeClass(&waitingMode, topologyExprsOneSameOneDiff),
   856  			shouldSucceed: true,
   857  		},
   858  		"two AllowedTopologies terms, with a pair of the same Values and a pair of different ones": {
   859  			class:         makeClass(&waitingMode, topologyValuesOneSameOneDiff),
   860  			shouldSucceed: true,
   861  		},
   862  		"duplicate AllowedTopologies terms - different MatchLabelExpressions order": {
   863  			class:         makeClass(&waitingMode, topologyDupTermsDiffExprOrder),
   864  			shouldSucceed: false,
   865  		},
   866  		"duplicate AllowedTopologies terms - different TopologySelectorRequirement values order": {
   867  			class:         makeClass(&waitingMode, topologyDupTermsDiffValueOrder),
   868  			shouldSucceed: false,
   869  		},
   870  	}
   871  
   872  	for testName, testCase := range cases {
   873  		errs := ValidateStorageClass(testCase.class)
   874  		if testCase.shouldSucceed && len(errs) != 0 {
   875  			t.Errorf("Expected success for test %q, got %v", testName, errs)
   876  		}
   877  		if !testCase.shouldSucceed && len(errs) == 0 {
   878  			t.Errorf("Expected failure for test %q, got success", testName)
   879  		}
   880  	}
   881  }
   882  
   883  func TestCSINodeValidation(t *testing.T) {
   884  	driverName := "driver-name"
   885  	driverName2 := "1io.kubernetes-storage-2-csi-driver3"
   886  	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" // 88 chars
   887  	nodeID := "nodeA"
   888  	longID := longName + longName + "abcdefghijklmnopqrstuvwxyz" // 202 chars
   889  	successCases := []storage.CSINode{{
   890  		// driver name: dot only
   891  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
   892  		Spec: storage.CSINodeSpec{
   893  			Drivers: []storage.CSINodeDriver{{
   894  				Name:         "io.kubernetes.storage.csi.driver",
   895  				NodeID:       nodeID,
   896  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
   897  			}},
   898  		},
   899  	}, {
   900  		// driver name: dash only
   901  		ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
   902  		Spec: storage.CSINodeSpec{
   903  			Drivers: []storage.CSINodeDriver{{
   904  				Name:         "io-kubernetes-storage-csi-driver",
   905  				NodeID:       nodeID,
   906  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
   907  			}},
   908  		},
   909  	}, {
   910  		// driver name: numbers
   911  		ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
   912  		Spec: storage.CSINodeSpec{
   913  			Drivers: []storage.CSINodeDriver{{
   914  				Name:         "1io-kubernetes-storage-2-csi-driver3",
   915  				NodeID:       nodeID,
   916  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
   917  			}},
   918  		},
   919  	}, {
   920  		// driver name: dot, dash
   921  		ObjectMeta: metav1.ObjectMeta{Name: "foo4"},
   922  		Spec: storage.CSINodeSpec{
   923  			Drivers: []storage.CSINodeDriver{{
   924  				Name:         "io.kubernetes.storage-csi-driver",
   925  				NodeID:       nodeID,
   926  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
   927  			}},
   928  		},
   929  	}, {
   930  		// driver name: dot, dash, and numbers
   931  		ObjectMeta: metav1.ObjectMeta{Name: "foo5"},
   932  		Spec: storage.CSINodeSpec{
   933  			Drivers: []storage.CSINodeDriver{{
   934  				Name:         driverName2,
   935  				NodeID:       nodeID,
   936  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
   937  			}},
   938  		},
   939  	}, {
   940  		// Driver name length 1
   941  		ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
   942  		Spec: storage.CSINodeSpec{
   943  			Drivers: []storage.CSINodeDriver{{
   944  				Name:         "a",
   945  				NodeID:       nodeID,
   946  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
   947  			}},
   948  		},
   949  	}, {
   950  		// multiple drivers with different node IDs, topology keys
   951  		ObjectMeta: metav1.ObjectMeta{Name: "foo6"},
   952  		Spec: storage.CSINodeSpec{
   953  			Drivers: []storage.CSINodeDriver{{
   954  				Name:         "driver1",
   955  				NodeID:       "node1",
   956  				TopologyKeys: []string{"key1", "key2"},
   957  			}, {
   958  				Name:         "driverB",
   959  				NodeID:       "nodeA",
   960  				TopologyKeys: []string{"keyA", "keyB"},
   961  			}},
   962  		},
   963  	}, {
   964  		// multiple drivers with same node IDs, topology keys
   965  		ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
   966  		Spec: storage.CSINodeSpec{
   967  			Drivers: []storage.CSINodeDriver{{
   968  				Name:         "driver1",
   969  				NodeID:       "node1",
   970  				TopologyKeys: []string{"key1"},
   971  			}, {
   972  				Name:         "driver2",
   973  				NodeID:       "node1",
   974  				TopologyKeys: []string{"key1"},
   975  			}},
   976  		},
   977  	}, {
   978  		// Volume limits being zero
   979  		ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
   980  		Spec: storage.CSINodeSpec{
   981  			Drivers: []storage.CSINodeDriver{{
   982  				Name:         "io.kubernetes.storage.csi.driver",
   983  				NodeID:       nodeID,
   984  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
   985  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(0)},
   986  			}},
   987  		},
   988  	}, {
   989  		// Volume limits with positive number
   990  		ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
   991  		Spec: storage.CSINodeSpec{
   992  			Drivers: []storage.CSINodeDriver{{
   993  				Name:         "io.kubernetes.storage.csi.driver",
   994  				NodeID:       nodeID,
   995  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
   996  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(1)},
   997  			}},
   998  		},
   999  	}, {
  1000  		// topology key names with -, _, and dot .
  1001  		ObjectMeta: metav1.ObjectMeta{Name: "foo8"},
  1002  		Spec: storage.CSINodeSpec{
  1003  			Drivers: []storage.CSINodeDriver{{
  1004  				Name:         "driver1",
  1005  				NodeID:       "node1",
  1006  				TopologyKeys: []string{"zone_1", "zone.2"},
  1007  			}, {
  1008  				Name:         "driver2",
  1009  				NodeID:       "node1",
  1010  				TopologyKeys: []string{"zone-3", "zone.4"},
  1011  			}},
  1012  		},
  1013  	}, {
  1014  		// topology prefix with - and dot.
  1015  		ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
  1016  		Spec: storage.CSINodeSpec{
  1017  			Drivers: []storage.CSINodeDriver{{
  1018  				Name:         "driver1",
  1019  				NodeID:       "node1",
  1020  				TopologyKeys: []string{"company-com/zone1", "company.com/zone2"},
  1021  			}},
  1022  		},
  1023  	}, {
  1024  		// No topology keys
  1025  		ObjectMeta: metav1.ObjectMeta{Name: "foo10"},
  1026  		Spec: storage.CSINodeSpec{
  1027  			Drivers: []storage.CSINodeDriver{{
  1028  				Name:   driverName,
  1029  				NodeID: nodeID,
  1030  			}},
  1031  		},
  1032  	}}
  1033  
  1034  	for _, csiNode := range successCases {
  1035  		if errs := ValidateCSINode(&csiNode, shorterIDValidationOption); len(errs) != 0 {
  1036  			t.Errorf("expected success: %v", errs)
  1037  		}
  1038  	}
  1039  
  1040  	nodeIDCase := storage.CSINode{
  1041  		// node ID length > 128 but < 192
  1042  		ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
  1043  		Spec: storage.CSINodeSpec{
  1044  			Drivers: []storage.CSINodeDriver{{
  1045  				Name:         driverName,
  1046  				NodeID:       longID,
  1047  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1048  			}},
  1049  		},
  1050  	}
  1051  
  1052  	if errs := ValidateCSINode(&nodeIDCase, longerIDValidateOption); len(errs) != 0 {
  1053  		t.Errorf("expected success: %v", errs)
  1054  	}
  1055  
  1056  	errorCases := []storage.CSINode{{
  1057  		// Empty driver name
  1058  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1059  		Spec: storage.CSINodeSpec{
  1060  			Drivers: []storage.CSINodeDriver{{
  1061  				Name:         "",
  1062  				NodeID:       nodeID,
  1063  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1064  			}},
  1065  		},
  1066  	}, {
  1067  		// Invalid start char in driver name
  1068  		ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
  1069  		Spec: storage.CSINodeSpec{
  1070  			Drivers: []storage.CSINodeDriver{{
  1071  				Name:         "_io.kubernetes.storage.csi.driver",
  1072  				NodeID:       nodeID,
  1073  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1074  			}},
  1075  		},
  1076  	}, {
  1077  		// Invalid end char in driver name
  1078  		ObjectMeta: metav1.ObjectMeta{Name: "foo4"},
  1079  		Spec: storage.CSINodeSpec{
  1080  			Drivers: []storage.CSINodeDriver{{
  1081  				Name:         "io.kubernetes.storage.csi.driver/",
  1082  				NodeID:       nodeID,
  1083  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1084  			}},
  1085  		},
  1086  	}, {
  1087  		// Invalid separators in driver name
  1088  		ObjectMeta: metav1.ObjectMeta{Name: "foo5"},
  1089  		Spec: storage.CSINodeSpec{
  1090  			Drivers: []storage.CSINodeDriver{{
  1091  				Name:         "io/kubernetes/storage/csi~driver",
  1092  				NodeID:       nodeID,
  1093  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1094  			}},
  1095  		},
  1096  	}, {
  1097  		// driver name: underscore only
  1098  		ObjectMeta: metav1.ObjectMeta{Name: "foo6"},
  1099  		Spec: storage.CSINodeSpec{
  1100  			Drivers: []storage.CSINodeDriver{{
  1101  				Name:         "io_kubernetes_storage_csi_driver",
  1102  				NodeID:       nodeID,
  1103  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1104  			}},
  1105  		},
  1106  	}, {
  1107  		// Driver name length > 63
  1108  		ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
  1109  		Spec: storage.CSINodeSpec{
  1110  			Drivers: []storage.CSINodeDriver{{
  1111  				Name:         longName,
  1112  				NodeID:       nodeID,
  1113  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1114  			}},
  1115  		},
  1116  	}, {
  1117  		// No driver name
  1118  		ObjectMeta: metav1.ObjectMeta{Name: "foo8"},
  1119  		Spec: storage.CSINodeSpec{
  1120  			Drivers: []storage.CSINodeDriver{{
  1121  				NodeID:       nodeID,
  1122  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1123  			}},
  1124  		},
  1125  	}, {
  1126  		// Empty individual topology key
  1127  		ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
  1128  		Spec: storage.CSINodeSpec{
  1129  			Drivers: []storage.CSINodeDriver{{
  1130  				Name:         driverName,
  1131  				NodeID:       nodeID,
  1132  				TopologyKeys: []string{"company.com/zone1", ""},
  1133  			}},
  1134  		},
  1135  	}, {
  1136  		// duplicate drivers in driver specs
  1137  		ObjectMeta: metav1.ObjectMeta{Name: "foo10"},
  1138  		Spec: storage.CSINodeSpec{
  1139  			Drivers: []storage.CSINodeDriver{{
  1140  				Name:         "driver1",
  1141  				NodeID:       "node1",
  1142  				TopologyKeys: []string{"key1", "key2"},
  1143  			}, {
  1144  				Name:         "driver1",
  1145  				NodeID:       "nodeX",
  1146  				TopologyKeys: []string{"keyA", "keyB"},
  1147  			}},
  1148  		},
  1149  	}, {
  1150  		// single driver with duplicate topology keys in driver specs
  1151  		ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
  1152  		Spec: storage.CSINodeSpec{
  1153  			Drivers: []storage.CSINodeDriver{{
  1154  				Name:         "driver1",
  1155  				NodeID:       "node1",
  1156  				TopologyKeys: []string{"key1", "key1"},
  1157  			}},
  1158  		},
  1159  	}, {
  1160  		// multiple drivers with one set of duplicate topology keys in driver specs
  1161  		ObjectMeta: metav1.ObjectMeta{Name: "foo12"},
  1162  		Spec: storage.CSINodeSpec{
  1163  			Drivers: []storage.CSINodeDriver{{
  1164  				Name:         "driver1",
  1165  				NodeID:       "node1",
  1166  				TopologyKeys: []string{"key1"},
  1167  			}, {
  1168  				Name:         "driver2",
  1169  				NodeID:       "nodeX",
  1170  				TopologyKeys: []string{"keyA", "keyA"},
  1171  			}},
  1172  		},
  1173  	}, {
  1174  		// Empty NodeID
  1175  		ObjectMeta: metav1.ObjectMeta{Name: "foo13"},
  1176  		Spec: storage.CSINodeSpec{
  1177  			Drivers: []storage.CSINodeDriver{{
  1178  				Name:         driverName,
  1179  				NodeID:       "",
  1180  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1181  			}},
  1182  		},
  1183  	}, {
  1184  		// Volume limits with negative number
  1185  		ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
  1186  		Spec: storage.CSINodeSpec{
  1187  			Drivers: []storage.CSINodeDriver{{
  1188  				Name:         "io.kubernetes.storage.csi.driver",
  1189  				NodeID:       nodeID,
  1190  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1191  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(-1)},
  1192  			}},
  1193  		},
  1194  	}, {
  1195  		// topology prefix should be lower case
  1196  		ObjectMeta: metav1.ObjectMeta{Name: "foo14"},
  1197  		Spec: storage.CSINodeSpec{
  1198  			Drivers: []storage.CSINodeDriver{{
  1199  				Name:         driverName,
  1200  				NodeID:       "node1",
  1201  				TopologyKeys: []string{"Company.Com/zone1", "company.com/zone2"},
  1202  			}},
  1203  		},
  1204  	},
  1205  		nodeIDCase,
  1206  	}
  1207  
  1208  	for _, csiNode := range errorCases {
  1209  		if errs := ValidateCSINode(&csiNode, shorterIDValidationOption); len(errs) == 0 {
  1210  			t.Errorf("Expected failure for test: %v", csiNode)
  1211  		}
  1212  	}
  1213  }
  1214  
  1215  func TestCSINodeUpdateValidation(t *testing.T) {
  1216  	//driverName := "driver-name"
  1217  	//driverName2 := "1io.kubernetes-storage-2-csi-driver3"
  1218  	//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"
  1219  	nodeID := "nodeA"
  1220  
  1221  	old := storage.CSINode{
  1222  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1223  		Spec: storage.CSINodeSpec{
  1224  			Drivers: []storage.CSINodeDriver{{
  1225  				Name:         "io.kubernetes.storage.csi.driver-1",
  1226  				NodeID:       nodeID,
  1227  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1228  			}, {
  1229  				Name:         "io.kubernetes.storage.csi.driver-2",
  1230  				NodeID:       nodeID,
  1231  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1232  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
  1233  			}},
  1234  		},
  1235  	}
  1236  
  1237  	successCases := []storage.CSINode{{
  1238  		// no change
  1239  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1240  		Spec: storage.CSINodeSpec{
  1241  			Drivers: []storage.CSINodeDriver{{
  1242  				Name:         "io.kubernetes.storage.csi.driver-1",
  1243  				NodeID:       nodeID,
  1244  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1245  			}, {
  1246  				Name:         "io.kubernetes.storage.csi.driver-2",
  1247  				NodeID:       nodeID,
  1248  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1249  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
  1250  			}},
  1251  		},
  1252  	}, {
  1253  		// remove a driver
  1254  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1255  		Spec: storage.CSINodeSpec{
  1256  			Drivers: []storage.CSINodeDriver{{
  1257  				Name:         "io.kubernetes.storage.csi.driver-1",
  1258  				NodeID:       nodeID,
  1259  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1260  			}},
  1261  		},
  1262  	}, {
  1263  		// add a driver
  1264  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1265  		Spec: storage.CSINodeSpec{
  1266  			Drivers: []storage.CSINodeDriver{{
  1267  				Name:         "io.kubernetes.storage.csi.driver-1",
  1268  				NodeID:       nodeID,
  1269  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1270  			}, {
  1271  				Name:         "io.kubernetes.storage.csi.driver-2",
  1272  				NodeID:       nodeID,
  1273  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1274  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
  1275  			}, {
  1276  				Name:         "io.kubernetes.storage.csi.driver-3",
  1277  				NodeID:       nodeID,
  1278  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1279  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(30)},
  1280  			}},
  1281  		},
  1282  	}, {
  1283  		// remove a driver and add a driver
  1284  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1285  		Spec: storage.CSINodeSpec{
  1286  			Drivers: []storage.CSINodeDriver{{
  1287  				Name:         "io.kubernetes.storage.csi.driver-1",
  1288  				NodeID:       nodeID,
  1289  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1290  			}, {
  1291  				Name:         "io.kubernetes.storage.csi.new-driver",
  1292  				NodeID:       nodeID,
  1293  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1294  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(30)},
  1295  			}},
  1296  		},
  1297  	}}
  1298  
  1299  	for _, csiNode := range successCases {
  1300  		if errs := ValidateCSINodeUpdate(&csiNode, &old, shorterIDValidationOption); len(errs) != 0 {
  1301  			t.Errorf("expected success: %+v", errs)
  1302  		}
  1303  	}
  1304  
  1305  	errorCases := []storage.CSINode{{
  1306  		// invalid change node id
  1307  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1308  		Spec: storage.CSINodeSpec{
  1309  			Drivers: []storage.CSINodeDriver{{
  1310  				Name:         "io.kubernetes.storage.csi.driver-1",
  1311  				NodeID:       "nodeB",
  1312  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1313  			}, {
  1314  				Name:         "io.kubernetes.storage.csi.driver-2",
  1315  				NodeID:       nodeID,
  1316  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1317  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
  1318  			}},
  1319  		},
  1320  	}, {
  1321  		// invalid change topology keys
  1322  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1323  		Spec: storage.CSINodeSpec{
  1324  			Drivers: []storage.CSINodeDriver{{
  1325  				Name:         "io.kubernetes.storage.csi.driver-1",
  1326  				NodeID:       nodeID,
  1327  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1328  			}, {
  1329  				Name:         "io.kubernetes.storage.csi.driver-2",
  1330  				NodeID:       nodeID,
  1331  				TopologyKeys: []string{"company.com/zone2"},
  1332  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
  1333  			}},
  1334  		},
  1335  	}, {
  1336  		// invalid change trying to set a previously unset allocatable
  1337  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1338  		Spec: storage.CSINodeSpec{
  1339  			Drivers: []storage.CSINodeDriver{{
  1340  				Name:         "io.kubernetes.storage.csi.driver-1",
  1341  				NodeID:       nodeID,
  1342  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1343  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(10)},
  1344  			}, {
  1345  				Name:         "io.kubernetes.storage.csi.driver-2",
  1346  				NodeID:       nodeID,
  1347  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1348  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
  1349  			}},
  1350  		},
  1351  	}, {
  1352  		// invalid change trying to update allocatable with a different volume limit
  1353  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1354  		Spec: storage.CSINodeSpec{
  1355  			Drivers: []storage.CSINodeDriver{{
  1356  				Name:         "io.kubernetes.storage.csi.driver-1",
  1357  				NodeID:       nodeID,
  1358  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1359  			}, {
  1360  				Name:         "io.kubernetes.storage.csi.driver-2",
  1361  				NodeID:       nodeID,
  1362  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1363  				Allocatable:  &storage.VolumeNodeResources{Count: utilpointer.Int32(21)},
  1364  			}},
  1365  		},
  1366  	}, {
  1367  		// invalid change trying to update allocatable with an empty volume limit
  1368  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1369  		Spec: storage.CSINodeSpec{
  1370  			Drivers: []storage.CSINodeDriver{{
  1371  				Name:         "io.kubernetes.storage.csi.driver-1",
  1372  				NodeID:       nodeID,
  1373  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1374  			}, {
  1375  				Name:         "io.kubernetes.storage.csi.driver-2",
  1376  				NodeID:       nodeID,
  1377  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1378  				Allocatable:  &storage.VolumeNodeResources{Count: nil},
  1379  			}},
  1380  		},
  1381  	}, {
  1382  		// invalid change trying to remove allocatable
  1383  		ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
  1384  		Spec: storage.CSINodeSpec{
  1385  			Drivers: []storage.CSINodeDriver{{
  1386  				Name:         "io.kubernetes.storage.csi.driver-1",
  1387  				NodeID:       nodeID,
  1388  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1389  			}, {
  1390  				Name:         "io.kubernetes.storage.csi.driver-2",
  1391  				NodeID:       nodeID,
  1392  				TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
  1393  			}},
  1394  		},
  1395  	}}
  1396  
  1397  	for _, csiNode := range errorCases {
  1398  		if errs := ValidateCSINodeUpdate(&csiNode, &old, shorterIDValidationOption); len(errs) == 0 {
  1399  			t.Errorf("Expected failure for test: %+v", csiNode)
  1400  		}
  1401  	}
  1402  }
  1403  
  1404  func TestCSIDriverValidation(t *testing.T) {
  1405  	// assume this feature is on for this test, detailed enabled/disabled tests in TestCSIDriverValidationSELinuxMountEnabledDisabled
  1406  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, true)()
  1407  
  1408  	driverName := "test-driver"
  1409  	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"
  1410  	invalidName := "-invalid-@#$%^&*()-"
  1411  	attachRequired := true
  1412  	attachNotRequired := false
  1413  	podInfoOnMount := true
  1414  	notPodInfoOnMount := false
  1415  	notRequiresRepublish := false
  1416  	storageCapacity := true
  1417  	notStorageCapacity := false
  1418  	seLinuxMount := true
  1419  	notSELinuxMount := false
  1420  	supportedFSGroupPolicy := storage.FileFSGroupPolicy
  1421  	invalidFSGroupPolicy := storage.FSGroupPolicy("invalid-mode")
  1422  	successCases := []storage.CSIDriver{{
  1423  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1424  		Spec: storage.CSIDriverSpec{
  1425  			AttachRequired:    &attachRequired,
  1426  			PodInfoOnMount:    &podInfoOnMount,
  1427  			RequiresRepublish: &notRequiresRepublish,
  1428  			StorageCapacity:   &storageCapacity,
  1429  			SELinuxMount:      &seLinuxMount,
  1430  		},
  1431  	}, {
  1432  		// driver name: dot only
  1433  		ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi.driver"},
  1434  		Spec: storage.CSIDriverSpec{
  1435  			AttachRequired:    &attachRequired,
  1436  			PodInfoOnMount:    &podInfoOnMount,
  1437  			RequiresRepublish: &notRequiresRepublish,
  1438  			StorageCapacity:   &notStorageCapacity,
  1439  			SELinuxMount:      &seLinuxMount,
  1440  		},
  1441  	}, {
  1442  		// driver name: dash only
  1443  		ObjectMeta: metav1.ObjectMeta{Name: "io-kubernetes-storage-csi-driver"},
  1444  		Spec: storage.CSIDriverSpec{
  1445  			AttachRequired:    &attachRequired,
  1446  			PodInfoOnMount:    &notPodInfoOnMount,
  1447  			RequiresRepublish: &notRequiresRepublish,
  1448  			StorageCapacity:   &storageCapacity,
  1449  			SELinuxMount:      &seLinuxMount,
  1450  		},
  1451  	}, {
  1452  		// driver name: numbers
  1453  		ObjectMeta: metav1.ObjectMeta{Name: "1csi2driver3"},
  1454  		Spec: storage.CSIDriverSpec{
  1455  			AttachRequired:    &attachRequired,
  1456  			PodInfoOnMount:    &podInfoOnMount,
  1457  			RequiresRepublish: &notRequiresRepublish,
  1458  			StorageCapacity:   &storageCapacity,
  1459  			SELinuxMount:      &seLinuxMount,
  1460  		},
  1461  	}, {
  1462  		// driver name: dot and dash
  1463  		ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi-driver"},
  1464  		Spec: storage.CSIDriverSpec{
  1465  			AttachRequired:    &attachRequired,
  1466  			PodInfoOnMount:    &podInfoOnMount,
  1467  			RequiresRepublish: &notRequiresRepublish,
  1468  			StorageCapacity:   &storageCapacity,
  1469  			SELinuxMount:      &seLinuxMount,
  1470  		},
  1471  	}, {
  1472  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1473  		Spec: storage.CSIDriverSpec{
  1474  			AttachRequired:    &attachRequired,
  1475  			PodInfoOnMount:    &notPodInfoOnMount,
  1476  			RequiresRepublish: &notRequiresRepublish,
  1477  			StorageCapacity:   &storageCapacity,
  1478  			SELinuxMount:      &seLinuxMount,
  1479  		},
  1480  	}, {
  1481  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1482  		Spec: storage.CSIDriverSpec{
  1483  			AttachRequired:    &attachRequired,
  1484  			PodInfoOnMount:    &podInfoOnMount,
  1485  			RequiresRepublish: &notRequiresRepublish,
  1486  			StorageCapacity:   &storageCapacity,
  1487  			SELinuxMount:      &seLinuxMount,
  1488  		},
  1489  	}, {
  1490  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1491  		Spec: storage.CSIDriverSpec{
  1492  			AttachRequired:    &attachNotRequired,
  1493  			PodInfoOnMount:    &notPodInfoOnMount,
  1494  			RequiresRepublish: &notRequiresRepublish,
  1495  			StorageCapacity:   &storageCapacity,
  1496  			SELinuxMount:      &seLinuxMount,
  1497  		},
  1498  	}, {
  1499  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1500  		Spec: storage.CSIDriverSpec{
  1501  			AttachRequired:    &attachNotRequired,
  1502  			PodInfoOnMount:    &notPodInfoOnMount,
  1503  			RequiresRepublish: &notRequiresRepublish,
  1504  			StorageCapacity:   &storageCapacity,
  1505  			VolumeLifecycleModes: []storage.VolumeLifecycleMode{
  1506  				storage.VolumeLifecyclePersistent,
  1507  			},
  1508  			SELinuxMount: &seLinuxMount,
  1509  		},
  1510  	}, {
  1511  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1512  		Spec: storage.CSIDriverSpec{
  1513  			AttachRequired:    &attachNotRequired,
  1514  			PodInfoOnMount:    &notPodInfoOnMount,
  1515  			RequiresRepublish: &notRequiresRepublish,
  1516  			StorageCapacity:   &storageCapacity,
  1517  			VolumeLifecycleModes: []storage.VolumeLifecycleMode{
  1518  				storage.VolumeLifecycleEphemeral,
  1519  			},
  1520  			SELinuxMount: &seLinuxMount,
  1521  		},
  1522  	}, {
  1523  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1524  		Spec: storage.CSIDriverSpec{
  1525  			AttachRequired:    &attachNotRequired,
  1526  			PodInfoOnMount:    &notPodInfoOnMount,
  1527  			RequiresRepublish: &notRequiresRepublish,
  1528  			StorageCapacity:   &storageCapacity,
  1529  			VolumeLifecycleModes: []storage.VolumeLifecycleMode{
  1530  				storage.VolumeLifecycleEphemeral,
  1531  				storage.VolumeLifecyclePersistent,
  1532  			},
  1533  			SELinuxMount: &seLinuxMount,
  1534  		},
  1535  	}, {
  1536  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1537  		Spec: storage.CSIDriverSpec{
  1538  			AttachRequired:    &attachNotRequired,
  1539  			PodInfoOnMount:    &notPodInfoOnMount,
  1540  			RequiresRepublish: &notRequiresRepublish,
  1541  			StorageCapacity:   &storageCapacity,
  1542  			VolumeLifecycleModes: []storage.VolumeLifecycleMode{
  1543  				storage.VolumeLifecycleEphemeral,
  1544  				storage.VolumeLifecyclePersistent,
  1545  				storage.VolumeLifecycleEphemeral,
  1546  			},
  1547  			SELinuxMount: &seLinuxMount,
  1548  		},
  1549  	}, {
  1550  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1551  		Spec: storage.CSIDriverSpec{
  1552  			AttachRequired:    &attachNotRequired,
  1553  			PodInfoOnMount:    &notPodInfoOnMount,
  1554  			RequiresRepublish: &notRequiresRepublish,
  1555  			StorageCapacity:   &storageCapacity,
  1556  			FSGroupPolicy:     &supportedFSGroupPolicy,
  1557  			SELinuxMount:      &seLinuxMount,
  1558  		},
  1559  	}, {
  1560  		// SELinuxMount: false
  1561  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1562  		Spec: storage.CSIDriverSpec{
  1563  			AttachRequired:    &attachNotRequired,
  1564  			PodInfoOnMount:    &notPodInfoOnMount,
  1565  			RequiresRepublish: &notRequiresRepublish,
  1566  			StorageCapacity:   &storageCapacity,
  1567  			SELinuxMount:      &notSELinuxMount,
  1568  		},
  1569  	}}
  1570  
  1571  	for _, csiDriver := range successCases {
  1572  		if errs := ValidateCSIDriver(&csiDriver); len(errs) != 0 {
  1573  			t.Errorf("expected success: %v", errs)
  1574  		}
  1575  	}
  1576  	errorCases := []storage.CSIDriver{{
  1577  		ObjectMeta: metav1.ObjectMeta{Name: invalidName},
  1578  		Spec: storage.CSIDriverSpec{
  1579  			AttachRequired:  &attachRequired,
  1580  			PodInfoOnMount:  &podInfoOnMount,
  1581  			StorageCapacity: &storageCapacity,
  1582  			SELinuxMount:    &seLinuxMount,
  1583  		},
  1584  	}, {
  1585  		ObjectMeta: metav1.ObjectMeta{Name: longName},
  1586  		Spec: storage.CSIDriverSpec{
  1587  			AttachRequired:  &attachNotRequired,
  1588  			PodInfoOnMount:  &notPodInfoOnMount,
  1589  			StorageCapacity: &storageCapacity,
  1590  			SELinuxMount:    &seLinuxMount,
  1591  		},
  1592  	}, {
  1593  		// AttachRequired not set
  1594  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1595  		Spec: storage.CSIDriverSpec{
  1596  			AttachRequired:  nil,
  1597  			PodInfoOnMount:  &podInfoOnMount,
  1598  			StorageCapacity: &storageCapacity,
  1599  			SELinuxMount:    &seLinuxMount,
  1600  		},
  1601  	}, {
  1602  		// PodInfoOnMount not set
  1603  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1604  		Spec: storage.CSIDriverSpec{
  1605  			AttachRequired:  &attachNotRequired,
  1606  			PodInfoOnMount:  nil,
  1607  			StorageCapacity: &storageCapacity,
  1608  			SELinuxMount:    &seLinuxMount,
  1609  		},
  1610  	}, {
  1611  		// StorageCapacity not set
  1612  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1613  		Spec: storage.CSIDriverSpec{
  1614  			AttachRequired:  &attachNotRequired,
  1615  			PodInfoOnMount:  &podInfoOnMount,
  1616  			StorageCapacity: nil,
  1617  			SELinuxMount:    &seLinuxMount,
  1618  		},
  1619  	}, {
  1620  		// invalid mode
  1621  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1622  		Spec: storage.CSIDriverSpec{
  1623  			AttachRequired:  &attachNotRequired,
  1624  			PodInfoOnMount:  &notPodInfoOnMount,
  1625  			StorageCapacity: &storageCapacity,
  1626  			VolumeLifecycleModes: []storage.VolumeLifecycleMode{
  1627  				"no-such-mode",
  1628  			},
  1629  			SELinuxMount: &seLinuxMount,
  1630  		},
  1631  	}, {
  1632  		// invalid fsGroupPolicy
  1633  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1634  		Spec: storage.CSIDriverSpec{
  1635  			AttachRequired:  &attachNotRequired,
  1636  			PodInfoOnMount:  &notPodInfoOnMount,
  1637  			FSGroupPolicy:   &invalidFSGroupPolicy,
  1638  			StorageCapacity: &storageCapacity,
  1639  			SELinuxMount:    &seLinuxMount,
  1640  		},
  1641  	}, {
  1642  		// no SELinuxMount
  1643  		ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1644  		Spec: storage.CSIDriverSpec{
  1645  			AttachRequired:  &attachNotRequired,
  1646  			PodInfoOnMount:  &notPodInfoOnMount,
  1647  			StorageCapacity: &storageCapacity,
  1648  		},
  1649  	}}
  1650  
  1651  	for _, csiDriver := range errorCases {
  1652  		if errs := ValidateCSIDriver(&csiDriver); len(errs) == 0 {
  1653  			t.Errorf("Expected failure for test: %v", csiDriver)
  1654  		}
  1655  	}
  1656  }
  1657  
  1658  func TestCSIDriverValidationUpdate(t *testing.T) {
  1659  	// assume this feature is on for this test, detailed enabled/disabled tests in TestCSIDriverValidationSELinuxMountEnabledDisabled
  1660  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, true)()
  1661  
  1662  	driverName := "test-driver"
  1663  	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"
  1664  	invalidName := "-invalid-@#$%^&*()-"
  1665  	attachRequired := true
  1666  	attachNotRequired := false
  1667  	podInfoOnMount := true
  1668  	storageCapacity := true
  1669  	notPodInfoOnMount := false
  1670  	gcp := "gcp"
  1671  	requiresRepublish := true
  1672  	notRequiresRepublish := false
  1673  	notStorageCapacity := false
  1674  	seLinuxMount := true
  1675  	notSELinuxMount := false
  1676  	resourceVersion := "1"
  1677  	old := storage.CSIDriver{
  1678  		ObjectMeta: metav1.ObjectMeta{Name: driverName, ResourceVersion: resourceVersion},
  1679  		Spec: storage.CSIDriverSpec{
  1680  			AttachRequired:    &attachNotRequired,
  1681  			PodInfoOnMount:    &notPodInfoOnMount,
  1682  			RequiresRepublish: &notRequiresRepublish,
  1683  			VolumeLifecycleModes: []storage.VolumeLifecycleMode{
  1684  				storage.VolumeLifecycleEphemeral,
  1685  				storage.VolumeLifecyclePersistent,
  1686  			},
  1687  			StorageCapacity: &storageCapacity,
  1688  			SELinuxMount:    &seLinuxMount,
  1689  		},
  1690  	}
  1691  
  1692  	successCases := []struct {
  1693  		name   string
  1694  		modify func(new *storage.CSIDriver)
  1695  	}{{
  1696  		name:   "no change",
  1697  		modify: func(new *storage.CSIDriver) {},
  1698  	}, {
  1699  		name: "change TokenRequests",
  1700  		modify: func(new *storage.CSIDriver) {
  1701  			new.Spec.TokenRequests = []storage.TokenRequest{{Audience: gcp}}
  1702  		},
  1703  	}, {
  1704  		name: "change RequiresRepublish",
  1705  		modify: func(new *storage.CSIDriver) {
  1706  			new.Spec.RequiresRepublish = &requiresRepublish
  1707  		},
  1708  	}, {
  1709  		name: "StorageCapacity changed",
  1710  		modify: func(new *storage.CSIDriver) {
  1711  			new.Spec.StorageCapacity = &notStorageCapacity
  1712  		},
  1713  	}, {
  1714  		name: "SELinuxMount changed",
  1715  		modify: func(new *storage.CSIDriver) {
  1716  			new.Spec.SELinuxMount = &notSELinuxMount
  1717  		},
  1718  	}, {
  1719  		name: "change PodInfoOnMount",
  1720  		modify: func(new *storage.CSIDriver) {
  1721  			new.Spec.PodInfoOnMount = &podInfoOnMount
  1722  		},
  1723  	}, {
  1724  		name: "change FSGroupPolicy",
  1725  		modify: func(new *storage.CSIDriver) {
  1726  			fileFSGroupPolicy := storage.FileFSGroupPolicy
  1727  			new.Spec.FSGroupPolicy = &fileFSGroupPolicy
  1728  		},
  1729  	}}
  1730  	for _, test := range successCases {
  1731  		t.Run(test.name, func(t *testing.T) {
  1732  			new := old.DeepCopy()
  1733  			test.modify(new)
  1734  			if errs := ValidateCSIDriverUpdate(new, &old); len(errs) != 0 {
  1735  				t.Errorf("Expected success for %+v: %v", new, errs)
  1736  			}
  1737  		})
  1738  	}
  1739  
  1740  	// Each test case changes exactly one field. None of that is valid.
  1741  	errorCases := []struct {
  1742  		name   string
  1743  		modify func(new *storage.CSIDriver)
  1744  	}{{
  1745  		name: "invalid name",
  1746  		modify: func(new *storage.CSIDriver) {
  1747  			new.Name = invalidName
  1748  		},
  1749  	}, {
  1750  		name: "long name",
  1751  		modify: func(new *storage.CSIDriver) {
  1752  			new.Name = longName
  1753  		},
  1754  	}, {
  1755  		name: "AttachRequired not set",
  1756  		modify: func(new *storage.CSIDriver) {
  1757  			new.Spec.AttachRequired = nil
  1758  		},
  1759  	}, {
  1760  		name: "AttachRequired changed",
  1761  		modify: func(new *storage.CSIDriver) {
  1762  			new.Spec.AttachRequired = &attachRequired
  1763  		},
  1764  	}, {
  1765  		name: "PodInfoOnMount not set",
  1766  		modify: func(new *storage.CSIDriver) {
  1767  			new.Spec.PodInfoOnMount = nil
  1768  		},
  1769  	}, {
  1770  		name: "invalid volume lifecycle mode",
  1771  		modify: func(new *storage.CSIDriver) {
  1772  			new.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
  1773  				"no-such-mode",
  1774  			}
  1775  		},
  1776  	}, {
  1777  		name: "volume lifecycle modes not set",
  1778  		modify: func(new *storage.CSIDriver) {
  1779  			new.Spec.VolumeLifecycleModes = nil
  1780  		},
  1781  	}, {
  1782  		name: "VolumeLifecyclePersistent removed",
  1783  		modify: func(new *storage.CSIDriver) {
  1784  			new.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
  1785  				storage.VolumeLifecycleEphemeral,
  1786  			}
  1787  		},
  1788  	}, {
  1789  		name: "VolumeLifecycleEphemeral removed",
  1790  		modify: func(new *storage.CSIDriver) {
  1791  			new.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
  1792  				storage.VolumeLifecyclePersistent,
  1793  			}
  1794  		},
  1795  	}, {
  1796  		name: "FSGroupPolicy invalidated",
  1797  		modify: func(new *storage.CSIDriver) {
  1798  			invalidFSGroupPolicy := storage.FSGroupPolicy("invalid")
  1799  			new.Spec.FSGroupPolicy = &invalidFSGroupPolicy
  1800  		},
  1801  	}, {
  1802  		name: "TokenRequests invalidated",
  1803  		modify: func(new *storage.CSIDriver) {
  1804  			new.Spec.TokenRequests = []storage.TokenRequest{{Audience: gcp}, {Audience: gcp}}
  1805  		},
  1806  	}, {
  1807  		name: "invalid nil StorageCapacity",
  1808  		modify: func(new *storage.CSIDriver) {
  1809  			new.Spec.StorageCapacity = nil
  1810  		},
  1811  	}, {
  1812  		name: "SELinuxMount not set",
  1813  		modify: func(new *storage.CSIDriver) {
  1814  			new.Spec.SELinuxMount = nil
  1815  		},
  1816  	}}
  1817  
  1818  	for _, test := range errorCases {
  1819  		t.Run(test.name, func(t *testing.T) {
  1820  			new := old.DeepCopy()
  1821  			test.modify(new)
  1822  			if errs := ValidateCSIDriverUpdate(new, &old); len(errs) == 0 {
  1823  				t.Errorf("Expected failure for test: %+v", new)
  1824  			}
  1825  		})
  1826  	}
  1827  }
  1828  
  1829  func TestCSIDriverStorageCapacityEnablement(t *testing.T) {
  1830  	run := func(t *testing.T, withField bool) {
  1831  		driverName := "test-driver"
  1832  		attachRequired := true
  1833  		podInfoOnMount := true
  1834  		requiresRepublish := true
  1835  		storageCapacity := true
  1836  		seLinuxMount := false
  1837  		csiDriver := storage.CSIDriver{
  1838  			ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1839  			Spec: storage.CSIDriverSpec{
  1840  				AttachRequired:    &attachRequired,
  1841  				PodInfoOnMount:    &podInfoOnMount,
  1842  				RequiresRepublish: &requiresRepublish,
  1843  				SELinuxMount:      &seLinuxMount,
  1844  			},
  1845  		}
  1846  		if withField {
  1847  			csiDriver.Spec.StorageCapacity = &storageCapacity
  1848  		}
  1849  		errs := ValidateCSIDriver(&csiDriver)
  1850  		success := withField
  1851  		if success && len(errs) != 0 {
  1852  			t.Errorf("expected success, got: %v", errs)
  1853  		}
  1854  		if !success && len(errs) == 0 {
  1855  			t.Errorf("expected error, got success")
  1856  		}
  1857  	}
  1858  
  1859  	yesNo := []bool{true, false}
  1860  	for _, withField := range yesNo {
  1861  		t.Run(fmt.Sprintf("with-field=%v", withField), func(t *testing.T) {
  1862  			run(t, withField)
  1863  		})
  1864  	}
  1865  }
  1866  
  1867  func TestValidateCSIStorageCapacity(t *testing.T) {
  1868  	storageClassName := "test-sc"
  1869  	invalidName := "-invalid-@#$%^&*()-"
  1870  
  1871  	goodCapacity := storage.CSIStorageCapacity{
  1872  		ObjectMeta: metav1.ObjectMeta{
  1873  			Name:      "csc-329803da-fdd2-42e4-af6f-7b07e7ccc305",
  1874  			Namespace: metav1.NamespaceDefault,
  1875  		},
  1876  		StorageClassName: storageClassName,
  1877  	}
  1878  	goodTopology := metav1.LabelSelector{
  1879  		MatchLabels: map[string]string{"foo": "bar"},
  1880  	}
  1881  
  1882  	scenarios := map[string]struct {
  1883  		isExpectedFailure bool
  1884  		capacity          *storage.CSIStorageCapacity
  1885  	}{
  1886  		"good-capacity": {
  1887  			capacity: &goodCapacity,
  1888  		},
  1889  		"missing-storage-class-name": {
  1890  			isExpectedFailure: true,
  1891  			capacity: func() *storage.CSIStorageCapacity {
  1892  				capacity := goodCapacity
  1893  				capacity.StorageClassName = ""
  1894  				return &capacity
  1895  			}(),
  1896  		},
  1897  		"bad-storage-class-name": {
  1898  			isExpectedFailure: true,
  1899  			capacity: func() *storage.CSIStorageCapacity {
  1900  				capacity := goodCapacity
  1901  				capacity.StorageClassName = invalidName
  1902  				return &capacity
  1903  			}(),
  1904  		},
  1905  		"good-capacity-value": {
  1906  			capacity: func() *storage.CSIStorageCapacity {
  1907  				capacity := goodCapacity
  1908  				capacity.Capacity = resource.NewQuantity(1, resource.BinarySI)
  1909  				return &capacity
  1910  			}(),
  1911  		},
  1912  		"bad-capacity-value": {
  1913  			isExpectedFailure: true,
  1914  			capacity: func() *storage.CSIStorageCapacity {
  1915  				capacity := goodCapacity
  1916  				capacity.Capacity = resource.NewQuantity(-11, resource.BinarySI)
  1917  				return &capacity
  1918  			}(),
  1919  		},
  1920  		"good-topology": {
  1921  			capacity: func() *storage.CSIStorageCapacity {
  1922  				capacity := goodCapacity
  1923  				capacity.NodeTopology = &goodTopology
  1924  				return &capacity
  1925  			}(),
  1926  		},
  1927  		"empty-topology": {
  1928  			capacity: func() *storage.CSIStorageCapacity {
  1929  				capacity := goodCapacity
  1930  				capacity.NodeTopology = &metav1.LabelSelector{}
  1931  				return &capacity
  1932  			}(),
  1933  		},
  1934  		"bad-topology-fields": {
  1935  			isExpectedFailure: true,
  1936  			capacity: func() *storage.CSIStorageCapacity {
  1937  				capacity := goodCapacity
  1938  				capacity.NodeTopology = &metav1.LabelSelector{
  1939  					MatchExpressions: []metav1.LabelSelectorRequirement{{
  1940  						Key:      "foo",
  1941  						Operator: metav1.LabelSelectorOperator("no-such-operator"),
  1942  						Values: []string{
  1943  							"bar",
  1944  						},
  1945  					}},
  1946  				}
  1947  				return &capacity
  1948  			}(),
  1949  		},
  1950  	}
  1951  
  1952  	for name, scenario := range scenarios {
  1953  		t.Run(name, func(t *testing.T) {
  1954  			errs := ValidateCSIStorageCapacity(scenario.capacity, CSIStorageCapacityValidateOptions{false})
  1955  			if len(errs) == 0 && scenario.isExpectedFailure {
  1956  				t.Errorf("Unexpected success")
  1957  			}
  1958  			if len(errs) > 0 && !scenario.isExpectedFailure {
  1959  				t.Errorf("Unexpected failure: %+v", errs)
  1960  			}
  1961  		})
  1962  	}
  1963  
  1964  }
  1965  
  1966  func TestCSIServiceAccountToken(t *testing.T) {
  1967  	driverName := "test-driver"
  1968  	gcp := "gcp"
  1969  	aws := "aws"
  1970  	notRequiresRepublish := false
  1971  	tests := []struct {
  1972  		desc      string
  1973  		csiDriver *storage.CSIDriver
  1974  		wantErr   bool
  1975  	}{{
  1976  		desc: "invalid - TokenRequests has tokens with the same audience",
  1977  		csiDriver: &storage.CSIDriver{
  1978  			ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1979  			Spec: storage.CSIDriverSpec{
  1980  				TokenRequests:     []storage.TokenRequest{{Audience: gcp}, {Audience: gcp}},
  1981  				RequiresRepublish: &notRequiresRepublish,
  1982  			},
  1983  		},
  1984  		wantErr: true,
  1985  	}, {
  1986  		desc: "invalid - TokenRequests has tokens with ExpirationSeconds less than 10min",
  1987  		csiDriver: &storage.CSIDriver{
  1988  			ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1989  			Spec: storage.CSIDriverSpec{
  1990  				TokenRequests:     []storage.TokenRequest{{Audience: gcp, ExpirationSeconds: utilpointer.Int64(10)}},
  1991  				RequiresRepublish: &notRequiresRepublish,
  1992  			},
  1993  		},
  1994  		wantErr: true,
  1995  	}, {
  1996  		desc: "invalid - TokenRequests has tokens with ExpirationSeconds longer than 1<<32 min",
  1997  		csiDriver: &storage.CSIDriver{
  1998  			ObjectMeta: metav1.ObjectMeta{Name: driverName},
  1999  			Spec: storage.CSIDriverSpec{
  2000  				TokenRequests:     []storage.TokenRequest{{Audience: gcp, ExpirationSeconds: utilpointer.Int64(1<<32 + 1)}},
  2001  				RequiresRepublish: &notRequiresRepublish,
  2002  			},
  2003  		},
  2004  		wantErr: true,
  2005  	}, {
  2006  		desc: "valid - TokenRequests has at most one token with empty string audience",
  2007  		csiDriver: &storage.CSIDriver{
  2008  			ObjectMeta: metav1.ObjectMeta{Name: driverName},
  2009  			Spec: storage.CSIDriverSpec{
  2010  				TokenRequests:     []storage.TokenRequest{{Audience: ""}},
  2011  				RequiresRepublish: &notRequiresRepublish,
  2012  			},
  2013  		},
  2014  	}, {
  2015  		desc: "valid - TokenRequests has tokens with different audience",
  2016  		csiDriver: &storage.CSIDriver{
  2017  			ObjectMeta: metav1.ObjectMeta{Name: driverName},
  2018  			Spec: storage.CSIDriverSpec{
  2019  				TokenRequests:     []storage.TokenRequest{{}, {Audience: gcp}, {Audience: aws}},
  2020  				RequiresRepublish: &notRequiresRepublish,
  2021  			},
  2022  		},
  2023  	}}
  2024  
  2025  	for _, test := range tests {
  2026  		test.csiDriver.Spec.AttachRequired = new(bool)
  2027  		test.csiDriver.Spec.PodInfoOnMount = new(bool)
  2028  		test.csiDriver.Spec.StorageCapacity = new(bool)
  2029  		test.csiDriver.Spec.SELinuxMount = new(bool)
  2030  		if errs := ValidateCSIDriver(test.csiDriver); test.wantErr != (len(errs) != 0) {
  2031  			t.Errorf("ValidateCSIDriver = %v, want err: %v", errs, test.wantErr)
  2032  		}
  2033  	}
  2034  }
  2035  
  2036  func TestCSIDriverValidationSELinuxMountEnabledDisabled(t *testing.T) {
  2037  	tests := []struct {
  2038  		name              string
  2039  		featureEnabled    bool
  2040  		seLinuxMountValue *bool
  2041  		expectError       bool
  2042  	}{{
  2043  		name:              "feature enabled, nil value",
  2044  		featureEnabled:    true,
  2045  		seLinuxMountValue: nil,
  2046  		expectError:       true,
  2047  	}, {
  2048  		name:              "feature enabled, non-nil value",
  2049  		featureEnabled:    true,
  2050  		seLinuxMountValue: utilpointer.Bool(true),
  2051  		expectError:       false,
  2052  	}, {
  2053  		name:              "feature disabled, nil value",
  2054  		featureEnabled:    false,
  2055  		seLinuxMountValue: nil,
  2056  		expectError:       false,
  2057  	}, {
  2058  		name:              "feature disabled, non-nil value",
  2059  		featureEnabled:    false,
  2060  		seLinuxMountValue: utilpointer.Bool(true),
  2061  		expectError:       false,
  2062  	}}
  2063  	for _, test := range tests {
  2064  		t.Run(test.name, func(t *testing.T) {
  2065  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, test.featureEnabled)()
  2066  			csiDriver := &storage.CSIDriver{
  2067  				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  2068  				Spec: storage.CSIDriverSpec{
  2069  					AttachRequired:    utilpointer.Bool(true),
  2070  					PodInfoOnMount:    utilpointer.Bool(true),
  2071  					RequiresRepublish: utilpointer.Bool(true),
  2072  					StorageCapacity:   utilpointer.Bool(true),
  2073  					SELinuxMount:      test.seLinuxMountValue,
  2074  				},
  2075  			}
  2076  			err := ValidateCSIDriver(csiDriver)
  2077  			if test.expectError && err == nil {
  2078  				t.Error("Expected validation error, got nil")
  2079  			}
  2080  			if !test.expectError && err != nil {
  2081  				t.Errorf("Validation returned error: %s", err)
  2082  			}
  2083  		})
  2084  	}
  2085  
  2086  	updateTests := []struct {
  2087  		name           string
  2088  		featureEnabled bool
  2089  		oldValue       *bool
  2090  		newValue       *bool
  2091  		expectError    bool
  2092  	}{{
  2093  		name:           "feature enabled, nil->nil",
  2094  		featureEnabled: true,
  2095  		oldValue:       nil,
  2096  		newValue:       nil,
  2097  		expectError:    true, // populated by defaulting and required when feature is enabled
  2098  	}, {
  2099  		name:           "feature enabled, nil->set",
  2100  		featureEnabled: true,
  2101  		oldValue:       nil,
  2102  		newValue:       utilpointer.Bool(true),
  2103  		expectError:    false,
  2104  	}, {
  2105  		name:           "feature enabled, set->set",
  2106  		featureEnabled: true,
  2107  		oldValue:       utilpointer.Bool(true),
  2108  		newValue:       utilpointer.Bool(true),
  2109  		expectError:    false,
  2110  	}, {
  2111  		name:           "feature enabled, set->nil",
  2112  		featureEnabled: true,
  2113  		oldValue:       utilpointer.Bool(true),
  2114  		newValue:       nil,
  2115  		expectError:    true, // populated by defaulting and required when feature is enabled
  2116  	}, {
  2117  		name:           "feature disabled, nil->nil",
  2118  		featureEnabled: false,
  2119  		oldValue:       nil,
  2120  		newValue:       nil,
  2121  		expectError:    false,
  2122  	}, {
  2123  		name:           "feature disabled, nil->set",
  2124  		featureEnabled: false,
  2125  		oldValue:       nil,
  2126  		newValue:       utilpointer.Bool(true),
  2127  		expectError:    false,
  2128  	}, {
  2129  		name:           "feature disabled, set->set",
  2130  		featureEnabled: false,
  2131  		oldValue:       utilpointer.Bool(true),
  2132  		newValue:       utilpointer.Bool(true),
  2133  		expectError:    false,
  2134  	}, {
  2135  		name:           "feature disabled, set->nil",
  2136  		featureEnabled: false,
  2137  		oldValue:       utilpointer.Bool(true),
  2138  		newValue:       nil,
  2139  		expectError:    false,
  2140  	}}
  2141  	for _, test := range updateTests {
  2142  		t.Run(test.name, func(t *testing.T) {
  2143  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, test.featureEnabled)()
  2144  			oldCSIDriver := &storage.CSIDriver{
  2145  				ObjectMeta: metav1.ObjectMeta{Name: "foo", ResourceVersion: "1"},
  2146  				Spec: storage.CSIDriverSpec{
  2147  					AttachRequired:    utilpointer.Bool(true),
  2148  					PodInfoOnMount:    utilpointer.Bool(true),
  2149  					RequiresRepublish: utilpointer.Bool(true),
  2150  					StorageCapacity:   utilpointer.Bool(true),
  2151  					SELinuxMount:      test.oldValue,
  2152  				},
  2153  			}
  2154  			newCSIDriver := oldCSIDriver.DeepCopy()
  2155  			newCSIDriver.Spec.SELinuxMount = test.newValue
  2156  			err := ValidateCSIDriverUpdate(newCSIDriver, oldCSIDriver)
  2157  			if test.expectError && err == nil {
  2158  				t.Error("Expected validation error, got nil")
  2159  			}
  2160  			if !test.expectError && err != nil {
  2161  				t.Errorf("Validation returned error: %s", err)
  2162  			}
  2163  		})
  2164  	}
  2165  }
  2166  
  2167  func TestValidateVolumeAttributesClass(t *testing.T) {
  2168  	successCases := []storage.VolumeAttributesClass{
  2169  		{
  2170  			// driverName without a slash
  2171  			ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  2172  			DriverName: "foo",
  2173  			Parameters: map[string]string{
  2174  				"foo-parameter": "free-form-string",
  2175  			},
  2176  		},
  2177  		{
  2178  			// some parameters
  2179  			ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  2180  			DriverName: "kubernetes.io/foo",
  2181  			Parameters: map[string]string{
  2182  				"kubernetes.io/foo-parameter": "free/form/string",
  2183  				"foo-parameter":               "free-form-string",
  2184  				"foo-parameter2":              "{\"embedded\": \"json\", \"with\": {\"structures\":\"inside\"}}",
  2185  				"foo-parameter3":              "",
  2186  			},
  2187  		}}
  2188  
  2189  	// Success cases are expected to pass validation.
  2190  	for testName, v := range successCases {
  2191  		if errs := ValidateVolumeAttributesClass(&v); len(errs) != 0 {
  2192  			t.Errorf("Expected success for %d, got %v", testName, errs)
  2193  		}
  2194  	}
  2195  
  2196  	// generate a map longer than maxParameterSize
  2197  	longParameters := make(map[string]string)
  2198  	totalSize := 0
  2199  	for totalSize < maxProvisionerParameterSize {
  2200  		k := fmt.Sprintf("param/%d", totalSize)
  2201  		v := fmt.Sprintf("value-%d", totalSize)
  2202  		longParameters[k] = v
  2203  		totalSize = totalSize + len(k) + len(v)
  2204  	}
  2205  
  2206  	errorCases := map[string]storage.VolumeAttributesClass{
  2207  		"namespace is present": {
  2208  			ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
  2209  			DriverName: "kubernetes.io/foo",
  2210  			Parameters: map[string]string{
  2211  				"foo-parameter": "free-form-string",
  2212  			},
  2213  		},
  2214  		"invalid driverName": {
  2215  			ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  2216  			DriverName: "kubernetes.io/invalid/foo",
  2217  			Parameters: map[string]string{
  2218  				"foo-parameter": "free-form-string",
  2219  			},
  2220  		},
  2221  		"invalid driverName with invalid chars": {
  2222  			ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  2223  			DriverName: "^/ ",
  2224  			Parameters: map[string]string{
  2225  				"foo-parameter": "free-form-string",
  2226  			},
  2227  		},
  2228  		"empty parameters": {
  2229  			ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  2230  			DriverName: "kubernetes.io/foo",
  2231  			Parameters: map[string]string{},
  2232  		},
  2233  		"nil parameters": {
  2234  			ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  2235  			DriverName: "kubernetes.io/foo",
  2236  		},
  2237  		"invalid empty parameter name": {
  2238  			ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  2239  			DriverName: "kubernetes.io/foo",
  2240  			Parameters: map[string]string{
  2241  				"": "value",
  2242  			},
  2243  		},
  2244  		"driverName: Required value": {
  2245  			ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  2246  			DriverName: "",
  2247  			Parameters: map[string]string{
  2248  				"foo-parameter": "free-form-string",
  2249  			},
  2250  		},
  2251  		"driverName: whitespace": {
  2252  			ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  2253  			DriverName: " ",
  2254  			Parameters: map[string]string{
  2255  				"foo-parameter": "free-form-string",
  2256  			},
  2257  		},
  2258  		"too long parameters": {
  2259  			ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  2260  			DriverName: "kubernetes.io/foo",
  2261  			Parameters: longParameters,
  2262  		},
  2263  	}
  2264  
  2265  	// Error cases are not expected to pass validation.
  2266  	for testName, v := range errorCases {
  2267  		if errs := ValidateVolumeAttributesClass(&v); len(errs) == 0 {
  2268  			t.Errorf("Expected failure for test: %s", testName)
  2269  		}
  2270  	}
  2271  }
  2272  
  2273  func TestValidateVolumeAttributesClassUpdate(t *testing.T) {
  2274  	cases := map[string]struct {
  2275  		oldClass      *storage.VolumeAttributesClass
  2276  		newClass      *storage.VolumeAttributesClass
  2277  		shouldSucceed bool
  2278  	}{
  2279  		"invalid driverName update": {
  2280  			oldClass: &storage.VolumeAttributesClass{
  2281  				DriverName: "kubernetes.io/foo",
  2282  			},
  2283  			newClass: &storage.VolumeAttributesClass{
  2284  				DriverName: "kubernetes.io/bar",
  2285  			},
  2286  			shouldSucceed: false,
  2287  		},
  2288  		"invalid parameter update which changes values": {
  2289  			oldClass: &storage.VolumeAttributesClass{
  2290  				DriverName: "kubernetes.io/foo",
  2291  				Parameters: map[string]string{
  2292  					"foo": "bar1",
  2293  				},
  2294  			},
  2295  			newClass: &storage.VolumeAttributesClass{
  2296  				DriverName: "kubernetes.io/foo",
  2297  				Parameters: map[string]string{
  2298  					"foo": "bar2",
  2299  				},
  2300  			},
  2301  			shouldSucceed: false,
  2302  		},
  2303  		"invalid parameter update which add new item": {
  2304  			oldClass: &storage.VolumeAttributesClass{
  2305  				DriverName: "kubernetes.io/foo",
  2306  				Parameters: map[string]string{},
  2307  			},
  2308  			newClass: &storage.VolumeAttributesClass{
  2309  				DriverName: "kubernetes.io/foo",
  2310  				Parameters: map[string]string{
  2311  					"foo": "bar",
  2312  				},
  2313  			},
  2314  			shouldSucceed: false,
  2315  		},
  2316  		"invalid parameter update which remove a item": {
  2317  			oldClass: &storage.VolumeAttributesClass{
  2318  				DriverName: "kubernetes.io/foo",
  2319  				Parameters: map[string]string{
  2320  					"foo": "bar",
  2321  				},
  2322  			},
  2323  			newClass: &storage.VolumeAttributesClass{
  2324  				DriverName: "kubernetes.io/foo",
  2325  				Parameters: map[string]string{},
  2326  			},
  2327  			shouldSucceed: false,
  2328  		},
  2329  	}
  2330  
  2331  	for testName, testCase := range cases {
  2332  		errs := ValidateVolumeAttributesClassUpdate(testCase.newClass, testCase.oldClass)
  2333  		if testCase.shouldSucceed && len(errs) != 0 {
  2334  			t.Errorf("Expected success for %v, got %v", testName, errs)
  2335  		}
  2336  		if !testCase.shouldSucceed && len(errs) == 0 {
  2337  			t.Errorf("Expected failure for %v, got success", testName)
  2338  		}
  2339  	}
  2340  }
  2341  

View as plain text