From 3268b0561bdf15c402ded0d7932c67cc57f4914a Mon Sep 17 00:00:00 2001 From: Tim Allclair Date: Mon, 10 Oct 2022 18:15:22 -0700 Subject: [PATCH] Validate etcd paths Kubernetes-commit: f1693a02c46f79f8c90a4ef17c4a750034f21484 --- pkg/storage/etcd3/linearized_read_test.go | 5 +- pkg/storage/etcd3/store.go | 138 +++++++++++++------- pkg/storage/etcd3/store_test.go | 90 +++++++++++-- pkg/storage/testing/store_tests.go | 146 +++++++++++----------- pkg/storage/testing/utils.go | 7 +- pkg/storage/testing/watcher_tests.go | 18 +-- pkg/storage/tests/cacher_test.go | 2 +- 7 files changed, 268 insertions(+), 138 deletions(-) diff --git a/pkg/storage/etcd3/linearized_read_test.go b/pkg/storage/etcd3/linearized_read_test.go index bb1b9df78..7331c8245 100644 --- a/pkg/storage/etcd3/linearized_read_test.go +++ b/pkg/storage/etcd3/linearized_read_test.go @@ -37,7 +37,8 @@ func TestLinearizedReadRevisionInvariant(t *testing.T) { // [1] https://etcd.io/docs/v3.5/learning/api_guarantees/#isolation-level-and-consistency-of-replicas ctx, store, etcdClient := testSetup(t) - key := "/testkey" + dir := "/testing" + key := dir + "/testkey" out := &example.Pod{} obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", SelfLink: "testlink"}} @@ -53,7 +54,7 @@ func TestLinearizedReadRevisionInvariant(t *testing.T) { } list := &example.PodList{} - if err := store.GetList(ctx, "/", storage.ListOptions{Predicate: storage.Everything, Recursive: true}, list); err != nil { + if err := store.GetList(ctx, dir, storage.ListOptions{Predicate: storage.Everything, Recursive: true}, list); err != nil { t.Errorf("Unexpected List error: %v", err) } finalRevision := list.ResourceVersion diff --git a/pkg/storage/etcd3/store.go b/pkg/storage/etcd3/store.go index a3f9d1547..51e5b9012 100644 --- a/pkg/storage/etcd3/store.go +++ b/pkg/storage/etcd3/store.go @@ -99,16 +99,21 @@ func New(c *clientv3.Client, codec runtime.Codec, newFunc func() runtime.Object, func newStore(c *clientv3.Client, codec runtime.Codec, newFunc func() runtime.Object, prefix string, groupResource schema.GroupResource, transformer value.Transformer, pagingEnabled bool, leaseManagerConfig LeaseManagerConfig) *store { versioner := storage.APIObjectVersioner{} + // for compatibility with etcd2 impl. + // no-op for default prefix of '/registry'. + // keeps compatibility with etcd2 impl for custom prefixes that don't start with '/' + pathPrefix := path.Join("/", prefix) + if !strings.HasSuffix(pathPrefix, "/") { + // Ensure the pathPrefix ends in "/" here to simplify key concatenation later. + pathPrefix += "/" + } result := &store{ - client: c, - codec: codec, - versioner: versioner, - transformer: transformer, - pagingEnabled: pagingEnabled, - // for compatibility with etcd2 impl. - // no-op for default prefix of '/registry'. - // keeps compatibility with etcd2 impl for custom prefixes that don't start with '/' - pathPrefix: path.Join("/", prefix), + client: c, + codec: codec, + versioner: versioner, + transformer: transformer, + pagingEnabled: pagingEnabled, + pathPrefix: pathPrefix, groupResource: groupResource, groupResourceString: groupResource.String(), watcher: newWatcher(c, codec, groupResource, newFunc, versioner), @@ -124,9 +129,12 @@ func (s *store) Versioner() storage.Versioner { // Get implements storage.Interface.Get. func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, out runtime.Object) error { - key = path.Join(s.pathPrefix, key) + preparedKey, err := s.prepareKey(key) + if err != nil { + return err + } startTime := time.Now() - getResp, err := s.client.KV.Get(ctx, key) + getResp, err := s.client.KV.Get(ctx, preparedKey) metrics.RecordEtcdRequestLatency("get", s.groupResourceString, startTime) if err != nil { return err @@ -139,11 +147,11 @@ func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, ou if opts.IgnoreNotFound { return runtime.SetZeroValue(out) } - return storage.NewKeyNotFoundError(key, 0) + return storage.NewKeyNotFoundError(preparedKey, 0) } kv := getResp.Kvs[0] - data, _, err := s.transformer.TransformFromStorage(ctx, kv.Value, authenticatedDataString(key)) + data, _, err := s.transformer.TransformFromStorage(ctx, kv.Value, authenticatedDataString(preparedKey)) if err != nil { return storage.NewInternalError(err.Error()) } @@ -153,6 +161,10 @@ func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, ou // Create implements storage.Interface.Create. func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error { + preparedKey, err := s.prepareKey(key) + if err != nil { + return err + } ctx, span := tracing.Start(ctx, "Create etcd3", attribute.String("audit-id", audit.GetAuditIDTruncated(ctx)), attribute.String("key", key), @@ -173,14 +185,13 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, return err } span.AddEvent("Encode succeeded", attribute.Int("len", len(data))) - key = path.Join(s.pathPrefix, key) opts, err := s.ttlOpts(ctx, int64(ttl)) if err != nil { return err } - newData, err := s.transformer.TransformToStorage(ctx, data, authenticatedDataString(key)) + newData, err := s.transformer.TransformToStorage(ctx, data, authenticatedDataString(preparedKey)) if err != nil { span.AddEvent("TransformToStorage failed", attribute.String("err", err.Error())) return storage.NewInternalError(err.Error()) @@ -189,9 +200,9 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, startTime := time.Now() txnResp, err := s.client.KV.Txn(ctx).If( - notFound(key), + notFound(preparedKey), ).Then( - clientv3.OpPut(key, string(newData), opts...), + clientv3.OpPut(preparedKey, string(newData), opts...), ).Commit() metrics.RecordEtcdRequestLatency("create", s.groupResourceString, startTime) if err != nil { @@ -201,7 +212,7 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, span.AddEvent("Txn call succeeded") if !txnResp.Succeeded { - return storage.NewKeyExistsError(key, 0) + return storage.NewKeyExistsError(preparedKey, 0) } if out != nil { @@ -220,12 +231,15 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, func (s *store) Delete( ctx context.Context, key string, out runtime.Object, preconditions *storage.Preconditions, validateDeletion storage.ValidateObjectFunc, cachedExistingObject runtime.Object) error { + preparedKey, err := s.prepareKey(key) + if err != nil { + return err + } v, err := conversion.EnforcePtr(out) if err != nil { return fmt.Errorf("unable to convert output object to pointer: %v", err) } - key = path.Join(s.pathPrefix, key) - return s.conditionalDelete(ctx, key, out, v, preconditions, validateDeletion, cachedExistingObject) + return s.conditionalDelete(ctx, preparedKey, out, v, preconditions, validateDeletion, cachedExistingObject) } func (s *store) conditionalDelete( @@ -346,6 +360,10 @@ func (s *store) conditionalDelete( func (s *store) GuaranteedUpdate( ctx context.Context, key string, destination runtime.Object, ignoreNotFound bool, preconditions *storage.Preconditions, tryUpdate storage.UpdateFunc, cachedExistingObject runtime.Object) error { + preparedKey, err := s.prepareKey(key) + if err != nil { + return err + } ctx, span := tracing.Start(ctx, "GuaranteedUpdate etcd3", attribute.String("audit-id", audit.GetAuditIDTruncated(ctx)), attribute.String("key", key), @@ -357,16 +375,15 @@ func (s *store) GuaranteedUpdate( if err != nil { return fmt.Errorf("unable to convert output object to pointer: %v", err) } - key = path.Join(s.pathPrefix, key) getCurrentState := func() (*objState, error) { startTime := time.Now() - getResp, err := s.client.KV.Get(ctx, key) + getResp, err := s.client.KV.Get(ctx, preparedKey) metrics.RecordEtcdRequestLatency("get", s.groupResourceString, startTime) if err != nil { return nil, err } - return s.getState(ctx, getResp, key, v, ignoreNotFound) + return s.getState(ctx, getResp, preparedKey, v, ignoreNotFound) } var origState *objState @@ -382,9 +399,9 @@ func (s *store) GuaranteedUpdate( } span.AddEvent("initial value restored") - transformContext := authenticatedDataString(key) + transformContext := authenticatedDataString(preparedKey) for { - if err := preconditions.Check(key, origState.obj); err != nil { + if err := preconditions.Check(preparedKey, origState.obj); err != nil { // If our data is already up to date, return the error if origStateIsCurrent { return err @@ -472,11 +489,11 @@ func (s *store) GuaranteedUpdate( startTime := time.Now() txnResp, err := s.client.KV.Txn(ctx).If( - clientv3.Compare(clientv3.ModRevision(key), "=", origState.rev), + clientv3.Compare(clientv3.ModRevision(preparedKey), "=", origState.rev), ).Then( - clientv3.OpPut(key, string(newData), opts...), + clientv3.OpPut(preparedKey, string(newData), opts...), ).Else( - clientv3.OpGet(key), + clientv3.OpGet(preparedKey), ).Commit() metrics.RecordEtcdRequestLatency("update", s.groupResourceString, startTime) if err != nil { @@ -487,8 +504,8 @@ func (s *store) GuaranteedUpdate( span.AddEvent("Transaction committed") if !txnResp.Succeeded { getResp := (*clientv3.GetResponse)(txnResp.Responses[0].GetResponseRange()) - klog.V(4).Infof("GuaranteedUpdate of %s failed because of a conflict, going to retry", key) - origState, err = s.getState(ctx, getResp, key, v, ignoreNotFound) + klog.V(4).Infof("GuaranteedUpdate of %s failed because of a conflict, going to retry", preparedKey) + origState, err = s.getState(ctx, getResp, preparedKey, v, ignoreNotFound) if err != nil { return err } @@ -526,18 +543,21 @@ func getNewItemFunc(listObj runtime.Object, v reflect.Value) func() runtime.Obje } func (s *store) Count(key string) (int64, error) { - key = path.Join(s.pathPrefix, key) + preparedKey, err := s.prepareKey(key) + if err != nil { + return 0, err + } // We need to make sure the key ended with "/" so that we only get children "directories". // e.g. if we have key "/a", "/a/b", "/ab", getting keys with prefix "/a" will return all three, // while with prefix "/a/" will return only "/a/b" which is the correct answer. - if !strings.HasSuffix(key, "/") { - key += "/" + if !strings.HasSuffix(preparedKey, "/") { + preparedKey += "/" } startTime := time.Now() - getResp, err := s.client.KV.Get(context.Background(), key, clientv3.WithRange(clientv3.GetPrefixRangeEnd(key)), clientv3.WithCountOnly()) - metrics.RecordEtcdRequestLatency("listWithCount", key, startTime) + getResp, err := s.client.KV.Get(context.Background(), preparedKey, clientv3.WithRange(clientv3.GetPrefixRangeEnd(preparedKey)), clientv3.WithCountOnly()) + metrics.RecordEtcdRequestLatency("listWithCount", preparedKey, startTime) if err != nil { return 0, err } @@ -546,6 +566,10 @@ func (s *store) Count(key string) (int64, error) { // GetList implements storage.Interface. func (s *store) GetList(ctx context.Context, key string, opts storage.ListOptions, listObj runtime.Object) error { + preparedKey, err := s.prepareKey(key) + if err != nil { + return err + } recursive := opts.Recursive resourceVersion := opts.ResourceVersion match := opts.ResourceVersionMatch @@ -566,16 +590,15 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption if err != nil || v.Kind() != reflect.Slice { return fmt.Errorf("need ptr to slice: %v", err) } - key = path.Join(s.pathPrefix, key) // For recursive lists, we need to make sure the key ended with "/" so that we only // get children "directories". e.g. if we have key "/a", "/a/b", "/ab", getting keys // with prefix "/a" will return all three, while with prefix "/a/" will return only // "/a/b" which is the correct answer. - if recursive && !strings.HasSuffix(key, "/") { - key += "/" + if recursive && !strings.HasSuffix(preparedKey, "/") { + preparedKey += "/" } - keyPrefix := key + keyPrefix := preparedKey // set the appropriate clientv3 options to filter the returned data set var limitOption *clientv3.OpOption @@ -614,7 +637,7 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption rangeEnd := clientv3.GetPrefixRangeEnd(keyPrefix) options = append(options, clientv3.WithRange(rangeEnd)) - key = continueKey + preparedKey = continueKey // If continueRV > 0, the LIST request needs a specific resource version. // continueRV==0 is invalid. @@ -681,7 +704,7 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption }() for { startTime := time.Now() - getResp, err = s.client.KV.Get(ctx, key, options...) + getResp, err = s.client.KV.Get(ctx, preparedKey, options...) if recursive { metrics.RecordEtcdRequestLatency("list", s.groupResourceString, startTime) } else { @@ -753,7 +776,7 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption } *limitOption = clientv3.WithLimit(limit) } - key = string(lastKey) + "\x00" + preparedKey = string(lastKey) + "\x00" if withRev == 0 { withRev = returnedRV options = append(options, clientv3.WithRev(withRev)) @@ -818,12 +841,15 @@ func growSlice(v reflect.Value, maxCapacity int, sizes ...int) { // Watch implements storage.Interface.Watch. func (s *store) Watch(ctx context.Context, key string, opts storage.ListOptions) (watch.Interface, error) { + preparedKey, err := s.prepareKey(key) + if err != nil { + return nil, err + } rev, err := s.versioner.ParseResourceVersion(opts.ResourceVersion) if err != nil { return nil, err } - key = path.Join(s.pathPrefix, key) - return s.watcher.Watch(ctx, key, int64(rev), opts.Recursive, opts.ProgressNotify, s.transformer, opts.Predicate) + return s.watcher.Watch(ctx, preparedKey, int64(rev), opts.Recursive, opts.ProgressNotify, s.transformer, opts.Predicate) } func (s *store) getState(ctx context.Context, getResp *clientv3.GetResponse, key string, v reflect.Value, ignoreNotFound bool) (*objState, error) { @@ -935,6 +961,30 @@ func (s *store) validateMinimumResourceVersion(minimumResourceVersion string, ac return nil } +func (s *store) prepareKey(key string) (string, error) { + if key == ".." || + strings.HasPrefix(key, "../") || + strings.HasSuffix(key, "/..") || + strings.Contains(key, "/../") { + return "", fmt.Errorf("invalid key: %q", key) + } + if key == "." || + strings.HasPrefix(key, "./") || + strings.HasSuffix(key, "/.") || + strings.Contains(key, "/./") { + return "", fmt.Errorf("invalid key: %q", key) + } + if key == "" || key == "/" { + return "", fmt.Errorf("empty key: %q", key) + } + // We ensured that pathPrefix ends in '/' in construction, so skip any leading '/' in the key now. + startIndex := 0 + if key[0] == '/' { + startIndex = 1 + } + return s.pathPrefix + key[startIndex:], nil +} + // decode decodes value of bytes into object. It will also set the object resource version to rev. // On success, objPtr would be set to the object. func decode(codec runtime.Codec, versioner storage.Versioner, value []byte, objPtr runtime.Object, rev int64) error { diff --git a/pkg/storage/etcd3/store_test.go b/pkg/storage/etcd3/store_test.go index faba8511c..ef2c0b067 100644 --- a/pkg/storage/etcd3/store_test.go +++ b/pkg/storage/etcd3/store_test.go @@ -21,8 +21,8 @@ import ( "fmt" "io/ioutil" "os" - "path" "reflect" + "strings" "sync/atomic" "testing" @@ -61,9 +61,8 @@ func newPod() runtime.Object { return &example.Pod{} } -func checkStorageInvariants(prefix string, etcdClient *clientv3.Client, codec runtime.Codec) storagetesting.KeyValidation { +func checkStorageInvariants(etcdClient *clientv3.Client, codec runtime.Codec) storagetesting.KeyValidation { return func(ctx context.Context, t *testing.T, key string) { - key = path.Join(prefix, key) getResp, err := etcdClient.KV.Get(ctx, key) if err != nil { t.Fatalf("etcdClient.KV.Get failed: %v", err) @@ -87,7 +86,7 @@ func checkStorageInvariants(prefix string, etcdClient *clientv3.Client, codec ru func TestCreate(t *testing.T) { ctx, store, etcdClient := testSetup(t) - storagetesting.RunTestCreate(ctx, t, store, checkStorageInvariants("/", etcdClient, store.codec)) + storagetesting.RunTestCreate(ctx, t, store, checkStorageInvariants(etcdClient, store.codec)) } func TestCreateWithTTL(t *testing.T) { @@ -160,7 +159,7 @@ func (s *storeWithPrefixTransformer) UpdatePrefixTransformer(modifier storagetes func TestGuaranteedUpdate(t *testing.T) { ctx, store, etcdClient := testSetup(t) - storagetesting.RunTestGuaranteedUpdate(ctx, t, &storeWithPrefixTransformer{store}, checkStorageInvariants("/", etcdClient, store.codec)) + storagetesting.RunTestGuaranteedUpdate(ctx, t, &storeWithPrefixTransformer{store}, checkStorageInvariants(etcdClient, store.codec)) } func TestGuaranteedUpdateWithTTL(t *testing.T) { @@ -287,9 +286,9 @@ func TestCount(t *testing.T) { func TestPrefix(t *testing.T) { testcases := map[string]string{ - "custom/prefix": "/custom/prefix", - "/custom//prefix//": "/custom/prefix", - "/registry": "/registry", + "custom/prefix": "/custom/prefix/", + "/custom//prefix//": "/custom/prefix/", + "/registry": "/registry/", } for configuredPrefix, effectivePrefix := range testcases { _, store, _ := testSetup(t, withPrefix(configuredPrefix)) @@ -548,3 +547,78 @@ func testSetup(t *testing.T, opts ...setupOption) (context.Context, *store, *cli ctx := context.Background() return ctx, store, client } + +func TestValidateKey(t *testing.T) { + validKeys := []string{ + "/foo/bar/baz/a.b.c/", + "/foo", + "foo/bar/baz", + "/foo/bar..baz/", + "/foo/bar..", + "foo", + "foo/bar", + "/foo/bar/", + } + invalidKeys := []string{ + "/foo/bar/../a.b.c/", + "..", + "/..", + "../", + "/foo/bar/..", + "../foo/bar", + "/../foo", + "/foo/bar/../", + ".", + "/.", + "./", + "/./", + "/foo/.", + "./bar", + "/foo/./bar/", + } + const ( + pathPrefix = "/first/second" + expectPrefix = pathPrefix + "/" + ) + _, store, _ := testSetup(t, withPrefix(pathPrefix)) + + for _, key := range validKeys { + k, err := store.prepareKey(key) + if err != nil { + t.Errorf("key %q should be valid; unexpected error: %v", key, err) + } else if !strings.HasPrefix(k, expectPrefix) { + t.Errorf("key %q should have prefix %q", k, expectPrefix) + } + } + + for _, key := range invalidKeys { + _, err := store.prepareKey(key) + if err == nil { + t.Errorf("key %q should be invalid", key) + } + } +} + +func TestInvalidKeys(t *testing.T) { + const invalidKey = "/foo/bar/../baz" + expectedError := fmt.Sprintf("invalid key: %q", invalidKey) + + expectInvalidKey := func(methodName string, err error) { + if err == nil { + t.Errorf("[%s] expected invalid key error; got nil", methodName) + } else if err.Error() != expectedError { + t.Errorf("[%s] expected invalid key error; got %v", methodName, err) + } + } + + ctx, store, _ := testSetup(t) + expectInvalidKey("Create", store.Create(ctx, invalidKey, nil, nil, 0)) + expectInvalidKey("Delete", store.Delete(ctx, invalidKey, nil, nil, nil, nil)) + _, watchErr := store.Watch(ctx, invalidKey, storage.ListOptions{}) + expectInvalidKey("Watch", watchErr) + expectInvalidKey("Get", store.Get(ctx, invalidKey, storage.GetOptions{}, nil)) + expectInvalidKey("GetList", store.GetList(ctx, invalidKey, storage.ListOptions{}, nil)) + expectInvalidKey("GuaranteedUpdate", store.GuaranteedUpdate(ctx, invalidKey, nil, true, nil, nil, nil)) + _, countErr := store.Count(invalidKey) + expectInvalidKey("Count", countErr) +} diff --git a/pkg/storage/testing/store_tests.go b/pkg/storage/testing/store_tests.go index dc9d354b0..f68a7df4f 100644 --- a/pkg/storage/testing/store_tests.go +++ b/pkg/storage/testing/store_tests.go @@ -47,7 +47,7 @@ type KeyValidation func(ctx context.Context, t *testing.T, key string) func RunTestCreate(ctx context.Context, t *testing.T, store storage.Interface, validation KeyValidation) { out := &example.Pod{} - obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", SelfLink: "testlink"}} + obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns", SelfLink: "testlink"}} // verify that kv pair is empty before set key := computePodKey(obj) @@ -73,7 +73,7 @@ func RunTestCreate(ctx context.Context, t *testing.T, store storage.Interface, v } func RunTestCreateWithTTL(ctx context.Context, t *testing.T, store storage.Interface) { - input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}} + input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}} out := &example.Pod{} key := computePodKey(input) @@ -89,7 +89,7 @@ func RunTestCreateWithTTL(ctx context.Context, t *testing.T, store storage.Inter } func RunTestCreateWithKeyExist(ctx context.Context, t *testing.T, store storage.Interface) { - obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}} + obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}} key, _ := testPropagateStore(ctx, t, store, obj) out := &example.Pod{} @@ -101,7 +101,7 @@ func RunTestCreateWithKeyExist(ctx context.Context, t *testing.T, store storage. func RunTestGet(ctx context.Context, t *testing.T, store storage.Interface) { // create an object to test - key, createdObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}) + key, createdObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}}) // update the object once to allow get by exact resource version to be tested updateObj := createdObj.DeepCopy() updateObj.Annotations = map[string]string{"test-annotation": "1"} @@ -115,7 +115,7 @@ func RunTestGet(ctx context.Context, t *testing.T, store storage.Interface) { t.Fatalf("Update failed: %v", err) } // create an additional object to increment the resource version for pods above the resource version of the foo object - secondObj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}} + secondObj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test-ns"}} lastUpdatedObj := &example.Pod{} if err := store.Create(ctx, computePodKey(secondObj), secondObj, lastUpdatedObj, 0); err != nil { t.Fatalf("Set failed: %v", err) @@ -225,7 +225,7 @@ func RunTestGet(ctx context.Context, t *testing.T, store storage.Interface) { } func RunTestUnconditionalDelete(ctx context.Context, t *testing.T, store storage.Interface) { - key, storedObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}) + key, storedObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}}) tests := []struct { name string @@ -269,7 +269,8 @@ func RunTestUnconditionalDelete(ctx context.Context, t *testing.T, store storage } func RunTestConditionalDelete(ctx context.Context, t *testing.T, store storage.Interface) { - key, storedObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", UID: "A"}}) + obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns", UID: "A"}} + key, storedObj := testPropagateStore(ctx, t, store, obj) tests := []struct { name string @@ -305,7 +306,8 @@ func RunTestConditionalDelete(ctx context.Context, t *testing.T, store storage.I } out.ResourceVersion = storedObj.ResourceVersion ExpectNoDiff(t, "incorrect pod:", storedObj, out) - key, storedObj = testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", UID: "A"}}) + obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns", UID: "A"}} + key, storedObj = testPropagateStore(ctx, t, store, obj) }) } } @@ -336,7 +338,7 @@ func RunTestConditionalDelete(ctx context.Context, t *testing.T, store storage.I // [DONE] Added TestPreconditionalDeleteWithSuggestion func RunTestDeleteWithSuggestion(ctx context.Context, t *testing.T, store storage.Interface) { - key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name"}}) + key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns"}}) out := &example.Pod{} if err := store.Delete(ctx, key, out, nil, storage.ValidateAllObjectFunc, originalPod); err != nil { @@ -349,7 +351,7 @@ func RunTestDeleteWithSuggestion(ctx context.Context, t *testing.T, store storag } func RunTestDeleteWithSuggestionAndConflict(ctx context.Context, t *testing.T, store storage.Interface) { - key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name"}}) + key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns"}}) // First update, so originalPod is outdated. updatedPod := &example.Pod{} @@ -373,7 +375,7 @@ func RunTestDeleteWithSuggestionAndConflict(ctx context.Context, t *testing.T, s } func RunTestDeleteWithSuggestionOfDeletedObject(ctx context.Context, t *testing.T, store storage.Interface) { - key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name"}}) + key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns"}}) // First delete, so originalPod is outdated. deletedPod := &example.Pod{} @@ -389,7 +391,7 @@ func RunTestDeleteWithSuggestionOfDeletedObject(ctx context.Context, t *testing. } func RunTestValidateDeletionWithSuggestion(ctx context.Context, t *testing.T, store storage.Interface) { - key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name"}}) + key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns"}}) // Check that validaing fresh object fails is called once and fails. validationCalls := 0 @@ -441,7 +443,7 @@ func RunTestValidateDeletionWithSuggestion(ctx context.Context, t *testing.T, st } func RunTestPreconditionalDeleteWithSuggestion(ctx context.Context, t *testing.T, store storage.Interface) { - key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name"}}) + key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns"}}) // First update, so originalPod is outdated. updatedPod := &example.Pod{} @@ -510,14 +512,14 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }{ { name: "rejects invalid resource version", - prefix: "/", + prefix: "/pods", pred: storage.Everything, rv: "abc", expectError: true, }, { name: "rejects resource version and continue token", - prefix: "/", + prefix: "/pods", pred: storage.SelectionPredicate{ Label: labels.Everything(), Field: fields.Everything(), @@ -529,26 +531,26 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "rejects resource version set too high", - prefix: "/", + prefix: "/pods", rv: strconv.FormatInt(math.MaxInt64, 10), expectRVTooLarge: true, }, { name: "test List on existing key", - prefix: "/first/", + prefix: "/pods/first/", pred: storage.Everything, expectedOut: []*example.Pod{preset[0]}, }, { name: "test List on existing key with resource version set to 0", - prefix: "/first/", + prefix: "/pods/first/", pred: storage.Everything, expectedOut: []*example.Pod{preset[0]}, rv: "0", }, { name: "test List on existing key with resource version set before first write, match=Exact", - prefix: "/first/", + prefix: "/pods/first/", pred: storage.Everything, expectedOut: []*example.Pod{}, rv: initialRV, @@ -557,7 +559,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List on existing key with resource version set to 0, match=NotOlderThan", - prefix: "/first/", + prefix: "/pods/first/", pred: storage.Everything, expectedOut: []*example.Pod{preset[0]}, rv: "0", @@ -565,7 +567,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List on existing key with resource version set to 0, match=Invalid", - prefix: "/first/", + prefix: "/pods/first/", pred: storage.Everything, rv: "0", rvMatch: "Invalid", @@ -573,7 +575,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List on existing key with resource version set before first write, match=NotOlderThan", - prefix: "/first/", + prefix: "/pods/first/", pred: storage.Everything, expectedOut: []*example.Pod{preset[0]}, rv: initialRV, @@ -581,7 +583,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List on existing key with resource version set before first write, match=Invalid", - prefix: "/first/", + prefix: "/pods/first/", pred: storage.Everything, rv: initialRV, rvMatch: "Invalid", @@ -589,14 +591,14 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List on existing key with resource version set to current resource version", - prefix: "/first/", + prefix: "/pods/first/", pred: storage.Everything, expectedOut: []*example.Pod{preset[0]}, rv: list.ResourceVersion, }, { name: "test List on existing key with resource version set to current resource version, match=Exact", - prefix: "/first/", + prefix: "/pods/first/", pred: storage.Everything, expectedOut: []*example.Pod{preset[0]}, rv: list.ResourceVersion, @@ -605,7 +607,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List on existing key with resource version set to current resource version, match=NotOlderThan", - prefix: "/first/", + prefix: "/pods/first/", pred: storage.Everything, expectedOut: []*example.Pod{preset[0]}, rv: list.ResourceVersion, @@ -613,13 +615,13 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List on non-existing key", - prefix: "/non-existing/", + prefix: "/pods/non-existing/", pred: storage.Everything, expectedOut: nil, }, { name: "test List with pod name matching", - prefix: "/first/", + prefix: "/pods/first/", pred: storage.SelectionPredicate{ Label: labels.Everything(), Field: fields.ParseSelectorOrDie("metadata.name!=bar"), @@ -628,7 +630,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List with limit", - prefix: "/second/", + prefix: "/pods/second/", pred: storage.SelectionPredicate{ Label: labels.Everything(), Field: fields.Everything(), @@ -640,7 +642,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List with limit at current resource version", - prefix: "/second/", + prefix: "/pods/second/", pred: storage.SelectionPredicate{ Label: labels.Everything(), Field: fields.Everything(), @@ -654,7 +656,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List with limit at current resource version and match=Exact", - prefix: "/second/", + prefix: "/pods/second/", pred: storage.SelectionPredicate{ Label: labels.Everything(), Field: fields.Everything(), @@ -669,7 +671,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List with limit at resource version 0", - prefix: "/second/", + prefix: "/pods/second/", pred: storage.SelectionPredicate{ Label: labels.Everything(), Field: fields.Everything(), @@ -683,7 +685,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List with limit at resource version 0 match=NotOlderThan", - prefix: "/second/", + prefix: "/pods/second/", pred: storage.SelectionPredicate{ Label: labels.Everything(), Field: fields.Everything(), @@ -698,7 +700,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List with limit at resource version before first write and match=Exact", - prefix: "/second/", + prefix: "/pods/second/", pred: storage.SelectionPredicate{ Label: labels.Everything(), Field: fields.Everything(), @@ -712,7 +714,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List with pregenerated continue token", - prefix: "/second/", + prefix: "/pods/second/", pred: storage.SelectionPredicate{ Label: labels.Everything(), Field: fields.Everything(), @@ -723,7 +725,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "ignores resource version 0 for List with pregenerated continue token", - prefix: "/second/", + prefix: "/pods/second/", pred: storage.SelectionPredicate{ Label: labels.Everything(), Field: fields.Everything(), @@ -735,13 +737,13 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List with multiple levels of directories and expect flattened result", - prefix: "/second/", + prefix: "/pods/second/", pred: storage.Everything, expectedOut: []*example.Pod{preset[1], preset[2]}, }, { name: "test List with filter returning only one item, ensure only a single page returned", - prefix: "/", + prefix: "/pods", pred: storage.SelectionPredicate{ Field: fields.OneTermEqualSelector("metadata.name", "barfoo"), Label: labels.Everything(), @@ -752,7 +754,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List with filter returning only one item, covers the entire list", - prefix: "/", + prefix: "/pods", pred: storage.SelectionPredicate{ Field: fields.OneTermEqualSelector("metadata.name", "barfoo"), Label: labels.Everything(), @@ -763,7 +765,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List with filter returning only one item, covers the entire list, with resource version 0", - prefix: "/", + prefix: "/pods", pred: storage.SelectionPredicate{ Field: fields.OneTermEqualSelector("metadata.name", "barfoo"), Label: labels.Everything(), @@ -775,7 +777,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "test List with filter returning two items, more pages possible", - prefix: "/", + prefix: "/pods", pred: storage.SelectionPredicate{ Field: fields.OneTermEqualSelector("metadata.name", "bar"), Label: labels.Everything(), @@ -786,7 +788,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "filter returns two items split across multiple pages", - prefix: "/", + prefix: "/pods", pred: storage.SelectionPredicate{ Field: fields.OneTermEqualSelector("metadata.name", "foo"), Label: labels.Everything(), @@ -796,7 +798,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "filter returns one item for last page, ends on last item, not full", - prefix: "/", + prefix: "/pods", pred: storage.SelectionPredicate{ Field: fields.OneTermEqualSelector("metadata.name", "foo"), Label: labels.Everything(), @@ -807,7 +809,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "filter returns one item for last page, starts on last item, full", - prefix: "/", + prefix: "/pods", pred: storage.SelectionPredicate{ Field: fields.OneTermEqualSelector("metadata.name", "foo"), Label: labels.Everything(), @@ -818,7 +820,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "filter returns one item for last page, starts on last item, partial page", - prefix: "/", + prefix: "/pods", pred: storage.SelectionPredicate{ Field: fields.OneTermEqualSelector("metadata.name", "foo"), Label: labels.Everything(), @@ -829,7 +831,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "filter returns two items, page size equal to total list size", - prefix: "/", + prefix: "/pods", pred: storage.SelectionPredicate{ Field: fields.OneTermEqualSelector("metadata.name", "foo"), Label: labels.Everything(), @@ -839,7 +841,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { }, { name: "filter returns one item, page size equal to total list size", - prefix: "/", + prefix: "/pods", pred: storage.SelectionPredicate{ Field: fields.OneTermEqualSelector("metadata.name", "barfoo"), Label: labels.Everything(), @@ -936,7 +938,7 @@ func RunTestListWithoutPaging(ctx context.Context, t *testing.T, store storage.I { name: "test List with limit when paging disabled", disablePaging: true, - prefix: "/second/", + prefix: "/pods/second/", pred: storage.SelectionPredicate{ Label: labels.Everything(), Field: fields.Everything(), @@ -1033,7 +1035,7 @@ func seedMultiLevelData(ctx context.Context, store storage.Interface) (string, [ // we want to figure out the resourceVersion before we create anything initialList := &example.PodList{} - if err := store.GetList(ctx, "/", storage.ListOptions{Predicate: storage.Everything, Recursive: true}, initialList); err != nil { + if err := store.GetList(ctx, "/pods", storage.ListOptions{Predicate: storage.Everything, Recursive: true}, initialList); err != nil { return "", nil, fmt.Errorf("failed to determine starting resourceVersion: %w", err) } initialRV := initialList.ResourceVersion @@ -1054,7 +1056,7 @@ func seedMultiLevelData(ctx context.Context, store storage.Interface) (string, [ } func RunTestGetListNonRecursive(ctx context.Context, t *testing.T, store storage.Interface) { - key, prevStoredObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}) + key, prevStoredObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}}) prevRV, _ := strconv.Atoi(prevStoredObj.ResourceVersion) storedObj := &example.Pod{} @@ -1259,7 +1261,7 @@ func RunTestListContinuation(ctx context.Context, t *testing.T, store storage.In Predicate: pred(1, ""), Recursive: true, } - if err := store.GetList(ctx, "/", options, out); err != nil { + if err := store.GetList(ctx, "/pods", options, out); err != nil { t.Fatalf("Unable to get initial list: %v", err) } if len(out.Continue) == 0 { @@ -1279,13 +1281,13 @@ func RunTestListContinuation(ctx context.Context, t *testing.T, store storage.In Predicate: pred(0, continueFromSecondItem), Recursive: true, } - if err := store.GetList(ctx, "/", options, out); err != nil { + if err := store.GetList(ctx, "/pods", options, out); err != nil { t.Fatalf("Unable to get second page: %v", err) } if len(out.Continue) != 0 { t.Fatalf("Unexpected continuation token set") } - key, rv, err := storage.DecodeContinue(continueFromSecondItem, "/") + key, rv, err := storage.DecodeContinue(continueFromSecondItem, "/pods") t.Logf("continue token was %d %s %v", rv, key, err) ExpectNoDiff(t, "incorrect second page", []example.Pod{*preset[1].storedObj, *preset[2].storedObj}, out.Items) if validation != nil { @@ -1299,7 +1301,7 @@ func RunTestListContinuation(ctx context.Context, t *testing.T, store storage.In Predicate: pred(1, continueFromSecondItem), Recursive: true, } - if err := store.GetList(ctx, "/", options, out); err != nil { + if err := store.GetList(ctx, "/pods", options, out); err != nil { t.Fatalf("Unable to get second page: %v", err) } if len(out.Continue) == 0 { @@ -1318,7 +1320,7 @@ func RunTestListContinuation(ctx context.Context, t *testing.T, store storage.In Predicate: pred(1, continueFromThirdItem), Recursive: true, } - if err := store.GetList(ctx, "/", options, out); err != nil { + if err := store.GetList(ctx, "/pods", options, out); err != nil { t.Fatalf("Unable to get second page: %v", err) } if len(out.Continue) != 0 { @@ -1357,7 +1359,7 @@ func RunTestListPaginationRareObject(ctx context.Context, t *testing.T, store st }, Recursive: true, } - if err := store.GetList(ctx, "/", options, out); err != nil { + if err := store.GetList(ctx, "/pods", options, out); err != nil { t.Fatalf("Unable to get initial list: %v", err) } if len(out.Continue) != 0 { @@ -1429,7 +1431,7 @@ func RunTestListContinuationWithFilter(ctx context.Context, t *testing.T, store Predicate: pred(2, ""), Recursive: true, } - if err := store.GetList(ctx, "/", options, out); err != nil { + if err := store.GetList(ctx, "/pods", options, out); err != nil { t.Errorf("Unable to get initial list: %v", err) } if len(out.Continue) == 0 { @@ -1456,7 +1458,7 @@ func RunTestListContinuationWithFilter(ctx context.Context, t *testing.T, store Predicate: pred(2, cont), Recursive: true, } - if err := store.GetList(ctx, "/", options, out); err != nil { + if err := store.GetList(ctx, "/pods", options, out); err != nil { t.Errorf("Unable to get second page: %v", err) } if len(out.Continue) != 0 { @@ -1531,7 +1533,7 @@ func RunTestListInconsistentContinuation(ctx context.Context, t *testing.T, stor Predicate: pred(1, ""), Recursive: true, } - if err := store.GetList(ctx, "/", options, out); err != nil { + if err := store.GetList(ctx, "/pods", options, out); err != nil { t.Fatalf("Unable to get initial list: %v", err) } if len(out.Continue) == 0 { @@ -1568,7 +1570,7 @@ func RunTestListInconsistentContinuation(ctx context.Context, t *testing.T, stor Predicate: pred(0, continueFromSecondItem), Recursive: true, } - err := store.GetList(ctx, "/", options, out) + err := store.GetList(ctx, "/pods", options, out) if err == nil { t.Fatalf("unexpected no error") } @@ -1590,7 +1592,7 @@ func RunTestListInconsistentContinuation(ctx context.Context, t *testing.T, stor Predicate: pred(1, inconsistentContinueFromSecondItem), Recursive: true, } - if err := store.GetList(ctx, "/", options, out); err != nil { + if err := store.GetList(ctx, "/pods", options, out); err != nil { t.Fatalf("Unable to get second page: %v", err) } if len(out.Continue) == 0 { @@ -1609,7 +1611,7 @@ func RunTestListInconsistentContinuation(ctx context.Context, t *testing.T, stor Predicate: pred(1, continueFromThirdItem), Recursive: true, } - if err := store.GetList(ctx, "/", options, out); err != nil { + if err := store.GetList(ctx, "/pods", options, out); err != nil { t.Fatalf("Unable to get second page: %v", err) } if len(out.Continue) != 0 { @@ -1678,7 +1680,7 @@ func RunTestConsistentList(ctx context.Context, t *testing.T, store InterfaceWit Predicate: predicate, Recursive: true, } - if err := store.GetList(ctx, "/", options, &result1); err != nil { + if err := store.GetList(ctx, "/pods", options, &result1); err != nil { t.Fatalf("failed to list objects: %v", err) } @@ -1691,7 +1693,7 @@ func RunTestConsistentList(ctx context.Context, t *testing.T, store InterfaceWit } result2 := example.PodList{} - if err := store.GetList(ctx, "/", options, &result2); err != nil { + if err := store.GetList(ctx, "/pods", options, &result2); err != nil { t.Fatalf("failed to list objects: %v", err) } @@ -1701,7 +1703,7 @@ func RunTestConsistentList(ctx context.Context, t *testing.T, store InterfaceWit options.ResourceVersionMatch = metav1.ResourceVersionMatchNotOlderThan result3 := example.PodList{} - if err := store.GetList(ctx, "/", options, &result3); err != nil { + if err := store.GetList(ctx, "/pods", options, &result3); err != nil { t.Fatalf("failed to list objects: %v", err) } @@ -1709,7 +1711,7 @@ func RunTestConsistentList(ctx context.Context, t *testing.T, store InterfaceWit options.ResourceVersionMatch = metav1.ResourceVersionMatchExact result4 := example.PodList{} - if err := store.GetList(ctx, "/", options, &result4); err != nil { + if err := store.GetList(ctx, "/pods", options, &result4); err != nil { t.Fatalf("failed to list objects: %v", err) } @@ -1717,7 +1719,7 @@ func RunTestConsistentList(ctx context.Context, t *testing.T, store InterfaceWit } func RunTestGuaranteedUpdate(ctx context.Context, t *testing.T, store InterfaceWithPrefixTransformer, validation KeyValidation) { - inputObj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", UID: "A"}} + inputObj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns", UID: "A"}} key := computePodKey(inputObj) tests := []struct { @@ -1873,7 +1875,7 @@ func RunTestGuaranteedUpdate(ctx context.Context, t *testing.T, store InterfaceW } func RunTestGuaranteedUpdateWithTTL(ctx context.Context, t *testing.T, store storage.Interface) { - input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}} + input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}} key := computePodKey(input) out := &example.Pod{} @@ -1894,7 +1896,7 @@ func RunTestGuaranteedUpdateWithTTL(ctx context.Context, t *testing.T, store sto } func RunTestGuaranteedUpdateChecksStoredData(ctx context.Context, t *testing.T, store InterfaceWithPrefixTransformer) { - input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}} + input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}} key := computePodKey(input) // serialize input into etcd with data that would be normalized by a write - @@ -1959,7 +1961,7 @@ func RunTestGuaranteedUpdateChecksStoredData(ctx context.Context, t *testing.T, } func RunTestGuaranteedUpdateWithConflict(ctx context.Context, t *testing.T, store storage.Interface) { - key, _ := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}) + key, _ := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}}) errChan := make(chan error, 1) var firstToFinish sync.WaitGroup @@ -2004,7 +2006,7 @@ func RunTestGuaranteedUpdateWithConflict(ctx context.Context, t *testing.T, stor } func RunTestGuaranteedUpdateWithSuggestionAndConflict(ctx context.Context, t *testing.T, store storage.Interface) { - key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}) + key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}}) // First, update without a suggestion so originalPod is outdated updatedPod := &example.Pod{} @@ -2127,7 +2129,7 @@ func RunTestTransformationFailure(ctx context.Context, t *testing.T, store Inter Predicate: storage.Everything, Recursive: true, } - if err := store.GetList(ctx, "/", storageOpts, &got); !storage.IsInternalError(err) { + if err := store.GetList(ctx, "/pods", storageOpts, &got); !storage.IsInternalError(err) { t.Errorf("Unexpected error %v", err) } diff --git a/pkg/storage/testing/utils.go b/pkg/storage/testing/utils.go index 710ffaf20..b2a588d85 100644 --- a/pkg/storage/testing/utils.go +++ b/pkg/storage/testing/utils.go @@ -28,7 +28,7 @@ import ( "github.com/google/go-cmp/cmp" "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/watch" @@ -37,6 +37,9 @@ import ( "k8s.io/apiserver/pkg/storage/value" ) +// basePath for storage keys used across tests +const basePath = "/keybase" + // CreateObjList will create a list from the array of objects. func CreateObjList(prefix string, helper storage.Interface, items []runtime.Object) error { for i := range items { @@ -78,7 +81,7 @@ func DeepEqualSafePodSpec() example.PodSpec { } func computePodKey(obj *example.Pod) string { - return fmt.Sprintf("/%s/%s", obj.Namespace, obj.Name) + return fmt.Sprintf("/pods/%s/%s", obj.Namespace, obj.Name) } // testPropagateStore helps propagates store with objects, automates key generation, and returns diff --git a/pkg/storage/testing/watcher_tests.go b/pkg/storage/testing/watcher_tests.go index 4c846cd8e..1644f32e1 100644 --- a/pkg/storage/testing/watcher_tests.go +++ b/pkg/storage/testing/watcher_tests.go @@ -54,12 +54,12 @@ func testWatch(ctx context.Context, t *testing.T, store storage.Interface, recur watchTests []*testWatchStruct }{{ name: "create a key", - key: "/somekey-1", + key: basePath + "/somekey-1", watchTests: []*testWatchStruct{{podFoo, true, watch.Added}}, pred: storage.Everything, }, { name: "key updated to match predicate", - key: "/somekey-3", + key: basePath + "/somekey-3", watchTests: []*testWatchStruct{{podFoo, false, ""}, {podBar, true, watch.Added}}, pred: storage.SelectionPredicate{ Label: labels.Everything(), @@ -71,12 +71,12 @@ func testWatch(ctx context.Context, t *testing.T, store storage.Interface, recur }, }, { name: "update", - key: "/somekey-4", + key: basePath + "/somekey-4", watchTests: []*testWatchStruct{{podFoo, true, watch.Added}, {podBar, true, watch.Modified}}, pred: storage.Everything, }, { name: "delete because of being filtered", - key: "/somekey-5", + key: basePath + "/somekey-5", watchTests: []*testWatchStruct{{podFoo, true, watch.Added}, {podBar, true, watch.Deleted}}, pred: storage.SelectionPredicate{ Label: labels.Everything(), @@ -214,11 +214,11 @@ func RunTestWatchError(ctx context.Context, t *testing.T, store InterfaceWithPre Predicate: storage.Everything, Recursive: true, } - if err := store.GetList(ctx, "/", storageOpts, list); err != nil { + if err := store.GetList(ctx, basePath, storageOpts, list); err != nil { t.Errorf("Unexpected error: %v", err) } - if err := store.GuaranteedUpdate(ctx, "//foo", &example.Pod{}, true, nil, storage.SimpleUpdate( + if err := store.GuaranteedUpdate(ctx, basePath+"//foo", &example.Pod{}, true, nil, storage.SimpleUpdate( func(runtime.Object) (runtime.Object, error) { return &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}, nil }), nil); err != nil { @@ -232,7 +232,7 @@ func RunTestWatchError(ctx context.Context, t *testing.T, store InterfaceWithPre }) defer revertTransformer() - w, err := store.Watch(ctx, "//foo", storage.ListOptions{ResourceVersion: list.ResourceVersion, Predicate: storage.Everything}) + w, err := store.Watch(ctx, basePath+"//foo", storage.ListOptions{ResourceVersion: list.ResourceVersion, Predicate: storage.Everything}) if err != nil { t.Fatalf("Watch failed: %v", err) } @@ -244,7 +244,7 @@ func RunTestWatchContextCancel(ctx context.Context, t *testing.T, store storage. cancel() // When we watch with a canceled context, we should detect that it's context canceled. // We won't take it as error and also close the watcher. - w, err := store.Watch(canceledCtx, "/abc", storage.ListOptions{ + w, err := store.Watch(canceledCtx, basePath+"/abc", storage.ListOptions{ ResourceVersion: "0", Predicate: storage.Everything, }) @@ -309,7 +309,7 @@ func RunTestWatchInitializationSignal(ctx context.Context, t *testing.T, store s // (it rather is used by wrappers of storage.Interface to implement its functionalities) // this test is currently considered optional. func RunOptionalTestProgressNotify(ctx context.Context, t *testing.T, store storage.Interface) { - key := "/somekey" + key := basePath + "/somekey" input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name"}} out := &example.Pod{} if err := store.Create(ctx, key, input, out, 0); err != nil { diff --git a/pkg/storage/tests/cacher_test.go b/pkg/storage/tests/cacher_test.go index 9223132a9..a1437403d 100644 --- a/pkg/storage/tests/cacher_test.go +++ b/pkg/storage/tests/cacher_test.go @@ -897,7 +897,7 @@ type setupOptions struct { type setupOption func(*setupOptions) func withDefaults(options *setupOptions) { - prefix := "" + prefix := "/pods" options.resourcePrefix = prefix options.keyFunc = func(obj runtime.Object) (string, error) { return storage.NamespaceKeyFunc(prefix, obj) }