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  }