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