...

Source file src/k8s.io/kubernetes/plugin/pkg/admission/storage/storageclass/setdefault/admission_test.go

Documentation: k8s.io/kubernetes/plugin/pkg/admission/storage/storageclass/setdefault

     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 setdefault
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  	"time"
    23  
    24  	"k8s.io/klog/v2"
    25  
    26  	storagev1 "k8s.io/api/storage/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apiserver/pkg/admission"
    29  	admissiontesting "k8s.io/apiserver/pkg/admission/testing"
    30  	"k8s.io/client-go/informers"
    31  	api "k8s.io/kubernetes/pkg/apis/core"
    32  	storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
    33  	"k8s.io/kubernetes/pkg/controller"
    34  )
    35  
    36  func TestAdmission(t *testing.T) {
    37  	empty := ""
    38  	foo := "foo"
    39  
    40  	defaultClass1 := &storagev1.StorageClass{
    41  		TypeMeta: metav1.TypeMeta{
    42  			Kind: "StorageClass",
    43  		},
    44  		ObjectMeta: metav1.ObjectMeta{
    45  			Name: "default1",
    46  			Annotations: map[string]string{
    47  				storageutil.IsDefaultStorageClassAnnotation: "true",
    48  			},
    49  		},
    50  		Provisioner: "default1",
    51  	}
    52  	defaultClass2 := &storagev1.StorageClass{
    53  		TypeMeta: metav1.TypeMeta{
    54  			Kind: "StorageClass",
    55  		},
    56  		ObjectMeta: metav1.ObjectMeta{
    57  			Name: "default2",
    58  			Annotations: map[string]string{
    59  				storageutil.IsDefaultStorageClassAnnotation: "true",
    60  			},
    61  		},
    62  		Provisioner: "default2",
    63  	}
    64  	// Class that has explicit default = false
    65  	classWithFalseDefault := &storagev1.StorageClass{
    66  		TypeMeta: metav1.TypeMeta{
    67  			Kind: "StorageClass",
    68  		},
    69  		ObjectMeta: metav1.ObjectMeta{
    70  			Name: "nondefault1",
    71  			Annotations: map[string]string{
    72  				storageutil.IsDefaultStorageClassAnnotation: "false",
    73  			},
    74  		},
    75  		Provisioner: "nondefault1",
    76  	}
    77  	// Class with missing default annotation (=non-default)
    78  	classWithNoDefault := &storagev1.StorageClass{
    79  		TypeMeta: metav1.TypeMeta{
    80  			Kind: "StorageClass",
    81  		},
    82  		ObjectMeta: metav1.ObjectMeta{
    83  			Name: "nondefault2",
    84  		},
    85  		Provisioner: "nondefault1",
    86  	}
    87  	// Class with empty default annotation (=non-default)
    88  	classWithEmptyDefault := &storagev1.StorageClass{
    89  		TypeMeta: metav1.TypeMeta{
    90  			Kind: "StorageClass",
    91  		},
    92  		ObjectMeta: metav1.ObjectMeta{
    93  			Name: "nondefault2",
    94  			Annotations: map[string]string{
    95  				storageutil.IsDefaultStorageClassAnnotation: "",
    96  			},
    97  		},
    98  		Provisioner: "nondefault1",
    99  	}
   100  	classWithCreateTime1 := &storagev1.StorageClass{
   101  		TypeMeta: metav1.TypeMeta{
   102  			Kind: "StorageClass",
   103  		},
   104  		ObjectMeta: metav1.ObjectMeta{
   105  			Name:              "default1",
   106  			CreationTimestamp: metav1.NewTime(time.Date(2022, time.Month(1), 1, 0, 0, 0, 1, time.UTC)),
   107  			Annotations: map[string]string{
   108  				storageutil.IsDefaultStorageClassAnnotation: "true",
   109  			},
   110  		},
   111  	}
   112  	classWithCreateTime2 := &storagev1.StorageClass{
   113  		TypeMeta: metav1.TypeMeta{
   114  			Kind: "StorageClass",
   115  		},
   116  		ObjectMeta: metav1.ObjectMeta{
   117  			Name:              "default2",
   118  			CreationTimestamp: metav1.NewTime(time.Date(2022, time.Month(1), 1, 0, 0, 0, 0, time.UTC)),
   119  			Annotations: map[string]string{
   120  				storageutil.IsDefaultStorageClassAnnotation: "true",
   121  			},
   122  		},
   123  	}
   124  
   125  	claimWithClass := &api.PersistentVolumeClaim{
   126  		TypeMeta: metav1.TypeMeta{
   127  			Kind: "PersistentVolumeClaim",
   128  		},
   129  		ObjectMeta: metav1.ObjectMeta{
   130  			Name:      "claimWithClass",
   131  			Namespace: "ns",
   132  		},
   133  		Spec: api.PersistentVolumeClaimSpec{
   134  			StorageClassName: &foo,
   135  		},
   136  	}
   137  	claimWithEmptyClass := &api.PersistentVolumeClaim{
   138  		TypeMeta: metav1.TypeMeta{
   139  			Kind: "PersistentVolumeClaim",
   140  		},
   141  		ObjectMeta: metav1.ObjectMeta{
   142  			Name:      "claimWithEmptyClass",
   143  			Namespace: "ns",
   144  		},
   145  		Spec: api.PersistentVolumeClaimSpec{
   146  			StorageClassName: &empty,
   147  		},
   148  	}
   149  	claimWithNoClass := &api.PersistentVolumeClaim{
   150  		TypeMeta: metav1.TypeMeta{
   151  			Kind: "PersistentVolumeClaim",
   152  		},
   153  		ObjectMeta: metav1.ObjectMeta{
   154  			Name:      "claimWithNoClass",
   155  			Namespace: "ns",
   156  		},
   157  	}
   158  
   159  	tests := []struct {
   160  		name              string
   161  		classes           []*storagev1.StorageClass
   162  		claim             *api.PersistentVolumeClaim
   163  		expectError       bool
   164  		expectedClassName string
   165  	}{
   166  		{
   167  			"no default, no modification of PVCs",
   168  			[]*storagev1.StorageClass{classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   169  			claimWithNoClass,
   170  			false,
   171  			"",
   172  		},
   173  		{
   174  			"one default, modify PVC with class=nil",
   175  			[]*storagev1.StorageClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   176  			claimWithNoClass,
   177  			false,
   178  			"default1",
   179  		},
   180  		{
   181  			"one default, no modification of PVC with class=''",
   182  			[]*storagev1.StorageClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   183  			claimWithEmptyClass,
   184  			false,
   185  			"",
   186  		},
   187  		{
   188  			"one default, no modification of PVC with class='foo'",
   189  			[]*storagev1.StorageClass{defaultClass1, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   190  			claimWithClass,
   191  			false,
   192  			"foo",
   193  		},
   194  		{
   195  			"two defaults, no modification of PVC with class=''",
   196  			[]*storagev1.StorageClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   197  			claimWithEmptyClass,
   198  			false,
   199  			"",
   200  		},
   201  		{
   202  			"two defaults, no modification of PVC with class='foo'",
   203  			[]*storagev1.StorageClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   204  			claimWithClass,
   205  			false,
   206  			"foo",
   207  		},
   208  		{
   209  			"two defaults with same creation time, choose the one with smaller name",
   210  			[]*storagev1.StorageClass{defaultClass1, defaultClass2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   211  			claimWithNoClass,
   212  			false,
   213  			defaultClass1.Name,
   214  		},
   215  		{
   216  			"two defaults, choose the one with newer creation time",
   217  			[]*storagev1.StorageClass{classWithCreateTime1, classWithCreateTime2, classWithFalseDefault, classWithNoDefault, classWithEmptyDefault},
   218  			claimWithNoClass,
   219  			false,
   220  			classWithCreateTime1.Name,
   221  		},
   222  	}
   223  
   224  	for _, test := range tests {
   225  		klog.V(4).Infof("starting test %q", test.name)
   226  
   227  		// clone the claim, it's going to be modified
   228  		claim := test.claim.DeepCopy()
   229  
   230  		ctrl := newPlugin()
   231  		informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
   232  		ctrl.SetExternalKubeInformerFactory(informerFactory)
   233  		for _, c := range test.classes {
   234  			informerFactory.Storage().V1().StorageClasses().Informer().GetStore().Add(c)
   235  		}
   236  		attrs := admission.NewAttributesRecord(
   237  			claim, // new object
   238  			nil,   // old object
   239  			api.Kind("PersistentVolumeClaim").WithVersion("version"),
   240  			claim.Namespace,
   241  			claim.Name,
   242  			api.Resource("persistentvolumeclaims").WithVersion("version"),
   243  			"", // subresource
   244  			admission.Create,
   245  			&metav1.CreateOptions{},
   246  			false, // dryRun
   247  			nil,   // userInfo
   248  		)
   249  		err := admissiontesting.WithReinvocationTesting(t, ctrl).Admit(context.TODO(), attrs, nil)
   250  		klog.Infof("Got %v", err)
   251  		if err != nil && !test.expectError {
   252  			t.Errorf("Test %q: unexpected error received: %v", test.name, err)
   253  		}
   254  		if err == nil && test.expectError {
   255  			t.Errorf("Test %q: expected error and no error recevied", test.name)
   256  		}
   257  
   258  		class := ""
   259  		if claim.Spec.StorageClassName != nil {
   260  			class = *claim.Spec.StorageClassName
   261  		}
   262  		if test.expectedClassName != "" && test.expectedClassName != class {
   263  			t.Errorf("Test %q: expected class name %q, got %q", test.name, test.expectedClassName, class)
   264  		}
   265  		if test.expectedClassName == "" && class != "" {
   266  			t.Errorf("Test %q: expected class name %q, got %q", test.name, test.expectedClassName, class)
   267  		}
   268  	}
   269  }
   270  

View as plain text