...

Source file src/k8s.io/kubernetes/pkg/registry/apps/replicaset/storage/storage_test.go

Documentation: k8s.io/kubernetes/pkg/registry/apps/replicaset/storage

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

View as plain text