Validate etcd paths

Kubernetes-commit: f1693a02c46f79f8c90a4ef17c4a750034f21484
This commit is contained in:
Tim Allclair 2022-10-10 18:15:22 -07:00 committed by Kubernetes Publisher
parent aa0e1e5e62
commit 3268b0561b
7 changed files with 268 additions and 138 deletions

View File

@ -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 // [1] https://etcd.io/docs/v3.5/learning/api_guarantees/#isolation-level-and-consistency-of-replicas
ctx, store, etcdClient := testSetup(t) ctx, store, etcdClient := testSetup(t)
key := "/testkey" dir := "/testing"
key := dir + "/testkey"
out := &example.Pod{} out := &example.Pod{}
obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", SelfLink: "testlink"}} obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", SelfLink: "testlink"}}
@ -53,7 +54,7 @@ func TestLinearizedReadRevisionInvariant(t *testing.T) {
} }
list := &example.PodList{} 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) t.Errorf("Unexpected List error: %v", err)
} }
finalRevision := list.ResourceVersion finalRevision := list.ResourceVersion

View File

@ -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 { 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{} 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{ result := &store{
client: c, client: c,
codec: codec, codec: codec,
versioner: versioner, versioner: versioner,
transformer: transformer, transformer: transformer,
pagingEnabled: pagingEnabled, pagingEnabled: pagingEnabled,
// for compatibility with etcd2 impl. pathPrefix: pathPrefix,
// no-op for default prefix of '/registry'.
// keeps compatibility with etcd2 impl for custom prefixes that don't start with '/'
pathPrefix: path.Join("/", prefix),
groupResource: groupResource, groupResource: groupResource,
groupResourceString: groupResource.String(), groupResourceString: groupResource.String(),
watcher: newWatcher(c, codec, groupResource, newFunc, versioner), watcher: newWatcher(c, codec, groupResource, newFunc, versioner),
@ -124,9 +129,12 @@ func (s *store) Versioner() storage.Versioner {
// Get implements storage.Interface.Get. // Get implements storage.Interface.Get.
func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, out runtime.Object) error { 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() 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) metrics.RecordEtcdRequestLatency("get", s.groupResourceString, startTime)
if err != nil { if err != nil {
return err return err
@ -139,11 +147,11 @@ func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, ou
if opts.IgnoreNotFound { if opts.IgnoreNotFound {
return runtime.SetZeroValue(out) return runtime.SetZeroValue(out)
} }
return storage.NewKeyNotFoundError(key, 0) return storage.NewKeyNotFoundError(preparedKey, 0)
} }
kv := getResp.Kvs[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 { if err != nil {
return storage.NewInternalError(err.Error()) 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. // Create implements storage.Interface.Create.
func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error { 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", ctx, span := tracing.Start(ctx, "Create etcd3",
attribute.String("audit-id", audit.GetAuditIDTruncated(ctx)), attribute.String("audit-id", audit.GetAuditIDTruncated(ctx)),
attribute.String("key", key), attribute.String("key", key),
@ -173,14 +185,13 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object,
return err return err
} }
span.AddEvent("Encode succeeded", attribute.Int("len", len(data))) span.AddEvent("Encode succeeded", attribute.Int("len", len(data)))
key = path.Join(s.pathPrefix, key)
opts, err := s.ttlOpts(ctx, int64(ttl)) opts, err := s.ttlOpts(ctx, int64(ttl))
if err != nil { if err != nil {
return err return err
} }
newData, err := s.transformer.TransformToStorage(ctx, data, authenticatedDataString(key)) newData, err := s.transformer.TransformToStorage(ctx, data, authenticatedDataString(preparedKey))
if err != nil { if err != nil {
span.AddEvent("TransformToStorage failed", attribute.String("err", err.Error())) span.AddEvent("TransformToStorage failed", attribute.String("err", err.Error()))
return storage.NewInternalError(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() startTime := time.Now()
txnResp, err := s.client.KV.Txn(ctx).If( txnResp, err := s.client.KV.Txn(ctx).If(
notFound(key), notFound(preparedKey),
).Then( ).Then(
clientv3.OpPut(key, string(newData), opts...), clientv3.OpPut(preparedKey, string(newData), opts...),
).Commit() ).Commit()
metrics.RecordEtcdRequestLatency("create", s.groupResourceString, startTime) metrics.RecordEtcdRequestLatency("create", s.groupResourceString, startTime)
if err != nil { 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") span.AddEvent("Txn call succeeded")
if !txnResp.Succeeded { if !txnResp.Succeeded {
return storage.NewKeyExistsError(key, 0) return storage.NewKeyExistsError(preparedKey, 0)
} }
if out != nil { if out != nil {
@ -220,12 +231,15 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object,
func (s *store) Delete( func (s *store) Delete(
ctx context.Context, key string, out runtime.Object, preconditions *storage.Preconditions, ctx context.Context, key string, out runtime.Object, preconditions *storage.Preconditions,
validateDeletion storage.ValidateObjectFunc, cachedExistingObject runtime.Object) error { validateDeletion storage.ValidateObjectFunc, cachedExistingObject runtime.Object) error {
preparedKey, err := s.prepareKey(key)
if err != nil {
return err
}
v, err := conversion.EnforcePtr(out) v, err := conversion.EnforcePtr(out)
if err != nil { if err != nil {
return fmt.Errorf("unable to convert output object to pointer: %v", err) return fmt.Errorf("unable to convert output object to pointer: %v", err)
} }
key = path.Join(s.pathPrefix, key) return s.conditionalDelete(ctx, preparedKey, out, v, preconditions, validateDeletion, cachedExistingObject)
return s.conditionalDelete(ctx, key, out, v, preconditions, validateDeletion, cachedExistingObject)
} }
func (s *store) conditionalDelete( func (s *store) conditionalDelete(
@ -346,6 +360,10 @@ func (s *store) conditionalDelete(
func (s *store) GuaranteedUpdate( func (s *store) GuaranteedUpdate(
ctx context.Context, key string, destination runtime.Object, ignoreNotFound bool, ctx context.Context, key string, destination runtime.Object, ignoreNotFound bool,
preconditions *storage.Preconditions, tryUpdate storage.UpdateFunc, cachedExistingObject runtime.Object) error { 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", ctx, span := tracing.Start(ctx, "GuaranteedUpdate etcd3",
attribute.String("audit-id", audit.GetAuditIDTruncated(ctx)), attribute.String("audit-id", audit.GetAuditIDTruncated(ctx)),
attribute.String("key", key), attribute.String("key", key),
@ -357,16 +375,15 @@ func (s *store) GuaranteedUpdate(
if err != nil { if err != nil {
return fmt.Errorf("unable to convert output object to pointer: %v", err) return fmt.Errorf("unable to convert output object to pointer: %v", err)
} }
key = path.Join(s.pathPrefix, key)
getCurrentState := func() (*objState, error) { getCurrentState := func() (*objState, error) {
startTime := time.Now() 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) metrics.RecordEtcdRequestLatency("get", s.groupResourceString, startTime)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return s.getState(ctx, getResp, key, v, ignoreNotFound) return s.getState(ctx, getResp, preparedKey, v, ignoreNotFound)
} }
var origState *objState var origState *objState
@ -382,9 +399,9 @@ func (s *store) GuaranteedUpdate(
} }
span.AddEvent("initial value restored") span.AddEvent("initial value restored")
transformContext := authenticatedDataString(key) transformContext := authenticatedDataString(preparedKey)
for { 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 our data is already up to date, return the error
if origStateIsCurrent { if origStateIsCurrent {
return err return err
@ -472,11 +489,11 @@ func (s *store) GuaranteedUpdate(
startTime := time.Now() startTime := time.Now()
txnResp, err := s.client.KV.Txn(ctx).If( txnResp, err := s.client.KV.Txn(ctx).If(
clientv3.Compare(clientv3.ModRevision(key), "=", origState.rev), clientv3.Compare(clientv3.ModRevision(preparedKey), "=", origState.rev),
).Then( ).Then(
clientv3.OpPut(key, string(newData), opts...), clientv3.OpPut(preparedKey, string(newData), opts...),
).Else( ).Else(
clientv3.OpGet(key), clientv3.OpGet(preparedKey),
).Commit() ).Commit()
metrics.RecordEtcdRequestLatency("update", s.groupResourceString, startTime) metrics.RecordEtcdRequestLatency("update", s.groupResourceString, startTime)
if err != nil { if err != nil {
@ -487,8 +504,8 @@ func (s *store) GuaranteedUpdate(
span.AddEvent("Transaction committed") span.AddEvent("Transaction committed")
if !txnResp.Succeeded { if !txnResp.Succeeded {
getResp := (*clientv3.GetResponse)(txnResp.Responses[0].GetResponseRange()) getResp := (*clientv3.GetResponse)(txnResp.Responses[0].GetResponseRange())
klog.V(4).Infof("GuaranteedUpdate of %s failed because of a conflict, going to retry", key) klog.V(4).Infof("GuaranteedUpdate of %s failed because of a conflict, going to retry", preparedKey)
origState, err = s.getState(ctx, getResp, key, v, ignoreNotFound) origState, err = s.getState(ctx, getResp, preparedKey, v, ignoreNotFound)
if err != nil { if err != nil {
return err 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) { 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". // 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, // 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. // while with prefix "/a/" will return only "/a/b" which is the correct answer.
if !strings.HasSuffix(key, "/") { if !strings.HasSuffix(preparedKey, "/") {
key += "/" preparedKey += "/"
} }
startTime := time.Now() startTime := time.Now()
getResp, err := s.client.KV.Get(context.Background(), key, clientv3.WithRange(clientv3.GetPrefixRangeEnd(key)), clientv3.WithCountOnly()) getResp, err := s.client.KV.Get(context.Background(), preparedKey, clientv3.WithRange(clientv3.GetPrefixRangeEnd(preparedKey)), clientv3.WithCountOnly())
metrics.RecordEtcdRequestLatency("listWithCount", key, startTime) metrics.RecordEtcdRequestLatency("listWithCount", preparedKey, startTime)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -546,6 +566,10 @@ func (s *store) Count(key string) (int64, error) {
// GetList implements storage.Interface. // GetList implements storage.Interface.
func (s *store) GetList(ctx context.Context, key string, opts storage.ListOptions, listObj runtime.Object) error { 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 recursive := opts.Recursive
resourceVersion := opts.ResourceVersion resourceVersion := opts.ResourceVersion
match := opts.ResourceVersionMatch 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 { if err != nil || v.Kind() != reflect.Slice {
return fmt.Errorf("need ptr to slice: %v", err) 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 // 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 // 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 // with prefix "/a" will return all three, while with prefix "/a/" will return only
// "/a/b" which is the correct answer. // "/a/b" which is the correct answer.
if recursive && !strings.HasSuffix(key, "/") { if recursive && !strings.HasSuffix(preparedKey, "/") {
key += "/" preparedKey += "/"
} }
keyPrefix := key keyPrefix := preparedKey
// set the appropriate clientv3 options to filter the returned data set // set the appropriate clientv3 options to filter the returned data set
var limitOption *clientv3.OpOption var limitOption *clientv3.OpOption
@ -614,7 +637,7 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption
rangeEnd := clientv3.GetPrefixRangeEnd(keyPrefix) rangeEnd := clientv3.GetPrefixRangeEnd(keyPrefix)
options = append(options, clientv3.WithRange(rangeEnd)) options = append(options, clientv3.WithRange(rangeEnd))
key = continueKey preparedKey = continueKey
// If continueRV > 0, the LIST request needs a specific resource version. // If continueRV > 0, the LIST request needs a specific resource version.
// continueRV==0 is invalid. // continueRV==0 is invalid.
@ -681,7 +704,7 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption
}() }()
for { for {
startTime := time.Now() startTime := time.Now()
getResp, err = s.client.KV.Get(ctx, key, options...) getResp, err = s.client.KV.Get(ctx, preparedKey, options...)
if recursive { if recursive {
metrics.RecordEtcdRequestLatency("list", s.groupResourceString, startTime) metrics.RecordEtcdRequestLatency("list", s.groupResourceString, startTime)
} else { } else {
@ -753,7 +776,7 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption
} }
*limitOption = clientv3.WithLimit(limit) *limitOption = clientv3.WithLimit(limit)
} }
key = string(lastKey) + "\x00" preparedKey = string(lastKey) + "\x00"
if withRev == 0 { if withRev == 0 {
withRev = returnedRV withRev = returnedRV
options = append(options, clientv3.WithRev(withRev)) options = append(options, clientv3.WithRev(withRev))
@ -818,12 +841,15 @@ func growSlice(v reflect.Value, maxCapacity int, sizes ...int) {
// Watch implements storage.Interface.Watch. // Watch implements storage.Interface.Watch.
func (s *store) Watch(ctx context.Context, key string, opts storage.ListOptions) (watch.Interface, error) { 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) rev, err := s.versioner.ParseResourceVersion(opts.ResourceVersion)
if err != nil { if err != nil {
return nil, err return nil, err
} }
key = path.Join(s.pathPrefix, key) return s.watcher.Watch(ctx, preparedKey, int64(rev), opts.Recursive, opts.ProgressNotify, s.transformer, opts.Predicate)
return s.watcher.Watch(ctx, key, 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) { 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 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. // 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. // 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 { func decode(codec runtime.Codec, versioner storage.Versioner, value []byte, objPtr runtime.Object, rev int64) error {

View File

@ -21,8 +21,8 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"reflect" "reflect"
"strings"
"sync/atomic" "sync/atomic"
"testing" "testing"
@ -61,9 +61,8 @@ func newPod() runtime.Object {
return &example.Pod{} 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) { return func(ctx context.Context, t *testing.T, key string) {
key = path.Join(prefix, key)
getResp, err := etcdClient.KV.Get(ctx, key) getResp, err := etcdClient.KV.Get(ctx, key)
if err != nil { if err != nil {
t.Fatalf("etcdClient.KV.Get failed: %v", err) 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) { func TestCreate(t *testing.T) {
ctx, store, etcdClient := testSetup(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) { func TestCreateWithTTL(t *testing.T) {
@ -160,7 +159,7 @@ func (s *storeWithPrefixTransformer) UpdatePrefixTransformer(modifier storagetes
func TestGuaranteedUpdate(t *testing.T) { func TestGuaranteedUpdate(t *testing.T) {
ctx, store, etcdClient := testSetup(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) { func TestGuaranteedUpdateWithTTL(t *testing.T) {
@ -287,9 +286,9 @@ func TestCount(t *testing.T) {
func TestPrefix(t *testing.T) { func TestPrefix(t *testing.T) {
testcases := map[string]string{ testcases := map[string]string{
"custom/prefix": "/custom/prefix", "custom/prefix": "/custom/prefix/",
"/custom//prefix//": "/custom/prefix", "/custom//prefix//": "/custom/prefix/",
"/registry": "/registry", "/registry": "/registry/",
} }
for configuredPrefix, effectivePrefix := range testcases { for configuredPrefix, effectivePrefix := range testcases {
_, store, _ := testSetup(t, withPrefix(configuredPrefix)) _, store, _ := testSetup(t, withPrefix(configuredPrefix))
@ -548,3 +547,78 @@ func testSetup(t *testing.T, opts ...setupOption) (context.Context, *store, *cli
ctx := context.Background() ctx := context.Background()
return ctx, store, client 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)
}

View File

@ -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) { func RunTestCreate(ctx context.Context, t *testing.T, store storage.Interface, validation KeyValidation) {
out := &example.Pod{} 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 // verify that kv pair is empty before set
key := computePodKey(obj) 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) { 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{} out := &example.Pod{}
key := computePodKey(input) 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) { 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) key, _ := testPropagateStore(ctx, t, store, obj)
out := &example.Pod{} 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) { func RunTestGet(ctx context.Context, t *testing.T, store storage.Interface) {
// create an object to test // 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 // update the object once to allow get by exact resource version to be tested
updateObj := createdObj.DeepCopy() updateObj := createdObj.DeepCopy()
updateObj.Annotations = map[string]string{"test-annotation": "1"} 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) 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 // 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{} lastUpdatedObj := &example.Pod{}
if err := store.Create(ctx, computePodKey(secondObj), secondObj, lastUpdatedObj, 0); err != nil { if err := store.Create(ctx, computePodKey(secondObj), secondObj, lastUpdatedObj, 0); err != nil {
t.Fatalf("Set failed: %v", err) 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) { 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 { tests := []struct {
name string 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) { 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 { tests := []struct {
name string name string
@ -305,7 +306,8 @@ func RunTestConditionalDelete(ctx context.Context, t *testing.T, store storage.I
} }
out.ResourceVersion = storedObj.ResourceVersion out.ResourceVersion = storedObj.ResourceVersion
ExpectNoDiff(t, "incorrect pod:", storedObj, out) 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 // [DONE] Added TestPreconditionalDeleteWithSuggestion
func RunTestDeleteWithSuggestion(ctx context.Context, t *testing.T, store storage.Interface) { 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{} out := &example.Pod{}
if err := store.Delete(ctx, key, out, nil, storage.ValidateAllObjectFunc, originalPod); err != nil { 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) { 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. // First update, so originalPod is outdated.
updatedPod := &example.Pod{} 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) { 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. // First delete, so originalPod is outdated.
deletedPod := &example.Pod{} 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) { 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. // Check that validaing fresh object fails is called once and fails.
validationCalls := 0 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) { 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. // First update, so originalPod is outdated.
updatedPod := &example.Pod{} updatedPod := &example.Pod{}
@ -510,14 +512,14 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) {
}{ }{
{ {
name: "rejects invalid resource version", name: "rejects invalid resource version",
prefix: "/", prefix: "/pods",
pred: storage.Everything, pred: storage.Everything,
rv: "abc", rv: "abc",
expectError: true, expectError: true,
}, },
{ {
name: "rejects resource version and continue token", name: "rejects resource version and continue token",
prefix: "/", prefix: "/pods",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
Field: fields.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", name: "rejects resource version set too high",
prefix: "/", prefix: "/pods",
rv: strconv.FormatInt(math.MaxInt64, 10), rv: strconv.FormatInt(math.MaxInt64, 10),
expectRVTooLarge: true, expectRVTooLarge: true,
}, },
{ {
name: "test List on existing key", name: "test List on existing key",
prefix: "/first/", prefix: "/pods/first/",
pred: storage.Everything, pred: storage.Everything,
expectedOut: []*example.Pod{preset[0]}, expectedOut: []*example.Pod{preset[0]},
}, },
{ {
name: "test List on existing key with resource version set to 0", name: "test List on existing key with resource version set to 0",
prefix: "/first/", prefix: "/pods/first/",
pred: storage.Everything, pred: storage.Everything,
expectedOut: []*example.Pod{preset[0]}, expectedOut: []*example.Pod{preset[0]},
rv: "0", rv: "0",
}, },
{ {
name: "test List on existing key with resource version set before first write, match=Exact", name: "test List on existing key with resource version set before first write, match=Exact",
prefix: "/first/", prefix: "/pods/first/",
pred: storage.Everything, pred: storage.Everything,
expectedOut: []*example.Pod{}, expectedOut: []*example.Pod{},
rv: initialRV, 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", name: "test List on existing key with resource version set to 0, match=NotOlderThan",
prefix: "/first/", prefix: "/pods/first/",
pred: storage.Everything, pred: storage.Everything,
expectedOut: []*example.Pod{preset[0]}, expectedOut: []*example.Pod{preset[0]},
rv: "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", name: "test List on existing key with resource version set to 0, match=Invalid",
prefix: "/first/", prefix: "/pods/first/",
pred: storage.Everything, pred: storage.Everything,
rv: "0", rv: "0",
rvMatch: "Invalid", 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", name: "test List on existing key with resource version set before first write, match=NotOlderThan",
prefix: "/first/", prefix: "/pods/first/",
pred: storage.Everything, pred: storage.Everything,
expectedOut: []*example.Pod{preset[0]}, expectedOut: []*example.Pod{preset[0]},
rv: initialRV, 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", name: "test List on existing key with resource version set before first write, match=Invalid",
prefix: "/first/", prefix: "/pods/first/",
pred: storage.Everything, pred: storage.Everything,
rv: initialRV, rv: initialRV,
rvMatch: "Invalid", 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", name: "test List on existing key with resource version set to current resource version",
prefix: "/first/", prefix: "/pods/first/",
pred: storage.Everything, pred: storage.Everything,
expectedOut: []*example.Pod{preset[0]}, expectedOut: []*example.Pod{preset[0]},
rv: list.ResourceVersion, rv: list.ResourceVersion,
}, },
{ {
name: "test List on existing key with resource version set to current resource version, match=Exact", name: "test List on existing key with resource version set to current resource version, match=Exact",
prefix: "/first/", prefix: "/pods/first/",
pred: storage.Everything, pred: storage.Everything,
expectedOut: []*example.Pod{preset[0]}, expectedOut: []*example.Pod{preset[0]},
rv: list.ResourceVersion, 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", name: "test List on existing key with resource version set to current resource version, match=NotOlderThan",
prefix: "/first/", prefix: "/pods/first/",
pred: storage.Everything, pred: storage.Everything,
expectedOut: []*example.Pod{preset[0]}, expectedOut: []*example.Pod{preset[0]},
rv: list.ResourceVersion, 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", name: "test List on non-existing key",
prefix: "/non-existing/", prefix: "/pods/non-existing/",
pred: storage.Everything, pred: storage.Everything,
expectedOut: nil, expectedOut: nil,
}, },
{ {
name: "test List with pod name matching", name: "test List with pod name matching",
prefix: "/first/", prefix: "/pods/first/",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
Field: fields.ParseSelectorOrDie("metadata.name!=bar"), 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", name: "test List with limit",
prefix: "/second/", prefix: "/pods/second/",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
Field: fields.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", name: "test List with limit at current resource version",
prefix: "/second/", prefix: "/pods/second/",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
Field: fields.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", name: "test List with limit at current resource version and match=Exact",
prefix: "/second/", prefix: "/pods/second/",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
Field: fields.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", name: "test List with limit at resource version 0",
prefix: "/second/", prefix: "/pods/second/",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
Field: fields.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", name: "test List with limit at resource version 0 match=NotOlderThan",
prefix: "/second/", prefix: "/pods/second/",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
Field: fields.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", name: "test List with limit at resource version before first write and match=Exact",
prefix: "/second/", prefix: "/pods/second/",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
Field: fields.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", name: "test List with pregenerated continue token",
prefix: "/second/", prefix: "/pods/second/",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
Field: fields.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", name: "ignores resource version 0 for List with pregenerated continue token",
prefix: "/second/", prefix: "/pods/second/",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
Field: fields.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", name: "test List with multiple levels of directories and expect flattened result",
prefix: "/second/", prefix: "/pods/second/",
pred: storage.Everything, pred: storage.Everything,
expectedOut: []*example.Pod{preset[1], preset[2]}, expectedOut: []*example.Pod{preset[1], preset[2]},
}, },
{ {
name: "test List with filter returning only one item, ensure only a single page returned", name: "test List with filter returning only one item, ensure only a single page returned",
prefix: "/", prefix: "/pods",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Field: fields.OneTermEqualSelector("metadata.name", "barfoo"), Field: fields.OneTermEqualSelector("metadata.name", "barfoo"),
Label: labels.Everything(), 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", name: "test List with filter returning only one item, covers the entire list",
prefix: "/", prefix: "/pods",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Field: fields.OneTermEqualSelector("metadata.name", "barfoo"), Field: fields.OneTermEqualSelector("metadata.name", "barfoo"),
Label: labels.Everything(), 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", name: "test List with filter returning only one item, covers the entire list, with resource version 0",
prefix: "/", prefix: "/pods",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Field: fields.OneTermEqualSelector("metadata.name", "barfoo"), Field: fields.OneTermEqualSelector("metadata.name", "barfoo"),
Label: labels.Everything(), 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", name: "test List with filter returning two items, more pages possible",
prefix: "/", prefix: "/pods",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Field: fields.OneTermEqualSelector("metadata.name", "bar"), Field: fields.OneTermEqualSelector("metadata.name", "bar"),
Label: labels.Everything(), 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", name: "filter returns two items split across multiple pages",
prefix: "/", prefix: "/pods",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Field: fields.OneTermEqualSelector("metadata.name", "foo"), Field: fields.OneTermEqualSelector("metadata.name", "foo"),
Label: labels.Everything(), 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", name: "filter returns one item for last page, ends on last item, not full",
prefix: "/", prefix: "/pods",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Field: fields.OneTermEqualSelector("metadata.name", "foo"), Field: fields.OneTermEqualSelector("metadata.name", "foo"),
Label: labels.Everything(), 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", name: "filter returns one item for last page, starts on last item, full",
prefix: "/", prefix: "/pods",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Field: fields.OneTermEqualSelector("metadata.name", "foo"), Field: fields.OneTermEqualSelector("metadata.name", "foo"),
Label: labels.Everything(), 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", name: "filter returns one item for last page, starts on last item, partial page",
prefix: "/", prefix: "/pods",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Field: fields.OneTermEqualSelector("metadata.name", "foo"), Field: fields.OneTermEqualSelector("metadata.name", "foo"),
Label: labels.Everything(), 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", name: "filter returns two items, page size equal to total list size",
prefix: "/", prefix: "/pods",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Field: fields.OneTermEqualSelector("metadata.name", "foo"), Field: fields.OneTermEqualSelector("metadata.name", "foo"),
Label: labels.Everything(), 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", name: "filter returns one item, page size equal to total list size",
prefix: "/", prefix: "/pods",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Field: fields.OneTermEqualSelector("metadata.name", "barfoo"), Field: fields.OneTermEqualSelector("metadata.name", "barfoo"),
Label: labels.Everything(), 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", name: "test List with limit when paging disabled",
disablePaging: true, disablePaging: true,
prefix: "/second/", prefix: "/pods/second/",
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
Field: fields.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 // we want to figure out the resourceVersion before we create anything
initialList := &example.PodList{} 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) return "", nil, fmt.Errorf("failed to determine starting resourceVersion: %w", err)
} }
initialRV := initialList.ResourceVersion 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) { 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) prevRV, _ := strconv.Atoi(prevStoredObj.ResourceVersion)
storedObj := &example.Pod{} storedObj := &example.Pod{}
@ -1259,7 +1261,7 @@ func RunTestListContinuation(ctx context.Context, t *testing.T, store storage.In
Predicate: pred(1, ""), Predicate: pred(1, ""),
Recursive: true, 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) t.Fatalf("Unable to get initial list: %v", err)
} }
if len(out.Continue) == 0 { if len(out.Continue) == 0 {
@ -1279,13 +1281,13 @@ func RunTestListContinuation(ctx context.Context, t *testing.T, store storage.In
Predicate: pred(0, continueFromSecondItem), Predicate: pred(0, continueFromSecondItem),
Recursive: true, 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) t.Fatalf("Unable to get second page: %v", err)
} }
if len(out.Continue) != 0 { if len(out.Continue) != 0 {
t.Fatalf("Unexpected continuation token set") 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) 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) ExpectNoDiff(t, "incorrect second page", []example.Pod{*preset[1].storedObj, *preset[2].storedObj}, out.Items)
if validation != nil { if validation != nil {
@ -1299,7 +1301,7 @@ func RunTestListContinuation(ctx context.Context, t *testing.T, store storage.In
Predicate: pred(1, continueFromSecondItem), Predicate: pred(1, continueFromSecondItem),
Recursive: true, 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) t.Fatalf("Unable to get second page: %v", err)
} }
if len(out.Continue) == 0 { if len(out.Continue) == 0 {
@ -1318,7 +1320,7 @@ func RunTestListContinuation(ctx context.Context, t *testing.T, store storage.In
Predicate: pred(1, continueFromThirdItem), Predicate: pred(1, continueFromThirdItem),
Recursive: true, 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) t.Fatalf("Unable to get second page: %v", err)
} }
if len(out.Continue) != 0 { if len(out.Continue) != 0 {
@ -1357,7 +1359,7 @@ func RunTestListPaginationRareObject(ctx context.Context, t *testing.T, store st
}, },
Recursive: true, 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) t.Fatalf("Unable to get initial list: %v", err)
} }
if len(out.Continue) != 0 { if len(out.Continue) != 0 {
@ -1429,7 +1431,7 @@ func RunTestListContinuationWithFilter(ctx context.Context, t *testing.T, store
Predicate: pred(2, ""), Predicate: pred(2, ""),
Recursive: true, 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) t.Errorf("Unable to get initial list: %v", err)
} }
if len(out.Continue) == 0 { if len(out.Continue) == 0 {
@ -1456,7 +1458,7 @@ func RunTestListContinuationWithFilter(ctx context.Context, t *testing.T, store
Predicate: pred(2, cont), Predicate: pred(2, cont),
Recursive: true, 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) t.Errorf("Unable to get second page: %v", err)
} }
if len(out.Continue) != 0 { if len(out.Continue) != 0 {
@ -1531,7 +1533,7 @@ func RunTestListInconsistentContinuation(ctx context.Context, t *testing.T, stor
Predicate: pred(1, ""), Predicate: pred(1, ""),
Recursive: true, 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) t.Fatalf("Unable to get initial list: %v", err)
} }
if len(out.Continue) == 0 { if len(out.Continue) == 0 {
@ -1568,7 +1570,7 @@ func RunTestListInconsistentContinuation(ctx context.Context, t *testing.T, stor
Predicate: pred(0, continueFromSecondItem), Predicate: pred(0, continueFromSecondItem),
Recursive: true, Recursive: true,
} }
err := store.GetList(ctx, "/", options, out) err := store.GetList(ctx, "/pods", options, out)
if err == nil { if err == nil {
t.Fatalf("unexpected no error") t.Fatalf("unexpected no error")
} }
@ -1590,7 +1592,7 @@ func RunTestListInconsistentContinuation(ctx context.Context, t *testing.T, stor
Predicate: pred(1, inconsistentContinueFromSecondItem), Predicate: pred(1, inconsistentContinueFromSecondItem),
Recursive: true, 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) t.Fatalf("Unable to get second page: %v", err)
} }
if len(out.Continue) == 0 { if len(out.Continue) == 0 {
@ -1609,7 +1611,7 @@ func RunTestListInconsistentContinuation(ctx context.Context, t *testing.T, stor
Predicate: pred(1, continueFromThirdItem), Predicate: pred(1, continueFromThirdItem),
Recursive: true, 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) t.Fatalf("Unable to get second page: %v", err)
} }
if len(out.Continue) != 0 { if len(out.Continue) != 0 {
@ -1678,7 +1680,7 @@ func RunTestConsistentList(ctx context.Context, t *testing.T, store InterfaceWit
Predicate: predicate, Predicate: predicate,
Recursive: true, 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) 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{} 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) 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 options.ResourceVersionMatch = metav1.ResourceVersionMatchNotOlderThan
result3 := example.PodList{} 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) 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 options.ResourceVersionMatch = metav1.ResourceVersionMatchExact
result4 := example.PodList{} 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) 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) { 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) key := computePodKey(inputObj)
tests := []struct { 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) { 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) key := computePodKey(input)
out := &example.Pod{} 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) { 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) key := computePodKey(input)
// serialize input into etcd with data that would be normalized by a write - // 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) { 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) errChan := make(chan error, 1)
var firstToFinish sync.WaitGroup 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) { 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 // First, update without a suggestion so originalPod is outdated
updatedPod := &example.Pod{} updatedPod := &example.Pod{}
@ -2127,7 +2129,7 @@ func RunTestTransformationFailure(ctx context.Context, t *testing.T, store Inter
Predicate: storage.Everything, Predicate: storage.Everything,
Recursive: true, 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) t.Errorf("Unexpected error %v", err)
} }

View File

@ -28,7 +28,7 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"k8s.io/apimachinery/pkg/api/meta" "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/runtime"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
@ -37,6 +37,9 @@ import (
"k8s.io/apiserver/pkg/storage/value" "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. // CreateObjList will create a list from the array of objects.
func CreateObjList(prefix string, helper storage.Interface, items []runtime.Object) error { func CreateObjList(prefix string, helper storage.Interface, items []runtime.Object) error {
for i := range items { for i := range items {
@ -78,7 +81,7 @@ func DeepEqualSafePodSpec() example.PodSpec {
} }
func computePodKey(obj *example.Pod) string { 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 // testPropagateStore helps propagates store with objects, automates key generation, and returns

View File

@ -54,12 +54,12 @@ func testWatch(ctx context.Context, t *testing.T, store storage.Interface, recur
watchTests []*testWatchStruct watchTests []*testWatchStruct
}{{ }{{
name: "create a key", name: "create a key",
key: "/somekey-1", key: basePath + "/somekey-1",
watchTests: []*testWatchStruct{{podFoo, true, watch.Added}}, watchTests: []*testWatchStruct{{podFoo, true, watch.Added}},
pred: storage.Everything, pred: storage.Everything,
}, { }, {
name: "key updated to match predicate", name: "key updated to match predicate",
key: "/somekey-3", key: basePath + "/somekey-3",
watchTests: []*testWatchStruct{{podFoo, false, ""}, {podBar, true, watch.Added}}, watchTests: []*testWatchStruct{{podFoo, false, ""}, {podBar, true, watch.Added}},
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
@ -71,12 +71,12 @@ func testWatch(ctx context.Context, t *testing.T, store storage.Interface, recur
}, },
}, { }, {
name: "update", name: "update",
key: "/somekey-4", key: basePath + "/somekey-4",
watchTests: []*testWatchStruct{{podFoo, true, watch.Added}, {podBar, true, watch.Modified}}, watchTests: []*testWatchStruct{{podFoo, true, watch.Added}, {podBar, true, watch.Modified}},
pred: storage.Everything, pred: storage.Everything,
}, { }, {
name: "delete because of being filtered", name: "delete because of being filtered",
key: "/somekey-5", key: basePath + "/somekey-5",
watchTests: []*testWatchStruct{{podFoo, true, watch.Added}, {podBar, true, watch.Deleted}}, watchTests: []*testWatchStruct{{podFoo, true, watch.Added}, {podBar, true, watch.Deleted}},
pred: storage.SelectionPredicate{ pred: storage.SelectionPredicate{
Label: labels.Everything(), Label: labels.Everything(),
@ -214,11 +214,11 @@ func RunTestWatchError(ctx context.Context, t *testing.T, store InterfaceWithPre
Predicate: storage.Everything, Predicate: storage.Everything,
Recursive: true, 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) 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) { func(runtime.Object) (runtime.Object, error) {
return &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}, nil return &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}, nil
}), nil); err != nil { }), nil); err != nil {
@ -232,7 +232,7 @@ func RunTestWatchError(ctx context.Context, t *testing.T, store InterfaceWithPre
}) })
defer revertTransformer() 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 { if err != nil {
t.Fatalf("Watch failed: %v", err) t.Fatalf("Watch failed: %v", err)
} }
@ -244,7 +244,7 @@ func RunTestWatchContextCancel(ctx context.Context, t *testing.T, store storage.
cancel() cancel()
// When we watch with a canceled context, we should detect that it's context canceled. // 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. // 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", ResourceVersion: "0",
Predicate: storage.Everything, 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) // (it rather is used by wrappers of storage.Interface to implement its functionalities)
// this test is currently considered optional. // this test is currently considered optional.
func RunOptionalTestProgressNotify(ctx context.Context, t *testing.T, store storage.Interface) { func RunOptionalTestProgressNotify(ctx context.Context, t *testing.T, store storage.Interface) {
key := "/somekey" key := basePath + "/somekey"
input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name"}} input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name"}}
out := &example.Pod{} out := &example.Pod{}
if err := store.Create(ctx, key, input, out, 0); err != nil { if err := store.Create(ctx, key, input, out, 0); err != nil {

View File

@ -897,7 +897,7 @@ type setupOptions struct {
type setupOption func(*setupOptions) type setupOption func(*setupOptions)
func withDefaults(options *setupOptions) { func withDefaults(options *setupOptions) {
prefix := "" prefix := "/pods"
options.resourcePrefix = prefix options.resourcePrefix = prefix
options.keyFunc = func(obj runtime.Object) (string, error) { return storage.NamespaceKeyFunc(prefix, obj) } options.keyFunc = func(obj runtime.Object) (string, error) { return storage.NamespaceKeyFunc(prefix, obj) }