k8s.io/kubernetes@v1.29.3/pkg/registry/core/replicationcontroller/storage/storage_test.go (about) 1 /* 2 Copyright 2014 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 18 19 import ( 20 "context" 21 "fmt" 22 "sync" 23 "testing" 24 25 "github.com/google/go-cmp/cmp" 26 apiequality "k8s.io/apimachinery/pkg/api/equality" 27 "k8s.io/apimachinery/pkg/api/errors" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/fields" 30 "k8s.io/apimachinery/pkg/labels" 31 "k8s.io/apimachinery/pkg/runtime" 32 genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 33 "k8s.io/apiserver/pkg/registry/generic" 34 genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing" 35 "k8s.io/apiserver/pkg/registry/rest" 36 etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing" 37 "k8s.io/kubernetes/pkg/apis/autoscaling" 38 api "k8s.io/kubernetes/pkg/apis/core" 39 "k8s.io/kubernetes/pkg/registry/registrytest" 40 ) 41 42 const ( 43 namespace = metav1.NamespaceDefault 44 name = "foo" 45 ) 46 47 func newStorage(t *testing.T) (ControllerStorage, *etcd3testing.EtcdTestServer) { 48 etcdStorage, server := registrytest.NewEtcdStorage(t, "") 49 restOptions := generic.RESTOptions{ 50 StorageConfig: etcdStorage, 51 Decorator: generic.UndecoratedStorage, 52 DeleteCollectionWorkers: 1, 53 ResourcePrefix: "replicationcontrollers", 54 } 55 storage, err := NewStorage(restOptions) 56 if err != nil { 57 t.Fatalf("unexpected error from REST storage: %v", err) 58 } 59 return storage, server 60 } 61 62 // createController is a helper function that returns a controller with the updated resource version. 63 func createController(storage *REST, rc api.ReplicationController, t *testing.T) (api.ReplicationController, error) { 64 ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), rc.Namespace) 65 obj, err := storage.Create(ctx, &rc, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 66 if err != nil { 67 t.Errorf("Failed to create controller, %v", err) 68 } 69 newRc := obj.(*api.ReplicationController) 70 return *newRc, nil 71 } 72 73 func validNewController() *api.ReplicationController { 74 return &api.ReplicationController{ 75 ObjectMeta: metav1.ObjectMeta{ 76 Name: name, 77 Namespace: namespace, 78 }, 79 Spec: api.ReplicationControllerSpec{ 80 Selector: map[string]string{"a": "b"}, 81 Template: &api.PodTemplateSpec{ 82 ObjectMeta: metav1.ObjectMeta{ 83 Labels: map[string]string{"a": "b"}, 84 }, 85 Spec: api.PodSpec{ 86 Containers: []api.Container{ 87 { 88 Name: "test", 89 Image: "test_image", 90 ImagePullPolicy: api.PullIfNotPresent, 91 TerminationMessagePolicy: api.TerminationMessageReadFile, 92 }, 93 }, 94 RestartPolicy: api.RestartPolicyAlways, 95 DNSPolicy: api.DNSClusterFirst, 96 }, 97 }, 98 }, 99 } 100 } 101 102 var validController = validNewController() 103 104 func TestCreate(t *testing.T) { 105 storage, server := newStorage(t) 106 defer server.Terminate(t) 107 defer storage.Controller.Store.DestroyFunc() 108 test := genericregistrytest.New(t, storage.Controller.Store) 109 controller := validNewController() 110 controller.ObjectMeta = metav1.ObjectMeta{} 111 test.TestCreate( 112 // valid 113 controller, 114 // invalid (invalid selector) 115 &api.ReplicationController{ 116 Spec: api.ReplicationControllerSpec{ 117 Replicas: 2, 118 Selector: map[string]string{}, 119 Template: validController.Spec.Template, 120 }, 121 }, 122 ) 123 } 124 125 func TestUpdate(t *testing.T) { 126 storage, server := newStorage(t) 127 defer server.Terminate(t) 128 defer storage.Controller.Store.DestroyFunc() 129 test := genericregistrytest.New(t, storage.Controller.Store) 130 test.TestUpdate( 131 // valid 132 validNewController(), 133 // valid updateFunc 134 func(obj runtime.Object) runtime.Object { 135 object := obj.(*api.ReplicationController) 136 object.Spec.Replicas = object.Spec.Replicas + 1 137 return object 138 }, 139 // invalid updateFunc 140 func(obj runtime.Object) runtime.Object { 141 object := obj.(*api.ReplicationController) 142 object.Name = "" 143 return object 144 }, 145 func(obj runtime.Object) runtime.Object { 146 object := obj.(*api.ReplicationController) 147 object.Spec.Selector = map[string]string{} 148 return object 149 }, 150 ) 151 } 152 153 func TestDelete(t *testing.T) { 154 storage, server := newStorage(t) 155 defer server.Terminate(t) 156 defer storage.Controller.Store.DestroyFunc() 157 test := genericregistrytest.New(t, storage.Controller.Store) 158 test.TestDelete(validNewController()) 159 } 160 161 func TestGenerationNumber(t *testing.T) { 162 storage, server := newStorage(t) 163 defer server.Terminate(t) 164 defer storage.Controller.Store.DestroyFunc() 165 modifiedSno := *validNewController() 166 modifiedSno.Generation = 100 167 modifiedSno.Status.ObservedGeneration = 10 168 ctx := genericapirequest.NewDefaultContext() 169 rc, err := createController(storage.Controller, modifiedSno, t) 170 if err != nil { 171 t.Errorf("unexpected error: %v", err) 172 } 173 ctrl, err := storage.Controller.Get(ctx, rc.Name, &metav1.GetOptions{}) 174 if err != nil { 175 t.Errorf("unexpected error: %v", err) 176 } 177 controller, _ := ctrl.(*api.ReplicationController) 178 179 // Generation initialization 180 if controller.Generation != 1 || controller.Status.ObservedGeneration != 0 { 181 t.Fatalf("Unexpected generation number %v, status generation %v", controller.Generation, controller.Status.ObservedGeneration) 182 } 183 184 // Updates to spec should increment the generation number 185 controller.Spec.Replicas++ 186 if _, _, err := storage.Controller.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}); err != nil { 187 t.Errorf("unexpected error: %v", err) 188 } 189 ctrl, err = storage.Controller.Get(ctx, rc.Name, &metav1.GetOptions{}) 190 if err != nil { 191 t.Errorf("unexpected error: %v", err) 192 } 193 controller, _ = ctrl.(*api.ReplicationController) 194 if controller.Generation != 2 || controller.Status.ObservedGeneration != 0 { 195 t.Fatalf("Unexpected generation, spec: %v, status: %v", controller.Generation, controller.Status.ObservedGeneration) 196 } 197 198 // Updates to status should not increment either spec or status generation numbers 199 controller.Status.Replicas++ 200 if _, _, err := storage.Controller.Update(ctx, controller.Name, rest.DefaultUpdatedObjectInfo(controller), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}); err != nil { 201 t.Errorf("unexpected error: %v", err) 202 } 203 ctrl, err = storage.Controller.Get(ctx, rc.Name, &metav1.GetOptions{}) 204 if err != nil { 205 t.Errorf("unexpected error: %v", err) 206 } 207 controller, _ = ctrl.(*api.ReplicationController) 208 if controller.Generation != 2 || controller.Status.ObservedGeneration != 0 { 209 t.Fatalf("Unexpected generation number, spec: %v, status: %v", controller.Generation, controller.Status.ObservedGeneration) 210 } 211 } 212 213 func TestGet(t *testing.T) { 214 storage, server := newStorage(t) 215 defer server.Terminate(t) 216 defer storage.Controller.Store.DestroyFunc() 217 test := genericregistrytest.New(t, storage.Controller.Store) 218 test.TestGet(validNewController()) 219 } 220 221 func TestList(t *testing.T) { 222 storage, server := newStorage(t) 223 defer server.Terminate(t) 224 defer storage.Controller.Store.DestroyFunc() 225 test := genericregistrytest.New(t, storage.Controller.Store) 226 test.TestList(validNewController()) 227 } 228 229 func TestWatch(t *testing.T) { 230 storage, server := newStorage(t) 231 defer server.Terminate(t) 232 defer storage.Controller.Store.DestroyFunc() 233 test := genericregistrytest.New(t, storage.Controller.Store) 234 test.TestWatch( 235 validController, 236 // matching labels 237 []labels.Set{ 238 {"a": "b"}, 239 }, 240 // not matching labels 241 []labels.Set{ 242 {"a": "c"}, 243 {"foo": "bar"}, 244 }, 245 // matching fields 246 []fields.Set{ 247 {"status.replicas": "0"}, 248 {"metadata.name": "foo"}, 249 {"status.replicas": "0", "metadata.name": "foo"}, 250 }, 251 // not matching fields 252 []fields.Set{ 253 {"status.replicas": "10"}, 254 {"metadata.name": "bar"}, 255 {"name": "foo"}, 256 {"status.replicas": "10", "metadata.name": "foo"}, 257 {"status.replicas": "0", "metadata.name": "bar"}, 258 }, 259 ) 260 } 261 262 //TODO TestUpdateStatus 263 264 func TestScaleGet(t *testing.T) { 265 storage, server := newStorage(t) 266 defer server.Terminate(t) 267 defer storage.Controller.Store.DestroyFunc() 268 269 ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace) 270 rc, err := createController(storage.Controller, *validController, t) 271 if err != nil { 272 t.Fatalf("error setting new replication controller %v: %v", *validController, err) 273 } 274 275 want := &autoscaling.Scale{ 276 ObjectMeta: metav1.ObjectMeta{ 277 Name: name, 278 Namespace: namespace, 279 UID: rc.UID, 280 ResourceVersion: rc.ResourceVersion, 281 CreationTimestamp: rc.CreationTimestamp, 282 }, 283 Spec: autoscaling.ScaleSpec{ 284 Replicas: validController.Spec.Replicas, 285 }, 286 Status: autoscaling.ScaleStatus{ 287 Replicas: validController.Status.Replicas, 288 Selector: labels.SelectorFromSet(validController.Spec.Template.Labels).String(), 289 }, 290 } 291 obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{}) 292 if err != nil { 293 t.Fatalf("error fetching scale for %s: %v", name, err) 294 } 295 got := obj.(*autoscaling.Scale) 296 if !apiequality.Semantic.DeepEqual(want, got) { 297 t.Errorf("unexpected scale: %s", cmp.Diff(want, got)) 298 } 299 } 300 301 func TestScaleUpdate(t *testing.T) { 302 storage, server := newStorage(t) 303 defer server.Terminate(t) 304 defer storage.Controller.Store.DestroyFunc() 305 306 ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace) 307 rc, err := createController(storage.Controller, *validController, t) 308 if err != nil { 309 t.Fatalf("error setting new replication controller %v: %v", *validController, err) 310 } 311 replicas := int32(12) 312 update := autoscaling.Scale{ 313 ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}, 314 Spec: autoscaling.ScaleSpec{ 315 Replicas: replicas, 316 }, 317 } 318 319 if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}); err != nil { 320 t.Fatalf("error updating scale %v: %v", update, err) 321 } 322 obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{}) 323 if err != nil { 324 t.Fatalf("error fetching scale for %s: %v", name, err) 325 } 326 scale := obj.(*autoscaling.Scale) 327 if scale.Spec.Replicas != replicas { 328 t.Errorf("wrong replicas count expected: %d got: %d", replicas, rc.Spec.Replicas) 329 } 330 331 update.ResourceVersion = rc.ResourceVersion 332 update.Spec.Replicas = 15 333 334 if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}); err != nil && !errors.IsConflict(err) { 335 t.Fatalf("unexpected error, expecting an update conflict but got %v", err) 336 } 337 } 338 339 func TestShortNames(t *testing.T) { 340 storage, server := newStorage(t) 341 defer server.Terminate(t) 342 defer storage.Controller.Store.DestroyFunc() 343 expected := []string{"rc"} 344 registrytest.AssertShortNames(t, storage.Controller, expected) 345 } 346 347 func TestCategories(t *testing.T) { 348 storage, server := newStorage(t) 349 defer server.Terminate(t) 350 defer storage.Controller.Store.DestroyFunc() 351 expected := []string{"all"} 352 registrytest.AssertCategories(t, storage.Controller, expected) 353 } 354 355 func TestScalePatchErrors(t *testing.T) { 356 storage, server := newStorage(t) 357 defer server.Terminate(t) 358 validObj := validController 359 resourceStore := storage.Controller.Store 360 scaleStore := storage.Scale 361 362 defer resourceStore.DestroyFunc() 363 ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace) 364 365 { 366 applyNotFoundPatch := func() rest.TransformFunc { 367 return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) { 368 t.Errorf("notfound patch called") 369 return currentObject, nil 370 } 371 } 372 _, _, err := scaleStore.Update(ctx, "bad-name", rest.DefaultUpdatedObjectInfo(nil, applyNotFoundPatch()), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 373 if !errors.IsNotFound(err) { 374 t.Errorf("expected notfound, got %v", err) 375 } 376 } 377 378 if _, err := resourceStore.Create(ctx, validObj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil { 379 t.Errorf("Unexpected error: %v", err) 380 } 381 382 { 383 applyBadUIDPatch := func() rest.TransformFunc { 384 return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) { 385 currentObject.(*autoscaling.Scale).UID = "123" 386 return currentObject, nil 387 } 388 } 389 _, _, err := scaleStore.Update(ctx, name, rest.DefaultUpdatedObjectInfo(nil, applyBadUIDPatch()), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 390 if !errors.IsConflict(err) { 391 t.Errorf("expected conflict, got %v", err) 392 } 393 } 394 395 { 396 applyBadResourceVersionPatch := func() rest.TransformFunc { 397 return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) { 398 currentObject.(*autoscaling.Scale).ResourceVersion = "123" 399 return currentObject, nil 400 } 401 } 402 _, _, err := scaleStore.Update(ctx, name, rest.DefaultUpdatedObjectInfo(nil, applyBadResourceVersionPatch()), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 403 if !errors.IsConflict(err) { 404 t.Errorf("expected conflict, got %v", err) 405 } 406 } 407 } 408 409 func TestScalePatchConflicts(t *testing.T) { 410 storage, server := newStorage(t) 411 defer server.Terminate(t) 412 validObj := validController 413 resourceStore := storage.Controller.Store 414 scaleStore := storage.Scale 415 416 defer resourceStore.DestroyFunc() 417 ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace) 418 if _, err := resourceStore.Create(ctx, validObj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil { 419 t.Fatalf("Unexpected error: %v", err) 420 } 421 applyLabelPatch := func(labelName, labelValue string) rest.TransformFunc { 422 return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) { 423 currentObject.(metav1.Object).SetLabels(map[string]string{labelName: labelValue}) 424 return currentObject, nil 425 } 426 } 427 stopCh := make(chan struct{}) 428 wg := &sync.WaitGroup{} 429 wg.Add(1) 430 go func() { 431 defer wg.Done() 432 // continuously submits a patch that updates a label and verifies the label update was effective 433 labelName := "timestamp" 434 for i := 0; ; i++ { 435 select { 436 case <-stopCh: 437 return 438 default: 439 expectedLabelValue := fmt.Sprint(i) 440 updated, _, err := resourceStore.Update(ctx, name, rest.DefaultUpdatedObjectInfo(nil, applyLabelPatch(labelName, fmt.Sprint(i))), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 441 if err != nil { 442 t.Errorf("error patching main resource: %v", err) 443 return 444 } 445 gotLabelValue := updated.(metav1.Object).GetLabels()[labelName] 446 if gotLabelValue != expectedLabelValue { 447 t.Errorf("wrong label value: expected: %s, got: %s", expectedLabelValue, gotLabelValue) 448 return 449 } 450 } 451 } 452 }() 453 454 // continuously submits a scale patch of replicas for a monotonically increasing replica value 455 applyReplicaPatch := func(replicas int) rest.TransformFunc { 456 return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) { 457 currentObject.(*autoscaling.Scale).Spec.Replicas = int32(replicas) 458 return currentObject, nil 459 } 460 } 461 for i := 0; i < 100; i++ { 462 result, _, err := scaleStore.Update(ctx, name, rest.DefaultUpdatedObjectInfo(nil, applyReplicaPatch(i)), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 463 if err != nil { 464 t.Fatalf("error patching scale: %v", err) 465 } 466 scale := result.(*autoscaling.Scale) 467 if scale.Spec.Replicas != int32(i) { 468 t.Errorf("wrong replicas count: expected: %d got: %d", i, scale.Spec.Replicas) 469 } 470 } 471 close(stopCh) 472 wg.Wait() 473 }