...

Source file src/k8s.io/kubernetes/pkg/controller/deployment/deployment_controller_test.go

Documentation: k8s.io/kubernetes/pkg/controller/deployment

     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 deployment
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strconv"
    23  	"testing"
    24  
    25  	apps "k8s.io/api/apps/v1"
    26  	v1 "k8s.io/api/core/v1"
    27  	extensions "k8s.io/api/extensions/v1beta1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/apimachinery/pkg/runtime/schema"
    31  	"k8s.io/apimachinery/pkg/util/intstr"
    32  	"k8s.io/apimachinery/pkg/util/uuid"
    33  	"k8s.io/client-go/informers"
    34  	"k8s.io/client-go/kubernetes/fake"
    35  	core "k8s.io/client-go/testing"
    36  	"k8s.io/client-go/tools/record"
    37  	"k8s.io/klog/v2"
    38  	"k8s.io/klog/v2/ktesting"
    39  	_ "k8s.io/kubernetes/pkg/apis/apps/install"
    40  	_ "k8s.io/kubernetes/pkg/apis/authentication/install"
    41  	_ "k8s.io/kubernetes/pkg/apis/authorization/install"
    42  	_ "k8s.io/kubernetes/pkg/apis/autoscaling/install"
    43  	_ "k8s.io/kubernetes/pkg/apis/batch/install"
    44  	_ "k8s.io/kubernetes/pkg/apis/certificates/install"
    45  	_ "k8s.io/kubernetes/pkg/apis/core/install"
    46  	_ "k8s.io/kubernetes/pkg/apis/policy/install"
    47  	_ "k8s.io/kubernetes/pkg/apis/rbac/install"
    48  	_ "k8s.io/kubernetes/pkg/apis/storage/install"
    49  	"k8s.io/kubernetes/pkg/controller"
    50  	"k8s.io/kubernetes/pkg/controller/deployment/util"
    51  	"k8s.io/kubernetes/pkg/controller/testutil"
    52  	"k8s.io/utils/ptr"
    53  )
    54  
    55  var (
    56  	alwaysReady = func() bool { return true }
    57  	noTimestamp = metav1.Time{}
    58  )
    59  
    60  func rs(name string, replicas int32, selector map[string]string, timestamp metav1.Time) *apps.ReplicaSet {
    61  	return &apps.ReplicaSet{
    62  		ObjectMeta: metav1.ObjectMeta{
    63  			Name:              name,
    64  			CreationTimestamp: timestamp,
    65  			Namespace:         metav1.NamespaceDefault,
    66  		},
    67  		Spec: apps.ReplicaSetSpec{
    68  			Replicas: ptr.To(replicas),
    69  			Selector: &metav1.LabelSelector{MatchLabels: selector},
    70  			Template: v1.PodTemplateSpec{},
    71  		},
    72  	}
    73  }
    74  
    75  func newRSWithStatus(name string, specReplicas, statusReplicas int32, selector map[string]string) *apps.ReplicaSet {
    76  	rs := rs(name, specReplicas, selector, noTimestamp)
    77  	rs.Status = apps.ReplicaSetStatus{
    78  		Replicas: statusReplicas,
    79  	}
    80  	return rs
    81  }
    82  
    83  func newDeployment(name string, replicas int32, revisionHistoryLimit *int32, maxSurge, maxUnavailable *intstr.IntOrString, selector map[string]string) *apps.Deployment {
    84  	d := apps.Deployment{
    85  		TypeMeta: metav1.TypeMeta{APIVersion: "apps/v1", Kind: "Deployment"},
    86  		ObjectMeta: metav1.ObjectMeta{
    87  			UID:         uuid.NewUUID(),
    88  			Name:        name,
    89  			Namespace:   metav1.NamespaceDefault,
    90  			Annotations: make(map[string]string),
    91  		},
    92  		Spec: apps.DeploymentSpec{
    93  			Strategy: apps.DeploymentStrategy{
    94  				Type: apps.RollingUpdateDeploymentStrategyType,
    95  				RollingUpdate: &apps.RollingUpdateDeployment{
    96  					MaxUnavailable: ptr.To(intstr.FromInt32(0)),
    97  					MaxSurge:       ptr.To(intstr.FromInt32(0)),
    98  				},
    99  			},
   100  			Replicas: ptr.To(replicas),
   101  			Selector: &metav1.LabelSelector{MatchLabels: selector},
   102  			Template: v1.PodTemplateSpec{
   103  				ObjectMeta: metav1.ObjectMeta{
   104  					Labels: selector,
   105  				},
   106  				Spec: v1.PodSpec{
   107  					Containers: []v1.Container{
   108  						{
   109  							Image: "foo/bar",
   110  						},
   111  					},
   112  				},
   113  			},
   114  			RevisionHistoryLimit: revisionHistoryLimit,
   115  		},
   116  	}
   117  	if maxSurge != nil {
   118  		d.Spec.Strategy.RollingUpdate.MaxSurge = maxSurge
   119  	}
   120  	if maxUnavailable != nil {
   121  		d.Spec.Strategy.RollingUpdate.MaxUnavailable = maxUnavailable
   122  	}
   123  	return &d
   124  }
   125  
   126  func newReplicaSet(d *apps.Deployment, name string, replicas int32) *apps.ReplicaSet {
   127  	return &apps.ReplicaSet{
   128  		TypeMeta: metav1.TypeMeta{Kind: "ReplicaSet"},
   129  		ObjectMeta: metav1.ObjectMeta{
   130  			Name:            name,
   131  			UID:             uuid.NewUUID(),
   132  			Namespace:       metav1.NamespaceDefault,
   133  			Labels:          d.Spec.Selector.MatchLabels,
   134  			OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(d, controllerKind)},
   135  		},
   136  		Spec: apps.ReplicaSetSpec{
   137  			Selector: d.Spec.Selector,
   138  			Replicas: ptr.To(replicas),
   139  			Template: d.Spec.Template,
   140  		},
   141  	}
   142  }
   143  
   144  type fixture struct {
   145  	t testing.TB
   146  
   147  	client *fake.Clientset
   148  	// Objects to put in the store.
   149  	dLister   []*apps.Deployment
   150  	rsLister  []*apps.ReplicaSet
   151  	podLister []*v1.Pod
   152  
   153  	// Actions expected to happen on the client. Objects from here are also
   154  	// preloaded into NewSimpleFake.
   155  	actions []core.Action
   156  	objects []runtime.Object
   157  }
   158  
   159  func (f *fixture) expectGetDeploymentAction(d *apps.Deployment) {
   160  	action := core.NewGetAction(schema.GroupVersionResource{Resource: "deployments"}, d.Namespace, d.Name)
   161  	f.actions = append(f.actions, action)
   162  }
   163  
   164  func (f *fixture) expectUpdateDeploymentStatusAction(d *apps.Deployment) {
   165  	action := core.NewUpdateAction(schema.GroupVersionResource{Resource: "deployments"}, d.Namespace, d)
   166  	action.Subresource = "status"
   167  	f.actions = append(f.actions, action)
   168  }
   169  
   170  func (f *fixture) expectUpdateDeploymentAction(d *apps.Deployment) {
   171  	action := core.NewUpdateAction(schema.GroupVersionResource{Resource: "deployments"}, d.Namespace, d)
   172  	f.actions = append(f.actions, action)
   173  }
   174  
   175  func (f *fixture) expectCreateRSAction(rs *apps.ReplicaSet) {
   176  	f.actions = append(f.actions, core.NewCreateAction(schema.GroupVersionResource{Resource: "replicasets"}, rs.Namespace, rs))
   177  }
   178  
   179  func newFixture(t testing.TB) *fixture {
   180  	f := &fixture{}
   181  	f.t = t
   182  	f.objects = []runtime.Object{}
   183  	return f
   184  }
   185  
   186  func (f *fixture) newController(ctx context.Context) (*DeploymentController, informers.SharedInformerFactory, error) {
   187  	f.client = fake.NewSimpleClientset(f.objects...)
   188  	informers := informers.NewSharedInformerFactory(f.client, controller.NoResyncPeriodFunc())
   189  	c, err := NewDeploymentController(ctx, informers.Apps().V1().Deployments(), informers.Apps().V1().ReplicaSets(), informers.Core().V1().Pods(), f.client)
   190  	if err != nil {
   191  		return nil, nil, err
   192  	}
   193  	c.eventRecorder = &record.FakeRecorder{}
   194  	c.dListerSynced = alwaysReady
   195  	c.rsListerSynced = alwaysReady
   196  	c.podListerSynced = alwaysReady
   197  	for _, d := range f.dLister {
   198  		informers.Apps().V1().Deployments().Informer().GetIndexer().Add(d)
   199  	}
   200  	for _, rs := range f.rsLister {
   201  		informers.Apps().V1().ReplicaSets().Informer().GetIndexer().Add(rs)
   202  	}
   203  	for _, pod := range f.podLister {
   204  		informers.Core().V1().Pods().Informer().GetIndexer().Add(pod)
   205  	}
   206  	return c, informers, nil
   207  }
   208  
   209  func (f *fixture) runExpectError(ctx context.Context, deploymentName string, startInformers bool) {
   210  	f.run_(ctx, deploymentName, startInformers, true)
   211  }
   212  
   213  func (f *fixture) run(ctx context.Context, deploymentName string) {
   214  	f.run_(ctx, deploymentName, true, false)
   215  }
   216  
   217  func (f *fixture) run_(ctx context.Context, deploymentName string, startInformers bool, expectError bool) {
   218  	c, informers, err := f.newController(ctx)
   219  	if err != nil {
   220  		f.t.Fatalf("error creating Deployment controller: %v", err)
   221  	}
   222  	if startInformers {
   223  		stopCh := make(chan struct{})
   224  		defer close(stopCh)
   225  		informers.Start(stopCh)
   226  	}
   227  
   228  	err = c.syncDeployment(ctx, deploymentName)
   229  	if !expectError && err != nil {
   230  		f.t.Errorf("error syncing deployment: %v", err)
   231  	} else if expectError && err == nil {
   232  		f.t.Error("expected error syncing deployment, got nil")
   233  	}
   234  
   235  	actions := filterInformerActions(f.client.Actions())
   236  	for i, action := range actions {
   237  		if len(f.actions) < i+1 {
   238  			f.t.Errorf("%d unexpected actions: %+v", len(actions)-len(f.actions), actions[i:])
   239  			break
   240  		}
   241  
   242  		expectedAction := f.actions[i]
   243  		if !(expectedAction.Matches(action.GetVerb(), action.GetResource().Resource) && action.GetSubresource() == expectedAction.GetSubresource()) {
   244  			f.t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expectedAction, action)
   245  			continue
   246  		}
   247  	}
   248  
   249  	if len(f.actions) > len(actions) {
   250  		f.t.Errorf("%d additional expected actions:%+v", len(f.actions)-len(actions), f.actions[len(actions):])
   251  	}
   252  }
   253  
   254  func filterInformerActions(actions []core.Action) []core.Action {
   255  	ret := []core.Action{}
   256  	for _, action := range actions {
   257  		if len(action.GetNamespace()) == 0 &&
   258  			(action.Matches("list", "pods") ||
   259  				action.Matches("list", "deployments") ||
   260  				action.Matches("list", "replicasets") ||
   261  				action.Matches("watch", "pods") ||
   262  				action.Matches("watch", "deployments") ||
   263  				action.Matches("watch", "replicasets")) {
   264  			continue
   265  		}
   266  		ret = append(ret, action)
   267  	}
   268  
   269  	return ret
   270  }
   271  
   272  func TestSyncDeploymentCreatesReplicaSet(t *testing.T) {
   273  	_, ctx := ktesting.NewTestContext(t)
   274  
   275  	f := newFixture(t)
   276  
   277  	d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   278  	f.dLister = append(f.dLister, d)
   279  	f.objects = append(f.objects, d)
   280  
   281  	rs := newReplicaSet(d, "deploymentrs-4186632231", 1)
   282  
   283  	f.expectCreateRSAction(rs)
   284  	f.expectUpdateDeploymentStatusAction(d)
   285  	f.expectUpdateDeploymentStatusAction(d)
   286  
   287  	f.run(ctx, testutil.GetKey(d, t))
   288  }
   289  
   290  func TestSyncDeploymentDontDoAnythingDuringDeletion(t *testing.T) {
   291  	_, ctx := ktesting.NewTestContext(t)
   292  
   293  	f := newFixture(t)
   294  
   295  	d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   296  	now := metav1.Now()
   297  	d.DeletionTimestamp = &now
   298  	f.dLister = append(f.dLister, d)
   299  	f.objects = append(f.objects, d)
   300  
   301  	f.expectUpdateDeploymentStatusAction(d)
   302  	f.run(ctx, testutil.GetKey(d, t))
   303  }
   304  
   305  func TestSyncDeploymentDeletionRace(t *testing.T) {
   306  	_, ctx := ktesting.NewTestContext(t)
   307  
   308  	f := newFixture(t)
   309  
   310  	d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   311  	d2 := *d
   312  	// Lister (cache) says NOT deleted.
   313  	f.dLister = append(f.dLister, d)
   314  	// Bare client says it IS deleted. This should be presumed more up-to-date.
   315  	now := metav1.Now()
   316  	d2.DeletionTimestamp = &now
   317  	f.objects = append(f.objects, &d2)
   318  
   319  	// The recheck is only triggered if a matching orphan exists.
   320  	rs := newReplicaSet(d, "rs1", 1)
   321  	rs.OwnerReferences = nil
   322  	f.objects = append(f.objects, rs)
   323  	f.rsLister = append(f.rsLister, rs)
   324  
   325  	// Expect to only recheck DeletionTimestamp.
   326  	f.expectGetDeploymentAction(d)
   327  	// Sync should fail and requeue to let cache catch up.
   328  	// Don't start informers, since we don't want cache to catch up for this test.
   329  	f.runExpectError(ctx, testutil.GetKey(d, t), false)
   330  }
   331  
   332  // issue: https://github.com/kubernetes/kubernetes/issues/23218
   333  func TestDontSyncDeploymentsWithEmptyPodSelector(t *testing.T) {
   334  	_, ctx := ktesting.NewTestContext(t)
   335  	ctx, cancel := context.WithCancel(ctx)
   336  	defer cancel()
   337  
   338  	f := newFixture(t)
   339  
   340  	d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   341  	d.Spec.Selector = &metav1.LabelSelector{}
   342  	f.dLister = append(f.dLister, d)
   343  	f.objects = append(f.objects, d)
   344  
   345  	// Normally there should be a status update to sync observedGeneration but the fake
   346  	// deployment has no generation set so there is no action happening here.
   347  	f.run(ctx, testutil.GetKey(d, t))
   348  }
   349  
   350  func TestReentrantRollback(t *testing.T) {
   351  	_, ctx := ktesting.NewTestContext(t)
   352  
   353  	f := newFixture(t)
   354  
   355  	d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   356  	d.Annotations = map[string]string{util.RevisionAnnotation: "2"}
   357  	setRollbackTo(d, &extensions.RollbackConfig{Revision: 0})
   358  	f.dLister = append(f.dLister, d)
   359  
   360  	rs1 := newReplicaSet(d, "deploymentrs-old", 0)
   361  	rs1.Annotations = map[string]string{util.RevisionAnnotation: "1"}
   362  	one := int64(1)
   363  	rs1.Spec.Template.Spec.TerminationGracePeriodSeconds = &one
   364  	rs1.Spec.Selector.MatchLabels[apps.DefaultDeploymentUniqueLabelKey] = "hash"
   365  
   366  	rs2 := newReplicaSet(d, "deploymentrs-new", 1)
   367  	rs2.Annotations = map[string]string{util.RevisionAnnotation: "2"}
   368  	rs2.Spec.Selector.MatchLabels[apps.DefaultDeploymentUniqueLabelKey] = "hash"
   369  
   370  	f.rsLister = append(f.rsLister, rs1, rs2)
   371  	f.objects = append(f.objects, d, rs1, rs2)
   372  
   373  	// Rollback is done here
   374  	f.expectUpdateDeploymentAction(d)
   375  	// Expect no update on replica sets though
   376  	f.run(ctx, testutil.GetKey(d, t))
   377  }
   378  
   379  // TestPodDeletionEnqueuesRecreateDeployment ensures that the deletion of a pod
   380  // will requeue a Recreate deployment iff there is no other pod returned from the
   381  // client.
   382  func TestPodDeletionEnqueuesRecreateDeployment(t *testing.T) {
   383  	logger, ctx := ktesting.NewTestContext(t)
   384  
   385  	f := newFixture(t)
   386  
   387  	foo := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   388  	foo.Spec.Strategy.Type = apps.RecreateDeploymentStrategyType
   389  	rs := newReplicaSet(foo, "foo-1", 1)
   390  	pod := generatePodFromRS(rs)
   391  
   392  	f.dLister = append(f.dLister, foo)
   393  	f.rsLister = append(f.rsLister, rs)
   394  	f.objects = append(f.objects, foo, rs)
   395  
   396  	c, _, err := f.newController(ctx)
   397  	if err != nil {
   398  		t.Fatalf("error creating Deployment controller: %v", err)
   399  	}
   400  	enqueued := false
   401  	c.enqueueDeployment = func(d *apps.Deployment) {
   402  		if d.Name == "foo" {
   403  			enqueued = true
   404  		}
   405  	}
   406  
   407  	c.deletePod(logger, pod)
   408  
   409  	if !enqueued {
   410  		t.Errorf("expected deployment %q to be queued after pod deletion", foo.Name)
   411  	}
   412  }
   413  
   414  // TestPodDeletionDoesntEnqueueRecreateDeployment ensures that the deletion of a pod
   415  // will not requeue a Recreate deployment iff there are other pods returned from the
   416  // client.
   417  func TestPodDeletionDoesntEnqueueRecreateDeployment(t *testing.T) {
   418  	logger, ctx := ktesting.NewTestContext(t)
   419  
   420  	f := newFixture(t)
   421  
   422  	foo := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   423  	foo.Spec.Strategy.Type = apps.RecreateDeploymentStrategyType
   424  	rs1 := newReplicaSet(foo, "foo-1", 1)
   425  	rs2 := newReplicaSet(foo, "foo-1", 1)
   426  	pod1 := generatePodFromRS(rs1)
   427  	pod2 := generatePodFromRS(rs2)
   428  
   429  	f.dLister = append(f.dLister, foo)
   430  	// Let's pretend this is a different pod. The gist is that the pod lister needs to
   431  	// return a non-empty list.
   432  	f.podLister = append(f.podLister, pod1, pod2)
   433  
   434  	c, _, err := f.newController(ctx)
   435  	if err != nil {
   436  		t.Fatalf("error creating Deployment controller: %v", err)
   437  	}
   438  	enqueued := false
   439  	c.enqueueDeployment = func(d *apps.Deployment) {
   440  		if d.Name == "foo" {
   441  			enqueued = true
   442  		}
   443  	}
   444  
   445  	c.deletePod(logger, pod1)
   446  
   447  	if enqueued {
   448  		t.Errorf("expected deployment %q not to be queued after pod deletion", foo.Name)
   449  	}
   450  }
   451  
   452  // TestPodDeletionPartialReplicaSetOwnershipEnqueueRecreateDeployment ensures that
   453  // the deletion of a pod will requeue a Recreate deployment iff there is no other
   454  // pod returned from the client in the case where a deployment has multiple replica
   455  // sets, some of which have empty owner references.
   456  func TestPodDeletionPartialReplicaSetOwnershipEnqueueRecreateDeployment(t *testing.T) {
   457  	logger, ctx := ktesting.NewTestContext(t)
   458  
   459  	f := newFixture(t)
   460  
   461  	foo := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   462  	foo.Spec.Strategy.Type = apps.RecreateDeploymentStrategyType
   463  	rs1 := newReplicaSet(foo, "foo-1", 1)
   464  	rs2 := newReplicaSet(foo, "foo-2", 2)
   465  	rs2.OwnerReferences = nil
   466  	pod := generatePodFromRS(rs1)
   467  
   468  	f.dLister = append(f.dLister, foo)
   469  	f.rsLister = append(f.rsLister, rs1, rs2)
   470  	f.objects = append(f.objects, foo, rs1, rs2)
   471  
   472  	c, _, err := f.newController(ctx)
   473  	if err != nil {
   474  		t.Fatalf("error creating Deployment controller: %v", err)
   475  	}
   476  	enqueued := false
   477  	c.enqueueDeployment = func(d *apps.Deployment) {
   478  		if d.Name == "foo" {
   479  			enqueued = true
   480  		}
   481  	}
   482  
   483  	c.deletePod(logger, pod)
   484  
   485  	if !enqueued {
   486  		t.Errorf("expected deployment %q to be queued after pod deletion", foo.Name)
   487  	}
   488  }
   489  
   490  // TestPodDeletionPartialReplicaSetOwnershipDoesntEnqueueRecreateDeployment that the
   491  // deletion of a pod will not requeue a Recreate deployment iff there are other pods
   492  // returned from the client in the case where a deployment has multiple replica sets,
   493  // some of which have empty owner references.
   494  func TestPodDeletionPartialReplicaSetOwnershipDoesntEnqueueRecreateDeployment(t *testing.T) {
   495  	logger, ctx := ktesting.NewTestContext(t)
   496  
   497  	f := newFixture(t)
   498  
   499  	foo := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   500  	foo.Spec.Strategy.Type = apps.RecreateDeploymentStrategyType
   501  	rs1 := newReplicaSet(foo, "foo-1", 1)
   502  	rs2 := newReplicaSet(foo, "foo-2", 2)
   503  	rs2.OwnerReferences = nil
   504  	pod := generatePodFromRS(rs1)
   505  
   506  	f.dLister = append(f.dLister, foo)
   507  	f.rsLister = append(f.rsLister, rs1, rs2)
   508  	f.objects = append(f.objects, foo, rs1, rs2)
   509  	// Let's pretend this is a different pod. The gist is that the pod lister needs to
   510  	// return a non-empty list.
   511  	f.podLister = append(f.podLister, pod)
   512  
   513  	c, _, err := f.newController(ctx)
   514  	if err != nil {
   515  		t.Fatalf("error creating Deployment controller: %v", err)
   516  	}
   517  	enqueued := false
   518  	c.enqueueDeployment = func(d *apps.Deployment) {
   519  		if d.Name == "foo" {
   520  			enqueued = true
   521  		}
   522  	}
   523  
   524  	c.deletePod(logger, pod)
   525  
   526  	if enqueued {
   527  		t.Errorf("expected deployment %q not to be queued after pod deletion", foo.Name)
   528  	}
   529  }
   530  
   531  func TestGetReplicaSetsForDeployment(t *testing.T) {
   532  	_, ctx := ktesting.NewTestContext(t)
   533  
   534  	f := newFixture(t)
   535  
   536  	// Two Deployments with same labels.
   537  	d1 := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   538  	d2 := newDeployment("bar", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   539  
   540  	// Two ReplicaSets that match labels for both Deployments,
   541  	// but have ControllerRefs to make ownership explicit.
   542  	rs1 := newReplicaSet(d1, "rs1", 1)
   543  	rs2 := newReplicaSet(d2, "rs2", 1)
   544  
   545  	f.dLister = append(f.dLister, d1, d2)
   546  	f.rsLister = append(f.rsLister, rs1, rs2)
   547  	f.objects = append(f.objects, d1, d2, rs1, rs2)
   548  
   549  	// Start the fixture.
   550  	c, informers, err := f.newController(ctx)
   551  	if err != nil {
   552  		t.Fatalf("error creating Deployment controller: %v", err)
   553  	}
   554  	stopCh := make(chan struct{})
   555  	defer close(stopCh)
   556  	informers.Start(stopCh)
   557  
   558  	rsList, err := c.getReplicaSetsForDeployment(ctx, d1)
   559  	if err != nil {
   560  		t.Fatalf("getReplicaSetsForDeployment() error: %v", err)
   561  	}
   562  	rsNames := []string{}
   563  	for _, rs := range rsList {
   564  		rsNames = append(rsNames, rs.Name)
   565  	}
   566  	if len(rsNames) != 1 || rsNames[0] != rs1.Name {
   567  		t.Errorf("getReplicaSetsForDeployment() = %v, want [%v]", rsNames, rs1.Name)
   568  	}
   569  
   570  	rsList, err = c.getReplicaSetsForDeployment(ctx, d2)
   571  	if err != nil {
   572  		t.Fatalf("getReplicaSetsForDeployment() error: %v", err)
   573  	}
   574  	rsNames = []string{}
   575  	for _, rs := range rsList {
   576  		rsNames = append(rsNames, rs.Name)
   577  	}
   578  	if len(rsNames) != 1 || rsNames[0] != rs2.Name {
   579  		t.Errorf("getReplicaSetsForDeployment() = %v, want [%v]", rsNames, rs2.Name)
   580  	}
   581  }
   582  
   583  func TestGetReplicaSetsForDeploymentAdoptRelease(t *testing.T) {
   584  	_, ctx := ktesting.NewTestContext(t)
   585  
   586  	f := newFixture(t)
   587  
   588  	d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   589  
   590  	// RS with matching labels, but orphaned. Should be adopted and returned.
   591  	rsAdopt := newReplicaSet(d, "rsAdopt", 1)
   592  	rsAdopt.OwnerReferences = nil
   593  	// RS with matching ControllerRef, but wrong labels. Should be released.
   594  	rsRelease := newReplicaSet(d, "rsRelease", 1)
   595  	rsRelease.Labels = map[string]string{"foo": "notbar"}
   596  
   597  	f.dLister = append(f.dLister, d)
   598  	f.rsLister = append(f.rsLister, rsAdopt, rsRelease)
   599  	f.objects = append(f.objects, d, rsAdopt, rsRelease)
   600  
   601  	// Start the fixture.
   602  	c, informers, err := f.newController(ctx)
   603  	if err != nil {
   604  		t.Fatalf("error creating Deployment controller: %v", err)
   605  	}
   606  	stopCh := make(chan struct{})
   607  	defer close(stopCh)
   608  	informers.Start(stopCh)
   609  
   610  	rsList, err := c.getReplicaSetsForDeployment(ctx, d)
   611  	if err != nil {
   612  		t.Fatalf("getReplicaSetsForDeployment() error: %v", err)
   613  	}
   614  	rsNames := []string{}
   615  	for _, rs := range rsList {
   616  		rsNames = append(rsNames, rs.Name)
   617  	}
   618  	if len(rsNames) != 1 || rsNames[0] != rsAdopt.Name {
   619  		t.Errorf("getReplicaSetsForDeployment() = %v, want [%v]", rsNames, rsAdopt.Name)
   620  	}
   621  }
   622  
   623  func TestGetPodMapForReplicaSets(t *testing.T) {
   624  	_, ctx := ktesting.NewTestContext(t)
   625  
   626  	f := newFixture(t)
   627  
   628  	d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   629  
   630  	rs1 := newReplicaSet(d, "rs1", 1)
   631  	rs2 := newReplicaSet(d, "rs2", 1)
   632  
   633  	// Add a Pod for each ReplicaSet.
   634  	pod1 := generatePodFromRS(rs1)
   635  	pod2 := generatePodFromRS(rs2)
   636  	// Add a Pod that has matching labels, but no ControllerRef.
   637  	pod3 := generatePodFromRS(rs1)
   638  	pod3.Name = "pod3"
   639  	pod3.OwnerReferences = nil
   640  	// Add a Pod that has matching labels and ControllerRef, but is inactive.
   641  	pod4 := generatePodFromRS(rs1)
   642  	pod4.Name = "pod4"
   643  	pod4.Status.Phase = v1.PodFailed
   644  
   645  	f.dLister = append(f.dLister, d)
   646  	f.rsLister = append(f.rsLister, rs1, rs2)
   647  	f.podLister = append(f.podLister, pod1, pod2, pod3, pod4)
   648  	f.objects = append(f.objects, d, rs1, rs2, pod1, pod2, pod3, pod4)
   649  
   650  	// Start the fixture.
   651  	c, informers, err := f.newController(ctx)
   652  	if err != nil {
   653  		t.Fatalf("error creating Deployment controller: %v", err)
   654  	}
   655  	stopCh := make(chan struct{})
   656  	defer close(stopCh)
   657  	informers.Start(stopCh)
   658  
   659  	podMap, err := c.getPodMapForDeployment(d, f.rsLister)
   660  	if err != nil {
   661  		t.Fatalf("getPodMapForDeployment() error: %v", err)
   662  	}
   663  	podCount := 0
   664  	for _, podList := range podMap {
   665  		podCount += len(podList)
   666  	}
   667  	if got, want := podCount, 3; got != want {
   668  		t.Errorf("podCount = %v, want %v", got, want)
   669  	}
   670  
   671  	if got, want := len(podMap), 2; got != want {
   672  		t.Errorf("len(podMap) = %v, want %v", got, want)
   673  	}
   674  	if got, want := len(podMap[rs1.UID]), 2; got != want {
   675  		t.Errorf("len(podMap[rs1]) = %v, want %v", got, want)
   676  	}
   677  	expect := map[string]struct{}{"rs1-pod": {}, "pod4": {}}
   678  	for _, pod := range podMap[rs1.UID] {
   679  		if _, ok := expect[pod.Name]; !ok {
   680  			t.Errorf("unexpected pod name for rs1: %s", pod.Name)
   681  		}
   682  	}
   683  	if got, want := len(podMap[rs2.UID]), 1; got != want {
   684  		t.Errorf("len(podMap[rs2]) = %v, want %v", got, want)
   685  	}
   686  	if got, want := podMap[rs2.UID][0].Name, "rs2-pod"; got != want {
   687  		t.Errorf("podMap[rs2] = [%v], want [%v]", got, want)
   688  	}
   689  }
   690  
   691  func TestAddReplicaSet(t *testing.T) {
   692  	logger, ctx := ktesting.NewTestContext(t)
   693  
   694  	f := newFixture(t)
   695  
   696  	d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   697  	d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   698  
   699  	// Two ReplicaSets that match labels for both Deployments,
   700  	// but have ControllerRefs to make ownership explicit.
   701  	rs1 := newReplicaSet(d1, "rs1", 1)
   702  	rs2 := newReplicaSet(d2, "rs2", 1)
   703  
   704  	f.dLister = append(f.dLister, d1, d2)
   705  	f.objects = append(f.objects, d1, d2, rs1, rs2)
   706  
   707  	// Create the fixture but don't start it,
   708  	// so nothing happens in the background.
   709  	dc, _, err := f.newController(ctx)
   710  	if err != nil {
   711  		t.Fatalf("error creating Deployment controller: %v", err)
   712  	}
   713  
   714  	dc.addReplicaSet(klog.FromContext(ctx), rs1)
   715  	if got, want := dc.queue.Len(), 1; got != want {
   716  		t.Fatalf("queue.Len() = %v, want %v", got, want)
   717  	}
   718  	key, done := dc.queue.Get()
   719  	if key == nil || done {
   720  		t.Fatalf("failed to enqueue controller for rs %v", rs1.Name)
   721  	}
   722  	expectedKey, _ := controller.KeyFunc(d1)
   723  	if got, want := key.(string), expectedKey; got != want {
   724  		t.Errorf("queue.Get() = %v, want %v", got, want)
   725  	}
   726  
   727  	dc.addReplicaSet(logger, rs2)
   728  	if got, want := dc.queue.Len(), 1; got != want {
   729  		t.Fatalf("queue.Len() = %v, want %v", got, want)
   730  	}
   731  	key, done = dc.queue.Get()
   732  	if key == nil || done {
   733  		t.Fatalf("failed to enqueue controller for rs %v", rs2.Name)
   734  	}
   735  	expectedKey, _ = controller.KeyFunc(d2)
   736  	if got, want := key.(string), expectedKey; got != want {
   737  		t.Errorf("queue.Get() = %v, want %v", got, want)
   738  	}
   739  }
   740  
   741  func TestAddReplicaSetOrphan(t *testing.T) {
   742  	logger, ctx := ktesting.NewTestContext(t)
   743  
   744  	f := newFixture(t)
   745  
   746  	// 2 will match the RS, 1 won't.
   747  	d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   748  	d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   749  	d3 := newDeployment("d3", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   750  	d3.Spec.Selector.MatchLabels = map[string]string{"foo": "notbar"}
   751  
   752  	// Make the RS an orphan. Expect matching Deployments to be queued.
   753  	rs := newReplicaSet(d1, "rs1", 1)
   754  	rs.OwnerReferences = nil
   755  
   756  	f.dLister = append(f.dLister, d1, d2, d3)
   757  	f.objects = append(f.objects, d1, d2, d3)
   758  
   759  	// Create the fixture but don't start it,
   760  	// so nothing happens in the background.
   761  	dc, _, err := f.newController(ctx)
   762  	if err != nil {
   763  		t.Fatalf("error creating Deployment controller: %v", err)
   764  	}
   765  
   766  	dc.addReplicaSet(logger, rs)
   767  	if got, want := dc.queue.Len(), 2; got != want {
   768  		t.Fatalf("queue.Len() = %v, want %v", got, want)
   769  	}
   770  }
   771  
   772  func TestUpdateReplicaSet(t *testing.T) {
   773  	logger, ctx := ktesting.NewTestContext(t)
   774  
   775  	f := newFixture(t)
   776  
   777  	d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   778  	d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   779  
   780  	// Two ReplicaSets that match labels for both Deployments,
   781  	// but have ControllerRefs to make ownership explicit.
   782  	rs1 := newReplicaSet(d1, "rs1", 1)
   783  	rs2 := newReplicaSet(d2, "rs2", 1)
   784  
   785  	f.dLister = append(f.dLister, d1, d2)
   786  	f.rsLister = append(f.rsLister, rs1, rs2)
   787  	f.objects = append(f.objects, d1, d2, rs1, rs2)
   788  
   789  	// Create the fixture but don't start it,
   790  	// so nothing happens in the background.
   791  	dc, _, err := f.newController(ctx)
   792  	if err != nil {
   793  		t.Fatalf("error creating Deployment controller: %v", err)
   794  	}
   795  
   796  	prev := *rs1
   797  	next := *rs1
   798  	bumpResourceVersion(&next)
   799  	dc.updateReplicaSet(logger, &prev, &next)
   800  	if got, want := dc.queue.Len(), 1; got != want {
   801  		t.Fatalf("queue.Len() = %v, want %v", got, want)
   802  	}
   803  	key, done := dc.queue.Get()
   804  	if key == nil || done {
   805  		t.Fatalf("failed to enqueue controller for rs %v", rs1.Name)
   806  	}
   807  	expectedKey, _ := controller.KeyFunc(d1)
   808  	if got, want := key.(string), expectedKey; got != want {
   809  		t.Errorf("queue.Get() = %v, want %v", got, want)
   810  	}
   811  
   812  	prev = *rs2
   813  	next = *rs2
   814  	bumpResourceVersion(&next)
   815  	dc.updateReplicaSet(logger, &prev, &next)
   816  	if got, want := dc.queue.Len(), 1; got != want {
   817  		t.Fatalf("queue.Len() = %v, want %v", got, want)
   818  	}
   819  	key, done = dc.queue.Get()
   820  	if key == nil || done {
   821  		t.Fatalf("failed to enqueue controller for rs %v", rs2.Name)
   822  	}
   823  	expectedKey, _ = controller.KeyFunc(d2)
   824  	if got, want := key.(string), expectedKey; got != want {
   825  		t.Errorf("queue.Get() = %v, want %v", got, want)
   826  	}
   827  }
   828  
   829  func TestUpdateReplicaSetOrphanWithNewLabels(t *testing.T) {
   830  	logger, ctx := ktesting.NewTestContext(t)
   831  
   832  	f := newFixture(t)
   833  
   834  	d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   835  	d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   836  
   837  	// RS matches both, but is an orphan.
   838  	rs := newReplicaSet(d1, "rs1", 1)
   839  	rs.OwnerReferences = nil
   840  
   841  	f.dLister = append(f.dLister, d1, d2)
   842  	f.rsLister = append(f.rsLister, rs)
   843  	f.objects = append(f.objects, d1, d2, rs)
   844  
   845  	// Create the fixture but don't start it,
   846  	// so nothing happens in the background.
   847  	dc, _, err := f.newController(ctx)
   848  	if err != nil {
   849  		t.Fatalf("error creating Deployment controller: %v", err)
   850  	}
   851  
   852  	// Change labels and expect all matching controllers to queue.
   853  	prev := *rs
   854  	prev.Labels = map[string]string{"foo": "notbar"}
   855  	next := *rs
   856  	bumpResourceVersion(&next)
   857  	dc.updateReplicaSet(logger, &prev, &next)
   858  	if got, want := dc.queue.Len(), 2; got != want {
   859  		t.Fatalf("queue.Len() = %v, want %v", got, want)
   860  	}
   861  }
   862  
   863  func TestUpdateReplicaSetChangeControllerRef(t *testing.T) {
   864  	logger, ctx := ktesting.NewTestContext(t)
   865  
   866  	f := newFixture(t)
   867  
   868  	d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   869  	d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   870  
   871  	rs := newReplicaSet(d1, "rs1", 1)
   872  
   873  	f.dLister = append(f.dLister, d1, d2)
   874  	f.rsLister = append(f.rsLister, rs)
   875  	f.objects = append(f.objects, d1, d2, rs)
   876  
   877  	// Create the fixture but don't start it,
   878  	// so nothing happens in the background.
   879  	dc, _, err := f.newController(ctx)
   880  	if err != nil {
   881  		t.Fatalf("error creating Deployment controller: %v", err)
   882  	}
   883  
   884  	// Change ControllerRef and expect both old and new to queue.
   885  	prev := *rs
   886  	prev.OwnerReferences = []metav1.OwnerReference{*metav1.NewControllerRef(d2, controllerKind)}
   887  	next := *rs
   888  	bumpResourceVersion(&next)
   889  	dc.updateReplicaSet(logger, &prev, &next)
   890  	if got, want := dc.queue.Len(), 2; got != want {
   891  		t.Fatalf("queue.Len() = %v, want %v", got, want)
   892  	}
   893  }
   894  
   895  func TestUpdateReplicaSetRelease(t *testing.T) {
   896  	logger, ctx := ktesting.NewTestContext(t)
   897  
   898  	f := newFixture(t)
   899  
   900  	d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   901  	d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   902  
   903  	rs := newReplicaSet(d1, "rs1", 1)
   904  
   905  	f.dLister = append(f.dLister, d1, d2)
   906  	f.rsLister = append(f.rsLister, rs)
   907  	f.objects = append(f.objects, d1, d2, rs)
   908  
   909  	// Create the fixture but don't start it,
   910  	// so nothing happens in the background.
   911  	dc, _, err := f.newController(ctx)
   912  	if err != nil {
   913  		t.Fatalf("error creating Deployment controller: %v", err)
   914  	}
   915  
   916  	// Remove ControllerRef and expect all matching controller to sync orphan.
   917  	prev := *rs
   918  	next := *rs
   919  	next.OwnerReferences = nil
   920  	bumpResourceVersion(&next)
   921  	dc.updateReplicaSet(logger, &prev, &next)
   922  	if got, want := dc.queue.Len(), 2; got != want {
   923  		t.Fatalf("queue.Len() = %v, want %v", got, want)
   924  	}
   925  }
   926  
   927  func TestDeleteReplicaSet(t *testing.T) {
   928  	logger, ctx := ktesting.NewTestContext(t)
   929  
   930  	f := newFixture(t)
   931  
   932  	d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   933  	d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   934  
   935  	// Two ReplicaSets that match labels for both Deployments,
   936  	// but have ControllerRefs to make ownership explicit.
   937  	rs1 := newReplicaSet(d1, "rs1", 1)
   938  	rs2 := newReplicaSet(d2, "rs2", 1)
   939  
   940  	f.dLister = append(f.dLister, d1, d2)
   941  	f.rsLister = append(f.rsLister, rs1, rs2)
   942  	f.objects = append(f.objects, d1, d2, rs1, rs2)
   943  
   944  	// Create the fixture but don't start it,
   945  	// so nothing happens in the background.
   946  	dc, _, err := f.newController(ctx)
   947  	if err != nil {
   948  		t.Fatalf("error creating Deployment controller: %v", err)
   949  	}
   950  
   951  	dc.deleteReplicaSet(logger, rs1)
   952  	if got, want := dc.queue.Len(), 1; got != want {
   953  		t.Fatalf("queue.Len() = %v, want %v", got, want)
   954  	}
   955  	key, done := dc.queue.Get()
   956  	if key == nil || done {
   957  		t.Fatalf("failed to enqueue controller for rs %v", rs1.Name)
   958  	}
   959  	expectedKey, _ := controller.KeyFunc(d1)
   960  	if got, want := key.(string), expectedKey; got != want {
   961  		t.Errorf("queue.Get() = %v, want %v", got, want)
   962  	}
   963  
   964  	dc.deleteReplicaSet(logger, rs2)
   965  	if got, want := dc.queue.Len(), 1; got != want {
   966  		t.Fatalf("queue.Len() = %v, want %v", got, want)
   967  	}
   968  	key, done = dc.queue.Get()
   969  	if key == nil || done {
   970  		t.Fatalf("failed to enqueue controller for rs %v", rs2.Name)
   971  	}
   972  	expectedKey, _ = controller.KeyFunc(d2)
   973  	if got, want := key.(string), expectedKey; got != want {
   974  		t.Errorf("queue.Get() = %v, want %v", got, want)
   975  	}
   976  }
   977  
   978  func TestDeleteReplicaSetOrphan(t *testing.T) {
   979  	logger, ctx := ktesting.NewTestContext(t)
   980  
   981  	f := newFixture(t)
   982  
   983  	d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   984  	d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"})
   985  
   986  	// Make the RS an orphan. Expect matching Deployments to be queued.
   987  	rs := newReplicaSet(d1, "rs1", 1)
   988  	rs.OwnerReferences = nil
   989  
   990  	f.dLister = append(f.dLister, d1, d2)
   991  	f.rsLister = append(f.rsLister, rs)
   992  	f.objects = append(f.objects, d1, d2, rs)
   993  
   994  	// Create the fixture but don't start it,
   995  	// so nothing happens in the background.
   996  	dc, _, err := f.newController(ctx)
   997  	if err != nil {
   998  		t.Fatalf("error creating Deployment controller: %v", err)
   999  	}
  1000  
  1001  	dc.deleteReplicaSet(logger, rs)
  1002  	if got, want := dc.queue.Len(), 0; got != want {
  1003  		t.Fatalf("queue.Len() = %v, want %v", got, want)
  1004  	}
  1005  }
  1006  
  1007  func BenchmarkGetPodMapForDeployment(b *testing.B) {
  1008  	_, ctx := ktesting.NewTestContext(b)
  1009  
  1010  	f := newFixture(b)
  1011  
  1012  	d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"})
  1013  
  1014  	rs1 := newReplicaSet(d, "rs1", 1)
  1015  	rs2 := newReplicaSet(d, "rs2", 1)
  1016  
  1017  	var pods []*v1.Pod
  1018  	var objects []runtime.Object
  1019  	for i := 0; i < 100; i++ {
  1020  		p1, p2 := generatePodFromRS(rs1), generatePodFromRS(rs2)
  1021  		p1.Name, p2.Name = p1.Name+fmt.Sprintf("-%d", i), p2.Name+fmt.Sprintf("-%d", i)
  1022  		pods = append(pods, p1, p2)
  1023  		objects = append(objects, p1, p2)
  1024  	}
  1025  
  1026  	f.dLister = append(f.dLister, d)
  1027  	f.rsLister = append(f.rsLister, rs1, rs2)
  1028  	f.podLister = append(f.podLister, pods...)
  1029  	f.objects = append(f.objects, d, rs1, rs2)
  1030  	f.objects = append(f.objects, objects...)
  1031  
  1032  	// Start the fixture.
  1033  	c, informers, err := f.newController(ctx)
  1034  	if err != nil {
  1035  		b.Fatalf("error creating Deployment controller: %v", err)
  1036  	}
  1037  	stopCh := make(chan struct{})
  1038  	defer close(stopCh)
  1039  	informers.Start(stopCh)
  1040  
  1041  	b.ReportAllocs()
  1042  	b.ResetTimer()
  1043  	for n := 0; n < b.N; n++ {
  1044  		m, err := c.getPodMapForDeployment(d, f.rsLister)
  1045  		if err != nil {
  1046  			b.Fatalf("getPodMapForDeployment() error: %v", err)
  1047  		}
  1048  		if len(m) != 2 {
  1049  			b.Errorf("Invalid map size, expected 2, got: %d", len(m))
  1050  		}
  1051  	}
  1052  }
  1053  
  1054  func bumpResourceVersion(obj metav1.Object) {
  1055  	ver, _ := strconv.ParseInt(obj.GetResourceVersion(), 10, 32)
  1056  	obj.SetResourceVersion(strconv.FormatInt(ver+1, 10))
  1057  }
  1058  
  1059  // generatePodFromRS creates a pod, with the input ReplicaSet's selector and its template
  1060  func generatePodFromRS(rs *apps.ReplicaSet) *v1.Pod {
  1061  	trueVar := true
  1062  	return &v1.Pod{
  1063  		ObjectMeta: metav1.ObjectMeta{
  1064  			Name:      rs.Name + "-pod",
  1065  			Namespace: rs.Namespace,
  1066  			Labels:    rs.Spec.Selector.MatchLabels,
  1067  			OwnerReferences: []metav1.OwnerReference{
  1068  				{UID: rs.UID, APIVersion: "v1beta1", Kind: "ReplicaSet", Name: rs.Name, Controller: &trueVar},
  1069  			},
  1070  		},
  1071  		Spec: rs.Spec.Template.Spec,
  1072  	}
  1073  }
  1074  

View as plain text