...

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

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

     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
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net/http"
    23  	"reflect"
    24  	"strings"
    25  	"sync"
    26  	"testing"
    27  
    28  	"github.com/google/go-cmp/cmp"
    29  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    30  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apimachinery/pkg/fields"
    33  	"k8s.io/apimachinery/pkg/labels"
    34  	"k8s.io/apimachinery/pkg/runtime"
    35  	"k8s.io/apimachinery/pkg/util/intstr"
    36  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    37  	"k8s.io/apiserver/pkg/registry/generic"
    38  	genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
    39  	"k8s.io/apiserver/pkg/registry/rest"
    40  	storeerr "k8s.io/apiserver/pkg/storage/errors"
    41  	etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
    42  	"k8s.io/kubernetes/pkg/apis/apps"
    43  	"k8s.io/kubernetes/pkg/apis/autoscaling"
    44  	api "k8s.io/kubernetes/pkg/apis/core"
    45  	"k8s.io/kubernetes/pkg/registry/registrytest"
    46  )
    47  
    48  const defaultReplicas = 100
    49  
    50  func newStorage(t *testing.T) (*DeploymentStorage, *etcd3testing.EtcdTestServer) {
    51  	etcdStorage, server := registrytest.NewEtcdStorage(t, apps.GroupName)
    52  	restOptions := generic.RESTOptions{StorageConfig: etcdStorage, Decorator: generic.UndecoratedStorage, DeleteCollectionWorkers: 1, ResourcePrefix: "deployments"}
    53  	deploymentStorage, err := NewStorage(restOptions)
    54  	if err != nil {
    55  		t.Fatalf("unexpected error from REST storage: %v", err)
    56  	}
    57  	return &deploymentStorage, server
    58  }
    59  
    60  var namespace = "foo-namespace"
    61  var name = "foo-deployment"
    62  
    63  func validNewDeployment() *apps.Deployment {
    64  	return &apps.Deployment{
    65  		ObjectMeta: metav1.ObjectMeta{
    66  			Name:      name,
    67  			Namespace: namespace,
    68  		},
    69  		Spec: apps.DeploymentSpec{
    70  			Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
    71  			Strategy: apps.DeploymentStrategy{
    72  				Type: apps.RollingUpdateDeploymentStrategyType,
    73  				RollingUpdate: &apps.RollingUpdateDeployment{
    74  					MaxSurge:       intstr.FromInt32(1),
    75  					MaxUnavailable: intstr.FromInt32(1),
    76  				},
    77  			},
    78  			Template: api.PodTemplateSpec{
    79  				ObjectMeta: metav1.ObjectMeta{
    80  					Labels: map[string]string{"a": "b"},
    81  				},
    82  				Spec: api.PodSpec{
    83  					Containers: []api.Container{
    84  						{
    85  							Name:                     "test",
    86  							Image:                    "test_image",
    87  							ImagePullPolicy:          api.PullIfNotPresent,
    88  							TerminationMessagePolicy: api.TerminationMessageReadFile,
    89  						},
    90  					},
    91  					RestartPolicy: api.RestartPolicyAlways,
    92  					DNSPolicy:     api.DNSClusterFirst,
    93  				},
    94  			},
    95  			Replicas: 7,
    96  		},
    97  		Status: apps.DeploymentStatus{
    98  			Replicas: 5,
    99  		},
   100  	}
   101  }
   102  
   103  var validDeployment = *validNewDeployment()
   104  
   105  func TestCreate(t *testing.T) {
   106  	storage, server := newStorage(t)
   107  	defer server.Terminate(t)
   108  	defer storage.Deployment.Store.DestroyFunc()
   109  	test := genericregistrytest.New(t, storage.Deployment.Store)
   110  	deployment := validNewDeployment()
   111  	deployment.ObjectMeta = metav1.ObjectMeta{}
   112  	test.TestCreate(
   113  		// valid
   114  		deployment,
   115  		// invalid (invalid selector)
   116  		&apps.Deployment{
   117  			Spec: apps.DeploymentSpec{
   118  				Selector: &metav1.LabelSelector{MatchLabels: map[string]string{}},
   119  				Template: validDeployment.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.Deployment.Store.DestroyFunc()
   129  	test := genericregistrytest.New(t, storage.Deployment.Store)
   130  	test.TestUpdate(
   131  		// valid
   132  		validNewDeployment(),
   133  		// updateFunc
   134  		func(obj runtime.Object) runtime.Object {
   135  			object := obj.(*apps.Deployment)
   136  			object.Spec.Template.Spec.NodeSelector = map[string]string{"c": "d"}
   137  			return object
   138  		},
   139  		// invalid updateFunc
   140  		func(obj runtime.Object) runtime.Object {
   141  			object := obj.(*apps.Deployment)
   142  			object.Name = ""
   143  			return object
   144  		},
   145  		func(obj runtime.Object) runtime.Object {
   146  			object := obj.(*apps.Deployment)
   147  			object.Spec.Template.Spec.RestartPolicy = api.RestartPolicyOnFailure
   148  			return object
   149  		},
   150  		func(obj runtime.Object) runtime.Object {
   151  			object := obj.(*apps.Deployment)
   152  			object.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{}}
   153  			return object
   154  		},
   155  	)
   156  }
   157  
   158  func TestDelete(t *testing.T) {
   159  	storage, server := newStorage(t)
   160  	defer server.Terminate(t)
   161  	defer storage.Deployment.Store.DestroyFunc()
   162  	test := genericregistrytest.New(t, storage.Deployment.Store)
   163  	test.TestDelete(validNewDeployment())
   164  }
   165  
   166  func TestGet(t *testing.T) {
   167  	storage, server := newStorage(t)
   168  	defer server.Terminate(t)
   169  	defer storage.Deployment.Store.DestroyFunc()
   170  	test := genericregistrytest.New(t, storage.Deployment.Store)
   171  	test.TestGet(validNewDeployment())
   172  }
   173  
   174  func TestList(t *testing.T) {
   175  	storage, server := newStorage(t)
   176  	defer server.Terminate(t)
   177  	defer storage.Deployment.Store.DestroyFunc()
   178  	test := genericregistrytest.New(t, storage.Deployment.Store)
   179  	test.TestList(validNewDeployment())
   180  }
   181  
   182  func TestWatch(t *testing.T) {
   183  	storage, server := newStorage(t)
   184  	defer server.Terminate(t)
   185  	defer storage.Deployment.Store.DestroyFunc()
   186  	test := genericregistrytest.New(t, storage.Deployment.Store)
   187  	test.TestWatch(
   188  		validNewDeployment(),
   189  		// matching labels
   190  		[]labels.Set{},
   191  		// not matching labels
   192  		[]labels.Set{
   193  			{"a": "c"},
   194  			{"foo": "bar"},
   195  		},
   196  		// matching fields
   197  		[]fields.Set{
   198  			{"metadata.name": name},
   199  		},
   200  		// not matching fields
   201  		[]fields.Set{
   202  			{"metadata.name": "bar"},
   203  			{"name": name},
   204  		},
   205  	)
   206  }
   207  
   208  func TestScaleGet(t *testing.T) {
   209  	storage, server := newStorage(t)
   210  	defer server.Terminate(t)
   211  	defer storage.Deployment.Store.DestroyFunc()
   212  	var deployment apps.Deployment
   213  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   214  	key := "/deployments/" + namespace + "/" + name
   215  	if err := storage.Deployment.Storage.Create(ctx, key, &validDeployment, &deployment, 0, false); err != nil {
   216  		t.Fatalf("error setting new deployment (key: %s) %v: %v", key, validDeployment, err)
   217  	}
   218  
   219  	selector, err := metav1.LabelSelectorAsSelector(validDeployment.Spec.Selector)
   220  	if err != nil {
   221  		t.Fatal(err)
   222  	}
   223  	want := &autoscaling.Scale{
   224  		ObjectMeta: metav1.ObjectMeta{
   225  			Name:              name,
   226  			Namespace:         namespace,
   227  			UID:               deployment.UID,
   228  			ResourceVersion:   deployment.ResourceVersion,
   229  			CreationTimestamp: deployment.CreationTimestamp,
   230  		},
   231  		Spec: autoscaling.ScaleSpec{
   232  			Replicas: validDeployment.Spec.Replicas,
   233  		},
   234  		Status: autoscaling.ScaleStatus{
   235  			Replicas: validDeployment.Status.Replicas,
   236  			Selector: selector.String(),
   237  		},
   238  	}
   239  	obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{})
   240  	if err != nil {
   241  		t.Fatalf("error fetching scale for %s: %v", name, err)
   242  	}
   243  	got := obj.(*autoscaling.Scale)
   244  	if !apiequality.Semantic.DeepEqual(want, got) {
   245  		t.Errorf("unexpected scale: %s", cmp.Diff(want, got))
   246  	}
   247  }
   248  
   249  func TestScaleUpdate(t *testing.T) {
   250  	storage, server := newStorage(t)
   251  	defer server.Terminate(t)
   252  	defer storage.Deployment.Store.DestroyFunc()
   253  	var deployment apps.Deployment
   254  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   255  	key := "/deployments/" + namespace + "/" + name
   256  	if err := storage.Deployment.Storage.Create(ctx, key, &validDeployment, &deployment, 0, false); err != nil {
   257  		t.Fatalf("error setting new deployment (key: %s) %v: %v", key, validDeployment, err)
   258  	}
   259  	replicas := int32(12)
   260  	update := autoscaling.Scale{
   261  		ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
   262  		Spec: autoscaling.ScaleSpec{
   263  			Replicas: replicas,
   264  		},
   265  	}
   266  
   267  	if _, _, err := storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}); err != nil {
   268  		t.Fatalf("error updating scale %v: %v", update, err)
   269  	}
   270  	obj, err := storage.Scale.Get(ctx, name, &metav1.GetOptions{})
   271  	if err != nil {
   272  		t.Fatalf("error fetching scale for %s: %v", name, err)
   273  	}
   274  	scale := obj.(*autoscaling.Scale)
   275  	if scale.Spec.Replicas != replicas {
   276  		t.Errorf("wrong replicas count expected: %d got: %d", replicas, deployment.Spec.Replicas)
   277  	}
   278  
   279  	update.ResourceVersion = deployment.ResourceVersion
   280  	update.Spec.Replicas = 15
   281  
   282  	if _, _, err = storage.Scale.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}); err != nil && !apierrors.IsConflict(err) {
   283  		t.Fatalf("unexpected error, expecting an update conflict but got %v", err)
   284  	}
   285  }
   286  
   287  func TestStatusUpdate(t *testing.T) {
   288  	storage, server := newStorage(t)
   289  	defer server.Terminate(t)
   290  	defer storage.Deployment.Store.DestroyFunc()
   291  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   292  	key := "/deployments/" + namespace + "/" + name
   293  	if err := storage.Deployment.Storage.Create(ctx, key, &validDeployment, nil, 0, false); err != nil {
   294  		t.Fatalf("unexpected error: %v", err)
   295  	}
   296  	update := apps.Deployment{
   297  		ObjectMeta: validDeployment.ObjectMeta,
   298  		Spec: apps.DeploymentSpec{
   299  			Replicas: defaultReplicas,
   300  		},
   301  		Status: apps.DeploymentStatus{
   302  			Replicas: defaultReplicas,
   303  		},
   304  	}
   305  
   306  	if _, _, err := storage.Status.Update(ctx, update.Name, rest.DefaultUpdatedObjectInfo(&update), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}); err != nil {
   307  		t.Fatalf("unexpected error: %v", err)
   308  	}
   309  	obj, err := storage.Deployment.Get(ctx, name, &metav1.GetOptions{})
   310  	if err != nil {
   311  		t.Fatalf("unexpected error: %v", err)
   312  	}
   313  
   314  	deployment := obj.(*apps.Deployment)
   315  	if deployment.Spec.Replicas != 7 {
   316  		t.Errorf("we expected .spec.replicas to not be updated but it was updated to %v", deployment.Spec.Replicas)
   317  	}
   318  	if deployment.Status.Replicas != defaultReplicas {
   319  		t.Errorf("we expected .status.replicas to be updated to %d but it was %v", defaultReplicas, deployment.Status.Replicas)
   320  	}
   321  }
   322  
   323  func TestEtcdCreateDeploymentRollback(t *testing.T) {
   324  	testCases := map[string]struct {
   325  		rollback apps.DeploymentRollback
   326  		errOK    func(error) bool
   327  	}{
   328  		"normal": {
   329  			rollback: apps.DeploymentRollback{
   330  				Name:               name,
   331  				UpdatedAnnotations: map[string]string{},
   332  				RollbackTo:         apps.RollbackConfig{Revision: 1},
   333  			},
   334  			errOK: func(err error) bool { return err == nil },
   335  		},
   336  		"noAnnotation": {
   337  			rollback: apps.DeploymentRollback{
   338  				Name:       name,
   339  				RollbackTo: apps.RollbackConfig{Revision: 1},
   340  			},
   341  			errOK: func(err error) bool { return err == nil },
   342  		},
   343  		"noName": {
   344  			rollback: apps.DeploymentRollback{
   345  				UpdatedAnnotations: map[string]string{},
   346  				RollbackTo:         apps.RollbackConfig{Revision: 1},
   347  			},
   348  			errOK: func(err error) bool { return err != nil },
   349  		},
   350  	}
   351  	storage, server := newStorage(t)
   352  	defer server.Terminate(t)
   353  	defer storage.Deployment.Store.DestroyFunc()
   354  	for k, test := range testCases {
   355  		rollbackStorage := storage.Rollback
   356  		deployment := validNewDeployment()
   357  		deployment.Namespace = fmt.Sprintf("namespace-%s", strings.ToLower(k))
   358  		ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), deployment.Namespace)
   359  
   360  		if _, err := storage.Deployment.Create(ctx, deployment, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil {
   361  			t.Fatalf("%s: unexpected error: %v", k, err)
   362  		}
   363  		rollbackRespStatus, err := rollbackStorage.Create(ctx, test.rollback.Name, &test.rollback, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
   364  		if !test.errOK(err) {
   365  			t.Errorf("%s: unexpected error: %v", k, err)
   366  		} else if err == nil {
   367  			// If rollback succeeded, verify Rollback response and Rollback field of deployment
   368  			status, ok := rollbackRespStatus.(*metav1.Status)
   369  			if !ok {
   370  				t.Errorf("%s: unexpected response format", k)
   371  			}
   372  			if status.Code != http.StatusOK || status.Status != metav1.StatusSuccess {
   373  				t.Errorf("%s: unexpected response, code: %d, status: %s", k, status.Code, status.Status)
   374  			}
   375  			d, err := storage.Deployment.Get(ctx, deployment.ObjectMeta.Name, &metav1.GetOptions{})
   376  			if err != nil {
   377  				t.Errorf("%s: unexpected error: %v", k, err)
   378  			} else if !reflect.DeepEqual(*d.(*apps.Deployment).Spec.RollbackTo, test.rollback.RollbackTo) {
   379  				t.Errorf("%s: expected: %v, got: %v", k, *d.(*apps.Deployment).Spec.RollbackTo, test.rollback.RollbackTo)
   380  			}
   381  		}
   382  	}
   383  }
   384  
   385  func TestCreateDeploymentRollbackValidation(t *testing.T) {
   386  	storage, server := newStorage(t)
   387  	rollbackStorage := storage.Rollback
   388  	rollback := apps.DeploymentRollback{
   389  		Name:               name,
   390  		UpdatedAnnotations: map[string]string{},
   391  		RollbackTo:         apps.RollbackConfig{Revision: 1},
   392  	}
   393  
   394  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   395  
   396  	if _, err := storage.Deployment.Create(ctx, validNewDeployment(), rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil {
   397  		t.Fatalf("Unexpected error: %v", err)
   398  	}
   399  
   400  	validationError := fmt.Errorf("admission deny")
   401  	alwaysDenyValidationFunc := func(ctx context.Context, obj runtime.Object) error { return validationError }
   402  	_, err := rollbackStorage.Create(ctx, rollback.Name, &rollback, alwaysDenyValidationFunc, &metav1.CreateOptions{})
   403  
   404  	if err == nil || validationError != err {
   405  		t.Errorf("expected: %v, got: %v", validationError, err)
   406  	}
   407  
   408  	storage.Deployment.Store.DestroyFunc()
   409  	server.Terminate(t)
   410  }
   411  
   412  // Ensure that when a deploymentRollback is created for a deployment that has already been deleted
   413  // by the API server, API server returns not-found error.
   414  func TestEtcdCreateDeploymentRollbackNoDeployment(t *testing.T) {
   415  	storage, server := newStorage(t)
   416  	defer server.Terminate(t)
   417  	defer storage.Deployment.Store.DestroyFunc()
   418  	rollbackStorage := storage.Rollback
   419  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   420  
   421  	_, err := rollbackStorage.Create(ctx, name, &apps.DeploymentRollback{
   422  		Name:               name,
   423  		UpdatedAnnotations: map[string]string{},
   424  		RollbackTo:         apps.RollbackConfig{Revision: 1},
   425  	}, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
   426  	if err == nil {
   427  		t.Fatalf("Expected not-found-error but got nothing")
   428  	}
   429  	if !apierrors.IsNotFound(storeerr.InterpretGetError(err, apps.Resource("deployments"), name)) {
   430  		t.Fatalf("Unexpected error returned: %#v", err)
   431  	}
   432  
   433  	_, err = storage.Deployment.Get(ctx, name, &metav1.GetOptions{})
   434  	if err == nil {
   435  		t.Fatalf("Expected not-found-error but got nothing")
   436  	}
   437  	if !apierrors.IsNotFound(storeerr.InterpretGetError(err, apps.Resource("deployments"), name)) {
   438  		t.Fatalf("Unexpected error: %v", err)
   439  	}
   440  }
   441  
   442  func TestShortNames(t *testing.T) {
   443  	storage, server := newStorage(t)
   444  	defer server.Terminate(t)
   445  	defer storage.Deployment.Store.DestroyFunc()
   446  	expected := []string{"deploy"}
   447  	registrytest.AssertShortNames(t, storage.Deployment, expected)
   448  }
   449  
   450  func TestCategories(t *testing.T) {
   451  	storage, server := newStorage(t)
   452  	defer server.Terminate(t)
   453  	defer storage.Deployment.Store.DestroyFunc()
   454  	expected := []string{"all"}
   455  	registrytest.AssertCategories(t, storage.Deployment, expected)
   456  }
   457  
   458  func TestScalePatchErrors(t *testing.T) {
   459  	storage, server := newStorage(t)
   460  	defer server.Terminate(t)
   461  	validObj := validNewDeployment()
   462  	resourceStore := storage.Deployment.Store
   463  	scaleStore := storage.Scale
   464  
   465  	defer resourceStore.DestroyFunc()
   466  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   467  
   468  	{
   469  		applyNotFoundPatch := func() rest.TransformFunc {
   470  			return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) {
   471  				t.Errorf("notfound patch called")
   472  				return currentObject, nil
   473  			}
   474  		}
   475  		_, _, err := scaleStore.Update(ctx, "bad-name", rest.DefaultUpdatedObjectInfo(nil, applyNotFoundPatch()), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
   476  		if !apierrors.IsNotFound(err) {
   477  			t.Errorf("expected notfound, got %v", err)
   478  		}
   479  	}
   480  
   481  	if _, err := resourceStore.Create(ctx, validObj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil {
   482  		t.Errorf("Unexpected error: %v", err)
   483  	}
   484  
   485  	{
   486  		applyBadUIDPatch := func() rest.TransformFunc {
   487  			return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) {
   488  				currentObject.(*autoscaling.Scale).UID = "123"
   489  				return currentObject, nil
   490  			}
   491  		}
   492  		_, _, err := scaleStore.Update(ctx, name, rest.DefaultUpdatedObjectInfo(nil, applyBadUIDPatch()), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
   493  		if !apierrors.IsConflict(err) {
   494  			t.Errorf("expected conflict, got %v", err)
   495  		}
   496  	}
   497  
   498  	{
   499  		applyBadResourceVersionPatch := func() rest.TransformFunc {
   500  			return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) {
   501  				currentObject.(*autoscaling.Scale).ResourceVersion = "123"
   502  				return currentObject, nil
   503  			}
   504  		}
   505  		_, _, err := scaleStore.Update(ctx, name, rest.DefaultUpdatedObjectInfo(nil, applyBadResourceVersionPatch()), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
   506  		if !apierrors.IsConflict(err) {
   507  			t.Errorf("expected conflict, got %v", err)
   508  		}
   509  	}
   510  }
   511  
   512  func TestScalePatchConflicts(t *testing.T) {
   513  	storage, server := newStorage(t)
   514  	defer server.Terminate(t)
   515  	validObj := validNewDeployment()
   516  	resourceStore := storage.Deployment.Store
   517  	scaleStore := storage.Scale
   518  
   519  	defer resourceStore.DestroyFunc()
   520  	ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), namespace)
   521  	if _, err := resourceStore.Create(ctx, validObj, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil {
   522  		t.Fatalf("Unexpected error: %v", err)
   523  	}
   524  	applyLabelPatch := func(labelName, labelValue string) rest.TransformFunc {
   525  		return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) {
   526  			currentObject.(metav1.Object).SetLabels(map[string]string{labelName: labelValue})
   527  			return currentObject, nil
   528  		}
   529  	}
   530  	stopCh := make(chan struct{})
   531  	wg := &sync.WaitGroup{}
   532  	wg.Add(1)
   533  	go func() {
   534  		defer wg.Done()
   535  		// continuously submits a patch that updates a label and verifies the label update was effective
   536  		labelName := "timestamp"
   537  		for i := 0; ; i++ {
   538  			select {
   539  			case <-stopCh:
   540  				return
   541  			default:
   542  				expectedLabelValue := fmt.Sprint(i)
   543  				updated, _, err := resourceStore.Update(ctx, name, rest.DefaultUpdatedObjectInfo(nil, applyLabelPatch(labelName, fmt.Sprint(i))), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
   544  				if err != nil {
   545  					t.Errorf("error patching main resource: %v", err)
   546  					return
   547  				}
   548  				gotLabelValue := updated.(metav1.Object).GetLabels()[labelName]
   549  				if gotLabelValue != expectedLabelValue {
   550  					t.Errorf("wrong label value: expected: %s, got: %s", expectedLabelValue, gotLabelValue)
   551  					return
   552  				}
   553  			}
   554  		}
   555  	}()
   556  
   557  	// continuously submits a scale patch of replicas for a monotonically increasing replica value
   558  	applyReplicaPatch := func(replicas int) rest.TransformFunc {
   559  		return func(_ context.Context, _, currentObject runtime.Object) (objToUpdate runtime.Object, patchErr error) {
   560  			currentObject.(*autoscaling.Scale).Spec.Replicas = int32(replicas)
   561  			return currentObject, nil
   562  		}
   563  	}
   564  	for i := 0; i < 100; i++ {
   565  		result, _, err := scaleStore.Update(ctx, name, rest.DefaultUpdatedObjectInfo(nil, applyReplicaPatch(i)), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
   566  		if err != nil {
   567  			t.Fatalf("error patching scale: %v", err)
   568  		}
   569  		scale := result.(*autoscaling.Scale)
   570  		if scale.Spec.Replicas != int32(i) {
   571  			t.Errorf("wrong replicas count: expected: %d got: %d", i, scale.Spec.Replicas)
   572  		}
   573  	}
   574  	close(stopCh)
   575  	wg.Wait()
   576  }
   577  

View as plain text