...

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

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

     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 deployment
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  
    23  	apps "k8s.io/api/apps/v1"
    24  	"k8s.io/apimachinery/pkg/util/intstr"
    25  	"k8s.io/client-go/kubernetes/fake"
    26  	core "k8s.io/client-go/testing"
    27  	"k8s.io/client-go/tools/record"
    28  	"k8s.io/klog/v2/ktesting"
    29  )
    30  
    31  func TestDeploymentController_reconcileNewReplicaSet(t *testing.T) {
    32  	tests := []struct {
    33  		deploymentReplicas  int32
    34  		maxSurge            intstr.IntOrString
    35  		oldReplicas         int32
    36  		newReplicas         int32
    37  		scaleExpected       bool
    38  		expectedNewReplicas int32
    39  	}{
    40  		{
    41  			// Should not scale up.
    42  			deploymentReplicas: 10,
    43  			maxSurge:           intstr.FromInt32(0),
    44  			oldReplicas:        10,
    45  			newReplicas:        0,
    46  			scaleExpected:      false,
    47  		},
    48  		{
    49  			deploymentReplicas:  10,
    50  			maxSurge:            intstr.FromInt32(2),
    51  			oldReplicas:         10,
    52  			newReplicas:         0,
    53  			scaleExpected:       true,
    54  			expectedNewReplicas: 2,
    55  		},
    56  		{
    57  			deploymentReplicas:  10,
    58  			maxSurge:            intstr.FromInt32(2),
    59  			oldReplicas:         5,
    60  			newReplicas:         0,
    61  			scaleExpected:       true,
    62  			expectedNewReplicas: 7,
    63  		},
    64  		{
    65  			deploymentReplicas: 10,
    66  			maxSurge:           intstr.FromInt32(2),
    67  			oldReplicas:        10,
    68  			newReplicas:        2,
    69  			scaleExpected:      false,
    70  		},
    71  		{
    72  			// Should scale down.
    73  			deploymentReplicas:  10,
    74  			maxSurge:            intstr.FromInt32(2),
    75  			oldReplicas:         2,
    76  			newReplicas:         11,
    77  			scaleExpected:       true,
    78  			expectedNewReplicas: 10,
    79  		},
    80  	}
    81  
    82  	for i := range tests {
    83  		test := tests[i]
    84  		t.Logf("executing scenario %d", i)
    85  		newRS := rs("foo-v2", test.newReplicas, nil, noTimestamp)
    86  		oldRS := rs("foo-v2", test.oldReplicas, nil, noTimestamp)
    87  		allRSs := []*apps.ReplicaSet{newRS, oldRS}
    88  		maxUnavailable := intstr.FromInt32(0)
    89  		deployment := newDeployment("foo", test.deploymentReplicas, nil, &test.maxSurge, &maxUnavailable, map[string]string{"foo": "bar"})
    90  		fake := fake.Clientset{}
    91  		controller := &DeploymentController{
    92  			client:        &fake,
    93  			eventRecorder: &record.FakeRecorder{},
    94  		}
    95  		_, ctx := ktesting.NewTestContext(t)
    96  		ctx, cancel := context.WithCancel(ctx)
    97  		defer cancel()
    98  		scaled, err := controller.reconcileNewReplicaSet(ctx, allRSs, newRS, deployment)
    99  		if err != nil {
   100  			t.Errorf("unexpected error: %v", err)
   101  			continue
   102  		}
   103  		if !test.scaleExpected {
   104  			if scaled || len(fake.Actions()) > 0 {
   105  				t.Errorf("unexpected scaling: %v", fake.Actions())
   106  			}
   107  			continue
   108  		}
   109  		if test.scaleExpected && !scaled {
   110  			t.Errorf("expected scaling to occur")
   111  			continue
   112  		}
   113  		if len(fake.Actions()) != 1 {
   114  			t.Errorf("expected 1 action during scale, got: %v", fake.Actions())
   115  			continue
   116  		}
   117  		updated := fake.Actions()[0].(core.UpdateAction).GetObject().(*apps.ReplicaSet)
   118  		if e, a := test.expectedNewReplicas, *(updated.Spec.Replicas); e != a {
   119  			t.Errorf("expected update to %d replicas, got %d", e, a)
   120  		}
   121  	}
   122  }
   123  
   124  func TestDeploymentController_reconcileOldReplicaSets(t *testing.T) {
   125  	tests := []struct {
   126  		deploymentReplicas  int32
   127  		maxUnavailable      intstr.IntOrString
   128  		oldReplicas         int32
   129  		newReplicas         int32
   130  		readyPodsFromOldRS  int
   131  		readyPodsFromNewRS  int
   132  		scaleExpected       bool
   133  		expectedOldReplicas int32
   134  	}{
   135  		{
   136  			deploymentReplicas:  10,
   137  			maxUnavailable:      intstr.FromInt32(0),
   138  			oldReplicas:         10,
   139  			newReplicas:         0,
   140  			readyPodsFromOldRS:  10,
   141  			readyPodsFromNewRS:  0,
   142  			scaleExpected:       true,
   143  			expectedOldReplicas: 9,
   144  		},
   145  		{
   146  			deploymentReplicas:  10,
   147  			maxUnavailable:      intstr.FromInt32(2),
   148  			oldReplicas:         10,
   149  			newReplicas:         0,
   150  			readyPodsFromOldRS:  10,
   151  			readyPodsFromNewRS:  0,
   152  			scaleExpected:       true,
   153  			expectedOldReplicas: 8,
   154  		},
   155  		{ // expect unhealthy replicas from old replica sets been cleaned up
   156  			deploymentReplicas:  10,
   157  			maxUnavailable:      intstr.FromInt32(2),
   158  			oldReplicas:         10,
   159  			newReplicas:         0,
   160  			readyPodsFromOldRS:  8,
   161  			readyPodsFromNewRS:  0,
   162  			scaleExpected:       true,
   163  			expectedOldReplicas: 8,
   164  		},
   165  		{ // expect 1 unhealthy replica from old replica sets been cleaned up, and 1 ready pod been scaled down
   166  			deploymentReplicas:  10,
   167  			maxUnavailable:      intstr.FromInt32(2),
   168  			oldReplicas:         10,
   169  			newReplicas:         0,
   170  			readyPodsFromOldRS:  9,
   171  			readyPodsFromNewRS:  0,
   172  			scaleExpected:       true,
   173  			expectedOldReplicas: 8,
   174  		},
   175  		{ // the unavailable pods from the newRS would not make us scale down old RSs in a further step
   176  			deploymentReplicas: 10,
   177  			maxUnavailable:     intstr.FromInt32(2),
   178  			oldReplicas:        8,
   179  			newReplicas:        2,
   180  			readyPodsFromOldRS: 8,
   181  			readyPodsFromNewRS: 0,
   182  			scaleExpected:      false,
   183  		},
   184  	}
   185  	for i := range tests {
   186  		test := tests[i]
   187  		t.Logf("executing scenario %d", i)
   188  
   189  		newSelector := map[string]string{"foo": "new"}
   190  		oldSelector := map[string]string{"foo": "old"}
   191  		newRS := rs("foo-new", test.newReplicas, newSelector, noTimestamp)
   192  		newRS.Status.AvailableReplicas = int32(test.readyPodsFromNewRS)
   193  		oldRS := rs("foo-old", test.oldReplicas, oldSelector, noTimestamp)
   194  		oldRS.Status.AvailableReplicas = int32(test.readyPodsFromOldRS)
   195  		oldRSs := []*apps.ReplicaSet{oldRS}
   196  		allRSs := []*apps.ReplicaSet{oldRS, newRS}
   197  		maxSurge := intstr.FromInt32(0)
   198  		deployment := newDeployment("foo", test.deploymentReplicas, nil, &maxSurge, &test.maxUnavailable, newSelector)
   199  		fakeClientset := fake.Clientset{}
   200  		controller := &DeploymentController{
   201  			client:        &fakeClientset,
   202  			eventRecorder: &record.FakeRecorder{},
   203  		}
   204  		_, ctx := ktesting.NewTestContext(t)
   205  		scaled, err := controller.reconcileOldReplicaSets(ctx, allRSs, oldRSs, newRS, deployment)
   206  		if err != nil {
   207  			t.Errorf("unexpected error: %v", err)
   208  			continue
   209  		}
   210  		if !test.scaleExpected && scaled {
   211  			t.Errorf("unexpected scaling: %v", fakeClientset.Actions())
   212  		}
   213  		if test.scaleExpected && !scaled {
   214  			t.Errorf("expected scaling to occur")
   215  			continue
   216  		}
   217  		continue
   218  	}
   219  }
   220  
   221  func TestDeploymentController_cleanupUnhealthyReplicas(t *testing.T) {
   222  	tests := []struct {
   223  		oldReplicas          int32
   224  		readyPods            int
   225  		unHealthyPods        int
   226  		maxCleanupCount      int
   227  		cleanupCountExpected int
   228  	}{
   229  		{
   230  			oldReplicas:          10,
   231  			readyPods:            8,
   232  			unHealthyPods:        2,
   233  			maxCleanupCount:      1,
   234  			cleanupCountExpected: 1,
   235  		},
   236  		{
   237  			oldReplicas:          10,
   238  			readyPods:            8,
   239  			unHealthyPods:        2,
   240  			maxCleanupCount:      3,
   241  			cleanupCountExpected: 2,
   242  		},
   243  		{
   244  			oldReplicas:          10,
   245  			readyPods:            8,
   246  			unHealthyPods:        2,
   247  			maxCleanupCount:      0,
   248  			cleanupCountExpected: 0,
   249  		},
   250  		{
   251  			oldReplicas:          10,
   252  			readyPods:            10,
   253  			unHealthyPods:        0,
   254  			maxCleanupCount:      3,
   255  			cleanupCountExpected: 0,
   256  		},
   257  	}
   258  
   259  	for i, test := range tests {
   260  		t.Logf("executing scenario %d", i)
   261  		oldRS := rs("foo-v2", test.oldReplicas, nil, noTimestamp)
   262  		oldRS.Status.AvailableReplicas = int32(test.readyPods)
   263  		oldRSs := []*apps.ReplicaSet{oldRS}
   264  		maxSurge := intstr.FromInt32(2)
   265  		maxUnavailable := intstr.FromInt32(2)
   266  		deployment := newDeployment("foo", 10, nil, &maxSurge, &maxUnavailable, nil)
   267  		fakeClientset := fake.Clientset{}
   268  
   269  		controller := &DeploymentController{
   270  			client:        &fakeClientset,
   271  			eventRecorder: &record.FakeRecorder{},
   272  		}
   273  		_, ctx := ktesting.NewTestContext(t)
   274  		_, cleanupCount, err := controller.cleanupUnhealthyReplicas(ctx, oldRSs, deployment, int32(test.maxCleanupCount))
   275  		if err != nil {
   276  			t.Errorf("unexpected error: %v", err)
   277  			continue
   278  		}
   279  		if int(cleanupCount) != test.cleanupCountExpected {
   280  			t.Errorf("expected %v unhealthy replicas been cleaned up, got %v", test.cleanupCountExpected, cleanupCount)
   281  			continue
   282  		}
   283  	}
   284  }
   285  
   286  func TestDeploymentController_scaleDownOldReplicaSetsForRollingUpdate(t *testing.T) {
   287  	tests := []struct {
   288  		deploymentReplicas  int32
   289  		maxUnavailable      intstr.IntOrString
   290  		readyPods           int
   291  		oldReplicas         int32
   292  		scaleExpected       bool
   293  		expectedOldReplicas int32
   294  	}{
   295  		{
   296  			deploymentReplicas:  10,
   297  			maxUnavailable:      intstr.FromInt32(0),
   298  			readyPods:           10,
   299  			oldReplicas:         10,
   300  			scaleExpected:       true,
   301  			expectedOldReplicas: 9,
   302  		},
   303  		{
   304  			deploymentReplicas:  10,
   305  			maxUnavailable:      intstr.FromInt32(2),
   306  			readyPods:           10,
   307  			oldReplicas:         10,
   308  			scaleExpected:       true,
   309  			expectedOldReplicas: 8,
   310  		},
   311  		{
   312  			deploymentReplicas: 10,
   313  			maxUnavailable:     intstr.FromInt32(2),
   314  			readyPods:          8,
   315  			oldReplicas:        10,
   316  			scaleExpected:      false,
   317  		},
   318  		{
   319  			deploymentReplicas: 10,
   320  			maxUnavailable:     intstr.FromInt32(2),
   321  			readyPods:          10,
   322  			oldReplicas:        0,
   323  			scaleExpected:      false,
   324  		},
   325  		{
   326  			deploymentReplicas: 10,
   327  			maxUnavailable:     intstr.FromInt32(2),
   328  			readyPods:          1,
   329  			oldReplicas:        10,
   330  			scaleExpected:      false,
   331  		},
   332  	}
   333  
   334  	for i := range tests {
   335  		test := tests[i]
   336  		t.Logf("executing scenario %d", i)
   337  		oldRS := rs("foo-v2", test.oldReplicas, nil, noTimestamp)
   338  		oldRS.Status.AvailableReplicas = int32(test.readyPods)
   339  		allRSs := []*apps.ReplicaSet{oldRS}
   340  		oldRSs := []*apps.ReplicaSet{oldRS}
   341  		maxSurge := intstr.FromInt32(0)
   342  		deployment := newDeployment("foo", test.deploymentReplicas, nil, &maxSurge, &test.maxUnavailable, map[string]string{"foo": "bar"})
   343  		fakeClientset := fake.Clientset{}
   344  		controller := &DeploymentController{
   345  			client:        &fakeClientset,
   346  			eventRecorder: &record.FakeRecorder{},
   347  		}
   348  		_, ctx := ktesting.NewTestContext(t)
   349  		scaled, err := controller.scaleDownOldReplicaSetsForRollingUpdate(ctx, allRSs, oldRSs, deployment)
   350  		if err != nil {
   351  			t.Errorf("unexpected error: %v", err)
   352  			continue
   353  		}
   354  		if !test.scaleExpected {
   355  			if scaled != 0 {
   356  				t.Errorf("unexpected scaling: %v", fakeClientset.Actions())
   357  			}
   358  			continue
   359  		}
   360  		if test.scaleExpected && scaled == 0 {
   361  			t.Errorf("expected scaling to occur; actions: %v", fakeClientset.Actions())
   362  			continue
   363  		}
   364  		// There are both list and update actions logged, so extract the update
   365  		// action for verification.
   366  		var updateAction core.UpdateAction
   367  		for _, action := range fakeClientset.Actions() {
   368  			switch a := action.(type) {
   369  			case core.UpdateAction:
   370  				if updateAction != nil {
   371  					t.Errorf("expected only 1 update action; had %v and found %v", updateAction, a)
   372  				} else {
   373  					updateAction = a
   374  				}
   375  			}
   376  		}
   377  		if updateAction == nil {
   378  			t.Errorf("expected an update action")
   379  			continue
   380  		}
   381  		updated := updateAction.GetObject().(*apps.ReplicaSet)
   382  		if e, a := test.expectedOldReplicas, *(updated.Spec.Replicas); e != a {
   383  			t.Errorf("expected update to %d replicas, got %d", e, a)
   384  		}
   385  	}
   386  }
   387  

View as plain text