k8s.io/apiserver@v0.29.3/pkg/storage/util_test.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package storage_test
    18  
    19  import (
    20  	"context"
    21  	"math/rand"
    22  	"sync"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/require"
    26  	"k8s.io/apimachinery/pkg/api/apitesting"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/apimachinery/pkg/runtime/schema"
    30  	"k8s.io/apimachinery/pkg/runtime/serializer"
    31  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    32  	"k8s.io/apiserver/pkg/apis/example"
    33  	examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
    34  	example2v1 "k8s.io/apiserver/pkg/apis/example2/v1"
    35  	"k8s.io/apiserver/pkg/storage"
    36  	"k8s.io/apiserver/pkg/storage/etcd3"
    37  	etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
    38  	"k8s.io/apiserver/pkg/storage/value/encrypt/identity"
    39  )
    40  
    41  var (
    42  	scheme = runtime.NewScheme()
    43  	codecs = serializer.NewCodecFactory(scheme)
    44  )
    45  
    46  func init() {
    47  	metav1.AddToGroupVersion(scheme, metav1.SchemeGroupVersion)
    48  	utilruntime.Must(example.AddToScheme(scheme))
    49  	utilruntime.Must(examplev1.AddToScheme(scheme))
    50  	utilruntime.Must(example2v1.AddToScheme(scheme))
    51  }
    52  
    53  func TestHighWaterMark(t *testing.T) {
    54  	var h storage.HighWaterMark
    55  
    56  	for i := int64(10); i < 20; i++ {
    57  		if !h.Update(i) {
    58  			t.Errorf("unexpected false for %v", i)
    59  		}
    60  		if h.Update(i - 1) {
    61  			t.Errorf("unexpected true for %v", i-1)
    62  		}
    63  	}
    64  
    65  	m := int64(0)
    66  	wg := sync.WaitGroup{}
    67  	for i := 0; i < 300; i++ {
    68  		wg.Add(1)
    69  		v := rand.Int63()
    70  		go func(v int64) {
    71  			defer wg.Done()
    72  			h.Update(v)
    73  		}(v)
    74  		if v > m {
    75  			m = v
    76  		}
    77  	}
    78  	wg.Wait()
    79  	if m != int64(h) {
    80  		t.Errorf("unexpected value, wanted %v, got %v", m, int64(h))
    81  	}
    82  }
    83  
    84  func TestGetCurrentResourceVersionFromStorage(t *testing.T) {
    85  	// test data
    86  	newEtcdTestStorage := func(t *testing.T, prefix string) (*etcd3testing.EtcdTestServer, storage.Interface) {
    87  		server, _ := etcd3testing.NewUnsecuredEtcd3TestClientServer(t)
    88  		storage := etcd3.New(server.V3Client, apitesting.TestCodec(codecs, examplev1.SchemeGroupVersion, example2v1.SchemeGroupVersion), func() runtime.Object { return &example.Pod{} }, func() runtime.Object { return &example.PodList{} }, prefix, "/pods", schema.GroupResource{Resource: "pods"}, identity.NewEncryptCheckTransformer(), etcd3.NewDefaultLeaseManagerConfig())
    89  		return server, storage
    90  	}
    91  	server, etcdStorage := newEtcdTestStorage(t, "")
    92  	defer server.Terminate(t)
    93  	versioner := storage.APIObjectVersioner{}
    94  
    95  	makePod := func(name string) *example.Pod {
    96  		return &example.Pod{
    97  			ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: name},
    98  		}
    99  	}
   100  	createPod := func(obj *example.Pod) *example.Pod {
   101  		key := "pods/" + obj.Namespace + "/" + obj.Name
   102  		out := &example.Pod{}
   103  		err := etcdStorage.Create(context.TODO(), key, obj, out, 0)
   104  		require.NoError(t, err)
   105  		return out
   106  	}
   107  	getPod := func(name, ns string) *example.Pod {
   108  		key := "pods/" + ns + "/" + name
   109  		out := &example.Pod{}
   110  		err := etcdStorage.Get(context.TODO(), key, storage.GetOptions{}, out)
   111  		require.NoError(t, err)
   112  		return out
   113  	}
   114  	makeReplicaSet := func(name string) *example2v1.ReplicaSet {
   115  		return &example2v1.ReplicaSet{
   116  			ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: name},
   117  		}
   118  	}
   119  	createReplicaSet := func(obj *example2v1.ReplicaSet) *example2v1.ReplicaSet {
   120  		key := "replicasets/" + obj.Namespace + "/" + obj.Name
   121  		out := &example2v1.ReplicaSet{}
   122  		err := etcdStorage.Create(context.TODO(), key, obj, out, 0)
   123  		require.NoError(t, err)
   124  		return out
   125  	}
   126  
   127  	// create a pod and make sure its RV is equal to the one maintained by etcd
   128  	pod := createPod(makePod("pod-1"))
   129  	currentStorageRV, err := storage.GetCurrentResourceVersionFromStorage(context.TODO(), etcdStorage, func() runtime.Object { return &example.PodList{} }, "/pods", "Pod")
   130  	require.NoError(t, err)
   131  	podRV, err := versioner.ParseResourceVersion(pod.ResourceVersion)
   132  	require.NoError(t, err)
   133  	require.Equal(t, currentStorageRV, podRV, "expected the global etcd RV to be equal to pod's RV")
   134  
   135  	// now create a replicaset (new resource) and make sure the target function returns global etcd RV
   136  	rs := createReplicaSet(makeReplicaSet("replicaset-1"))
   137  	currentStorageRV, err = storage.GetCurrentResourceVersionFromStorage(context.TODO(), etcdStorage, func() runtime.Object { return &example.PodList{} }, "/pods", "Pod")
   138  	require.NoError(t, err)
   139  	rsRV, err := versioner.ParseResourceVersion(rs.ResourceVersion)
   140  	require.NoError(t, err)
   141  	require.Equal(t, currentStorageRV, rsRV, "expected the global etcd RV to be equal to replicaset's RV")
   142  
   143  	// ensure that the pod's RV hasn't been changed
   144  	currentPod := getPod(pod.Name, pod.Namespace)
   145  	currentPodRV, err := versioner.ParseResourceVersion(currentPod.ResourceVersion)
   146  	require.NoError(t, err)
   147  	require.Equal(t, currentPodRV, podRV, "didn't expect to see the pod's RV changed")
   148  }
   149  
   150  func TestHasInitialEventsEndBookmarkAnnotation(t *testing.T) {
   151  	createPod := func(name string) *example.Pod {
   152  		return &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: name}}
   153  	}
   154  	createAnnotatedPod := func(name, value string) *example.Pod {
   155  		p := createPod(name)
   156  		p.Annotations = map[string]string{}
   157  		p.Annotations["k8s.io/initial-events-end"] = value
   158  		return p
   159  	}
   160  	scenarios := []struct {
   161  		name             string
   162  		object           runtime.Object
   163  		expectAnnotation bool
   164  	}{
   165  		{
   166  			name:             "a standard obj with the initial-events-end annotation set to true",
   167  			object:           createAnnotatedPod("p1", "true"),
   168  			expectAnnotation: true,
   169  		},
   170  		{
   171  			name:   "a standard obj with the initial-events-end annotation set to false",
   172  			object: createAnnotatedPod("p1", "false"),
   173  		},
   174  		{
   175  			name:   "a standard obj without the annotation",
   176  			object: createPod("p1"),
   177  		},
   178  	}
   179  
   180  	for _, scenario := range scenarios {
   181  		t.Run(scenario.name, func(t *testing.T) {
   182  			hasAnnotation, err := storage.HasInitialEventsEndBookmarkAnnotation(scenario.object)
   183  			require.NoError(t, err)
   184  			require.Equal(t, scenario.expectAnnotation, hasAnnotation)
   185  		})
   186  	}
   187  }