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 }