Make ResourceVersion a string internally instead of uint64

Allows us to define different watch versioning regimes in the future
as well as to encode information with the resource version.

This changes /watch/resources?resourceVersion=3 to start the watch at
4 instead of 3, which means clients can read a resource version and
then send it back to the server. Clients should no longer do math on
resource versions.
This commit is contained in:
Clayton Coleman
2014-10-07 16:51:28 -04:00
parent 31e02b882b
commit 82bcdd3b3b
54 changed files with 518 additions and 240 deletions

View File

@@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"reflect"
"strconv"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/coreos/go-etcd/etcd"
@@ -62,12 +63,43 @@ type EtcdGetSet interface {
Watch(prefix string, waitIndex uint64, recursive bool, receiver chan *etcd.Response, stop chan bool) (*etcd.Response, error)
}
type EtcdResourceVersioner interface {
SetResourceVersion(obj runtime.Object, version uint64) error
ResourceVersion(obj runtime.Object) (uint64, error)
}
// RuntimeVersionAdapter converts a string based versioner to EtcdResourceVersioner
type RuntimeVersionAdapter struct {
Versioner runtime.ResourceVersioner
}
// SetResourceVersion implements EtcdResourceVersioner
func (a RuntimeVersionAdapter) SetResourceVersion(obj runtime.Object, version uint64) error {
if version == 0 {
return a.Versioner.SetResourceVersion(obj, "")
}
s := strconv.FormatUint(version, 10)
return a.Versioner.SetResourceVersion(obj, s)
}
// SetResourceVersion implements EtcdResourceVersioner
func (a RuntimeVersionAdapter) ResourceVersion(obj runtime.Object) (uint64, error) {
version, err := a.Versioner.ResourceVersion(obj)
if err != nil {
return 0, err
}
if version == "" {
return 0, nil
}
return strconv.ParseUint(version, 10, 64)
}
// EtcdHelper offers common object marshalling/unmarshalling operations on an etcd client.
type EtcdHelper struct {
Client EtcdGetSet
Codec runtime.Codec
// optional, no atomic operations can be performed without this interface
ResourceVersioner runtime.ResourceVersioner
ResourceVersioner EtcdResourceVersioner
}
// IsEtcdNotFound returns true iff err is an etcd not found error.

View File

@@ -44,7 +44,7 @@ func (*TestResource) IsAnAPIObject() {}
var scheme *runtime.Scheme
var codec runtime.Codec
var versioner = runtime.NewTypeMetaResourceVersioner()
var versioner = RuntimeVersionAdapter{runtime.NewTypeMetaResourceVersioner()}
func init() {
scheme = runtime.NewScheme()
@@ -89,11 +89,11 @@ func TestExtractToList(t *testing.T) {
},
}
expect := api.PodList{
TypeMeta: api.TypeMeta{ResourceVersion: 10},
TypeMeta: api.TypeMeta{ResourceVersion: "10"},
Items: []api.Pod{
{TypeMeta: api.TypeMeta{ID: "foo", ResourceVersion: 1}},
{TypeMeta: api.TypeMeta{ID: "bar", ResourceVersion: 2}},
{TypeMeta: api.TypeMeta{ID: "baz", ResourceVersion: 3}},
{TypeMeta: api.TypeMeta{ID: "foo", ResourceVersion: "1"}},
{TypeMeta: api.TypeMeta{ID: "bar", ResourceVersion: "2"}},
{TypeMeta: api.TypeMeta{ID: "baz", ResourceVersion: "3"}},
},
}
@@ -204,7 +204,7 @@ func TestSetObj(t *testing.T) {
}
func TestSetObjWithVersion(t *testing.T) {
obj := &api.Pod{TypeMeta: api.TypeMeta{ID: "foo", ResourceVersion: 1}}
obj := &api.Pod{TypeMeta: api.TypeMeta{ID: "foo", ResourceVersion: "1"}}
fakeClient := NewFakeEtcdClient(t)
fakeClient.TestIndex = true
fakeClient.Data["/some/key"] = EtcdResponseWithError{
@@ -254,7 +254,7 @@ func TestSetObjWithoutResourceVersioner(t *testing.T) {
func TestAtomicUpdate(t *testing.T) {
fakeClient := NewFakeEtcdClient(t)
fakeClient.TestIndex = true
helper := EtcdHelper{fakeClient, codec, runtime.NewTypeMetaResourceVersioner()}
helper := EtcdHelper{fakeClient, codec, versioner}
// Create a new node.
fakeClient.ExpectNotFoundGet("/some/key")
@@ -308,7 +308,7 @@ func TestAtomicUpdate(t *testing.T) {
func TestAtomicUpdateNoChange(t *testing.T) {
fakeClient := NewFakeEtcdClient(t)
fakeClient.TestIndex = true
helper := EtcdHelper{fakeClient, codec, runtime.NewTypeMetaResourceVersioner()}
helper := EtcdHelper{fakeClient, codec, versioner}
// Create a new node.
fakeClient.ExpectNotFoundGet("/some/key")
@@ -339,7 +339,7 @@ func TestAtomicUpdateNoChange(t *testing.T) {
func TestAtomicUpdate_CreateCollision(t *testing.T) {
fakeClient := NewFakeEtcdClient(t)
fakeClient.TestIndex = true
helper := EtcdHelper{fakeClient, codec, runtime.NewTypeMetaResourceVersioner()}
helper := EtcdHelper{fakeClient, codec, versioner}
fakeClient.ExpectNotFoundGet("/some/key")

View File

@@ -82,7 +82,7 @@ type TransformFunc func(runtime.Object) (runtime.Object, error)
// etcdWatcher converts a native etcd watch to a watch.Interface.
type etcdWatcher struct {
encoding runtime.Codec
versioner runtime.ResourceVersioner
versioner EtcdResourceVersioner
transform TransformFunc
list bool // If we're doing a recursive watch, should be true.
@@ -107,7 +107,7 @@ const watchWaitDuration = 100 * time.Millisecond
// newEtcdWatcher returns a new etcdWatcher; if list is true, watch sub-nodes. If you provide a transform
// and a versioner, the versioner must be able to handle the objects that transform creates.
func newEtcdWatcher(list bool, filter FilterFunc, encoding runtime.Codec, versioner runtime.ResourceVersioner, transform TransformFunc) *etcdWatcher {
func newEtcdWatcher(list bool, filter FilterFunc, encoding runtime.Codec, versioner EtcdResourceVersioner, transform TransformFunc) *etcdWatcher {
w := &etcdWatcher{
encoding: encoding,
versioner: versioner,

View File

@@ -395,7 +395,7 @@ func TestWatchFromZeroIndex(t *testing.T) {
testCases := map[string]struct {
Response EtcdResponseWithError
ExpectedVersion uint64
ExpectedVersion string
ExpectedType watch.EventType
}{
"get value created": {
@@ -410,7 +410,7 @@ func TestWatchFromZeroIndex(t *testing.T) {
EtcdIndex: 2,
},
},
1,
"1",
watch.Added,
},
"get value modified": {
@@ -425,7 +425,7 @@ func TestWatchFromZeroIndex(t *testing.T) {
EtcdIndex: 3,
},
},
2,
"2",
watch.Modified,
},
}
@@ -510,10 +510,10 @@ func TestWatchListFromZeroIndex(t *testing.T) {
if !ok {
t.Fatalf("expected a pod, got %#v", event.Object)
}
if actualPod.ResourceVersion != 1 {
if actualPod.ResourceVersion != "1" {
t.Errorf("Expected pod with resource version %d, Got %#v", 1, actualPod)
}
pod.ResourceVersion = 1
pod.ResourceVersion = "1"
if e, a := pod, event.Object; !reflect.DeepEqual(e, a) {
t.Errorf("Expected %v, got %v", e, a)
}