k8s.io/kubernetes@v1.29.3/pkg/registry/storage/csidriver/strategy_test.go (about)

     1  /*
     2  Copyright 2019 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 csidriver
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/require"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	"k8s.io/apimachinery/pkg/util/validation/field"
    25  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    26  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    27  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    28  	"k8s.io/kubernetes/pkg/apis/storage"
    29  	"k8s.io/kubernetes/pkg/features"
    30  )
    31  
    32  func getValidCSIDriver(name string) *storage.CSIDriver {
    33  	enabled := true
    34  	return &storage.CSIDriver{
    35  		ObjectMeta: metav1.ObjectMeta{
    36  			Name: name,
    37  		},
    38  		Spec: storage.CSIDriverSpec{
    39  			AttachRequired:    &enabled,
    40  			PodInfoOnMount:    &enabled,
    41  			StorageCapacity:   &enabled,
    42  			RequiresRepublish: &enabled,
    43  			SELinuxMount:      &enabled,
    44  		},
    45  	}
    46  }
    47  
    48  func TestCSIDriverStrategy(t *testing.T) {
    49  	ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
    50  		APIGroup:   "storage.k8s.io",
    51  		APIVersion: "v1",
    52  		Resource:   "csidrivers",
    53  	})
    54  	if Strategy.NamespaceScoped() {
    55  		t.Errorf("CSIDriver must not be namespace scoped")
    56  	}
    57  	if Strategy.AllowCreateOnUpdate() {
    58  		t.Errorf("CSIDriver should not allow create on update")
    59  	}
    60  
    61  	csiDriver := getValidCSIDriver("valid-csidriver")
    62  
    63  	Strategy.PrepareForCreate(ctx, csiDriver)
    64  
    65  	errs := Strategy.Validate(ctx, csiDriver)
    66  	if len(errs) != 0 {
    67  		t.Errorf("unexpected error validating %v", errs)
    68  	}
    69  
    70  	// Update of spec is disallowed
    71  	newCSIDriver := csiDriver.DeepCopy()
    72  	attachNotRequired := false
    73  	newCSIDriver.Spec.AttachRequired = &attachNotRequired
    74  
    75  	Strategy.PrepareForUpdate(ctx, newCSIDriver, csiDriver)
    76  
    77  	errs = Strategy.ValidateUpdate(ctx, newCSIDriver, csiDriver)
    78  	if len(errs) == 0 {
    79  		t.Errorf("Expected a validation error")
    80  	}
    81  }
    82  
    83  func TestCSIDriverPrepareForUpdate(t *testing.T) {
    84  	ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
    85  		APIGroup:   "storage.k8s.io",
    86  		APIVersion: "v1",
    87  		Resource:   "csidrivers",
    88  	})
    89  
    90  	attachRequired := true
    91  	podInfoOnMount := true
    92  	driverWithNothing := &storage.CSIDriver{
    93  		ObjectMeta: metav1.ObjectMeta{
    94  			Name: "foo",
    95  		},
    96  	}
    97  	driverWithPersistent := &storage.CSIDriver{
    98  		ObjectMeta: metav1.ObjectMeta{
    99  			Name: "foo",
   100  		},
   101  		Spec: storage.CSIDriverSpec{
   102  			AttachRequired: &attachRequired,
   103  			PodInfoOnMount: &podInfoOnMount,
   104  			VolumeLifecycleModes: []storage.VolumeLifecycleMode{
   105  				storage.VolumeLifecyclePersistent,
   106  			},
   107  		},
   108  	}
   109  	enabled := true
   110  	disabled := false
   111  	gcp := "gcp"
   112  	driverWithCapacityEnabled := &storage.CSIDriver{
   113  		ObjectMeta: metav1.ObjectMeta{
   114  			Name: "foo",
   115  		},
   116  		Spec: storage.CSIDriverSpec{
   117  			StorageCapacity: &enabled,
   118  		},
   119  	}
   120  	driverWithCapacityDisabled := &storage.CSIDriver{
   121  		ObjectMeta: metav1.ObjectMeta{
   122  			Name: "foo",
   123  		},
   124  		Spec: storage.CSIDriverSpec{
   125  			StorageCapacity: &disabled,
   126  		},
   127  	}
   128  	driverWithServiceAccountTokenGCP := &storage.CSIDriver{
   129  		ObjectMeta: metav1.ObjectMeta{
   130  			Name: "foo",
   131  		},
   132  		Spec: storage.CSIDriverSpec{
   133  			TokenRequests:     []storage.TokenRequest{{Audience: gcp}},
   134  			RequiresRepublish: &enabled,
   135  		},
   136  	}
   137  	driverWithSELinuxMountEnabled := &storage.CSIDriver{
   138  		ObjectMeta: metav1.ObjectMeta{
   139  			Name: "foo",
   140  		},
   141  		Spec: storage.CSIDriverSpec{
   142  			SELinuxMount: &enabled,
   143  		},
   144  	}
   145  	driverWithSELinuxMountDisabled := &storage.CSIDriver{
   146  		ObjectMeta: metav1.ObjectMeta{
   147  			Name: "foo",
   148  		},
   149  		Spec: storage.CSIDriverSpec{
   150  			SELinuxMount: &disabled,
   151  		},
   152  	}
   153  
   154  	resultPersistent := []storage.VolumeLifecycleMode{storage.VolumeLifecyclePersistent}
   155  
   156  	tests := []struct {
   157  		name                                string
   158  		old, update                         *storage.CSIDriver
   159  		seLinuxMountReadWriteOncePodEnabled bool
   160  		wantCapacity                        *bool
   161  		wantModes                           []storage.VolumeLifecycleMode
   162  		wantTokenRequests                   []storage.TokenRequest
   163  		wantRequiresRepublish               *bool
   164  		wantGeneration                      int64
   165  		wantSELinuxMount                    *bool
   166  	}{
   167  		{
   168  			name:         "capacity feature enabled, before: none, update: enabled",
   169  			old:          driverWithNothing,
   170  			update:       driverWithCapacityEnabled,
   171  			wantCapacity: &enabled,
   172  		},
   173  		{
   174  			name:         "capacity feature enabled, before: enabled, update: disabled",
   175  			old:          driverWithCapacityEnabled,
   176  			update:       driverWithCapacityDisabled,
   177  			wantCapacity: &disabled,
   178  		},
   179  		{
   180  			name:      "inline feature enabled, before: none, update: persistent",
   181  			old:       driverWithNothing,
   182  			update:    driverWithPersistent,
   183  			wantModes: resultPersistent,
   184  		},
   185  		{
   186  			name:                  "service account token feature enabled, before: none, update: audience=gcp",
   187  			old:                   driverWithNothing,
   188  			update:                driverWithServiceAccountTokenGCP,
   189  			wantTokenRequests:     []storage.TokenRequest{{Audience: gcp}},
   190  			wantRequiresRepublish: &enabled,
   191  			wantGeneration:        1,
   192  		},
   193  		{
   194  			name:                                "SELinux mount support feature enabled, before: nil, update: on",
   195  			seLinuxMountReadWriteOncePodEnabled: true,
   196  			old:                                 driverWithNothing,
   197  			update:                              driverWithSELinuxMountEnabled,
   198  			wantSELinuxMount:                    &enabled,
   199  			wantGeneration:                      1,
   200  		},
   201  		{
   202  			name:                                "SELinux mount support feature enabled, before: off, update: on",
   203  			seLinuxMountReadWriteOncePodEnabled: true,
   204  			old:                                 driverWithSELinuxMountDisabled,
   205  			update:                              driverWithSELinuxMountEnabled,
   206  			wantSELinuxMount:                    &enabled,
   207  			wantGeneration:                      1,
   208  		},
   209  		{
   210  			name:                                "SELinux mount support feature enabled, before: on, update: off",
   211  			seLinuxMountReadWriteOncePodEnabled: true,
   212  			old:                                 driverWithSELinuxMountEnabled,
   213  			update:                              driverWithSELinuxMountDisabled,
   214  			wantSELinuxMount:                    &disabled,
   215  			wantGeneration:                      1,
   216  		},
   217  		{
   218  			name:                                "SELinux mount support feature disabled, before: nil, update: on",
   219  			seLinuxMountReadWriteOncePodEnabled: false,
   220  			old:                                 driverWithNothing,
   221  			update:                              driverWithSELinuxMountEnabled,
   222  			wantSELinuxMount:                    nil,
   223  			wantGeneration:                      0,
   224  		},
   225  		{
   226  			name:                                "SELinux mount support feature disabled, before: off, update: on",
   227  			seLinuxMountReadWriteOncePodEnabled: false,
   228  			old:                                 driverWithSELinuxMountDisabled,
   229  			update:                              driverWithSELinuxMountEnabled,
   230  			wantSELinuxMount:                    &enabled,
   231  			wantGeneration:                      1,
   232  		},
   233  		{
   234  			name:                                "SELinux mount support feature enabled, before: on, update: off",
   235  			seLinuxMountReadWriteOncePodEnabled: false,
   236  			old:                                 driverWithSELinuxMountEnabled,
   237  			update:                              driverWithSELinuxMountDisabled,
   238  			wantSELinuxMount:                    &disabled,
   239  			wantGeneration:                      1,
   240  		},
   241  	}
   242  
   243  	for _, test := range tests {
   244  		t.Run(test.name, func(t *testing.T) {
   245  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, test.seLinuxMountReadWriteOncePodEnabled)()
   246  
   247  			csiDriver := test.update.DeepCopy()
   248  			Strategy.PrepareForUpdate(ctx, csiDriver, test.old)
   249  			require.Equal(t, test.wantGeneration, csiDriver.GetGeneration())
   250  			require.Equal(t, test.wantCapacity, csiDriver.Spec.StorageCapacity)
   251  			require.Equal(t, test.wantModes, csiDriver.Spec.VolumeLifecycleModes)
   252  			require.Equal(t, test.wantTokenRequests, csiDriver.Spec.TokenRequests)
   253  			require.Equal(t, test.wantRequiresRepublish, csiDriver.Spec.RequiresRepublish)
   254  			require.Equal(t, test.wantSELinuxMount, csiDriver.Spec.SELinuxMount)
   255  		})
   256  	}
   257  }
   258  
   259  func TestCSIDriverValidation(t *testing.T) {
   260  	enabled := true
   261  	disabled := true
   262  	gcp := "gcp"
   263  
   264  	tests := []struct {
   265  		name        string
   266  		csiDriver   *storage.CSIDriver
   267  		expectError bool
   268  	}{
   269  		{
   270  			"valid csidriver",
   271  			getValidCSIDriver("foo"),
   272  			false,
   273  		},
   274  		{
   275  			"true for all flags",
   276  			&storage.CSIDriver{
   277  				ObjectMeta: metav1.ObjectMeta{
   278  					Name: "foo",
   279  				},
   280  				Spec: storage.CSIDriverSpec{
   281  					AttachRequired:    &enabled,
   282  					PodInfoOnMount:    &enabled,
   283  					StorageCapacity:   &enabled,
   284  					RequiresRepublish: &enabled,
   285  					SELinuxMount:      &enabled,
   286  				},
   287  			},
   288  			false,
   289  		},
   290  		{
   291  			"false for all flags",
   292  			&storage.CSIDriver{
   293  				ObjectMeta: metav1.ObjectMeta{
   294  					Name: "foo",
   295  				},
   296  				Spec: storage.CSIDriverSpec{
   297  					AttachRequired:    &disabled,
   298  					PodInfoOnMount:    &disabled,
   299  					StorageCapacity:   &disabled,
   300  					RequiresRepublish: &disabled,
   301  					SELinuxMount:      &disabled,
   302  				},
   303  			},
   304  			false,
   305  		},
   306  		{
   307  			"invalid driver name",
   308  			&storage.CSIDriver{
   309  				ObjectMeta: metav1.ObjectMeta{
   310  					Name: "*foo#",
   311  				},
   312  				Spec: storage.CSIDriverSpec{
   313  					AttachRequired:    &enabled,
   314  					PodInfoOnMount:    &enabled,
   315  					StorageCapacity:   &enabled,
   316  					RequiresRepublish: &enabled,
   317  					SELinuxMount:      &enabled,
   318  				},
   319  			},
   320  			true,
   321  		},
   322  		{
   323  			"invalid volume mode",
   324  			&storage.CSIDriver{
   325  				ObjectMeta: metav1.ObjectMeta{
   326  					Name: "foo",
   327  				},
   328  				Spec: storage.CSIDriverSpec{
   329  					AttachRequired:  &enabled,
   330  					PodInfoOnMount:  &enabled,
   331  					StorageCapacity: &enabled,
   332  					VolumeLifecycleModes: []storage.VolumeLifecycleMode{
   333  						storage.VolumeLifecycleMode("no-such-mode"),
   334  					},
   335  					RequiresRepublish: &enabled,
   336  					SELinuxMount:      &enabled,
   337  				},
   338  			},
   339  			true,
   340  		},
   341  		{
   342  			"persistent volume mode",
   343  			&storage.CSIDriver{
   344  				ObjectMeta: metav1.ObjectMeta{
   345  					Name: "foo",
   346  				},
   347  				Spec: storage.CSIDriverSpec{
   348  					AttachRequired:  &enabled,
   349  					PodInfoOnMount:  &enabled,
   350  					StorageCapacity: &enabled,
   351  					VolumeLifecycleModes: []storage.VolumeLifecycleMode{
   352  						storage.VolumeLifecyclePersistent,
   353  					},
   354  					RequiresRepublish: &enabled,
   355  					SELinuxMount:      &enabled,
   356  				},
   357  			},
   358  			false,
   359  		},
   360  		{
   361  			"ephemeral volume mode",
   362  			&storage.CSIDriver{
   363  				ObjectMeta: metav1.ObjectMeta{
   364  					Name: "foo",
   365  				},
   366  				Spec: storage.CSIDriverSpec{
   367  					AttachRequired:  &enabled,
   368  					PodInfoOnMount:  &enabled,
   369  					StorageCapacity: &enabled,
   370  					VolumeLifecycleModes: []storage.VolumeLifecycleMode{
   371  						storage.VolumeLifecycleEphemeral,
   372  					},
   373  					RequiresRepublish: &enabled,
   374  					SELinuxMount:      &enabled,
   375  				},
   376  			},
   377  			false,
   378  		},
   379  		{
   380  			"both volume modes",
   381  			&storage.CSIDriver{
   382  				ObjectMeta: metav1.ObjectMeta{
   383  					Name: "foo",
   384  				},
   385  				Spec: storage.CSIDriverSpec{
   386  					AttachRequired:  &enabled,
   387  					PodInfoOnMount:  &enabled,
   388  					StorageCapacity: &enabled,
   389  					VolumeLifecycleModes: []storage.VolumeLifecycleMode{
   390  						storage.VolumeLifecyclePersistent,
   391  						storage.VolumeLifecycleEphemeral,
   392  					},
   393  					RequiresRepublish: &enabled,
   394  					SELinuxMount:      &enabled,
   395  				},
   396  			},
   397  			false,
   398  		},
   399  		{
   400  			"service account token with gcp as audience",
   401  			&storage.CSIDriver{
   402  				ObjectMeta: metav1.ObjectMeta{
   403  					Name: "foo",
   404  				},
   405  				Spec: storage.CSIDriverSpec{
   406  					AttachRequired:    &enabled,
   407  					PodInfoOnMount:    &enabled,
   408  					StorageCapacity:   &enabled,
   409  					TokenRequests:     []storage.TokenRequest{{Audience: gcp}},
   410  					RequiresRepublish: &enabled,
   411  					SELinuxMount:      &enabled,
   412  				},
   413  			},
   414  			false,
   415  		},
   416  		{
   417  			"invalid SELinuxMount",
   418  			&storage.CSIDriver{
   419  				ObjectMeta: metav1.ObjectMeta{
   420  					Name: "foo",
   421  				},
   422  				Spec: storage.CSIDriverSpec{
   423  					AttachRequired:  &enabled,
   424  					PodInfoOnMount:  &enabled,
   425  					StorageCapacity: &enabled,
   426  					SELinuxMount:    nil,
   427  				},
   428  			},
   429  			true,
   430  		},
   431  	}
   432  
   433  	for _, test := range tests {
   434  		t.Run(test.name, func(t *testing.T) {
   435  			// assume this feature is on for this test, detailed enabled/disabled tests in TestCSIDriverValidationSELinuxMountEnabledDisabled
   436  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, true)()
   437  
   438  			testValidation := func(csiDriver *storage.CSIDriver, apiVersion string) field.ErrorList {
   439  				ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
   440  					APIGroup:   "storage.k8s.io",
   441  					APIVersion: "v1",
   442  					Resource:   "csidrivers",
   443  				})
   444  				return Strategy.Validate(ctx, csiDriver)
   445  			}
   446  
   447  			err := testValidation(test.csiDriver, "v1")
   448  			if len(err) > 0 && !test.expectError {
   449  				t.Errorf("Validation of v1 object failed: %+v", err)
   450  			}
   451  			if len(err) == 0 && test.expectError {
   452  				t.Errorf("Validation of v1 object unexpectedly succeeded")
   453  			}
   454  		})
   455  	}
   456  }