k8s.io/apiserver@v0.29.3/pkg/storage/cacher/cacher_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 cacher 18 19 import ( 20 "context" 21 "fmt" 22 "testing" 23 "time" 24 25 apiequality "k8s.io/apimachinery/pkg/api/equality" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/fields" 28 "k8s.io/apimachinery/pkg/labels" 29 "k8s.io/apimachinery/pkg/runtime" 30 "k8s.io/apimachinery/pkg/runtime/schema" 31 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 32 "k8s.io/apimachinery/pkg/util/wait" 33 "k8s.io/apiserver/pkg/apis/example" 34 examplev1 "k8s.io/apiserver/pkg/apis/example/v1" 35 "k8s.io/apiserver/pkg/features" 36 "k8s.io/apiserver/pkg/storage" 37 etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing" 38 storagetesting "k8s.io/apiserver/pkg/storage/testing" 39 utilfeature "k8s.io/apiserver/pkg/util/feature" 40 featuregatetesting "k8s.io/component-base/featuregate/testing" 41 "k8s.io/utils/clock" 42 ) 43 44 func init() { 45 metav1.AddToGroupVersion(scheme, metav1.SchemeGroupVersion) 46 utilruntime.Must(example.AddToScheme(scheme)) 47 utilruntime.Must(examplev1.AddToScheme(scheme)) 48 } 49 50 // GetPodAttrs returns labels and fields of a given object for filtering purposes. 51 func GetPodAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { 52 pod, ok := obj.(*example.Pod) 53 if !ok { 54 return nil, nil, fmt.Errorf("not a pod") 55 } 56 return labels.Set(pod.ObjectMeta.Labels), PodToSelectableFields(pod), nil 57 } 58 59 // PodToSelectableFields returns a field set that represents the object 60 // TODO: fields are not labels, and the validation rules for them do not apply. 61 func PodToSelectableFields(pod *example.Pod) fields.Set { 62 // The purpose of allocation with a given number of elements is to reduce 63 // amount of allocations needed to create the fields.Set. If you add any 64 // field here or the number of object-meta related fields changes, this should 65 // be adjusted. 66 podSpecificFieldsSet := make(fields.Set, 5) 67 podSpecificFieldsSet["spec.nodeName"] = pod.Spec.NodeName 68 podSpecificFieldsSet["spec.restartPolicy"] = string(pod.Spec.RestartPolicy) 69 podSpecificFieldsSet["status.phase"] = string(pod.Status.Phase) 70 return AddObjectMetaFieldsSet(podSpecificFieldsSet, &pod.ObjectMeta, true) 71 } 72 73 func AddObjectMetaFieldsSet(source fields.Set, objectMeta *metav1.ObjectMeta, hasNamespaceField bool) fields.Set { 74 source["metadata.name"] = objectMeta.Name 75 if hasNamespaceField { 76 source["metadata.namespace"] = objectMeta.Namespace 77 } 78 return source 79 } 80 81 func checkStorageInvariants(ctx context.Context, t *testing.T, key string) { 82 // No-op function since cacher simply passes object creation to the underlying storage. 83 } 84 85 func TestCreate(t *testing.T) { 86 ctx, cacher, terminate := testSetup(t) 87 t.Cleanup(terminate) 88 storagetesting.RunTestCreate(ctx, t, cacher, checkStorageInvariants) 89 } 90 91 func TestCreateWithTTL(t *testing.T) { 92 ctx, cacher, terminate := testSetup(t) 93 t.Cleanup(terminate) 94 storagetesting.RunTestCreateWithTTL(ctx, t, cacher) 95 } 96 97 func TestCreateWithKeyExist(t *testing.T) { 98 ctx, cacher, terminate := testSetup(t) 99 t.Cleanup(terminate) 100 storagetesting.RunTestCreateWithKeyExist(ctx, t, cacher) 101 } 102 103 func TestGet(t *testing.T) { 104 ctx, cacher, terminate := testSetup(t) 105 t.Cleanup(terminate) 106 storagetesting.RunTestGet(ctx, t, cacher) 107 } 108 109 func TestUnconditionalDelete(t *testing.T) { 110 ctx, cacher, terminate := testSetup(t) 111 t.Cleanup(terminate) 112 storagetesting.RunTestUnconditionalDelete(ctx, t, cacher) 113 } 114 115 func TestConditionalDelete(t *testing.T) { 116 ctx, cacher, terminate := testSetup(t) 117 t.Cleanup(terminate) 118 storagetesting.RunTestConditionalDelete(ctx, t, cacher) 119 } 120 121 func TestDeleteWithSuggestion(t *testing.T) { 122 ctx, cacher, terminate := testSetup(t) 123 t.Cleanup(terminate) 124 storagetesting.RunTestDeleteWithSuggestion(ctx, t, cacher) 125 } 126 127 func TestDeleteWithSuggestionAndConflict(t *testing.T) { 128 ctx, cacher, terminate := testSetup(t) 129 t.Cleanup(terminate) 130 storagetesting.RunTestDeleteWithSuggestionAndConflict(ctx, t, cacher) 131 } 132 133 func TestDeleteWithSuggestionOfDeletedObject(t *testing.T) { 134 ctx, cacher, terminate := testSetup(t) 135 t.Cleanup(terminate) 136 storagetesting.RunTestDeleteWithSuggestionOfDeletedObject(ctx, t, cacher) 137 } 138 139 func TestValidateDeletionWithSuggestion(t *testing.T) { 140 ctx, cacher, terminate := testSetup(t) 141 t.Cleanup(terminate) 142 storagetesting.RunTestValidateDeletionWithSuggestion(ctx, t, cacher) 143 } 144 145 func TestValidateDeletionWithOnlySuggestionValid(t *testing.T) { 146 ctx, cacher, terminate := testSetup(t) 147 t.Cleanup(terminate) 148 storagetesting.RunTestValidateDeletionWithOnlySuggestionValid(ctx, t, cacher) 149 } 150 151 func TestDeleteWithConflict(t *testing.T) { 152 ctx, cacher, terminate := testSetup(t) 153 t.Cleanup(terminate) 154 storagetesting.RunTestDeleteWithConflict(ctx, t, cacher) 155 } 156 157 func TestPreconditionalDeleteWithSuggestion(t *testing.T) { 158 ctx, cacher, terminate := testSetup(t) 159 t.Cleanup(terminate) 160 storagetesting.RunTestPreconditionalDeleteWithSuggestion(ctx, t, cacher) 161 } 162 163 func TestPreconditionalDeleteWithSuggestionPass(t *testing.T) { 164 ctx, cacher, terminate := testSetup(t) 165 t.Cleanup(terminate) 166 storagetesting.RunTestPreconditionalDeleteWithOnlySuggestionPass(ctx, t, cacher) 167 } 168 169 func TestList(t *testing.T) { 170 ctx, cacher, server, terminate := testSetupWithEtcdServer(t) 171 t.Cleanup(terminate) 172 storagetesting.RunTestList(ctx, t, cacher, compactStorage(cacher, server.V3Client), true) 173 } 174 175 func TestListWithListFromCache(t *testing.T) { 176 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, true)() 177 ctx, cacher, server, terminate := testSetupWithEtcdServer(t) 178 t.Cleanup(terminate) 179 storagetesting.RunTestList(ctx, t, cacher, compactStorage(cacher, server.V3Client), true) 180 } 181 182 func TestGetListNonRecursive(t *testing.T) { 183 ctx, cacher, terminate := testSetup(t) 184 t.Cleanup(terminate) 185 storagetesting.RunTestGetListNonRecursive(ctx, t, cacher) 186 } 187 188 func checkStorageCalls(t *testing.T, pageSize, estimatedProcessedObjects uint64) { 189 // No-op function for now, since cacher passes pagination calls to underlying storage. 190 } 191 192 func TestListContinuation(t *testing.T) { 193 ctx, cacher, terminate := testSetup(t) 194 t.Cleanup(terminate) 195 storagetesting.RunTestListContinuation(ctx, t, cacher, checkStorageCalls) 196 } 197 198 func TestListPaginationRareObject(t *testing.T) { 199 ctx, cacher, terminate := testSetup(t) 200 t.Cleanup(terminate) 201 storagetesting.RunTestListPaginationRareObject(ctx, t, cacher, checkStorageCalls) 202 } 203 204 func TestListContinuationWithFilter(t *testing.T) { 205 ctx, cacher, terminate := testSetup(t) 206 t.Cleanup(terminate) 207 storagetesting.RunTestListContinuationWithFilter(ctx, t, cacher, checkStorageCalls) 208 } 209 210 func TestListInconsistentContinuation(t *testing.T) { 211 ctx, cacher, terminate := testSetup(t) 212 t.Cleanup(terminate) 213 // TODO(#109831): Enable use of this by setting compaction. 214 storagetesting.RunTestListInconsistentContinuation(ctx, t, cacher, nil) 215 } 216 217 func TestConsistentList(t *testing.T) { 218 // TODO(#109831): Enable use of this test and run it. 219 } 220 221 func TestGuaranteedUpdate(t *testing.T) { 222 // TODO(#109831): Enable use of this test and run it. 223 } 224 225 func TestGuaranteedUpdateWithTTL(t *testing.T) { 226 ctx, cacher, terminate := testSetup(t) 227 t.Cleanup(terminate) 228 storagetesting.RunTestGuaranteedUpdateWithTTL(ctx, t, cacher) 229 } 230 231 func TestGuaranteedUpdateChecksStoredData(t *testing.T) { 232 // TODO(#109831): Enable use of this test and run it. 233 } 234 235 func TestGuaranteedUpdateWithConflict(t *testing.T) { 236 ctx, cacher, terminate := testSetup(t) 237 t.Cleanup(terminate) 238 storagetesting.RunTestGuaranteedUpdateWithConflict(ctx, t, cacher) 239 } 240 241 func TestGuaranteedUpdateWithSuggestionAndConflict(t *testing.T) { 242 ctx, cacher, terminate := testSetup(t) 243 t.Cleanup(terminate) 244 storagetesting.RunTestGuaranteedUpdateWithSuggestionAndConflict(ctx, t, cacher) 245 } 246 247 func TestTransformationFailure(t *testing.T) { 248 // TODO(#109831): Enable use of this test and run it. 249 } 250 251 func TestCount(t *testing.T) { 252 ctx, cacher, terminate := testSetup(t) 253 t.Cleanup(terminate) 254 storagetesting.RunTestCount(ctx, t, cacher) 255 } 256 257 func TestWatch(t *testing.T) { 258 ctx, cacher, terminate := testSetup(t) 259 t.Cleanup(terminate) 260 storagetesting.RunTestWatch(ctx, t, cacher) 261 } 262 263 func TestWatchFromZero(t *testing.T) { 264 ctx, cacher, server, terminate := testSetupWithEtcdServer(t) 265 t.Cleanup(terminate) 266 storagetesting.RunTestWatchFromZero(ctx, t, cacher, compactStorage(cacher, server.V3Client)) 267 } 268 269 func TestDeleteTriggerWatch(t *testing.T) { 270 ctx, cacher, terminate := testSetup(t) 271 t.Cleanup(terminate) 272 storagetesting.RunTestDeleteTriggerWatch(ctx, t, cacher) 273 } 274 275 func TestWatchFromNonZero(t *testing.T) { 276 ctx, cacher, terminate := testSetup(t) 277 t.Cleanup(terminate) 278 storagetesting.RunTestWatchFromNonZero(ctx, t, cacher) 279 } 280 281 func TestDelayedWatchDelivery(t *testing.T) { 282 ctx, cacher, terminate := testSetup(t) 283 t.Cleanup(terminate) 284 storagetesting.RunTestDelayedWatchDelivery(ctx, t, cacher) 285 } 286 287 func TestWatchError(t *testing.T) { 288 // TODO(#109831): Enable use of this test and run it. 289 } 290 291 func TestWatchContextCancel(t *testing.T) { 292 // TODO(#109831): Enable use of this test and run it. 293 } 294 295 func TestWatcherTimeout(t *testing.T) { 296 ctx, cacher, terminate := testSetup(t) 297 t.Cleanup(terminate) 298 storagetesting.RunTestWatcherTimeout(ctx, t, cacher) 299 } 300 301 func TestWatchDeleteEventObjectHaveLatestRV(t *testing.T) { 302 ctx, cacher, terminate := testSetup(t) 303 t.Cleanup(terminate) 304 storagetesting.RunTestWatchDeleteEventObjectHaveLatestRV(ctx, t, cacher) 305 } 306 307 func TestWatchInitializationSignal(t *testing.T) { 308 ctx, cacher, terminate := testSetup(t) 309 t.Cleanup(terminate) 310 storagetesting.RunTestWatchInitializationSignal(ctx, t, cacher) 311 } 312 313 func TestClusterScopedWatch(t *testing.T) { 314 ctx, cacher, terminate := testSetup(t, withClusterScopedKeyFunc, withSpecNodeNameIndexerFuncs) 315 t.Cleanup(terminate) 316 storagetesting.RunTestClusterScopedWatch(ctx, t, cacher) 317 } 318 319 func TestNamespaceScopedWatch(t *testing.T) { 320 ctx, cacher, terminate := testSetup(t, withSpecNodeNameIndexerFuncs) 321 t.Cleanup(terminate) 322 storagetesting.RunTestNamespaceScopedWatch(ctx, t, cacher) 323 } 324 325 func TestWatchDispatchBookmarkEvents(t *testing.T) { 326 ctx, cacher, terminate := testSetup(t) 327 t.Cleanup(terminate) 328 storagetesting.RunTestWatchDispatchBookmarkEvents(ctx, t, cacher, true) 329 } 330 331 func TestWatchBookmarksWithCorrectResourceVersion(t *testing.T) { 332 ctx, cacher, terminate := testSetup(t) 333 t.Cleanup(terminate) 334 storagetesting.RunTestOptionalWatchBookmarksWithCorrectResourceVersion(ctx, t, cacher) 335 } 336 337 func TestSendInitialEventsBackwardCompatibility(t *testing.T) { 338 ctx, store, terminate := testSetup(t) 339 t.Cleanup(terminate) 340 storagetesting.RunSendInitialEventsBackwardCompatibility(ctx, t, store) 341 } 342 343 func TestCacherWatchSemantics(t *testing.T) { 344 store, terminate := testSetupWithEtcdAndCreateWrapper(t) 345 t.Cleanup(terminate) 346 storagetesting.RunWatchSemantics(context.TODO(), t, store) 347 } 348 349 func TestCacherWatchSemanticInitialEventsExtended(t *testing.T) { 350 store, terminate := testSetupWithEtcdAndCreateWrapper(t) 351 t.Cleanup(terminate) 352 storagetesting.RunWatchSemanticInitialEventsExtended(context.TODO(), t, store) 353 } 354 355 // =================================================== 356 // Test-setup related function are following. 357 // =================================================== 358 359 type tearDownFunc func() 360 361 type setupOptions struct { 362 resourcePrefix string 363 keyFunc func(runtime.Object) (string, error) 364 indexerFuncs map[string]storage.IndexerFunc 365 clock clock.WithTicker 366 } 367 368 type setupOption func(*setupOptions) 369 370 func withDefaults(options *setupOptions) { 371 prefix := "/pods" 372 373 options.resourcePrefix = prefix 374 options.keyFunc = func(obj runtime.Object) (string, error) { return storage.NamespaceKeyFunc(prefix, obj) } 375 options.clock = clock.RealClock{} 376 } 377 378 func withClusterScopedKeyFunc(options *setupOptions) { 379 options.keyFunc = func(obj runtime.Object) (string, error) { 380 return storage.NoNamespaceKeyFunc(options.resourcePrefix, obj) 381 } 382 } 383 384 func withSpecNodeNameIndexerFuncs(options *setupOptions) { 385 options.indexerFuncs = map[string]storage.IndexerFunc{ 386 "spec.nodeName": func(obj runtime.Object) string { 387 pod, ok := obj.(*example.Pod) 388 if !ok { 389 return "" 390 } 391 return pod.Spec.NodeName 392 }, 393 } 394 } 395 396 func testSetup(t *testing.T, opts ...setupOption) (context.Context, *Cacher, tearDownFunc) { 397 ctx, cacher, _, tearDown := testSetupWithEtcdServer(t, opts...) 398 return ctx, cacher, tearDown 399 } 400 401 func testSetupWithEtcdServer(t *testing.T, opts ...setupOption) (context.Context, *Cacher, *etcd3testing.EtcdTestServer, tearDownFunc) { 402 setupOpts := setupOptions{} 403 opts = append([]setupOption{withDefaults}, opts...) 404 for _, opt := range opts { 405 opt(&setupOpts) 406 } 407 408 server, etcdStorage := newEtcdTestStorage(t, etcd3testing.PathPrefix()) 409 // Inject one list error to make sure we test the relist case. 410 wrappedStorage := &storagetesting.StorageInjectingListErrors{ 411 Interface: etcdStorage, 412 Errors: 1, 413 } 414 415 config := Config{ 416 Storage: wrappedStorage, 417 Versioner: storage.APIObjectVersioner{}, 418 GroupResource: schema.GroupResource{Resource: "pods"}, 419 ResourcePrefix: setupOpts.resourcePrefix, 420 KeyFunc: setupOpts.keyFunc, 421 GetAttrsFunc: GetPodAttrs, 422 NewFunc: newPod, 423 NewListFunc: newPodList, 424 IndexerFuncs: setupOpts.indexerFuncs, 425 Codec: codecs.LegacyCodec(examplev1.SchemeGroupVersion), 426 Clock: setupOpts.clock, 427 } 428 cacher, err := NewCacherFromConfig(config) 429 if err != nil { 430 t.Fatalf("Failed to initialize cacher: %v", err) 431 } 432 ctx := context.Background() 433 terminate := func() { 434 cacher.Stop() 435 server.Terminate(t) 436 } 437 438 // Since some tests depend on the fact that GetList shouldn't fail, 439 // we wait until the error from the underlying storage is consumed. 440 if err := wait.PollInfinite(100*time.Millisecond, wrappedStorage.ErrorsConsumed); err != nil { 441 t.Fatalf("Failed to inject list errors: %v", err) 442 } 443 444 return ctx, cacher, server, terminate 445 } 446 447 func testSetupWithEtcdAndCreateWrapper(t *testing.T, opts ...setupOption) (storage.Interface, tearDownFunc) { 448 _, cacher, _, tearDown := testSetupWithEtcdServer(t, opts...) 449 450 if err := cacher.ready.wait(context.TODO()); err != nil { 451 t.Fatalf("unexpected error waiting for the cache to be ready") 452 } 453 return &createWrapper{Cacher: cacher}, tearDown 454 } 455 456 type createWrapper struct { 457 *Cacher 458 } 459 460 func (c *createWrapper) Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error { 461 if err := c.Cacher.Create(ctx, key, obj, out, ttl); err != nil { 462 return err 463 } 464 return wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, wait.ForeverTestTimeout, true, func(ctx context.Context) (bool, error) { 465 currentObj := c.Cacher.newFunc() 466 err := c.Cacher.Get(ctx, key, storage.GetOptions{ResourceVersion: "0"}, currentObj) 467 if err != nil { 468 if storage.IsNotFound(err) { 469 return false, nil 470 } 471 return false, err 472 } 473 if !apiequality.Semantic.DeepEqual(currentObj, out) { 474 return false, nil 475 } 476 return true, nil 477 }) 478 }