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

@@ -35,7 +35,7 @@ type ListerWatcher interface {
// ResourceVersion field will be used to start the watch in the right place.
List() (runtime.Object, error)
// Watch should begin a watch at the specified version.
Watch(resourceVersion uint64) (watch.Interface, error)
Watch(resourceVersion string) (watch.Interface, error)
}
// Reflector watches a specified resource and causes all changes to be reflected in the given store.
@@ -71,7 +71,7 @@ func (r *Reflector) Run() {
}
func (r *Reflector) listAndWatch() {
var resourceVersion uint64
var resourceVersion string
list, err := r.listerWatcher.List()
if err != nil {
@@ -124,7 +124,7 @@ func (r *Reflector) syncWith(items []runtime.Object) error {
}
// watchHandler watches w and keeps *resourceVersion up to date.
func (r *Reflector) watchHandler(w watch.Interface, resourceVersion *uint64) error {
func (r *Reflector) watchHandler(w watch.Interface, resourceVersion *string) error {
start := time.Now()
eventCount := 0
for {
@@ -157,7 +157,7 @@ func (r *Reflector) watchHandler(w watch.Interface, resourceVersion *uint64) err
default:
glog.Errorf("unable to understand watch event %#v", event)
}
*resourceVersion = jsonBase.ResourceVersion() + 1
*resourceVersion = jsonBase.ResourceVersion()
eventCount++
}

View File

@@ -18,6 +18,7 @@ package cache
import (
"fmt"
"strconv"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
@@ -27,11 +28,11 @@ import (
type testLW struct {
ListFunc func() (runtime.Object, error)
WatchFunc func(resourceVersion uint64) (watch.Interface, error)
WatchFunc func(resourceVersion string) (watch.Interface, error)
}
func (t *testLW) List() (runtime.Object, error) { return t.ListFunc() }
func (t *testLW) Watch(resourceVersion uint64) (watch.Interface, error) {
func (t *testLW) Watch(resourceVersion string) (watch.Interface, error) {
return t.WatchFunc(resourceVersion)
}
@@ -42,7 +43,7 @@ func TestReflector_watchHandlerError(t *testing.T) {
go func() {
fw.Stop()
}()
var resumeRV uint64
var resumeRV string
err := g.watchHandler(fw, &resumeRV)
if err == nil {
t.Errorf("unexpected non-error")
@@ -58,11 +59,11 @@ func TestReflector_watchHandler(t *testing.T) {
go func() {
fw.Add(&api.Service{TypeMeta: api.TypeMeta{ID: "rejected"}})
fw.Delete(&api.Pod{TypeMeta: api.TypeMeta{ID: "foo"}})
fw.Modify(&api.Pod{TypeMeta: api.TypeMeta{ID: "bar", ResourceVersion: 55}})
fw.Add(&api.Pod{TypeMeta: api.TypeMeta{ID: "baz", ResourceVersion: 32}})
fw.Modify(&api.Pod{TypeMeta: api.TypeMeta{ID: "bar", ResourceVersion: "55"}})
fw.Add(&api.Pod{TypeMeta: api.TypeMeta{ID: "baz", ResourceVersion: "32"}})
fw.Stop()
}()
var resumeRV uint64
var resumeRV string
err := g.watchHandler(fw, &resumeRV)
if err != nil {
t.Errorf("unexpected error %v", err)
@@ -70,13 +71,13 @@ func TestReflector_watchHandler(t *testing.T) {
table := []struct {
ID string
RV uint64
RV string
exists bool
}{
{"foo", 0, false},
{"rejected", 0, false},
{"bar", 55, true},
{"baz", 32, true},
{"foo", "", false},
{"rejected", "", false},
{"bar", "55", true},
{"baz", "32", true},
}
for _, item := range table {
obj, exists := s.Get(item.ID)
@@ -92,7 +93,7 @@ func TestReflector_watchHandler(t *testing.T) {
}
// RV should stay 1 higher than the last id we see.
if e, a := uint64(33), resumeRV; e != a {
if e, a := "33", resumeRV; e != a {
t.Errorf("expected %v, got %v", e, a)
}
}
@@ -103,9 +104,9 @@ func TestReflector_listAndWatch(t *testing.T) {
// The ListFunc says that it's at revision 1. Therefore, we expect our WatchFunc
// to get called at the beginning of the watch with 1, and again with 4 when we
// inject an error at 3.
expectedRVs := []uint64{1, 4}
expectedRVs := []string{"1", "4"}
lw := &testLW{
WatchFunc: func(rv uint64) (watch.Interface, error) {
WatchFunc: func(rv string) (watch.Interface, error) {
fw := watch.NewFake()
if e, a := expectedRVs[0], rv; e != a {
t.Errorf("Expected rv %v, but got %v", e, a)
@@ -117,7 +118,7 @@ func TestReflector_listAndWatch(t *testing.T) {
return fw, nil
},
ListFunc: func() (runtime.Object, error) {
return &api.PodList{TypeMeta: api.TypeMeta{ResourceVersion: 1}}, nil
return &api.PodList{TypeMeta: api.TypeMeta{ResourceVersion: "1"}}, nil
},
}
s := NewFIFO()
@@ -130,9 +131,9 @@ func TestReflector_listAndWatch(t *testing.T) {
if fw == nil {
fw = <-createdFakes
}
sendingRV := uint64(i + 2)
sendingRV := strconv.FormatUint(uint64(i+2), 10)
fw.Add(&api.Pod{TypeMeta: api.TypeMeta{ID: id, ResourceVersion: sendingRV}})
if sendingRV == 3 {
if sendingRV == "3" {
// Inject a failure.
fw.Stop()
fw = nil
@@ -145,7 +146,7 @@ func TestReflector_listAndWatch(t *testing.T) {
if e, a := id, pod.ID; e != a {
t.Errorf("%v: Expected %v, got %v", i, e, a)
}
if e, a := uint64(i+2), pod.ResourceVersion; e != a {
if e, a := strconv.FormatUint(uint64(i+2), 10), pod.ResourceVersion; e != a {
t.Errorf("%v: Expected %v, got %v", i, e, a)
}
}
@@ -156,10 +157,10 @@ func TestReflector_listAndWatch(t *testing.T) {
}
func TestReflector_listAndWatchWithErrors(t *testing.T) {
mkPod := func(id string, rv uint64) *api.Pod {
mkPod := func(id string, rv string) *api.Pod {
return &api.Pod{TypeMeta: api.TypeMeta{ID: id, ResourceVersion: rv}}
}
mkList := func(rv uint64, pods ...*api.Pod) *api.PodList {
mkList := func(rv string, pods ...*api.Pod) *api.PodList {
list := &api.PodList{TypeMeta: api.TypeMeta{ResourceVersion: rv}}
for _, pod := range pods {
list.Items = append(list.Items, *pod)
@@ -173,29 +174,29 @@ func TestReflector_listAndWatchWithErrors(t *testing.T) {
watchErr error
}{
{
list: mkList(1),
list: mkList("1"),
events: []watch.Event{
{watch.Added, mkPod("foo", 2)},
{watch.Added, mkPod("bar", 3)},
{watch.Added, mkPod("foo", "2")},
{watch.Added, mkPod("bar", "3")},
},
}, {
list: mkList(3, mkPod("foo", 2), mkPod("bar", 3)),
list: mkList("3", mkPod("foo", "2"), mkPod("bar", "3")),
events: []watch.Event{
{watch.Deleted, mkPod("foo", 4)},
{watch.Added, mkPod("qux", 5)},
{watch.Deleted, mkPod("foo", "4")},
{watch.Added, mkPod("qux", "5")},
},
}, {
listErr: fmt.Errorf("a list error"),
}, {
list: mkList(5, mkPod("bar", 3), mkPod("qux", 5)),
list: mkList("5", mkPod("bar", "3"), mkPod("qux", "5")),
watchErr: fmt.Errorf("a watch error"),
}, {
list: mkList(5, mkPod("bar", 3), mkPod("qux", 5)),
list: mkList("5", mkPod("bar", "3"), mkPod("qux", "5")),
events: []watch.Event{
{watch.Added, mkPod("baz", 6)},
{watch.Added, mkPod("baz", "6")},
},
}, {
list: mkList(6, mkPod("bar", 3), mkPod("qux", 5), mkPod("baz", 6)),
list: mkList("6", mkPod("bar", "3"), mkPod("qux", "5"), mkPod("baz", "6")),
},
}
@@ -204,7 +205,7 @@ func TestReflector_listAndWatchWithErrors(t *testing.T) {
if item.list != nil {
// Test that the list is what currently exists in the store.
current := s.List()
checkMap := map[string]uint64{}
checkMap := map[string]string{}
for _, item := range current {
pod := item.(*api.Pod)
checkMap[pod.ID] = pod.ResourceVersion
@@ -220,7 +221,7 @@ func TestReflector_listAndWatchWithErrors(t *testing.T) {
}
watchRet, watchErr := item.events, item.watchErr
lw := &testLW{
WatchFunc: func(rv uint64) (watch.Interface, error) {
WatchFunc: func(rv string) (watch.Interface, error) {
if watchErr != nil {
return nil, watchErr
}