k8s.io/kubernetes@v1.29.3/pkg/registry/core/persistentvolume/strategy_test.go (about) 1 /* 2 Copyright 2015 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 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 // ensure types are installed 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 }