1
16
17 package csinode
18
19 import (
20 "reflect"
21 "testing"
22
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 "k8s.io/kubernetes/pkg/apis/storage"
27 utilpointer "k8s.io/utils/pointer"
28 )
29
30 func TestPrepareForCreate(t *testing.T) {
31 valid := getValidCSINode("foo")
32 emptyAllocatable := &storage.CSINode{
33 ObjectMeta: metav1.ObjectMeta{
34 Name: "foo",
35 },
36 Spec: storage.CSINodeSpec{
37 Drivers: []storage.CSINodeDriver{
38 {
39 Name: "valid-driver-name",
40 NodeID: "valid-node",
41 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
42 },
43 },
44 },
45 }
46
47 volumeLimitsCases := []struct {
48 name string
49 obj *storage.CSINode
50 expected *storage.CSINode
51 }{
52 {
53 "empty allocatable",
54 emptyAllocatable,
55 emptyAllocatable,
56 },
57 {
58 "valid allocatable",
59 valid,
60 valid,
61 },
62 }
63
64 for _, test := range volumeLimitsCases {
65 t.Run(test.name, func(t *testing.T) {
66 testPrepareForCreate(t, test.obj, test.expected)
67 })
68 }
69 }
70
71 func testPrepareForCreate(t *testing.T, obj, expected *storage.CSINode) {
72 ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
73 APIGroup: "storage.k8s.io",
74 APIVersion: "v1beta1",
75 Resource: "csinodes",
76 })
77 Strategy.PrepareForCreate(ctx, obj)
78 if !reflect.DeepEqual(*expected, *obj) {
79 t.Errorf("Object mismatch! Expected:\n%#v\ngot:\n%#v", *expected, *obj)
80 }
81 }
82
83 func TestPrepareForUpdate(t *testing.T) {
84 valid := getValidCSINode("foo")
85 differentAllocatable := &storage.CSINode{
86 ObjectMeta: metav1.ObjectMeta{
87 Name: "foo",
88 },
89 Spec: storage.CSINodeSpec{
90 Drivers: []storage.CSINodeDriver{
91 {
92 Name: "valid-driver-name",
93 NodeID: "valid-node",
94 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
95 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32Ptr(20)},
96 },
97 },
98 },
99 }
100 emptyAllocatable := &storage.CSINode{
101 ObjectMeta: metav1.ObjectMeta{
102 Name: "foo",
103 },
104 Spec: storage.CSINodeSpec{
105 Drivers: []storage.CSINodeDriver{
106 {
107 Name: "valid-driver-name",
108 NodeID: "valid-node",
109 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
110 },
111 },
112 },
113 }
114
115 volumeLimitsCases := []struct {
116 name string
117 old *storage.CSINode
118 new *storage.CSINode
119 expected *storage.CSINode
120 }{
121 {
122 "allow empty allocatable when it's not set",
123 emptyAllocatable,
124 emptyAllocatable,
125 emptyAllocatable,
126 },
127 {
128 "allow valid allocatable when it's already set",
129 valid,
130 differentAllocatable,
131 differentAllocatable,
132 },
133 {
134 "allow valid allocatable when it's not set",
135 emptyAllocatable,
136 valid,
137 valid,
138 },
139 }
140
141 for _, test := range volumeLimitsCases {
142 t.Run(test.name, func(t *testing.T) {
143 testPrepareForUpdate(t, test.new, test.old, test.expected)
144 })
145 }
146 }
147
148 func testPrepareForUpdate(t *testing.T, obj, old, expected *storage.CSINode) {
149 ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
150 APIGroup: "storage.k8s.io",
151 APIVersion: "v1beta1",
152 Resource: "csinodes",
153 })
154 Strategy.PrepareForUpdate(ctx, obj, old)
155 if !reflect.DeepEqual(*expected, *obj) {
156 t.Errorf("Object mismatch! Expected:\n%#v\ngot:\n%#v", *expected, *obj)
157 }
158 }
159
160 func TestCSINodeStrategy(t *testing.T) {
161 ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
162 APIGroup: "storage.k8s.io",
163 APIVersion: "v1beta1",
164 Resource: "csinodes",
165 })
166 if Strategy.NamespaceScoped() {
167 t.Errorf("CSINode must not be namespace scoped")
168 }
169 if Strategy.AllowCreateOnUpdate() {
170 t.Errorf("CSINode should not allow create on update")
171 }
172
173 csiNode := getValidCSINode("valid-csinode")
174
175 Strategy.PrepareForCreate(ctx, csiNode)
176
177 errs := Strategy.Validate(ctx, csiNode)
178 if len(errs) != 0 {
179 t.Errorf("unexpected error validating %v", errs)
180 }
181
182
183 newCSINode := csiNode.DeepCopy()
184 newCSINode.Spec.Drivers[0].NodeID = "valid-node-2"
185
186 Strategy.PrepareForUpdate(ctx, newCSINode, csiNode)
187
188 errs = Strategy.ValidateUpdate(ctx, newCSINode, csiNode)
189 if len(errs) == 0 {
190 t.Errorf("expected validation error")
191 }
192 }
193
194 func TestCSINodeValidation(t *testing.T) {
195 tests := []struct {
196 name string
197 csiNode *storage.CSINode
198 expectError bool
199 }{
200 {
201 "valid csinode",
202 getValidCSINode("foo"),
203 false,
204 },
205 {
206 "valid csinode with empty allocatable",
207 &storage.CSINode{
208 ObjectMeta: metav1.ObjectMeta{
209 Name: "foo",
210 },
211 Spec: storage.CSINodeSpec{
212 Drivers: []storage.CSINodeDriver{
213 {
214 Name: "valid-driver-name",
215 NodeID: "valid-node",
216 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
217 },
218 },
219 },
220 },
221 false,
222 },
223 {
224 "valid csinode with missing volume limits",
225 &storage.CSINode{
226 ObjectMeta: metav1.ObjectMeta{
227 Name: "foo",
228 },
229 Spec: storage.CSINodeSpec{
230 Drivers: []storage.CSINodeDriver{
231 {
232 Name: "valid-driver-name",
233 NodeID: "valid-node",
234 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
235 Allocatable: &storage.VolumeNodeResources{Count: nil},
236 },
237 },
238 },
239 },
240 false,
241 },
242 {
243 "invalid driver name",
244 &storage.CSINode{
245 ObjectMeta: metav1.ObjectMeta{
246 Name: "foo",
247 },
248 Spec: storage.CSINodeSpec{
249 Drivers: []storage.CSINodeDriver{
250 {
251 Name: "$csi-driver@",
252 NodeID: "valid-node",
253 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
254 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32Ptr(10)},
255 },
256 },
257 },
258 },
259 true,
260 },
261 {
262 "empty node id",
263 &storage.CSINode{
264 ObjectMeta: metav1.ObjectMeta{
265 Name: "foo",
266 },
267 Spec: storage.CSINodeSpec{
268 Drivers: []storage.CSINodeDriver{
269 {
270 Name: "valid-driver-name",
271 NodeID: "",
272 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
273 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32Ptr(10)},
274 },
275 },
276 },
277 },
278 true,
279 },
280 {
281 "invalid allocatable with negative volumes limit",
282 &storage.CSINode{
283 ObjectMeta: metav1.ObjectMeta{
284 Name: "foo",
285 },
286 Spec: storage.CSINodeSpec{
287 Drivers: []storage.CSINodeDriver{
288 {
289 Name: "valid-driver-name",
290 NodeID: "valid-node",
291 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
292 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32Ptr(-1)},
293 },
294 },
295 },
296 },
297 true,
298 },
299 {
300 "invalid topology keys",
301 &storage.CSINode{
302 ObjectMeta: metav1.ObjectMeta{
303 Name: "foo",
304 },
305 Spec: storage.CSINodeSpec{
306 Drivers: []storage.CSINodeDriver{
307 {
308 Name: "valid-driver-name",
309 NodeID: "valid-node",
310 TopologyKeys: []string{"company.com/zone1", ""},
311 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32Ptr(10)},
312 },
313 },
314 },
315 },
316 true,
317 },
318 }
319
320 for _, test := range tests {
321 t.Run(test.name, func(t *testing.T) {
322
323 testValidation := func(csiNode *storage.CSINode, apiVersion string) field.ErrorList {
324 ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{
325 APIGroup: "storage.k8s.io",
326 APIVersion: "v1beta1",
327 Resource: "csinodes",
328 })
329 return Strategy.Validate(ctx, csiNode)
330 }
331
332 betaErr := testValidation(test.csiNode, "v1beta1")
333 if len(betaErr) > 0 && !test.expectError {
334 t.Errorf("Validation of v1beta1 object failed: %+v", betaErr)
335 }
336 if len(betaErr) == 0 && test.expectError {
337 t.Errorf("Validation of v1beta1 object unexpectedly succeeded")
338 }
339 })
340 }
341 }
342
343 func getValidCSINode(name string) *storage.CSINode {
344 return &storage.CSINode{
345 ObjectMeta: metav1.ObjectMeta{
346 Name: name,
347 },
348 Spec: storage.CSINodeSpec{
349 Drivers: []storage.CSINodeDriver{
350 {
351 Name: "valid-driver-name",
352 NodeID: "valid-node",
353 TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
354 Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32Ptr(10)},
355 },
356 },
357 },
358 }
359 }
360
View as plain text