1
16
17 package persistentvolume
18
19 import (
20 "context"
21 "github.com/google/go-cmp/cmp"
22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23 utilfeature "k8s.io/apiserver/pkg/util/feature"
24 featuregatetesting "k8s.io/component-base/featuregate/testing"
25 apitesting "k8s.io/kubernetes/pkg/api/testing"
26 api "k8s.io/kubernetes/pkg/apis/core"
27 "k8s.io/kubernetes/pkg/features"
28 "reflect"
29 "testing"
30 "time"
31
32
33 _ "k8s.io/kubernetes/pkg/apis/core/install"
34 )
35
36 func TestSelectableFieldLabelConversions(t *testing.T) {
37 apitesting.TestSelectableFieldLabelConversionsOfKind(t,
38 "v1",
39 "PersistentVolume",
40 PersistentVolumeToSelectableFields(&api.PersistentVolume{}),
41 map[string]string{"name": "metadata.name"},
42 )
43 }
44
45 func TestStatusUpdate(t *testing.T) {
46 now := metav1.Now()
47 origin := metav1.NewTime(now.Add(time.Hour))
48 later := metav1.NewTime(now.Add(time.Hour * 2))
49 NowFunc = func() metav1.Time { return now }
50 defer func() {
51 NowFunc = metav1.Now
52 }()
53 tests := []struct {
54 name string
55 fg bool
56 oldObj *api.PersistentVolume
57 newObj *api.PersistentVolume
58 expectedObj *api.PersistentVolume
59 }{
60 {
61 name: "feature enabled: timestamp is updated when phase changes",
62 fg: true,
63 oldObj: &api.PersistentVolume{
64 ObjectMeta: metav1.ObjectMeta{
65 Name: "foo",
66 },
67 Status: api.PersistentVolumeStatus{
68 Phase: api.VolumePending,
69 },
70 },
71 newObj: &api.PersistentVolume{
72 ObjectMeta: metav1.ObjectMeta{
73 Name: "foo",
74 },
75 Status: api.PersistentVolumeStatus{
76 Phase: api.VolumeBound,
77 },
78 },
79 expectedObj: &api.PersistentVolume{
80 ObjectMeta: metav1.ObjectMeta{
81 Name: "foo",
82 },
83 Status: api.PersistentVolumeStatus{
84 Phase: api.VolumeBound,
85 LastPhaseTransitionTime: &now,
86 },
87 },
88 },
89 {
90 name: "feature enabled: timestamp is updated when phase changes and old pv has a timestamp",
91 fg: true,
92 oldObj: &api.PersistentVolume{
93 ObjectMeta: metav1.ObjectMeta{
94 Name: "foo",
95 },
96 Status: api.PersistentVolumeStatus{
97 Phase: api.VolumePending,
98 LastPhaseTransitionTime: &origin,
99 },
100 },
101 newObj: &api.PersistentVolume{
102 ObjectMeta: metav1.ObjectMeta{
103 Name: "foo",
104 },
105 Status: api.PersistentVolumeStatus{
106 Phase: api.VolumeBound,
107 },
108 },
109 expectedObj: &api.PersistentVolume{
110 ObjectMeta: metav1.ObjectMeta{
111 Name: "foo",
112 },
113 Status: api.PersistentVolumeStatus{
114 Phase: api.VolumeBound,
115 LastPhaseTransitionTime: &now,
116 },
117 },
118 },
119 {
120 name: "feature enabled: user timestamp change is respected on no phase change",
121 fg: true,
122 oldObj: &api.PersistentVolume{
123 ObjectMeta: metav1.ObjectMeta{
124 Name: "foo",
125 },
126 Status: api.PersistentVolumeStatus{
127 Phase: api.VolumePending,
128 },
129 },
130 newObj: &api.PersistentVolume{
131 ObjectMeta: metav1.ObjectMeta{
132 Name: "foo",
133 },
134 Status: api.PersistentVolumeStatus{
135 Phase: api.VolumePending,
136 LastPhaseTransitionTime: &later,
137 },
138 },
139 expectedObj: &api.PersistentVolume{
140 ObjectMeta: metav1.ObjectMeta{
141 Name: "foo",
142 },
143 Status: api.PersistentVolumeStatus{
144 Phase: api.VolumePending,
145 LastPhaseTransitionTime: &later,
146 },
147 },
148 },
149 {
150 name: "feature enabled: user timestamp is respected on phase change",
151 fg: true,
152 oldObj: &api.PersistentVolume{
153 ObjectMeta: metav1.ObjectMeta{
154 Name: "foo",
155 },
156 Status: api.PersistentVolumeStatus{
157 Phase: api.VolumePending,
158 LastPhaseTransitionTime: &origin,
159 },
160 },
161 newObj: &api.PersistentVolume{
162 ObjectMeta: metav1.ObjectMeta{
163 Name: "foo",
164 },
165 Status: api.PersistentVolumeStatus{
166 Phase: api.VolumeBound,
167 LastPhaseTransitionTime: &later,
168 },
169 },
170 expectedObj: &api.PersistentVolume{
171 ObjectMeta: metav1.ObjectMeta{
172 Name: "foo",
173 },
174 Status: api.PersistentVolumeStatus{
175 Phase: api.VolumeBound,
176 LastPhaseTransitionTime: &later,
177 },
178 },
179 },
180 {
181 name: "feature enabled: user timestamp change is respected on no phase change when old pv has a timestamp",
182 fg: true,
183 oldObj: &api.PersistentVolume{
184 ObjectMeta: metav1.ObjectMeta{
185 Name: "foo",
186 },
187 Status: api.PersistentVolumeStatus{
188 Phase: api.VolumeBound,
189 LastPhaseTransitionTime: &origin,
190 },
191 },
192 newObj: &api.PersistentVolume{
193 ObjectMeta: metav1.ObjectMeta{
194 Name: "foo",
195 },
196 Status: api.PersistentVolumeStatus{
197 Phase: api.VolumeBound,
198 LastPhaseTransitionTime: &later,
199 },
200 },
201 expectedObj: &api.PersistentVolume{
202 ObjectMeta: metav1.ObjectMeta{
203 Name: "foo",
204 },
205 Status: api.PersistentVolumeStatus{
206 Phase: api.VolumeBound,
207 LastPhaseTransitionTime: &later,
208 },
209 },
210 },
211 {
212 name: "feature enabled: timestamp is updated when phase changes and both new and old timestamp matches",
213 fg: true,
214 oldObj: &api.PersistentVolume{
215 ObjectMeta: metav1.ObjectMeta{
216 Name: "foo",
217 },
218 Status: api.PersistentVolumeStatus{
219 Phase: api.VolumePending,
220 LastPhaseTransitionTime: &origin,
221 },
222 },
223 newObj: &api.PersistentVolume{
224 ObjectMeta: metav1.ObjectMeta{
225 Name: "foo",
226 },
227 Status: api.PersistentVolumeStatus{
228 Phase: api.VolumeBound,
229 LastPhaseTransitionTime: &origin,
230 },
231 },
232 expectedObj: &api.PersistentVolume{
233 ObjectMeta: metav1.ObjectMeta{
234 Name: "foo",
235 },
236 Status: api.PersistentVolumeStatus{
237 Phase: api.VolumeBound,
238 LastPhaseTransitionTime: &now,
239 },
240 },
241 },
242 {
243 name: "feature disabled: timestamp is not updated",
244 fg: false,
245 oldObj: &api.PersistentVolume{
246 ObjectMeta: metav1.ObjectMeta{
247 Name: "foo",
248 },
249 Status: api.PersistentVolumeStatus{
250 Phase: api.VolumePending,
251 },
252 },
253 newObj: &api.PersistentVolume{
254 ObjectMeta: metav1.ObjectMeta{
255 Name: "foo",
256 },
257 Status: api.PersistentVolumeStatus{
258 Phase: api.VolumeBound,
259 },
260 },
261 expectedObj: &api.PersistentVolume{
262 ObjectMeta: metav1.ObjectMeta{
263 Name: "foo",
264 },
265 Status: api.PersistentVolumeStatus{
266 Phase: api.VolumeBound,
267 },
268 },
269 },
270 {
271 name: "feature disabled: user timestamp is overwritten on phase change to nil",
272 fg: false,
273 oldObj: &api.PersistentVolume{
274 ObjectMeta: metav1.ObjectMeta{
275 Name: "foo",
276 },
277 Status: api.PersistentVolumeStatus{
278 Phase: api.VolumePending,
279 },
280 },
281 newObj: &api.PersistentVolume{
282 ObjectMeta: metav1.ObjectMeta{
283 Name: "foo",
284 },
285 Status: api.PersistentVolumeStatus{
286 Phase: api.VolumePending,
287 LastPhaseTransitionTime: &later,
288 },
289 },
290 expectedObj: &api.PersistentVolume{
291 ObjectMeta: metav1.ObjectMeta{
292 Name: "foo",
293 },
294 Status: api.PersistentVolumeStatus{
295 Phase: api.VolumePending,
296 LastPhaseTransitionTime: nil,
297 },
298 },
299 },
300 {
301 name: "feature disabled: user timestamp change is respected on phase change",
302 fg: false,
303 oldObj: &api.PersistentVolume{
304 ObjectMeta: metav1.ObjectMeta{
305 Name: "foo",
306 },
307 Status: api.PersistentVolumeStatus{
308 Phase: api.VolumePending,
309 LastPhaseTransitionTime: &origin,
310 },
311 },
312 newObj: &api.PersistentVolume{
313 ObjectMeta: metav1.ObjectMeta{
314 Name: "foo",
315 },
316 Status: api.PersistentVolumeStatus{
317 Phase: api.VolumeBound,
318 LastPhaseTransitionTime: &later,
319 },
320 },
321 expectedObj: &api.PersistentVolume{
322 ObjectMeta: metav1.ObjectMeta{
323 Name: "foo",
324 },
325 Status: api.PersistentVolumeStatus{
326 Phase: api.VolumeBound,
327 LastPhaseTransitionTime: &later,
328 },
329 },
330 },
331 {
332 name: "feature disabled: user timestamp change is respected on no phase change",
333 fg: false,
334 oldObj: &api.PersistentVolume{
335 ObjectMeta: metav1.ObjectMeta{
336 Name: "foo",
337 },
338 Status: api.PersistentVolumeStatus{
339 Phase: api.VolumeBound,
340 LastPhaseTransitionTime: &origin,
341 },
342 },
343 newObj: &api.PersistentVolume{
344 ObjectMeta: metav1.ObjectMeta{
345 Name: "foo",
346 },
347 Status: api.PersistentVolumeStatus{
348 Phase: api.VolumeBound,
349 LastPhaseTransitionTime: &later,
350 },
351 },
352 expectedObj: &api.PersistentVolume{
353 ObjectMeta: metav1.ObjectMeta{
354 Name: "foo",
355 },
356 Status: api.PersistentVolumeStatus{
357 Phase: api.VolumeBound,
358 LastPhaseTransitionTime: &later,
359 },
360 },
361 },
362 }
363
364 for _, tc := range tests {
365 t.Run(tc.name, func(t *testing.T) {
366 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentVolumeLastPhaseTransitionTime, tc.fg)()
367
368 obj := tc.newObj.DeepCopy()
369 StatusStrategy.PrepareForUpdate(context.TODO(), obj, tc.oldObj.DeepCopy())
370 if !reflect.DeepEqual(obj, tc.expectedObj) {
371 t.Errorf("object diff: %s", cmp.Diff(obj, tc.expectedObj))
372 }
373 })
374 }
375 }
376
377 func TestStatusCreate(t *testing.T) {
378 now := metav1.Now()
379 NowFunc = func() metav1.Time { return now }
380 defer func() {
381 NowFunc = metav1.Now
382 }()
383 tests := []struct {
384 name string
385 fg bool
386 newObj *api.PersistentVolume
387 expectedObj *api.PersistentVolume
388 }{
389 {
390 name: "feature enabled: pv is in pending phase and has a timestamp",
391 fg: true,
392 newObj: &api.PersistentVolume{
393 ObjectMeta: metav1.ObjectMeta{
394 Name: "foo",
395 },
396 },
397 expectedObj: &api.PersistentVolume{
398 ObjectMeta: metav1.ObjectMeta{
399 Name: "foo",
400 },
401 Status: api.PersistentVolumeStatus{
402 Phase: api.VolumePending,
403 LastPhaseTransitionTime: &now,
404 },
405 },
406 },
407 {
408 name: "feature disabled: pv does not have phase and timestamp",
409 fg: false,
410 newObj: &api.PersistentVolume{
411 ObjectMeta: metav1.ObjectMeta{
412 Name: "foo",
413 },
414 },
415 expectedObj: &api.PersistentVolume{
416 ObjectMeta: metav1.ObjectMeta{
417 Name: "foo",
418 },
419 },
420 },
421 }
422
423 for _, tc := range tests {
424 t.Run(tc.name, func(t *testing.T) {
425
426 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PersistentVolumeLastPhaseTransitionTime, tc.fg)()
427 obj := tc.newObj.DeepCopy()
428 StatusStrategy.PrepareForCreate(context.TODO(), obj)
429 if !reflect.DeepEqual(obj, tc.expectedObj) {
430 t.Errorf("object diff: %s", cmp.Diff(obj, tc.expectedObj))
431 }
432 })
433 }
434 }
435
View as plain text