k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/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  func TestUpdateStatus(t *testing.T) {
   263  	storage, server := newStorage(t)
   264  	defer server.Terminate(t)
   265  	defer storage.Controller.Store.Destroy()
   266  
   267  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   268  	rcStart, err := createController(storage.Controller, *validController, t)
   269  	if err != nil {
   270  		t.Fatalf("error setting new replication controller %v: %v", *validController, err)
   271  	}
   272  
   273  	rc := rcStart.DeepCopy()
   274  	rc.Status.Replicas++
   275  	_, _, err = storage.Status.Update(ctx, rc.Name, rest.DefaultUpdatedObjectInfo(rc), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
   276  	if err != nil {
   277  		t.Fatalf("Unexpected error: %v", err)
   278  	}
   279  	obj, err := storage.Status.Get(ctx, rc.Name, &metav1.GetOptions{})
   280  	if err != nil {
   281  		t.Errorf("unexpected error: %v", err)
   282  	}
   283  	rcGot := obj.(*api.ReplicationController)
   284  	// only compare relevant changes b/c of difference in metadata
   285  	if !apiequality.Semantic.DeepEqual(rc.Status, rcGot.Status) {
   286  		t.Errorf("unexpected object: %s", cmp.Diff(rc.Status, rcGot.Status))
   287  	}
   288  }
   289  
   290  func TestScaleGet(t *testing.T) {
   291  	storage, server := newStorage(t)
   292  	defer server.Terminate(t)
   293  	defer storage.Controller.Store.DestroyFunc()
   294  
   295  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   296  	rc, err := createController(storage.Controller, *validController, t)
   297  	if err != nil {
   298  		t.Fatalf("error setting new replication controller %v: %v", *validController, err)
   299  	}
   300  
   301  	want := &autoscaling.Scale{
   302  		ObjectMeta: metav1.ObjectMeta{
   303  			Name:              name,
   304  			Namespace:         namespace,
   305  			UID:               rc.UID,
   306  			ResourceVersion:   rc.ResourceVersion,
   307  			CreationTimestamp: rc.CreationTimestamp,
   308  		},
   309  		Spec: autoscaling.ScaleSpec{
   310  			Replicas: validController.Spec.Replicas,
   311  		},
   312  		Status: autoscaling.ScaleStatus{
   313  			Replicas: validController.Status.Replicas,
   314  			Selector: labels.SelectorFromSet(validController.Spec.Template.Labels).String(),
   315  		},
   316  	}
   317  	obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{})
   318  	if err != nil {
   319  		t.Fatalf("error fetching scale for %s: %v", name, err)
   320  	}
   321  	got := obj.(*autoscaling.Scale)
   322  	if !apiequality.Semantic.DeepEqual(want, got) {
   323  		t.Errorf("unexpected scale: %s", cmp.Diff(want, got))
   324  	}
   325  }
   326  
   327  func TestScaleUpdate(t *testing.T) {
   328  	storage, server := newStorage(t)
   329  	defer server.Terminate(t)
   330  	defer storage.Controller.Store.DestroyFunc()
   331  
   332  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   333  	rc, err := createController(storage.Controller, *validController, t)
   334  	if err != nil {
   335  		t.Fatalf("error setting new replication controller %v: %v", *validController, err)
   336  	}
   337  	replicas := int32(12)
   338  	update := autoscaling.Scale{
   339  		ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
   340  		Spec: autoscaling.ScaleSpec{
   341  			Replicas: replicas,
   342  		},
   343  	}
   344  
   345  	if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}); err != nil {
   346  		t.Fatalf("error updating scale %v: %v", update, err)
   347  	}
   348  	obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{})
   349  	if err != nil {
   350  		t.Fatalf("error fetching scale for %s: %v", name, err)
   351  	}
   352  	scale := obj.(*autoscaling.Scale)
   353  	if scale.Spec.Replicas != replicas {
   354  		t.Errorf("wrong replicas count expected: %d got: %d", replicas, rc.Spec.Replicas)
   355  	}
   356  
   357  	update.ResourceVersion = rc.ResourceVersion
   358  	update.Spec.Replicas = 15
   359  
   360  	if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}); err != nil && !errors.IsConflict(err) {
   361  		t.Fatalf("unexpected error, expecting an update conflict but got %v", err)
   362  	}
   363  }
   364  
   365  func TestShortNames(t *testing.T) {
   366  	storage, server := newStorage(t)
   367  	defer server.Terminate(t)
   368  	defer storage.Controller.Store.DestroyFunc()
   369  	expected := []string{"rc"}
   370  	registrytest.AssertShortNames(t, storage.Controller, expected)
   371  }
   372  
   373  func TestCategories(t *testing.T) {
   374  	storage, server := newStorage(t)
   375  	defer server.Terminate(t)
   376  	defer storage.Controller.Store.DestroyFunc()
   377  	expected := []string{"all"}
   378  	registrytest.AssertCategories(t, storage.Controller, expected)
   379  }
   380  
   381  func TestScalePatchErrors(t *testing.T) {
   382  	storage, server := newStorage(t)
   383  	defer server.Terminate(t)
   384  	validObj := validController
   385  	resourceStore := storage.Controller.Store
   386  	scaleStore := storage.Scale
   387  
   388  	defer resourceStore.DestroyFunc()
   389  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   390  
   391  	{
   392  		applyNotFoundPatch := func() rest.TransformFunc {
   393  			return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) {
   394  				t.Errorf("notfound patch called")
   395  				return currentObject, nil
   396  			}
   397  		}
   398  		_, _, err := scaleStore.Update(ctx, "bad-name", rest.DefaultUpdatedObjectInfo(nil, applyNotFoundPatch()), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
   399  		if !errors.IsNotFound(err) {
   400  			t.Errorf("expected notfound, got %v", err)
   401  		}
   402  	}
   403  
   404  	if _, err := resourceStore.Create(ctx, validObj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil {
   405  		t.Errorf("Unexpected error: %v", err)
   406  	}
   407  
   408  	{
   409  		applyBadUIDPatch := func() rest.TransformFunc {
   410  			return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) {
   411  				currentObject.(*autoscaling.Scale).UID = "123"
   412  				return currentObject, nil
   413  			}
   414  		}
   415  		_, _, err := scaleStore.Update(ctx, name, rest.DefaultUpdatedObjectInfo(nil, applyBadUIDPatch()), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
   416  		if !errors.IsConflict(err) {
   417  			t.Errorf("expected conflict, got %v", err)
   418  		}
   419  	}
   420  
   421  	{
   422  		applyBadResourceVersionPatch := func() rest.TransformFunc {
   423  			return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) {
   424  				currentObject.(*autoscaling.Scale).ResourceVersion = "123"
   425  				return currentObject, nil
   426  			}
   427  		}
   428  		_, _, err := scaleStore.Update(ctx, name, rest.DefaultUpdatedObjectInfo(nil, applyBadResourceVersionPatch()), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
   429  		if !errors.IsConflict(err) {
   430  			t.Errorf("expected conflict, got %v", err)
   431  		}
   432  	}
   433  }
   434  
   435  func TestScalePatchConflicts(t *testing.T) {
   436  	storage, server := newStorage(t)
   437  	defer server.Terminate(t)
   438  	validObj := validController
   439  	resourceStore := storage.Controller.Store
   440  	scaleStore := storage.Scale
   441  
   442  	defer resourceStore.DestroyFunc()
   443  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   444  	if _, err := resourceStore.Create(ctx, validObj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil {
   445  		t.Fatalf("Unexpected error: %v", err)
   446  	}
   447  	applyLabelPatch := func(labelName, labelValue string) rest.TransformFunc {
   448  		return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) {
   449  			currentObject.(metav1.Object).SetLabels(map[string]string{labelName: labelValue})
   450  			return currentObject, nil
   451  		}
   452  	}
   453  	stopCh := make(chan struct{})
   454  	wg := &sync.WaitGroup{}
   455  	wg.Add(1)
   456  	go func() {
   457  		defer wg.Done()
   458  		// continuously submits a patch that updates a label and verifies the label update was effective
   459  		labelName := "timestamp"
   460  		for i := 0; ; i++ {
   461  			select {
   462  			case <-stopCh:
   463  				return
   464  			default:
   465  				expectedLabelValue := fmt.Sprint(i)
   466  				updated, _, err := resourceStore.Update(ctx, name, rest.DefaultUpdatedObjectInfo(nil, applyLabelPatch(labelName, fmt.Sprint(i))), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
   467  				if err != nil {
   468  					t.Errorf("error patching main resource: %v", err)
   469  					return
   470  				}
   471  				gotLabelValue := updated.(metav1.Object).GetLabels()[labelName]
   472  				if gotLabelValue != expectedLabelValue {
   473  					t.Errorf("wrong label value: expected: %s, got: %s", expectedLabelValue, gotLabelValue)
   474  					return
   475  				}
   476  			}
   477  		}
   478  	}()
   479  
   480  	// continuously submits a scale patch of replicas for a monotonically increasing replica value
   481  	applyReplicaPatch := func(replicas int) rest.TransformFunc {
   482  		return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) {
   483  			currentObject.(*autoscaling.Scale).Spec.Replicas = int32(replicas)
   484  			return currentObject, nil
   485  		}
   486  	}
   487  	for i := 0; i < 100; i++ {
   488  		result, _, err := scaleStore.Update(ctx, name, rest.DefaultUpdatedObjectInfo(nil, applyReplicaPatch(i)), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
   489  		if err != nil {
   490  			t.Fatalf("error patching scale: %v", err)
   491  		}
   492  		scale := result.(*autoscaling.Scale)
   493  		if scale.Spec.Replicas != int32(i) {
   494  			t.Errorf("wrong replicas count: expected: %d got: %d", i, scale.Spec.Replicas)
   495  		}
   496  	}
   497  	close(stopCh)
   498  	wg.Wait()
   499  }