k8s.io/apiserver@v0.31.1/pkg/registry/rest/resttest/resttest.go (about) 1 /* 2 Copyright 2014 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 resttest 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "reflect" 24 "strings" 25 "testing" 26 "time" 27 28 apiequality "k8s.io/apimachinery/pkg/api/equality" 29 "k8s.io/apimachinery/pkg/api/errors" 30 "k8s.io/apimachinery/pkg/api/meta" 31 "k8s.io/apimachinery/pkg/api/validation/path" 32 metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" 33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 "k8s.io/apimachinery/pkg/conversion" 35 "k8s.io/apimachinery/pkg/fields" 36 "k8s.io/apimachinery/pkg/labels" 37 "k8s.io/apimachinery/pkg/runtime" 38 "k8s.io/apimachinery/pkg/types" 39 "k8s.io/apimachinery/pkg/util/wait" 40 genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 41 "k8s.io/apiserver/pkg/registry/rest" 42 ) 43 44 // TODO(apelisse): Tests in this file should be more hermertic by always 45 // removing objects that they create. That would avoid name-collisions. 46 47 type Tester struct { 48 *testing.T 49 storage rest.Storage 50 clusterScope bool 51 createOnUpdate bool 52 generatesName bool 53 returnDeletedObject bool 54 namer func(int) string 55 } 56 57 func New(t *testing.T, storage rest.Storage) *Tester { 58 return &Tester{ 59 T: t, 60 storage: storage, 61 namer: defaultNamer, 62 } 63 } 64 65 func defaultNamer(i int) string { 66 return fmt.Sprintf("foo%d", i) 67 } 68 69 // Namer allows providing a custom name maker 70 // By default "foo%d" is used 71 func (t *Tester) Namer(namer func(int) string) *Tester { 72 t.namer = namer 73 return t 74 } 75 76 func (t *Tester) ClusterScope() *Tester { 77 t.clusterScope = true 78 return t 79 } 80 81 func (t *Tester) AllowCreateOnUpdate() *Tester { 82 t.createOnUpdate = true 83 return t 84 } 85 86 func (t *Tester) GeneratesName() *Tester { 87 t.generatesName = true 88 return t 89 } 90 91 func (t *Tester) ReturnDeletedObject() *Tester { 92 t.returnDeletedObject = true 93 return t 94 } 95 96 // TestNamespace returns the namespace that will be used when creating contexts. 97 // Returns NamespaceNone for cluster-scoped objects. 98 func (t *Tester) TestNamespace() string { 99 if t.clusterScope { 100 return metav1.NamespaceNone 101 } 102 return "test" 103 } 104 105 // TestContext returns a namespaced context that will be used when making storage calls. 106 // Namespace is determined by TestNamespace() 107 func (t *Tester) TestContext() context.Context { 108 return genericapirequest.WithNamespace(genericapirequest.NewContext(), t.TestNamespace()) 109 } 110 111 func (t *Tester) getObjectMetaOrFail(obj runtime.Object) metav1.Object { 112 objMeta, err := meta.Accessor(obj) 113 if err != nil { 114 t.Fatalf("object does not have ObjectMeta: %v\n%#v", err, obj) 115 } 116 return objMeta 117 } 118 119 func (t *Tester) setObjectMeta(obj runtime.Object, name string) { 120 meta := t.getObjectMetaOrFail(obj) 121 meta.SetName(name) 122 if t.clusterScope { 123 meta.SetNamespace(metav1.NamespaceNone) 124 } else { 125 meta.SetNamespace(genericapirequest.NamespaceValue(t.TestContext())) 126 } 127 meta.SetGenerateName("") 128 meta.SetGeneration(1) 129 } 130 131 type AssignFunc func([]runtime.Object) []runtime.Object 132 type EmitFunc func(runtime.Object, string) error 133 type GetFunc func(context.Context, runtime.Object) (runtime.Object, error) 134 type InitWatchFunc func() 135 type InjectErrFunc func(err error) 136 type IsErrorFunc func(err error) bool 137 type CreateFunc func(context.Context, runtime.Object) error 138 type SetRVFunc func(uint64) 139 type UpdateFunc func(runtime.Object) runtime.Object 140 141 // Test creating an object. 142 func (t *Tester) TestCreate(valid runtime.Object, createFn CreateFunc, getFn GetFunc, invalid ...runtime.Object) { 143 dryRunOpts := metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}} 144 opts := metav1.CreateOptions{} 145 t.testCreateHasMetadata(valid.DeepCopyObject()) 146 if !t.generatesName { 147 t.testCreateGeneratesName(valid.DeepCopyObject()) 148 } 149 t.testCreateDryRun(valid.DeepCopyObject(), getFn) 150 t.testCreateDryRunEquals(valid.DeepCopyObject()) 151 t.testCreateEquals(valid.DeepCopyObject(), getFn) 152 t.testCreateAlreadyExisting(valid.DeepCopyObject(), createFn, dryRunOpts) 153 t.testCreateAlreadyExisting(valid.DeepCopyObject(), createFn, opts) 154 if t.clusterScope { 155 t.testCreateDiscardsObjectNamespace(valid.DeepCopyObject(), dryRunOpts) 156 t.testCreateDiscardsObjectNamespace(valid.DeepCopyObject(), opts) 157 t.testCreateIgnoresContextNamespace(valid.DeepCopyObject(), dryRunOpts) 158 t.testCreateIgnoresContextNamespace(valid.DeepCopyObject(), opts) 159 t.testCreateIgnoresMismatchedNamespace(valid.DeepCopyObject(), dryRunOpts) 160 t.testCreateIgnoresMismatchedNamespace(valid.DeepCopyObject(), opts) 161 t.testCreateResetsUserData(valid.DeepCopyObject(), dryRunOpts) 162 t.testCreateResetsUserData(valid.DeepCopyObject(), opts) 163 } else { 164 t.testCreateRejectsMismatchedNamespace(valid.DeepCopyObject(), dryRunOpts) 165 t.testCreateRejectsMismatchedNamespace(valid.DeepCopyObject(), opts) 166 } 167 t.testCreateInvokesValidation(dryRunOpts, invalid...) 168 t.testCreateInvokesValidation(opts, invalid...) 169 t.testCreateValidatesNames(valid.DeepCopyObject(), dryRunOpts) 170 t.testCreateValidatesNames(valid.DeepCopyObject(), opts) 171 } 172 173 // Test updating an object. 174 func (t *Tester) TestUpdate(valid runtime.Object, createFn CreateFunc, getFn GetFunc, updateFn UpdateFunc, invalidUpdateFn ...UpdateFunc) { 175 dryRunOpts := metav1.UpdateOptions{DryRun: []string{metav1.DryRunAll}} 176 opts := metav1.UpdateOptions{} 177 t.testUpdateEquals(valid.DeepCopyObject(), createFn, getFn, updateFn) 178 t.testUpdateFailsOnVersionTooOld(valid.DeepCopyObject(), createFn, getFn) 179 t.testUpdateOnNotFound(valid.DeepCopyObject(), dryRunOpts) 180 t.testUpdateOnNotFound(valid.DeepCopyObject(), opts) 181 if !t.clusterScope { 182 t.testUpdateRejectsMismatchedNamespace(valid.DeepCopyObject(), createFn, getFn) 183 } 184 t.testUpdateInvokesValidation(valid.DeepCopyObject(), createFn, invalidUpdateFn...) 185 t.testUpdateWithWrongUID(valid.DeepCopyObject(), createFn, getFn, dryRunOpts) 186 t.testUpdateWithWrongUID(valid.DeepCopyObject(), createFn, getFn, opts) 187 t.testUpdateRetrievesOldObject(valid.DeepCopyObject(), createFn, getFn) 188 t.testUpdatePropagatesUpdatedObjectError(valid.DeepCopyObject(), createFn, getFn, dryRunOpts) 189 t.testUpdatePropagatesUpdatedObjectError(valid.DeepCopyObject(), createFn, getFn, opts) 190 t.testUpdateIgnoreGenerationUpdates(valid.DeepCopyObject(), createFn, getFn) 191 } 192 193 // Test deleting an object. 194 func (t *Tester) TestDelete(valid runtime.Object, createFn CreateFunc, getFn GetFunc, isNotFoundFn IsErrorFunc) { 195 dryRunOpts := metav1.DeleteOptions{DryRun: []string{metav1.DryRunAll}} 196 opts := metav1.DeleteOptions{} 197 t.testDeleteNonExist(valid.DeepCopyObject(), dryRunOpts) 198 t.testDeleteNonExist(valid.DeepCopyObject(), opts) 199 t.testDeleteNoGraceful(valid.DeepCopyObject(), createFn, getFn, isNotFoundFn, true) 200 t.testDeleteNoGraceful(valid.DeepCopyObject(), createFn, getFn, isNotFoundFn, false) 201 t.testDeleteWithUID(valid.DeepCopyObject(), createFn, getFn, isNotFoundFn, dryRunOpts) 202 t.testDeleteWithUID(valid.DeepCopyObject(), createFn, getFn, isNotFoundFn, opts) 203 t.testDeleteWithResourceVersion(valid.DeepCopyObject(), createFn, getFn, isNotFoundFn, dryRunOpts) 204 t.testDeleteWithResourceVersion(valid.DeepCopyObject(), createFn, getFn, isNotFoundFn, opts) 205 } 206 207 // Test gracefully deleting an object. 208 func (t *Tester) TestDeleteGraceful(valid runtime.Object, createFn CreateFunc, getFn GetFunc, expectedGrace int64) { 209 t.testDeleteDryRunGracefulHasdefault(valid.DeepCopyObject(), createFn, expectedGrace) 210 t.testDeleteGracefulHasDefault(valid.DeepCopyObject(), createFn, getFn, expectedGrace) 211 t.testDeleteGracefulWithValue(valid.DeepCopyObject(), createFn, getFn, expectedGrace) 212 t.testDeleteGracefulUsesZeroOnNil(valid.DeepCopyObject(), createFn, expectedGrace) 213 t.testDeleteGracefulExtend(valid.DeepCopyObject(), createFn, getFn, expectedGrace) 214 t.testDeleteGracefulShorten(valid.DeepCopyObject(), createFn, getFn, expectedGrace) 215 t.testDeleteGracefulImmediate(valid.DeepCopyObject(), createFn, getFn, expectedGrace) 216 } 217 218 // Test getting object. 219 func (t *Tester) TestGet(valid runtime.Object) { 220 t.testGetFound(valid.DeepCopyObject()) 221 t.testGetNotFound(valid.DeepCopyObject()) 222 t.testGetMimatchedNamespace(valid.DeepCopyObject()) 223 if !t.clusterScope { 224 t.testGetDifferentNamespace(valid.DeepCopyObject()) 225 } 226 } 227 228 // Test listing objects. 229 func (t *Tester) TestList(valid runtime.Object, assignFn AssignFunc) { 230 t.testListNotFound(assignFn) 231 t.testListFound(valid.DeepCopyObject(), assignFn) 232 t.testListMatchLabels(valid.DeepCopyObject(), assignFn) 233 t.testListTableConversion(valid.DeepCopyObject(), assignFn) 234 } 235 236 // Test watching objects. 237 func (t *Tester) TestWatch( 238 valid runtime.Object, emitFn EmitFunc, 239 labelsPass, labelsFail []labels.Set, fieldsPass, fieldsFail []fields.Set, actions []string) { 240 t.testWatchLabels(valid.DeepCopyObject(), emitFn, labelsPass, labelsFail, actions) 241 t.testWatchFields(valid.DeepCopyObject(), emitFn, fieldsPass, fieldsFail, actions) 242 } 243 244 // ============================================================================= 245 // Creation tests. 246 247 func (t *Tester) delete(ctx context.Context, obj runtime.Object) error { 248 objectMeta, err := meta.Accessor(obj) 249 if err != nil { 250 return err 251 } 252 deleter, ok := t.storage.(rest.GracefulDeleter) 253 if !ok { 254 return fmt.Errorf("Expected deleting storage, got %v", t.storage) 255 } 256 _, _, err = deleter.Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, nil) 257 return err 258 } 259 260 func (t *Tester) testCreateAlreadyExisting(obj runtime.Object, createFn CreateFunc, opts metav1.CreateOptions) { 261 ctx := t.TestContext() 262 263 foo := obj.DeepCopyObject() 264 t.setObjectMeta(foo, t.namer(1)) 265 if err := createFn(ctx, foo); err != nil { 266 t.Errorf("unexpected error: %v", err) 267 } 268 defer t.delete(ctx, foo) 269 270 _, err := t.storage.(rest.Creater).Create(ctx, foo, rest.ValidateAllObjectFunc, &opts) 271 if !errors.IsAlreadyExists(err) { 272 t.Errorf("expected already exists err, got %v", err) 273 } 274 } 275 276 func (t *Tester) testCreateDryRun(obj runtime.Object, getFn GetFunc) { 277 ctx := t.TestContext() 278 279 foo := obj.DeepCopyObject() 280 t.setObjectMeta(foo, t.namer(2)) 281 282 _, err := t.storage.(rest.Creater).Create(ctx, foo, rest.ValidateAllObjectFunc, &metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}}) 283 if err != nil { 284 t.Errorf("unexpected error: %v", err) 285 } 286 287 _, err = getFn(ctx, foo) 288 if !errors.IsNotFound(err) { 289 t.Errorf("Expected NotFound error, got '%v'", err) 290 } 291 } 292 293 func (t *Tester) testCreateDryRunEquals(obj runtime.Object) { 294 ctx := t.TestContext() 295 296 foo := obj.DeepCopyObject() 297 t.setObjectMeta(foo, t.namer(2)) 298 299 createdFake, err := t.storage.(rest.Creater).Create(ctx, foo, rest.ValidateAllObjectFunc, &metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}}) 300 if err != nil { 301 t.Errorf("unexpected error: %v", err) 302 } 303 304 created, err := t.storage.(rest.Creater).Create(ctx, foo, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 305 if err != nil { 306 t.Errorf("unexpected error: %v", err) 307 } 308 defer t.delete(ctx, created) 309 310 // Set resource version which might be unset in created object. 311 createdMeta := t.getObjectMetaOrFail(created) 312 createdFakeMeta := t.getObjectMetaOrFail(createdFake) 313 createdMeta.SetCreationTimestamp(createdFakeMeta.GetCreationTimestamp()) 314 createdFakeMeta.SetResourceVersion("") 315 createdMeta.SetResourceVersion("") 316 createdMeta.SetUID(createdFakeMeta.GetUID()) 317 318 if e, a := created, createdFake; !apiequality.Semantic.DeepEqual(e, a) { 319 t.Errorf("unexpected obj: %#v, expected %#v", e, a) 320 } 321 } 322 323 func (t *Tester) testCreateEquals(obj runtime.Object, getFn GetFunc) { 324 ctx := t.TestContext() 325 326 foo := obj.DeepCopyObject() 327 t.setObjectMeta(foo, t.namer(2)) 328 329 created, err := t.storage.(rest.Creater).Create(ctx, foo, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 330 if err != nil { 331 t.Errorf("unexpected error: %v", err) 332 } 333 defer t.delete(ctx, created) 334 335 got, err := getFn(ctx, foo) 336 if err != nil { 337 t.Errorf("unexpected error: %v", err) 338 } 339 340 // Set resource version which might be unset in created object. 341 createdMeta := t.getObjectMetaOrFail(created) 342 gotMeta := t.getObjectMetaOrFail(got) 343 createdMeta.SetResourceVersion(gotMeta.GetResourceVersion()) 344 345 if e, a := created, got; !apiequality.Semantic.DeepEqual(e, a) { 346 t.Errorf("unexpected obj: %#v, expected %#v", e, a) 347 } 348 } 349 350 func (t *Tester) testCreateDiscardsObjectNamespace(valid runtime.Object, opts metav1.CreateOptions) { 351 objectMeta := t.getObjectMetaOrFail(valid) 352 353 // Ignore non-empty namespace in object meta 354 objectMeta.SetNamespace("not-default") 355 356 // Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted 357 created, err := t.storage.(rest.Creater).Create(t.TestContext(), valid.DeepCopyObject(), rest.ValidateAllObjectFunc, &opts) 358 if err != nil { 359 t.Fatalf("Unexpected error: %v", err) 360 } 361 defer t.delete(t.TestContext(), created) 362 createdObjectMeta := t.getObjectMetaOrFail(created) 363 if createdObjectMeta.GetNamespace() != metav1.NamespaceNone { 364 t.Errorf("Expected empty namespace on created object, got '%v'", createdObjectMeta.GetNamespace()) 365 } 366 } 367 368 func (t *Tester) testCreateGeneratesName(valid runtime.Object) { 369 objectMeta := t.getObjectMetaOrFail(valid) 370 objectMeta.SetName("") 371 objectMeta.SetGenerateName("test-") 372 373 created, err := t.storage.(rest.Creater).Create(t.TestContext(), valid, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 374 if err != nil { 375 t.Fatalf("Unexpected error: %v", err) 376 } 377 defer t.delete(t.TestContext(), created) 378 createdMeta := t.getObjectMetaOrFail(created) 379 if createdMeta.GetName() == "test-" || !strings.HasPrefix(createdMeta.GetName(), "test-") { 380 t.Errorf("unexpected name: %#v", valid) 381 } 382 } 383 384 func (t *Tester) testCreateHasMetadata(valid runtime.Object) { 385 objectMeta := t.getObjectMetaOrFail(valid) 386 objectMeta.SetName(t.namer(1)) 387 objectMeta.SetNamespace(t.TestNamespace()) 388 389 obj, err := t.storage.(rest.Creater).Create(t.TestContext(), valid, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 390 if err != nil { 391 t.Fatalf("Unexpected error: %v", err) 392 } 393 if obj == nil { 394 t.Fatalf("Unexpected object from result: %#v", obj) 395 } 396 defer t.delete(t.TestContext(), obj) 397 createdMeta := t.getObjectMetaOrFail(obj) 398 if !metav1.HasObjectMetaSystemFieldValues(createdMeta) { 399 t.Errorf("storage did not populate object meta field values") 400 } 401 } 402 403 func (t *Tester) testCreateIgnoresContextNamespace(valid runtime.Object, opts metav1.CreateOptions) { 404 // Ignore non-empty namespace in context 405 ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), "not-default2") 406 407 // Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted 408 created, err := t.storage.(rest.Creater).Create(ctx, valid.DeepCopyObject(), rest.ValidateAllObjectFunc, &opts) 409 if err != nil { 410 t.Fatalf("Unexpected error: %v", err) 411 } 412 defer t.delete(ctx, created) 413 createdObjectMeta := t.getObjectMetaOrFail(created) 414 if createdObjectMeta.GetNamespace() != metav1.NamespaceNone { 415 t.Errorf("Expected empty namespace on created object, got '%v'", createdObjectMeta.GetNamespace()) 416 } 417 } 418 419 func (t *Tester) testCreateIgnoresMismatchedNamespace(valid runtime.Object, opts metav1.CreateOptions) { 420 objectMeta := t.getObjectMetaOrFail(valid) 421 422 // Ignore non-empty namespace in object meta 423 objectMeta.SetNamespace("not-default") 424 ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), "not-default2") 425 426 // Ideally, we'd get an error back here, but at least verify the namespace wasn't persisted 427 created, err := t.storage.(rest.Creater).Create(ctx, valid.DeepCopyObject(), rest.ValidateAllObjectFunc, &opts) 428 if err != nil { 429 t.Fatalf("Unexpected error: %v", err) 430 } 431 defer t.delete(ctx, created) 432 createdObjectMeta := t.getObjectMetaOrFail(created) 433 if createdObjectMeta.GetNamespace() != metav1.NamespaceNone { 434 t.Errorf("Expected empty namespace on created object, got '%v'", createdObjectMeta.GetNamespace()) 435 } 436 } 437 438 func (t *Tester) testCreateValidatesNames(valid runtime.Object, opts metav1.CreateOptions) { 439 for _, invalidName := range path.NameMayNotBe { 440 objCopy := valid.DeepCopyObject() 441 objCopyMeta := t.getObjectMetaOrFail(objCopy) 442 objCopyMeta.SetName(invalidName) 443 444 ctx := t.TestContext() 445 _, err := t.storage.(rest.Creater).Create(ctx, objCopy, rest.ValidateAllObjectFunc, &opts) 446 if !errors.IsInvalid(err) { 447 t.Errorf("%s: Expected to get an invalid resource error, got '%v'", invalidName, err) 448 } 449 } 450 451 for _, invalidSuffix := range path.NameMayNotContain { 452 objCopy := valid.DeepCopyObject() 453 objCopyMeta := t.getObjectMetaOrFail(objCopy) 454 objCopyMeta.SetName(objCopyMeta.GetName() + invalidSuffix) 455 456 ctx := t.TestContext() 457 _, err := t.storage.(rest.Creater).Create(ctx, objCopy, rest.ValidateAllObjectFunc, &opts) 458 if !errors.IsInvalid(err) { 459 t.Errorf("%s: Expected to get an invalid resource error, got '%v'", invalidSuffix, err) 460 } 461 } 462 } 463 464 func (t *Tester) testCreateInvokesValidation(opts metav1.CreateOptions, invalid ...runtime.Object) { 465 for i, obj := range invalid { 466 ctx := t.TestContext() 467 _, err := t.storage.(rest.Creater).Create(ctx, obj, rest.ValidateAllObjectFunc, &opts) 468 if !errors.IsInvalid(err) { 469 t.Errorf("%d: Expected to get an invalid resource error, got %v", i, err) 470 } 471 } 472 } 473 474 func (t *Tester) testCreateRejectsMismatchedNamespace(valid runtime.Object, opts metav1.CreateOptions) { 475 objectMeta := t.getObjectMetaOrFail(valid) 476 objectMeta.SetNamespace("not-default") 477 478 _, err := t.storage.(rest.Creater).Create(t.TestContext(), valid, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 479 if err == nil { 480 t.Errorf("Expected an error, but we didn't get one") 481 } else if !strings.Contains(err.Error(), "does not match the namespace sent on the request") { 482 t.Errorf("Expected 'does not match the namespace sent on the request' error, got '%v'", err.Error()) 483 } 484 } 485 486 func (t *Tester) testCreateResetsUserData(valid runtime.Object, opts metav1.CreateOptions) { 487 objectMeta := t.getObjectMetaOrFail(valid) 488 now := metav1.Now() 489 objectMeta.SetUID("bad-uid") 490 objectMeta.SetCreationTimestamp(now) 491 492 obj, err := t.storage.(rest.Creater).Create(t.TestContext(), valid, rest.ValidateAllObjectFunc, &opts) 493 if err != nil { 494 t.Fatalf("Unexpected error: %v", err) 495 } 496 if obj == nil { 497 t.Fatalf("Unexpected object from result: %#v", obj) 498 } 499 defer t.delete(t.TestContext(), obj) 500 createdMeta := t.getObjectMetaOrFail(obj) 501 if createdMeta.GetUID() == "bad-uid" || createdMeta.GetCreationTimestamp() == now { 502 t.Errorf("ObjectMeta did not reset basic fields: %#v", objectMeta) 503 } 504 } 505 506 // ============================================================================= 507 // Update tests. 508 509 func (t *Tester) testUpdateEquals(obj runtime.Object, createFn CreateFunc, getFn GetFunc, updateFn UpdateFunc) { 510 ctx := t.TestContext() 511 512 foo := obj.DeepCopyObject() 513 t.setObjectMeta(foo, t.namer(2)) 514 if err := createFn(ctx, foo); err != nil { 515 t.Errorf("unexpected error: %v", err) 516 } 517 518 toUpdate, err := getFn(ctx, foo) 519 if err != nil { 520 t.Errorf("unexpected error: %v", err) 521 } 522 toUpdate = updateFn(toUpdate) 523 toUpdateMeta := t.getObjectMetaOrFail(toUpdate) 524 updated, created, err := t.storage.(rest.Updater).Update(ctx, toUpdateMeta.GetName(), rest.DefaultUpdatedObjectInfo(toUpdate), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 525 if err != nil { 526 t.Errorf("unexpected error: %v", err) 527 } 528 if created { 529 t.Errorf("unexpected creation") 530 } 531 got, err := getFn(ctx, foo) 532 if err != nil { 533 t.Errorf("unexpected error: %v", err) 534 } 535 // Set resource version which might be unset in created object. 536 updatedMeta := t.getObjectMetaOrFail(updated) 537 gotMeta := t.getObjectMetaOrFail(got) 538 updatedMeta.SetResourceVersion(gotMeta.GetResourceVersion()) 539 540 if e, a := updated, got; !apiequality.Semantic.DeepEqual(e, a) { 541 t.Errorf("unexpected obj: %#v, expected %#v", e, a) 542 } 543 } 544 545 func (t *Tester) testUpdateFailsOnVersionTooOld(obj runtime.Object, createFn CreateFunc, getFn GetFunc) { 546 ctx := t.TestContext() 547 548 foo := obj.DeepCopyObject() 549 t.setObjectMeta(foo, t.namer(3)) 550 551 if err := createFn(ctx, foo); err != nil { 552 t.Errorf("unexpected error: %v", err) 553 } 554 555 storedFoo, err := getFn(ctx, foo) 556 if err != nil { 557 t.Errorf("unexpected error: %v", err) 558 } 559 560 older := storedFoo.DeepCopyObject() 561 olderMeta := t.getObjectMetaOrFail(older) 562 olderMeta.SetResourceVersion("1") 563 564 _, _, err = t.storage.(rest.Updater).Update(t.TestContext(), olderMeta.GetName(), rest.DefaultUpdatedObjectInfo(older), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 565 if err == nil { 566 t.Errorf("Expected an error, but we didn't get one") 567 } else if !errors.IsConflict(err) { 568 t.Errorf("Expected Conflict error, got '%v'", err) 569 } 570 } 571 572 func (t *Tester) testUpdateInvokesValidation(obj runtime.Object, createFn CreateFunc, invalidUpdateFn ...UpdateFunc) { 573 ctx := t.TestContext() 574 575 foo := obj.DeepCopyObject() 576 t.setObjectMeta(foo, t.namer(4)) 577 if err := createFn(ctx, foo); err != nil { 578 t.Errorf("unexpected error: %v", err) 579 } 580 581 for _, update := range invalidUpdateFn { 582 toUpdate := update(foo.DeepCopyObject()) 583 toUpdateMeta := t.getObjectMetaOrFail(toUpdate) 584 got, created, err := t.storage.(rest.Updater).Update(t.TestContext(), toUpdateMeta.GetName(), rest.DefaultUpdatedObjectInfo(toUpdate), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 585 if got != nil || created { 586 t.Errorf("expected nil object and no creation for object: %v", toUpdate) 587 } 588 if !errors.IsInvalid(err) && !errors.IsBadRequest(err) { 589 t.Errorf("expected invalid or bad request error, got %v", err) 590 } 591 } 592 } 593 594 func (t *Tester) testUpdateWithWrongUID(obj runtime.Object, createFn CreateFunc, getFn GetFunc, opts metav1.UpdateOptions) { 595 ctx := t.TestContext() 596 foo := obj.DeepCopyObject() 597 t.setObjectMeta(foo, t.namer(5)) 598 objectMeta := t.getObjectMetaOrFail(foo) 599 objectMeta.SetUID(types.UID("UID0000")) 600 if err := createFn(ctx, foo); err != nil { 601 t.Errorf("unexpected error: %v", err) 602 } 603 defer t.delete(ctx, foo) 604 objectMeta.SetUID(types.UID("UID1111")) 605 606 obj, created, err := t.storage.(rest.Updater).Update(ctx, objectMeta.GetName(), rest.DefaultUpdatedObjectInfo(foo), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &opts) 607 if created || obj != nil { 608 t.Errorf("expected nil object and no creation for object: %v", foo) 609 } 610 if err == nil || !errors.IsConflict(err) { 611 t.Errorf("unexpected error: %v", err) 612 } 613 } 614 615 func (t *Tester) testUpdateRetrievesOldObject(obj runtime.Object, createFn CreateFunc, getFn GetFunc) { 616 ctx := t.TestContext() 617 foo := obj.DeepCopyObject() 618 t.setObjectMeta(foo, t.namer(6)) 619 objectMeta := t.getObjectMetaOrFail(foo) 620 objectMeta.SetAnnotations(map[string]string{"A": "1"}) 621 if err := createFn(ctx, foo); err != nil { 622 t.Errorf("unexpected error: %v", err) 623 return 624 } 625 626 storedFoo, err := getFn(ctx, foo) 627 if err != nil { 628 t.Errorf("unexpected error: %v", err) 629 return 630 } 631 632 storedFooWithUpdates := storedFoo.DeepCopyObject() 633 objectMeta = t.getObjectMetaOrFail(storedFooWithUpdates) 634 objectMeta.SetAnnotations(map[string]string{"A": "2"}) 635 636 // Make sure a custom transform is called, and sees the expected updatedObject and oldObject 637 // This tests the mechanism used to pass the old and new object to admission 638 calledUpdatedObject := 0 639 noopTransform := func(_ context.Context, updatedObject runtime.Object, oldObject runtime.Object) (runtime.Object, error) { 640 if !reflect.DeepEqual(storedFoo, oldObject) { 641 t.Errorf("Expected\n\t%#v\ngot\n\t%#v", storedFoo, oldObject) 642 } 643 if !reflect.DeepEqual(storedFooWithUpdates, updatedObject) { 644 t.Errorf("Expected\n\t%#v\ngot\n\t%#v", storedFooWithUpdates, updatedObject) 645 } 646 calledUpdatedObject++ 647 return updatedObject, nil 648 } 649 650 updatedObj, created, err := t.storage.(rest.Updater).Update(ctx, objectMeta.GetName(), rest.DefaultUpdatedObjectInfo(storedFooWithUpdates, noopTransform), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 651 if err != nil { 652 t.Errorf("unexpected error: %v", err) 653 return 654 } 655 if created { 656 t.Errorf("expected no creation for object") 657 return 658 } 659 if updatedObj == nil { 660 t.Errorf("expected non-nil object from update") 661 return 662 } 663 if calledUpdatedObject != 1 { 664 t.Errorf("expected UpdatedObject() to be called 1 time, was called %d", calledUpdatedObject) 665 return 666 } 667 } 668 669 func (t *Tester) testUpdatePropagatesUpdatedObjectError(obj runtime.Object, createFn CreateFunc, getFn GetFunc, opts metav1.UpdateOptions) { 670 ctx := t.TestContext() 671 foo := obj.DeepCopyObject() 672 name := t.namer(7) 673 t.setObjectMeta(foo, name) 674 if err := createFn(ctx, foo); err != nil { 675 t.Errorf("unexpected error: %v", err) 676 return 677 } 678 defer t.delete(ctx, foo) 679 680 // Make sure our transform is called, and sees the expected updatedObject and oldObject 681 propagateErr := fmt.Errorf("custom updated object error for %v", foo) 682 noopTransform := func(_ context.Context, updatedObject runtime.Object, oldObject runtime.Object) (runtime.Object, error) { 683 return nil, propagateErr 684 } 685 686 _, _, err := t.storage.(rest.Updater).Update(ctx, name, rest.DefaultUpdatedObjectInfo(foo, noopTransform), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &opts) 687 if err != propagateErr { 688 t.Errorf("expected propagated error, got %#v", err) 689 } 690 } 691 692 func (t *Tester) testUpdateIgnoreGenerationUpdates(obj runtime.Object, createFn CreateFunc, getFn GetFunc) { 693 ctx := t.TestContext() 694 695 foo := obj.DeepCopyObject() 696 name := t.namer(8) 697 t.setObjectMeta(foo, name) 698 699 if err := createFn(ctx, foo); err != nil { 700 t.Errorf("unexpected error: %v", err) 701 } 702 703 storedFoo, err := getFn(ctx, foo) 704 if err != nil { 705 t.Errorf("unexpected error: %v", err) 706 } 707 708 older := storedFoo.DeepCopyObject() 709 olderMeta := t.getObjectMetaOrFail(older) 710 olderMeta.SetGeneration(2) 711 712 _, _, err = t.storage.(rest.Updater).Update(t.TestContext(), olderMeta.GetName(), rest.DefaultUpdatedObjectInfo(older), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 713 if err != nil { 714 t.Errorf("Unexpected error: %v", err) 715 } 716 717 updatedFoo, err := getFn(ctx, older) 718 if err != nil { 719 t.Errorf("unexpected error: %v", err) 720 } 721 if exp, got := int64(1), t.getObjectMetaOrFail(updatedFoo).GetGeneration(); exp != got { 722 t.Errorf("Unexpected generation update: expected %d, got %d", exp, got) 723 } 724 } 725 726 func (t *Tester) testUpdateOnNotFound(obj runtime.Object, opts metav1.UpdateOptions) { 727 t.setObjectMeta(obj, t.namer(0)) 728 _, created, err := t.storage.(rest.Updater).Update(t.TestContext(), t.namer(0), rest.DefaultUpdatedObjectInfo(obj), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &opts) 729 if t.createOnUpdate { 730 if err != nil { 731 t.Errorf("creation allowed on updated, but got an error: %v", err) 732 } 733 if !created { 734 t.Errorf("creation allowed on update, but object not created") 735 } 736 } else { 737 if err == nil { 738 t.Errorf("Expected an error, but we didn't get one") 739 } else if !errors.IsNotFound(err) { 740 t.Errorf("Expected NotFound error, got '%v'", err) 741 } 742 } 743 } 744 745 func (t *Tester) testUpdateRejectsMismatchedNamespace(obj runtime.Object, createFn CreateFunc, getFn GetFunc) { 746 ctx := t.TestContext() 747 748 foo := obj.DeepCopyObject() 749 t.setObjectMeta(foo, t.namer(1)) 750 if err := createFn(ctx, foo); err != nil { 751 t.Errorf("unexpected error: %v", err) 752 } 753 754 storedFoo, err := getFn(ctx, foo) 755 if err != nil { 756 t.Errorf("unexpected error: %v", err) 757 } 758 759 objectMeta := t.getObjectMetaOrFail(storedFoo) 760 objectMeta.SetName(t.namer(1)) 761 objectMeta.SetNamespace("not-default") 762 763 obj, updated, err := t.storage.(rest.Updater).Update(t.TestContext(), "foo1", rest.DefaultUpdatedObjectInfo(storedFoo), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 764 if obj != nil || updated { 765 t.Errorf("expected nil object and not updated") 766 } 767 if err == nil { 768 t.Errorf("expected an error, but didn't get one") 769 } else if !strings.Contains(err.Error(), "does not match the namespace sent on the request") { 770 t.Errorf("expected 'does not match the namespace sent on the request' error, got '%v'", err.Error()) 771 } 772 } 773 774 // ============================================================================= 775 // Deletion tests. 776 777 func (t *Tester) testDeleteNoGraceful(obj runtime.Object, createFn CreateFunc, getFn GetFunc, isNotFoundFn IsErrorFunc, dryRun bool) { 778 ctx := t.TestContext() 779 780 foo := obj.DeepCopyObject() 781 t.setObjectMeta(foo, t.namer(1)) 782 if err := createFn(ctx, foo); err != nil { 783 t.Errorf("unexpected error: %v", err) 784 } 785 defer t.delete(ctx, foo) 786 objectMeta := t.getObjectMetaOrFail(foo) 787 opts := metav1.NewDeleteOptions(10) 788 if dryRun { 789 opts.DryRun = []string{metav1.DryRunAll} 790 } 791 obj, wasDeleted, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, opts) 792 if err != nil { 793 t.Errorf("unexpected error: %v", err) 794 } 795 if !wasDeleted { 796 t.Errorf("unexpected, object %s should have been deleted immediately", objectMeta.GetName()) 797 } 798 if !t.returnDeletedObject { 799 if status, ok := obj.(*metav1.Status); !ok { 800 t.Errorf("expected status of delete, got %v", status) 801 } else if status.Status != metav1.StatusSuccess { 802 t.Errorf("expected success, got: %v", status.Status) 803 } 804 } 805 806 _, err = getFn(ctx, foo) 807 if !dryRun && (err == nil || !isNotFoundFn(err)) { 808 t.Errorf("unexpected error: %v", err) 809 } else if dryRun && isNotFoundFn(err) { 810 t.Error("object should not have been removed in dry-run") 811 } 812 } 813 814 func (t *Tester) testDeleteNonExist(obj runtime.Object, opts metav1.DeleteOptions) { 815 objectMeta := t.getObjectMetaOrFail(obj) 816 817 _, _, err := t.storage.(rest.GracefulDeleter).Delete(t.TestContext(), objectMeta.GetName(), rest.ValidateAllObjectFunc, &opts) 818 if err == nil || !errors.IsNotFound(err) { 819 t.Errorf("unexpected error: %v", err) 820 } 821 822 } 823 824 // This test the fast-fail path. We test that the precondition gets verified 825 // again before deleting the object in tests of pkg/storage/etcd. 826 func (t *Tester) testDeleteWithUID(obj runtime.Object, createFn CreateFunc, getFn GetFunc, isNotFoundFn IsErrorFunc, opts metav1.DeleteOptions) { 827 ctx := t.TestContext() 828 829 foo := obj.DeepCopyObject() 830 t.setObjectMeta(foo, t.namer(1)) 831 objectMeta := t.getObjectMetaOrFail(foo) 832 objectMeta.SetUID(types.UID("UID0000")) 833 if err := createFn(ctx, foo); err != nil { 834 t.Errorf("unexpected error: %v", err) 835 } 836 opts.Preconditions = metav1.NewPreconditionDeleteOptions("UID1111").Preconditions 837 _, _, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, &opts) 838 if err == nil || !errors.IsConflict(err) { 839 t.Errorf("unexpected error: %v", err) 840 } 841 842 obj, _, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, metav1.NewPreconditionDeleteOptions("UID0000")) 843 if err != nil { 844 t.Errorf("unexpected error: %v", err) 845 } 846 847 if !t.returnDeletedObject { 848 if status, ok := obj.(*metav1.Status); !ok { 849 t.Errorf("expected status of delete, got %v", status) 850 } else if status.Status != metav1.StatusSuccess { 851 t.Errorf("expected success, got: %v", status.Status) 852 } 853 } 854 855 _, err = getFn(ctx, foo) 856 if err == nil || !isNotFoundFn(err) { 857 t.Errorf("unexpected error: %v", err) 858 } 859 } 860 861 // This test the fast-fail path. We test that the precondition gets verified 862 // again before deleting the object in tests of pkg/storage/etcd. 863 func (t *Tester) testDeleteWithResourceVersion(obj runtime.Object, createFn CreateFunc, getFn GetFunc, isNotFoundFn IsErrorFunc, opts metav1.DeleteOptions) { 864 ctx := t.TestContext() 865 866 foo := obj.DeepCopyObject() 867 t.setObjectMeta(foo, t.namer(1)) 868 if err := createFn(ctx, foo); err != nil { 869 t.Errorf("unexpected error: %v", err) 870 } 871 newObj, err := getFn(ctx, foo) 872 if err != nil { 873 t.Errorf("unexpected error: %v", err) 874 } 875 objectMeta := t.getObjectMetaOrFail(newObj) 876 877 opts.Preconditions = metav1.NewRVDeletionPrecondition("wrongVersion").Preconditions 878 _, wasDeleted, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, &opts) 879 if err == nil || !errors.IsConflict(err) { 880 t.Errorf("unexpected error: %v", err) 881 } 882 if wasDeleted { 883 t.Errorf("unexpected, object %s should not have been deleted immediately", objectMeta.GetName()) 884 } 885 obj, _, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, metav1.NewRVDeletionPrecondition(objectMeta.GetResourceVersion())) 886 if err != nil { 887 t.Errorf("unexpected error: %v", err) 888 } 889 890 if !t.returnDeletedObject { 891 if status, ok := obj.(*metav1.Status); !ok { 892 t.Errorf("expected status of delete, got %v", status) 893 } else if status.Status != metav1.StatusSuccess { 894 t.Errorf("expected success, got: %v", status.Status) 895 } 896 } 897 898 _, err = getFn(ctx, foo) 899 if err == nil || !isNotFoundFn(err) { 900 t.Errorf("unexpected error: %v", err) 901 } 902 } 903 904 // ============================================================================= 905 // Graceful Deletion tests. 906 907 func (t *Tester) testDeleteDryRunGracefulHasdefault(obj runtime.Object, createFn CreateFunc, expectedGrace int64) { 908 ctx := t.TestContext() 909 910 foo := obj.DeepCopyObject() 911 t.setObjectMeta(foo, t.namer(1)) 912 defer t.delete(ctx, foo) 913 if err := createFn(ctx, foo); err != nil { 914 t.Errorf("unexpected error: %v", err) 915 } 916 objectMeta := t.getObjectMetaOrFail(foo) 917 object, wasDeleted, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, &metav1.DeleteOptions{DryRun: []string{metav1.DryRunAll}}) 918 if err != nil { 919 t.Errorf("unexpected error: %v", err) 920 } 921 if wasDeleted { 922 t.Errorf("unexpected, object %s should not have been deleted immediately", objectMeta.GetName()) 923 } 924 objectMeta = t.getObjectMetaOrFail(object) 925 if objectMeta.GetDeletionTimestamp() == nil || objectMeta.GetDeletionGracePeriodSeconds() == nil || *objectMeta.GetDeletionGracePeriodSeconds() != expectedGrace { 926 t.Errorf("unexpected deleted meta: %#v", objectMeta) 927 } 928 _, _, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, &metav1.DeleteOptions{}) 929 if err != nil { 930 t.Errorf("unexpected error: %v", err) 931 } 932 } 933 934 func (t *Tester) testDeleteGracefulHasDefault(obj runtime.Object, createFn CreateFunc, getFn GetFunc, expectedGrace int64) { 935 ctx := t.TestContext() 936 937 foo := obj.DeepCopyObject() 938 t.setObjectMeta(foo, t.namer(1)) 939 if err := createFn(ctx, foo); err != nil { 940 t.Errorf("unexpected error: %v", err) 941 } 942 objectMeta := t.getObjectMetaOrFail(foo) 943 generation := objectMeta.GetGeneration() 944 _, wasDeleted, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, &metav1.DeleteOptions{}) 945 if err != nil { 946 t.Errorf("unexpected error: %v", err) 947 } 948 if wasDeleted { 949 t.Errorf("unexpected, object %s should not have been deleted immediately", objectMeta.GetName()) 950 } 951 if _, err := getFn(ctx, foo); err != nil { 952 t.Fatalf("did not gracefully delete resource: %v", err) 953 } 954 955 object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.GetName(), &metav1.GetOptions{}) 956 if err != nil { 957 t.Fatalf("unexpected error, object should exist: %v", err) 958 } 959 objectMeta = t.getObjectMetaOrFail(object) 960 if objectMeta.GetDeletionTimestamp() == nil || objectMeta.GetDeletionGracePeriodSeconds() == nil || *objectMeta.GetDeletionGracePeriodSeconds() != expectedGrace { 961 t.Errorf("unexpected deleted meta: %#v", objectMeta) 962 } 963 if generation >= objectMeta.GetGeneration() { 964 t.Error("Generation wasn't bumped when deletion timestamp was set") 965 } 966 } 967 968 func (t *Tester) testDeleteGracefulWithValue(obj runtime.Object, createFn CreateFunc, getFn GetFunc, expectedGrace int64) { 969 ctx := t.TestContext() 970 971 foo := obj.DeepCopyObject() 972 t.setObjectMeta(foo, t.namer(2)) 973 if err := createFn(ctx, foo); err != nil { 974 t.Errorf("unexpected error: %v", err) 975 } 976 objectMeta := t.getObjectMetaOrFail(foo) 977 generation := objectMeta.GetGeneration() 978 _, wasDeleted, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, metav1.NewDeleteOptions(expectedGrace+2)) 979 if err != nil { 980 t.Errorf("unexpected error: %v", err) 981 } 982 if wasDeleted { 983 t.Errorf("unexpected, object %s should not have been deleted immediately", objectMeta.GetName()) 984 } 985 if _, err := getFn(ctx, foo); err != nil { 986 t.Fatalf("did not gracefully delete resource: %v", err) 987 } 988 989 object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.GetName(), &metav1.GetOptions{}) 990 if err != nil { 991 t.Errorf("unexpected error, object should exist: %v", err) 992 } 993 objectMeta = t.getObjectMetaOrFail(object) 994 if objectMeta.GetDeletionTimestamp() == nil || objectMeta.GetDeletionGracePeriodSeconds() == nil || *objectMeta.GetDeletionGracePeriodSeconds() != expectedGrace+2 { 995 t.Errorf("unexpected deleted meta: %#v", objectMeta) 996 } 997 if generation >= objectMeta.GetGeneration() { 998 t.Error("Generation wasn't bumped when deletion timestamp was set") 999 } 1000 } 1001 1002 func (t *Tester) testDeleteGracefulExtend(obj runtime.Object, createFn CreateFunc, getFn GetFunc, expectedGrace int64) { 1003 ctx := t.TestContext() 1004 1005 foo := obj.DeepCopyObject() 1006 t.setObjectMeta(foo, t.namer(3)) 1007 if err := createFn(ctx, foo); err != nil { 1008 t.Errorf("unexpected error: %v", err) 1009 } 1010 objectMeta := t.getObjectMetaOrFail(foo) 1011 generation := objectMeta.GetGeneration() 1012 _, wasDeleted, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, metav1.NewDeleteOptions(expectedGrace)) 1013 if err != nil { 1014 t.Errorf("unexpected error: %v", err) 1015 } 1016 if wasDeleted { 1017 t.Errorf("unexpected, object %s should not have been deleted immediately", objectMeta.GetName()) 1018 } 1019 if _, err := getFn(ctx, foo); err != nil { 1020 t.Fatalf("did not gracefully delete resource: %v", err) 1021 } 1022 1023 // second delete duration is ignored 1024 _, wasDeleted, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, metav1.NewDeleteOptions(expectedGrace+2)) 1025 if err != nil { 1026 t.Errorf("unexpected error: %v", err) 1027 } 1028 if wasDeleted { 1029 t.Errorf("unexpected, object %s should not have been deleted immediately", objectMeta.GetName()) 1030 } 1031 object, err := t.storage.(rest.Getter).Get(ctx, objectMeta.GetName(), &metav1.GetOptions{}) 1032 if err != nil { 1033 t.Errorf("unexpected error, object should exist: %v", err) 1034 } 1035 objectMeta = t.getObjectMetaOrFail(object) 1036 if objectMeta.GetDeletionTimestamp() == nil || objectMeta.GetDeletionGracePeriodSeconds() == nil || *objectMeta.GetDeletionGracePeriodSeconds() != expectedGrace { 1037 t.Errorf("unexpected deleted meta: %#v", objectMeta) 1038 } 1039 if generation >= objectMeta.GetGeneration() { 1040 t.Error("Generation wasn't bumped when deletion timestamp was set") 1041 } 1042 } 1043 1044 func (t *Tester) testDeleteGracefulImmediate(obj runtime.Object, createFn CreateFunc, getFn GetFunc, expectedGrace int64) { 1045 ctx := t.TestContext() 1046 1047 foo := obj.DeepCopyObject() 1048 t.setObjectMeta(foo, "foo4") 1049 if err := createFn(ctx, foo); err != nil { 1050 t.Errorf("unexpected error: %v", err) 1051 } 1052 objectMeta := t.getObjectMetaOrFail(foo) 1053 generation := objectMeta.GetGeneration() 1054 _, wasDeleted, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, metav1.NewDeleteOptions(expectedGrace)) 1055 if err != nil { 1056 t.Errorf("unexpected error: %v", err) 1057 } 1058 if wasDeleted { 1059 t.Errorf("unexpected, object %s should not have been deleted immediately", objectMeta.GetName()) 1060 } 1061 if _, err := getFn(ctx, foo); err != nil { 1062 t.Fatalf("did not gracefully delete resource: %v", err) 1063 } 1064 1065 // second delete is immediate, resource is deleted 1066 out, wasDeleted, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, metav1.NewDeleteOptions(0)) 1067 if err != nil { 1068 t.Errorf("unexpected error: %v", err) 1069 } 1070 if !wasDeleted { 1071 t.Errorf("unexpected, object %s should have been deleted immediately", objectMeta.GetName()) 1072 } 1073 _, err = t.storage.(rest.Getter).Get(ctx, objectMeta.GetName(), &metav1.GetOptions{}) 1074 if !errors.IsNotFound(err) { 1075 t.Errorf("unexpected error, object should be deleted immediately: %v", err) 1076 } 1077 objectMeta = t.getObjectMetaOrFail(out) 1078 // the second delete shouldn't update the object, so the objectMeta.GetDeletionGracePeriodSeconds() should eqaul to the value set in the first delete. 1079 if objectMeta.GetDeletionTimestamp() == nil || objectMeta.GetDeletionGracePeriodSeconds() == nil || *objectMeta.GetDeletionGracePeriodSeconds() != 0 { 1080 t.Errorf("unexpected deleted meta: %#v", objectMeta) 1081 } 1082 if generation >= objectMeta.GetGeneration() { 1083 t.Error("Generation wasn't bumped when deletion timestamp was set") 1084 } 1085 } 1086 1087 func (t *Tester) testDeleteGracefulUsesZeroOnNil(obj runtime.Object, createFn CreateFunc, expectedGrace int64) { 1088 ctx := t.TestContext() 1089 1090 foo := obj.DeepCopyObject() 1091 t.setObjectMeta(foo, t.namer(5)) 1092 if err := createFn(ctx, foo); err != nil { 1093 t.Errorf("unexpected error: %v", err) 1094 } 1095 objectMeta := t.getObjectMetaOrFail(foo) 1096 _, wasDeleted, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, nil) 1097 if err != nil { 1098 t.Errorf("unexpected error: %v", err) 1099 } 1100 if !wasDeleted { 1101 t.Errorf("unexpected, object %s should have been deleted immediately", objectMeta.GetName()) 1102 } 1103 if _, err := t.storage.(rest.Getter).Get(ctx, objectMeta.GetName(), &metav1.GetOptions{}); !errors.IsNotFound(err) { 1104 t.Errorf("unexpected error, object should not exist: %v", err) 1105 } 1106 } 1107 1108 // Regression test for bug discussed in #27539 1109 func (t *Tester) testDeleteGracefulShorten(obj runtime.Object, createFn CreateFunc, getFn GetFunc, expectedGrace int64) { 1110 ctx := t.TestContext() 1111 1112 foo := obj.DeepCopyObject() 1113 t.setObjectMeta(foo, t.namer(6)) 1114 if err := createFn(ctx, foo); err != nil { 1115 t.Errorf("unexpected error: %v", err) 1116 } 1117 bigGrace := int64(time.Hour) 1118 if expectedGrace > bigGrace { 1119 bigGrace = 2 * expectedGrace 1120 } 1121 objectMeta := t.getObjectMetaOrFail(foo) 1122 _, wasDeleted, err := t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, metav1.NewDeleteOptions(bigGrace)) 1123 if err != nil { 1124 t.Errorf("unexpected error: %v", err) 1125 } 1126 if wasDeleted { 1127 t.Errorf("unexpected, object %s should not have been deleted immediately", objectMeta.GetName()) 1128 } 1129 object, err := getFn(ctx, foo) 1130 if err != nil { 1131 t.Fatalf("did not gracefully delete resource: %v", err) 1132 } 1133 objectMeta = t.getObjectMetaOrFail(object) 1134 deletionTimestamp := *objectMeta.GetDeletionTimestamp() 1135 1136 // second delete duration is ignored 1137 _, wasDeleted, err = t.storage.(rest.GracefulDeleter).Delete(ctx, objectMeta.GetName(), rest.ValidateAllObjectFunc, metav1.NewDeleteOptions(expectedGrace)) 1138 if err != nil { 1139 t.Errorf("unexpected error: %v", err) 1140 } 1141 if wasDeleted { 1142 t.Errorf("unexpected, object %s should not have been deleted immediately", objectMeta.GetName()) 1143 } 1144 object, err = t.storage.(rest.Getter).Get(ctx, objectMeta.GetName(), &metav1.GetOptions{}) 1145 if err != nil { 1146 t.Errorf("unexpected error, object should exist: %v", err) 1147 } 1148 objectMeta = t.getObjectMetaOrFail(object) 1149 if objectMeta.GetDeletionTimestamp() == nil || objectMeta.GetDeletionGracePeriodSeconds() == nil || 1150 *objectMeta.GetDeletionGracePeriodSeconds() != expectedGrace || !objectMeta.GetDeletionTimestamp().Before(&deletionTimestamp) { 1151 t.Errorf("unexpected deleted meta: %#v", objectMeta) 1152 } 1153 } 1154 1155 // ============================================================================= 1156 // Get tests. 1157 1158 // testGetDifferentNamespace ensures same-name objects in different namespaces do not clash 1159 func (t *Tester) testGetDifferentNamespace(obj runtime.Object) { 1160 if t.clusterScope { 1161 t.Fatalf("the test does not work in cluster-scope") 1162 } 1163 1164 objMeta := t.getObjectMetaOrFail(obj) 1165 objMeta.SetName(t.namer(5)) 1166 1167 ctx1 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar3") 1168 objMeta.SetNamespace(genericapirequest.NamespaceValue(ctx1)) 1169 _, err := t.storage.(rest.Creater).Create(ctx1, obj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 1170 if err != nil { 1171 t.Errorf("unexpected error: %v", err) 1172 } 1173 1174 ctx2 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar4") 1175 objMeta.SetNamespace(genericapirequest.NamespaceValue(ctx2)) 1176 _, err = t.storage.(rest.Creater).Create(ctx2, obj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 1177 if err != nil { 1178 t.Errorf("unexpected error: %v", err) 1179 } 1180 1181 got1, err := t.storage.(rest.Getter).Get(ctx1, objMeta.GetName(), &metav1.GetOptions{}) 1182 if err != nil { 1183 t.Errorf("unexpected error: %v", err) 1184 } 1185 got1Meta := t.getObjectMetaOrFail(got1) 1186 if got1Meta.GetName() != objMeta.GetName() { 1187 t.Errorf("unexpected name of object: %#v, expected: %s", got1, objMeta.GetName()) 1188 } 1189 if got1Meta.GetNamespace() != genericapirequest.NamespaceValue(ctx1) { 1190 t.Errorf("unexpected namespace of object: %#v, expected: %s", got1, genericapirequest.NamespaceValue(ctx1)) 1191 } 1192 1193 got2, err := t.storage.(rest.Getter).Get(ctx2, objMeta.GetName(), &metav1.GetOptions{}) 1194 if err != nil { 1195 t.Errorf("unexpected error: %v", err) 1196 } 1197 got2Meta := t.getObjectMetaOrFail(got2) 1198 if got2Meta.GetName() != objMeta.GetName() { 1199 t.Errorf("unexpected name of object: %#v, expected: %s", got2, objMeta.GetName()) 1200 } 1201 if got2Meta.GetNamespace() != genericapirequest.NamespaceValue(ctx2) { 1202 t.Errorf("unexpected namespace of object: %#v, expected: %s", got2, genericapirequest.NamespaceValue(ctx2)) 1203 } 1204 } 1205 1206 func (t *Tester) testGetFound(obj runtime.Object) { 1207 ctx := t.TestContext() 1208 t.setObjectMeta(obj, t.namer(1)) 1209 1210 existing, err := t.storage.(rest.Creater).Create(ctx, obj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 1211 if err != nil { 1212 t.Errorf("unexpected error: %v", err) 1213 } 1214 existingMeta := t.getObjectMetaOrFail(existing) 1215 1216 got, err := t.storage.(rest.Getter).Get(ctx, t.namer(1), &metav1.GetOptions{}) 1217 if err != nil { 1218 t.Errorf("unexpected error: %v", err) 1219 } 1220 gotMeta := t.getObjectMetaOrFail(got) 1221 gotMeta.SetResourceVersion(existingMeta.GetResourceVersion()) 1222 if e, a := existing, got; !apiequality.Semantic.DeepEqual(e, a) { 1223 t.Errorf("unexpected obj: %#v, expected %#v", e, a) 1224 } 1225 } 1226 1227 func (t *Tester) testGetMimatchedNamespace(obj runtime.Object) { 1228 ctx1 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar1") 1229 ctx2 := genericapirequest.WithNamespace(genericapirequest.NewContext(), "bar2") 1230 objMeta := t.getObjectMetaOrFail(obj) 1231 objMeta.SetName(t.namer(4)) 1232 objMeta.SetNamespace(genericapirequest.NamespaceValue(ctx1)) 1233 _, err := t.storage.(rest.Creater).Create(ctx1, obj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 1234 if err != nil { 1235 t.Errorf("unexpected error: %v", err) 1236 } 1237 _, err = t.storage.(rest.Getter).Get(ctx2, t.namer(4), &metav1.GetOptions{}) 1238 if t.clusterScope { 1239 if err != nil { 1240 t.Errorf("unexpected error: %v", err) 1241 } 1242 } else { 1243 if !errors.IsNotFound(err) { 1244 t.Errorf("unexpected error returned: %#v", err) 1245 } 1246 } 1247 } 1248 1249 func (t *Tester) testGetNotFound(obj runtime.Object) { 1250 ctx := t.TestContext() 1251 t.setObjectMeta(obj, t.namer(2)) 1252 _, err := t.storage.(rest.Creater).Create(ctx, obj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 1253 if err != nil { 1254 t.Errorf("unexpected error: %v", err) 1255 } 1256 _, err = t.storage.(rest.Getter).Get(ctx, t.namer(3), &metav1.GetOptions{}) 1257 if !errors.IsNotFound(err) { 1258 t.Errorf("unexpected error returned: %#v", err) 1259 } 1260 } 1261 1262 // ============================================================================= 1263 // List tests. 1264 1265 func listToItems(listObj runtime.Object) ([]runtime.Object, error) { 1266 v, err := conversion.EnforcePtr(listObj) 1267 if err != nil { 1268 return nil, fmt.Errorf("unexpected error: %v", err) 1269 } 1270 items := v.FieldByName("Items") 1271 if !items.IsValid() { 1272 return nil, fmt.Errorf("unexpected Items field in %v", listObj) 1273 } 1274 if items.Type().Kind() != reflect.Slice { 1275 return nil, fmt.Errorf("unexpected Items field type: %v", items.Type().Kind()) 1276 } 1277 result := make([]runtime.Object, items.Len()) 1278 for i := 0; i < items.Len(); i++ { 1279 result[i] = items.Index(i).Addr().Interface().(runtime.Object) 1280 } 1281 return result, nil 1282 } 1283 1284 func (t *Tester) testListFound(obj runtime.Object, assignFn AssignFunc) { 1285 ctx := t.TestContext() 1286 1287 foo1 := obj.DeepCopyObject() 1288 t.setObjectMeta(foo1, t.namer(1)) 1289 foo2 := obj.DeepCopyObject() 1290 t.setObjectMeta(foo2, t.namer(2)) 1291 1292 existing := assignFn([]runtime.Object{foo1, foo2}) 1293 1294 listObj, err := t.storage.(rest.Lister).List(ctx, nil) 1295 if err != nil { 1296 t.Errorf("unexpected error: %v", err) 1297 } 1298 items, err := listToItems(listObj) 1299 if err != nil { 1300 t.Errorf("unexpected error: %v", err) 1301 } 1302 if len(items) != len(existing) { 1303 t.Errorf("unexpected number of items: %v", len(items)) 1304 } 1305 if !apiequality.Semantic.DeepEqual(existing, items) { 1306 t.Errorf("expected: %#v, got: %#v", existing, items) 1307 } 1308 } 1309 1310 func (t *Tester) testListMatchLabels(obj runtime.Object, assignFn AssignFunc) { 1311 ctx := t.TestContext() 1312 testLabels := map[string]string{"key": "value"} 1313 1314 foo3 := obj.DeepCopyObject() 1315 t.setObjectMeta(foo3, "foo3") 1316 foo4 := obj.DeepCopyObject() 1317 foo4Meta := t.getObjectMetaOrFail(foo4) 1318 foo4Meta.SetName("foo4") 1319 foo4Meta.SetNamespace(genericapirequest.NamespaceValue(ctx)) 1320 foo4Meta.SetLabels(testLabels) 1321 1322 objs := ([]runtime.Object{foo3, foo4}) 1323 1324 assignFn(objs) 1325 filtered := []runtime.Object{objs[1]} 1326 1327 selector := labels.SelectorFromSet(labels.Set(testLabels)) 1328 options := &metainternalversion.ListOptions{LabelSelector: selector} 1329 listObj, err := t.storage.(rest.Lister).List(ctx, options) 1330 if err != nil { 1331 t.Errorf("unexpected error: %v", err) 1332 } 1333 items, err := listToItems(listObj) 1334 if err != nil { 1335 t.Errorf("unexpected error: %v", err) 1336 } 1337 if len(items) != len(filtered) { 1338 t.Errorf("unexpected number of items: %v", len(items)) 1339 } 1340 if !apiequality.Semantic.DeepEqual(filtered, items) { 1341 t.Errorf("expected: %#v, got: %#v", filtered, items) 1342 } 1343 } 1344 1345 func (t *Tester) testListNotFound(assignFn AssignFunc) { 1346 ctx := t.TestContext() 1347 _ = assignFn([]runtime.Object{}) 1348 1349 listObj, err := t.storage.(rest.Lister).List(ctx, nil) 1350 if err != nil { 1351 t.Errorf("unexpected error: %v", err) 1352 } 1353 items, err := listToItems(listObj) 1354 if err != nil { 1355 t.Errorf("unexpected error: %v", err) 1356 } 1357 if len(items) != 0 { 1358 t.Errorf("unexpected items: %#v", items) 1359 } 1360 } 1361 1362 // testListTableConversion verifies a set of known bounds and expected limitations for the values 1363 // returned from a TableList. These conditions may be changed if necessary with adequate review. 1364 func (t *Tester) testListTableConversion(obj runtime.Object, assignFn AssignFunc) { 1365 ctx := t.TestContext() 1366 testLabels := map[string]string{"key": "value"} 1367 1368 foo3 := obj.DeepCopyObject() 1369 t.setObjectMeta(foo3, "foo3") 1370 foo4 := obj.DeepCopyObject() 1371 foo4Meta := t.getObjectMetaOrFail(foo4) 1372 foo4Meta.SetName("foo4") 1373 foo4Meta.SetNamespace(genericapirequest.NamespaceValue(ctx)) 1374 foo4Meta.SetLabels(testLabels) 1375 1376 objs := ([]runtime.Object{foo3, foo4}) 1377 1378 assignFn(objs) 1379 1380 options := &metainternalversion.ListOptions{} 1381 listObj, err := t.storage.(rest.Lister).List(ctx, options) 1382 if err != nil { 1383 t.Errorf("unexpected error: %v", err) 1384 } 1385 items, err := listToItems(listObj) 1386 if err != nil { 1387 t.Errorf("unexpected error: %v", err) 1388 } 1389 if len(items) != len(objs) { 1390 t.Errorf("unexpected number of items: %v", len(items)) 1391 } 1392 if !apiequality.Semantic.DeepEqual(objs, items) { 1393 t.Errorf("expected: %#v, got: %#v", objs, items) 1394 } 1395 1396 m, err := meta.ListAccessor(listObj) 1397 if err != nil { 1398 t.Fatalf("list should support ListMeta %T: %v", listObj, err) 1399 } 1400 m.SetContinue("continuetoken") 1401 m.SetResourceVersion("11") 1402 1403 table, err := t.storage.(rest.TableConvertor).ConvertToTable(ctx, listObj, nil) 1404 if err != nil { 1405 t.Errorf("unexpected error: %v", err) 1406 } 1407 if table.ResourceVersion != "11" || table.Continue != "continuetoken" { 1408 t.Errorf("printer lost list meta: %#v", table.ListMeta) 1409 } 1410 if len(table.Rows) != len(items) { 1411 t.Errorf("unexpected number of rows: %v", len(table.Rows)) 1412 } 1413 columns := table.ColumnDefinitions 1414 if len(columns) == 0 { 1415 t.Fatalf("unexpected number of columns: %v\n%#v", len(columns), columns) 1416 } 1417 if !strings.EqualFold(columns[0].Name, "Name") || columns[0].Type != "string" || columns[0].Format != "name" { 1418 t.Errorf("expect column 0 to be the name column: %#v", columns[0]) 1419 } 1420 for j, column := range columns { 1421 if len(column.Name) == 0 { 1422 t.Errorf("column %d has no name", j) 1423 } 1424 switch column.Type { 1425 case "string", "date", "integer", "number", "boolean": 1426 default: 1427 t.Errorf("column %d has unexpected type: %q", j, column.Type) 1428 } 1429 switch { 1430 case column.Format == "": 1431 case column.Format == "name" && column.Type == "string": 1432 default: 1433 t.Errorf("column %d has unexpected format: %q with type %q", j, column.Format, column.Type) 1434 } 1435 if column.Priority < 0 || column.Priority > 2 { 1436 t.Errorf("column %d has unexpected priority: %q", j, column.Priority) 1437 } 1438 if len(column.Description) == 0 { 1439 t.Errorf("column %d has no description", j) 1440 } 1441 if column.Name == "Created At" && column.Type != "date" && column.Format != "" { 1442 t.Errorf("column %d looks like a created at column, but has a different type and format: %#v", j, column) 1443 } 1444 } 1445 for i, row := range table.Rows { 1446 if len(row.Cells) != len(table.ColumnDefinitions) { 1447 t.Errorf("row %d did not have the correct number of cells: %d in %v, expected %d", i, len(row.Cells), row.Cells, len(table.ColumnDefinitions)) 1448 } 1449 for j, cell := range row.Cells { 1450 // do not add to this test without discussion - may break clients 1451 switch cell.(type) { 1452 case float64, int64, int32, int, string, bool: 1453 case []interface{}: 1454 case nil: 1455 default: 1456 t.Errorf("row %d, cell %d has an unrecognized type, only JSON serialization safe types are allowed: %T ", i, j, cell) 1457 } 1458 } 1459 if len(row.Cells) != len(table.ColumnDefinitions) { 1460 t.Fatalf("unmatched row length on row %d: %#v", i, row.Cells) 1461 } 1462 } 1463 data, _ := json.MarshalIndent(table, "", " ") 1464 t.Logf("%s", string(data)) 1465 } 1466 1467 // ============================================================================= 1468 // Watching tests. 1469 1470 func (t *Tester) testWatchFields(obj runtime.Object, emitFn EmitFunc, fieldsPass, fieldsFail []fields.Set, actions []string) { 1471 ctx := t.TestContext() 1472 1473 for _, field := range fieldsPass { 1474 for _, action := range actions { 1475 options := &metainternalversion.ListOptions{FieldSelector: field.AsSelector(), ResourceVersion: "1"} 1476 watcher, err := t.storage.(rest.Watcher).Watch(ctx, options) 1477 if err != nil { 1478 t.Errorf("unexpected error: %v, %v", err, action) 1479 } 1480 1481 if err := emitFn(obj, action); err != nil { 1482 t.Errorf("unexpected error: %v", err) 1483 } 1484 1485 select { 1486 case _, ok := <-watcher.ResultChan(): 1487 if !ok { 1488 t.Errorf("watch channel should be open") 1489 } 1490 case <-time.After(wait.ForeverTestTimeout): 1491 t.Errorf("unexpected timeout from result channel") 1492 } 1493 watcher.Stop() 1494 } 1495 } 1496 1497 for _, field := range fieldsFail { 1498 for _, action := range actions { 1499 options := &metainternalversion.ListOptions{FieldSelector: field.AsSelector(), ResourceVersion: "1"} 1500 watcher, err := t.storage.(rest.Watcher).Watch(ctx, options) 1501 if err != nil { 1502 t.Errorf("unexpected error: %v", err) 1503 } 1504 if err := emitFn(obj, action); err != nil { 1505 t.Errorf("unexpected error: %v", err) 1506 } 1507 1508 select { 1509 case <-watcher.ResultChan(): 1510 t.Errorf("unexpected result from result channel") 1511 case <-time.After(time.Millisecond * 500): 1512 // expected case 1513 } 1514 watcher.Stop() 1515 } 1516 } 1517 } 1518 1519 func (t *Tester) testWatchLabels(obj runtime.Object, emitFn EmitFunc, labelsPass, labelsFail []labels.Set, actions []string) { 1520 ctx := t.TestContext() 1521 1522 for _, label := range labelsPass { 1523 for _, action := range actions { 1524 options := &metainternalversion.ListOptions{LabelSelector: label.AsSelector(), ResourceVersion: "1"} 1525 watcher, err := t.storage.(rest.Watcher).Watch(ctx, options) 1526 if err != nil { 1527 t.Errorf("unexpected error: %v", err) 1528 } 1529 if err := emitFn(obj, action); err != nil { 1530 t.Errorf("unexpected error: %v", err) 1531 } 1532 1533 select { 1534 case _, ok := <-watcher.ResultChan(): 1535 if !ok { 1536 t.Errorf("watch channel should be open") 1537 } 1538 case <-time.After(wait.ForeverTestTimeout): 1539 t.Errorf("unexpected timeout from result channel") 1540 } 1541 watcher.Stop() 1542 } 1543 } 1544 1545 for _, label := range labelsFail { 1546 for _, action := range actions { 1547 options := &metainternalversion.ListOptions{LabelSelector: label.AsSelector(), ResourceVersion: "1"} 1548 watcher, err := t.storage.(rest.Watcher).Watch(ctx, options) 1549 if err != nil { 1550 t.Errorf("unexpected error: %v", err) 1551 } 1552 if err := emitFn(obj, action); err != nil { 1553 t.Errorf("unexpected error: %v", err) 1554 } 1555 1556 select { 1557 case <-watcher.ResultChan(): 1558 t.Errorf("unexpected result from result channel") 1559 case <-time.After(time.Millisecond * 500): 1560 // expected case 1561 } 1562 watcher.Stop() 1563 } 1564 } 1565 }