mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 19:01:49 +00:00
Refactor etcd3 list consistency test
This commit is contained in:
parent
bbe1ebc82a
commit
cd5da36c92
@ -22,8 +22,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -33,8 +31,6 @@ import (
|
|||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/apitesting"
|
"k8s.io/apimachinery/pkg/api/apitesting"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
@ -269,6 +265,11 @@ func TestListInconsistentContinuation(t *testing.T) {
|
|||||||
storagetesting.RunTestListInconsistentContinuation(ctx, t, store, compactStorage(client))
|
storagetesting.RunTestListInconsistentContinuation(ctx, t, store, compactStorage(client))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConsistentList(t *testing.T) {
|
||||||
|
ctx, store, _ := testSetup(t)
|
||||||
|
storagetesting.RunTestConsistentList(ctx, t, &storeWithPrefixTransformer{store})
|
||||||
|
}
|
||||||
|
|
||||||
func TestCount(t *testing.T) {
|
func TestCount(t *testing.T) {
|
||||||
ctx, store, _ := testSetup(t)
|
ctx, store, _ := testSetup(t)
|
||||||
storagetesting.RunTestCount(ctx, t, store)
|
storagetesting.RunTestCount(ctx, t, store)
|
||||||
@ -509,12 +510,6 @@ func withoutPaging() setupOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func withTransformer(transformer value.Transformer) setupOption {
|
|
||||||
return func(options *setupOptions) {
|
|
||||||
options.transformer = transformer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func withLeaseConfig(leaseConfig LeaseManagerConfig) setupOption {
|
func withLeaseConfig(leaseConfig LeaseManagerConfig) setupOption {
|
||||||
return func(options *setupOptions) {
|
return func(options *setupOptions) {
|
||||||
options.leaseConfig = leaseConfig
|
options.leaseConfig = leaseConfig
|
||||||
@ -565,111 +560,3 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// fancyTransformer creates next object on each call to
|
|
||||||
// TransformFromStorage call.
|
|
||||||
type fancyTransformer struct {
|
|
||||||
transformer value.Transformer
|
|
||||||
store *store
|
|
||||||
|
|
||||||
lock sync.Mutex
|
|
||||||
index int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *fancyTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, bool, error) {
|
|
||||||
if err := t.createObject(ctx); err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
return t.transformer.TransformFromStorage(ctx, data, dataCtx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *fancyTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, error) {
|
|
||||||
return t.transformer.TransformToStorage(ctx, data, dataCtx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *fancyTransformer) createObject(ctx context.Context) error {
|
|
||||||
t.lock.Lock()
|
|
||||||
defer t.lock.Unlock()
|
|
||||||
|
|
||||||
t.index++
|
|
||||||
key := fmt.Sprintf("pod-%d", t.index)
|
|
||||||
obj := &example.Pod{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: key,
|
|
||||||
Labels: map[string]string{
|
|
||||||
"even": strconv.FormatBool(t.index%2 == 0),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
out := &example.Pod{}
|
|
||||||
return t.store.Create(ctx, key, obj, out, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConsistentList(t *testing.T) {
|
|
||||||
transformer := &fancyTransformer{
|
|
||||||
transformer: newTestTransformer(),
|
|
||||||
}
|
|
||||||
ctx, store, _ := testSetup(t, withTransformer(transformer))
|
|
||||||
transformer.store = store
|
|
||||||
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
if err := transformer.createObject(ctx); err != nil {
|
|
||||||
t.Fatalf("failed to create object: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getAttrs := func(obj runtime.Object) (labels.Set, fields.Set, error) {
|
|
||||||
pod, ok := obj.(*example.Pod)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, fmt.Errorf("invalid object")
|
|
||||||
}
|
|
||||||
return labels.Set(pod.Labels), nil, nil
|
|
||||||
}
|
|
||||||
predicate := storage.SelectionPredicate{
|
|
||||||
Label: labels.Set{"even": "true"}.AsSelector(),
|
|
||||||
GetAttrs: getAttrs,
|
|
||||||
Limit: 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
result1 := example.PodList{}
|
|
||||||
options := storage.ListOptions{
|
|
||||||
Predicate: predicate,
|
|
||||||
Recursive: true,
|
|
||||||
}
|
|
||||||
if err := store.GetList(ctx, "/", options, &result1); err != nil {
|
|
||||||
t.Fatalf("failed to list objects: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// List objects from the returned resource version.
|
|
||||||
options = storage.ListOptions{
|
|
||||||
Predicate: predicate,
|
|
||||||
ResourceVersion: result1.ResourceVersion,
|
|
||||||
ResourceVersionMatch: metav1.ResourceVersionMatchExact,
|
|
||||||
Recursive: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
result2 := example.PodList{}
|
|
||||||
if err := store.GetList(ctx, "/", options, &result2); err != nil {
|
|
||||||
t.Fatalf("failed to list objects: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
storagetesting.ExpectNoDiff(t, "incorrect lists", result1, result2)
|
|
||||||
|
|
||||||
// Now also verify the ResourceVersionMatchNotOlderThan.
|
|
||||||
options.ResourceVersionMatch = metav1.ResourceVersionMatchNotOlderThan
|
|
||||||
|
|
||||||
result3 := example.PodList{}
|
|
||||||
if err := store.GetList(ctx, "/", options, &result3); err != nil {
|
|
||||||
t.Fatalf("failed to list objects: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
options.ResourceVersion = result3.ResourceVersion
|
|
||||||
options.ResourceVersionMatch = metav1.ResourceVersionMatchExact
|
|
||||||
|
|
||||||
result4 := example.PodList{}
|
|
||||||
if err := store.GetList(ctx, "/", options, &result4); err != nil {
|
|
||||||
t.Fatalf("failed to list objects: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
storagetesting.ExpectNoDiff(t, "incorrect lists", result3, result4)
|
|
||||||
}
|
|
||||||
|
@ -1574,6 +1574,94 @@ type InterfaceWithPrefixTransformer interface {
|
|||||||
UpdatePrefixTransformer(PrefixTransformerModifier) func()
|
UpdatePrefixTransformer(PrefixTransformerModifier) func()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RunTestConsistentList(ctx context.Context, t *testing.T, store InterfaceWithPrefixTransformer) {
|
||||||
|
nextPod := func(index uint32) (string, *example.Pod) {
|
||||||
|
key := fmt.Sprintf("pod-%d", index)
|
||||||
|
obj := &example.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: key,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"even": strconv.FormatBool(index%2 == 0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return key, obj
|
||||||
|
}
|
||||||
|
|
||||||
|
transformer := &reproducingTransformer{
|
||||||
|
store: store,
|
||||||
|
nextObject: nextPod,
|
||||||
|
}
|
||||||
|
|
||||||
|
revertTransformer := store.UpdatePrefixTransformer(
|
||||||
|
func(previousTransformer *PrefixTransformer) value.Transformer {
|
||||||
|
transformer.wrapped = previousTransformer
|
||||||
|
return transformer
|
||||||
|
})
|
||||||
|
defer revertTransformer()
|
||||||
|
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
if err := transformer.createObject(ctx); err != nil {
|
||||||
|
t.Fatalf("failed to create object: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAttrs := func(obj runtime.Object) (labels.Set, fields.Set, error) {
|
||||||
|
pod, ok := obj.(*example.Pod)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("invalid object")
|
||||||
|
}
|
||||||
|
return labels.Set(pod.Labels), nil, nil
|
||||||
|
}
|
||||||
|
predicate := storage.SelectionPredicate{
|
||||||
|
Label: labels.Set{"even": "true"}.AsSelector(),
|
||||||
|
GetAttrs: getAttrs,
|
||||||
|
Limit: 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
result1 := example.PodList{}
|
||||||
|
options := storage.ListOptions{
|
||||||
|
Predicate: predicate,
|
||||||
|
Recursive: true,
|
||||||
|
}
|
||||||
|
if err := store.GetList(ctx, "/", options, &result1); err != nil {
|
||||||
|
t.Fatalf("failed to list objects: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List objects from the returned resource version.
|
||||||
|
options = storage.ListOptions{
|
||||||
|
Predicate: predicate,
|
||||||
|
ResourceVersion: result1.ResourceVersion,
|
||||||
|
ResourceVersionMatch: metav1.ResourceVersionMatchExact,
|
||||||
|
Recursive: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
result2 := example.PodList{}
|
||||||
|
if err := store.GetList(ctx, "/", options, &result2); err != nil {
|
||||||
|
t.Fatalf("failed to list objects: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpectNoDiff(t, "incorrect lists", result1, result2)
|
||||||
|
|
||||||
|
// Now also verify the ResourceVersionMatchNotOlderThan.
|
||||||
|
options.ResourceVersionMatch = metav1.ResourceVersionMatchNotOlderThan
|
||||||
|
|
||||||
|
result3 := example.PodList{}
|
||||||
|
if err := store.GetList(ctx, "/", options, &result3); err != nil {
|
||||||
|
t.Fatalf("failed to list objects: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
options.ResourceVersion = result3.ResourceVersion
|
||||||
|
options.ResourceVersionMatch = metav1.ResourceVersionMatchExact
|
||||||
|
|
||||||
|
result4 := example.PodList{}
|
||||||
|
if err := store.GetList(ctx, "/", options, &result4); err != nil {
|
||||||
|
t.Fatalf("failed to list objects: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpectNoDiff(t, "incorrect lists", result3, result4)
|
||||||
|
}
|
||||||
|
|
||||||
func RunTestGuaranteedUpdate(ctx context.Context, t *testing.T, store InterfaceWithPrefixTransformer, validation KeyValidation) {
|
func RunTestGuaranteedUpdate(ctx context.Context, t *testing.T, store InterfaceWithPrefixTransformer, validation KeyValidation) {
|
||||||
key := "/testkey"
|
key := "/testkey"
|
||||||
|
|
||||||
|
@ -235,3 +235,33 @@ func (p *PrefixTransformer) TransformToStorage(ctx context.Context, data []byte,
|
|||||||
func (p *PrefixTransformer) GetReadsAndReset() uint64 {
|
func (p *PrefixTransformer) GetReadsAndReset() uint64 {
|
||||||
return atomic.SwapUint64(&p.reads, 0)
|
return atomic.SwapUint64(&p.reads, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reproducingTransformer is a custom test-only transformer used purely
|
||||||
|
// for testing consistency.
|
||||||
|
// It allows for creating predefined objects on TransformFromStorage operations,
|
||||||
|
// which allows for precise in time injection of new objects in the middle of
|
||||||
|
// read operations.
|
||||||
|
type reproducingTransformer struct {
|
||||||
|
wrapped value.Transformer
|
||||||
|
store storage.Interface
|
||||||
|
|
||||||
|
index uint32
|
||||||
|
nextObject func(uint32) (string, *example.Pod)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *reproducingTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, bool, error) {
|
||||||
|
if err := rt.createObject(ctx); err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
return rt.wrapped.TransformFromStorage(ctx, data, dataCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *reproducingTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, error) {
|
||||||
|
return rt.wrapped.TransformToStorage(ctx, data, dataCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt *reproducingTransformer) createObject(ctx context.Context) error {
|
||||||
|
key, obj := rt.nextObject(atomic.AddUint32(&rt.index, 1))
|
||||||
|
out := &example.Pod{}
|
||||||
|
return rt.store.Create(ctx, key, obj, out, 0)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user