Validate etcd paths
Kubernetes-commit: f1693a02c46f79f8c90a4ef17c4a750034f21484
This commit is contained in:
		
							parent
							
								
									aa0e1e5e62
								
							
						
					
					
						commit
						3268b0561b
					
				|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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 { | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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
 | ||||||
|  |  | ||||||
|  | @ -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 { | ||||||
|  |  | ||||||
|  | @ -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) } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue