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  }