1
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
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
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
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
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,
238 nil,
239 api.Kind("PersistentVolumeClaim").WithVersion("version"),
240 claim.Namespace,
241 claim.Name,
242 api.Resource("persistentvolumeclaims").WithVersion("version"),
243 "",
244 admission.Create,
245 &metav1.CreateOptions{},
246 false,
247 nil,
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