Merge pull request #37692 from wojtek-t/storage_resource_version_for_get

Automatic merge from submit-queue

Handle RV in Get calls to storage interface.

Ref #37473
This commit is contained in:
Kubernetes Submit Queue 2016-12-03 11:44:29 -08:00 committed by GitHub
commit 16a9c0b49c
11 changed files with 95 additions and 21 deletions

View File

@ -174,7 +174,7 @@ func (e *Etcd) tryUpdate(fn func() error) error {
// etcd. If the key does not exist, the object will have an empty ResourceVersion.
func (e *Etcd) Get() (*api.RangeAllocation, error) {
existing := &api.RangeAllocation{}
if err := e.storage.Get(context.TODO(), e.baseKey, existing, true); err != nil {
if err := e.storage.Get(context.TODO(), e.baseKey, "", existing, true); err != nil {
return nil, storeerr.InterpretGetError(err, e.resource, "")
}
return existing, nil

View File

@ -78,7 +78,7 @@ func TestStore(t *testing.T) {
other := allocator.NewAllocationMap(100, "rangeSpecValue")
allocation := &api.RangeAllocation{}
if err := storage.storage.Get(context.TODO(), key(), allocation, false); err != nil {
if err := storage.storage.Get(context.TODO(), key(), "", allocation, false); err != nil {
t.Fatal(err)
}
if allocation.Range != "rangeSpecValue" {

View File

@ -262,7 +262,7 @@ func (e *Store) Create(ctx api.Context, obj runtime.Object) (runtime.Object, err
if !kubeerr.IsAlreadyExists(err) {
return nil, err
}
if errGet := e.Storage.Get(ctx, key, out, false); errGet != nil {
if errGet := e.Storage.Get(ctx, key, "", out, false); errGet != nil {
return nil, err
}
accessor, errGetAcc := meta.Accessor(out)
@ -483,7 +483,8 @@ func (e *Store) Get(ctx api.Context, name string) (runtime.Object, error) {
if err != nil {
return nil, err
}
if err := e.Storage.Get(ctx, key, obj, false); err != nil {
// TODO: Once we pass GetOptions to this method, pass the ResourceVersion from it.
if err := e.Storage.Get(ctx, key, "", obj, false); err != nil {
return nil, storeerr.InterpretGetError(err, e.QualifiedResource, name)
}
if e.Decorator != nil {
@ -695,7 +696,7 @@ func (e *Store) Delete(ctx api.Context, name string, options *api.DeleteOptions)
}
obj := e.NewFunc()
if err := e.Storage.Get(ctx, key, obj, false); err != nil {
if err := e.Storage.Get(ctx, key, "", obj, false); err != nil {
return nil, storeerr.InterpretDeleteError(err, e.QualifiedResource, name)
}
// support older consumers of delete by treating "nil" as delete immediately

View File

@ -345,8 +345,48 @@ func (c *Cacher) WatchList(ctx context.Context, key string, resourceVersion stri
}
// Implements storage.Interface.
func (c *Cacher) Get(ctx context.Context, key string, objPtr runtime.Object, ignoreNotFound bool) error {
return c.storage.Get(ctx, key, objPtr, ignoreNotFound)
func (c *Cacher) Get(ctx context.Context, key string, resourceVersion string, objPtr runtime.Object, ignoreNotFound bool) error {
if resourceVersion == "" {
// If resourceVersion is not specified, serve it from underlying
// storage (for backward compatibility).
return c.storage.Get(ctx, key, resourceVersion, objPtr, ignoreNotFound)
}
// If resourceVersion is specified, serve it from cache.
// It's guaranteed that the returned value is at least that
// fresh as the given resourceVersion.
getRV, err := ParseListResourceVersion(resourceVersion)
if err != nil {
return err
}
// Do not create a trace - it's not for free and there are tons
// of Get requests. We can add it if it will be really needed.
c.ready.wait()
objVal, err := conversion.EnforcePtr(objPtr)
if err != nil {
return err
}
obj, exists, readResourceVersion, err := c.watchCache.WaitUntilFreshAndGet(getRV, key, nil)
if err != nil {
return fmt.Errorf("failed to wait for fresh get: %v", err)
}
if exists {
elem, ok := obj.(*storeElement)
if !ok {
return fmt.Errorf("non *storeElement returned from storage: %v", obj)
}
objVal.Set(reflect.ValueOf(elem.Object).Elem())
} else {
objVal.Set(reflect.Zero(objVal.Type()))
if !ignoreNotFound {
return NewKeyNotFoundError(key, int64(readResourceVersion))
}
}
return nil
}
// Implements storage.Interface.
@ -384,7 +424,7 @@ func (c *Cacher) GetToList(ctx context.Context, key string, resourceVersion stri
obj, exists, readResourceVersion, err := c.watchCache.WaitUntilFreshAndGet(listRV, key, trace)
if err != nil {
return fmt.Errorf("failed to wait for fresh list: %v", err)
return fmt.Errorf("failed to wait for fresh get: %v", err)
}
trace.Step("Got from cache")

View File

@ -90,12 +90,43 @@ func updatePod(t *testing.T, s storage.Interface, obj, old *api.Pod) *api.Pod {
}
obj.ResourceVersion = ""
result := &api.Pod{}
if err := s.Get(context.TODO(), key, result, false); err != nil {
if err := s.Get(context.TODO(), key, "", result, false); err != nil {
t.Errorf("unexpected error: %v", err)
}
return result
}
func TestGet(t *testing.T) {
server, etcdStorage := newEtcdTestStorage(t, testapi.Default.Codec(), etcdtest.PathPrefix())
defer server.Terminate(t)
cacher := newTestCacher(etcdStorage, 10)
defer cacher.Stop()
podFoo := makeTestPod("foo")
fooCreated := updatePod(t, etcdStorage, podFoo, nil)
// We pass the ResourceVersion from the above Create() operation.
result := &api.Pod{}
if err := cacher.Get(context.TODO(), "pods/ns/foo", fooCreated.ResourceVersion, result, true); err != nil {
t.Errorf("Unexpected error: %v", err)
}
if e, a := *fooCreated, *result; !reflect.DeepEqual(e, a) {
t.Errorf("Expected: %#v, got: %#v", e, a)
}
if err := cacher.Get(context.TODO(), "pods/ns/bar", fooCreated.ResourceVersion, result, true); err != nil {
t.Errorf("Unexpected error: %v", err)
}
emptyPod := api.Pod{}
if e, a := emptyPod, *result; !reflect.DeepEqual(e, a) {
t.Errorf("Expected: %#v, got: %#v", e, a)
}
if err := cacher.Get(context.TODO(), "pods/ns/bar", fooCreated.ResourceVersion, result, false); !storage.IsNotFound(err) {
t.Errorf("Unexpected error: %v", err)
}
}
func TestList(t *testing.T) {
server, etcdStorage := newEtcdTestStorage(t, testapi.Default.Codec(), etcdtest.PathPrefix())
defer server.Terminate(t)

View File

@ -232,7 +232,7 @@ func (h *etcdHelper) WatchList(ctx context.Context, key string, resourceVersion
}
// Implements storage.Interface.
func (h *etcdHelper) Get(ctx context.Context, key string, objPtr runtime.Object, ignoreNotFound bool) error {
func (h *etcdHelper) Get(ctx context.Context, key string, resourceVersion string, objPtr runtime.Object, ignoreNotFound bool) error {
if ctx == nil {
glog.Errorf("Context is nil")
}

View File

@ -240,7 +240,7 @@ func TestGet(t *testing.T) {
t.Errorf("Unexpected error %#v", err)
}
expect = got
if err := helper.Get(context.TODO(), key, &got, false); err != nil {
if err := helper.Get(context.TODO(), key, "", &got, false); err != nil {
t.Errorf("Unexpected error %#v", err)
}
if !reflect.DeepEqual(got, expect) {
@ -256,7 +256,7 @@ func TestGetNotFoundErr(t *testing.T) {
helper := newEtcdHelper(server.Client, testapi.Default.Codec(), key)
var got api.Pod
err := helper.Get(context.TODO(), boguskey, &got, false)
err := helper.Get(context.TODO(), boguskey, "", &got, false)
if !storage.IsNotFound(err) {
t.Errorf("Unexpected reponse on key=%v, err=%v", key, err)
}
@ -276,7 +276,7 @@ func TestCreate(t *testing.T) {
if err != nil {
t.Errorf("Unexpected error %#v", err)
}
err = helper.Get(context.TODO(), "/some/key", returnedObj, false)
err = helper.Get(context.TODO(), "/some/key", "", returnedObj, false)
if err != nil {
t.Errorf("Unexpected error %#v", err)
}
@ -332,7 +332,7 @@ func TestGuaranteedUpdate(t *testing.T) {
}
objCheck := &storagetesting.TestResource{}
err = helper.Get(context.TODO(), key, objCheck, false)
err = helper.Get(context.TODO(), key, "", objCheck, false)
if err != nil {
t.Errorf("Unexpected error %#v", err)
}
@ -442,7 +442,7 @@ func TestGuaranteedUpdate_CreateCollision(t *testing.T) {
wgDone.Wait()
stored := &storagetesting.TestResource{}
err := helper.Get(context.TODO(), key, stored, false)
err := helper.Get(context.TODO(), key, "", stored, false)
if err != nil {
t.Errorf("Unexpected error %#v", stored)
}
@ -562,7 +562,7 @@ func TestDeleteWithRetry(t *testing.T) {
if fake.getCount != expectedRetries {
t.Errorf("Expect %d retries, got %d", expectedRetries, fake.getCount)
}
err = helper.Get(context.TODO(), "/some/key", obj, false)
err = helper.Get(context.TODO(), "/some/key", "", obj, false)
if !storage.IsNotFound(err) {
t.Errorf("Expect an NotFound error, got %v", err)
}

View File

@ -96,7 +96,7 @@ func (s *store) Versioner() storage.Versioner {
}
// Get implements storage.Interface.Get.
func (s *store) Get(ctx context.Context, key string, out runtime.Object, ignoreNotFound bool) error {
func (s *store) Get(ctx context.Context, key string, resourceVersion string, out runtime.Object, ignoreNotFound bool) error {
key = keyWithPrefix(s.pathPrefix, key)
getResp, err := s.client.KV.Get(ctx, key, s.getOps...)
if err != nil {

View File

@ -133,7 +133,7 @@ func TestGet(t *testing.T) {
for i, tt := range tests {
out := &api.Pod{}
err := store.Get(ctx, tt.key, out, tt.ignoreNotFound)
err := store.Get(ctx, tt.key, "", out, tt.ignoreNotFound)
if tt.expectNotFoundErr {
if err == nil || !storage.IsNotFound(err) {
t.Errorf("#%d: expecting not found error, but get: %s", i, err)

View File

@ -124,7 +124,9 @@ type Interface interface {
// Get unmarshals json found at key into objPtr. On a not found error, will either
// return a zero object of the requested type, or an error, depending on ignoreNotFound.
// Treats empty responses and nil response nodes exactly like a not found error.
Get(ctx context.Context, key string, objPtr runtime.Object, ignoreNotFound bool) error
// The returned contents may be delayed, but it is guaranteed that they will
// be have at least 'resourceVersion'.
Get(ctx context.Context, key string, resourceVersion string, objPtr runtime.Object, ignoreNotFound bool) error
// GetToList unmarshals json found at key and opaque it into *List api object
// (an object that satisfies the runtime.IsList definition).

View File

@ -70,7 +70,7 @@ func TestIgnoreClusterName(t *testing.T) {
assert.Empty(t, nsNew.ClusterName)
nsEtcd := v1.Namespace{}
err = etcdStorage.Get(ctx, ns.Name, &nsEtcd, false)
err = etcdStorage.Get(ctx, ns.Name, "", &nsEtcd, false)
assert.Nil(t, err)
assert.Equal(t, ns.Name, nsEtcd.Name)
assert.Empty(t, nsEtcd.ClusterName)
@ -81,7 +81,7 @@ func TestIgnoreClusterName(t *testing.T) {
assert.Empty(t, nsNew.ClusterName)
nsEtcd = v1.Namespace{}
err = etcdStorage.Get(ctx, ns.Name, &nsEtcd, false)
err = etcdStorage.Get(ctx, ns.Name, "", &nsEtcd, false)
assert.Nil(t, err)
assert.Equal(t, ns.Name, nsEtcd.Name)
assert.Empty(t, nsEtcd.ClusterName)