...

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

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

     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 util
    18  
    19  import (
    20  	"fmt"
    21  	"math"
    22  	"math/rand"
    23  	"reflect"
    24  	"sort"
    25  	"strconv"
    26  	"testing"
    27  	"time"
    28  
    29  	apps "k8s.io/api/apps/v1"
    30  	v1 "k8s.io/api/core/v1"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apimachinery/pkg/types"
    33  	"k8s.io/apimachinery/pkg/util/intstr"
    34  	"k8s.io/apiserver/pkg/storage/names"
    35  	"k8s.io/client-go/informers"
    36  	"k8s.io/client-go/kubernetes/fake"
    37  	"k8s.io/klog/v2/ktesting"
    38  	"k8s.io/kubernetes/pkg/controller"
    39  	"k8s.io/utils/ptr"
    40  )
    41  
    42  func newDControllerRef(d *apps.Deployment) *metav1.OwnerReference {
    43  	isController := true
    44  	return &metav1.OwnerReference{
    45  		APIVersion: "apps/v1",
    46  		Kind:       "Deployment",
    47  		Name:       d.GetName(),
    48  		UID:        d.GetUID(),
    49  		Controller: &isController,
    50  	}
    51  }
    52  
    53  // generateRS creates a replica set, with the input deployment's template as its template
    54  func generateRS(deployment apps.Deployment) apps.ReplicaSet {
    55  	template := deployment.Spec.Template.DeepCopy()
    56  	return apps.ReplicaSet{
    57  		ObjectMeta: metav1.ObjectMeta{
    58  			UID:             randomUID(),
    59  			Name:            names.SimpleNameGenerator.GenerateName("replicaset"),
    60  			Labels:          template.Labels,
    61  			OwnerReferences: []metav1.OwnerReference{*newDControllerRef(&deployment)},
    62  		},
    63  		Spec: apps.ReplicaSetSpec{
    64  			Replicas: new(int32),
    65  			Template: *template,
    66  			Selector: &metav1.LabelSelector{MatchLabels: template.Labels},
    67  		},
    68  	}
    69  }
    70  
    71  func randomUID() types.UID {
    72  	return types.UID(strconv.FormatInt(rand.Int63(), 10))
    73  }
    74  
    75  // generateDeployment creates a deployment, with the input image as its template
    76  func generateDeployment(image string) apps.Deployment {
    77  	podLabels := map[string]string{"name": image}
    78  	terminationSec := int64(30)
    79  	enableServiceLinks := v1.DefaultEnableServiceLinks
    80  	return apps.Deployment{
    81  		ObjectMeta: metav1.ObjectMeta{
    82  			Name:        image,
    83  			Annotations: make(map[string]string),
    84  		},
    85  		Spec: apps.DeploymentSpec{
    86  			Replicas: func(i int32) *int32 { return &i }(1),
    87  			Selector: &metav1.LabelSelector{MatchLabels: podLabels},
    88  			Template: v1.PodTemplateSpec{
    89  				ObjectMeta: metav1.ObjectMeta{
    90  					Labels: podLabels,
    91  				},
    92  				Spec: v1.PodSpec{
    93  					Containers: []v1.Container{
    94  						{
    95  							Name:                   image,
    96  							Image:                  image,
    97  							ImagePullPolicy:        v1.PullAlways,
    98  							TerminationMessagePath: v1.TerminationMessagePathDefault,
    99  						},
   100  					},
   101  					DNSPolicy:                     v1.DNSClusterFirst,
   102  					TerminationGracePeriodSeconds: &terminationSec,
   103  					RestartPolicy:                 v1.RestartPolicyAlways,
   104  					SecurityContext:               &v1.PodSecurityContext{},
   105  					EnableServiceLinks:            &enableServiceLinks,
   106  				},
   107  			},
   108  		},
   109  	}
   110  }
   111  
   112  func generatePodTemplateSpec(name, nodeName string, annotations, labels map[string]string) v1.PodTemplateSpec {
   113  	return v1.PodTemplateSpec{
   114  		ObjectMeta: metav1.ObjectMeta{
   115  			Name:        name,
   116  			Annotations: annotations,
   117  			Labels:      labels,
   118  		},
   119  		Spec: v1.PodSpec{
   120  			NodeName: nodeName,
   121  		},
   122  	}
   123  }
   124  
   125  func TestEqualIgnoreHash(t *testing.T) {
   126  	tests := []struct {
   127  		Name           string
   128  		former, latter v1.PodTemplateSpec
   129  		expected       bool
   130  	}{
   131  		{
   132  			"Same spec, same labels",
   133  			generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
   134  			generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
   135  			true,
   136  		},
   137  		{
   138  			"Same spec, only pod-template-hash label value is different",
   139  			generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
   140  			generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-2", "something": "else"}),
   141  			true,
   142  		},
   143  		{
   144  			"Same spec, the former doesn't have pod-template-hash label",
   145  			generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{"something": "else"}),
   146  			generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-2", "something": "else"}),
   147  			true,
   148  		},
   149  		{
   150  			"Same spec, the label is different, the former doesn't have pod-template-hash label, same number of labels",
   151  			generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{"something": "else"}),
   152  			generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-2"}),
   153  			false,
   154  		},
   155  		{
   156  			"Same spec, the label is different, the latter doesn't have pod-template-hash label, same number of labels",
   157  			generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1"}),
   158  			generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{"something": "else"}),
   159  			false,
   160  		},
   161  		{
   162  			"Same spec, the label is different, and the pod-template-hash label value is the same",
   163  			generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1"}),
   164  			generatePodTemplateSpec("foo", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
   165  			false,
   166  		},
   167  		{
   168  			"Different spec, same labels",
   169  			generatePodTemplateSpec("foo", "foo-node", map[string]string{"former": "value"}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
   170  			generatePodTemplateSpec("foo", "foo-node", map[string]string{"latter": "value"}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
   171  			false,
   172  		},
   173  		{
   174  			"Different spec, different pod-template-hash label value",
   175  			generatePodTemplateSpec("foo-1", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-1", "something": "else"}),
   176  			generatePodTemplateSpec("foo-2", "foo-node", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-2", "something": "else"}),
   177  			false,
   178  		},
   179  		{
   180  			"Different spec, the former doesn't have pod-template-hash label",
   181  			generatePodTemplateSpec("foo-1", "foo-node-1", map[string]string{}, map[string]string{"something": "else"}),
   182  			generatePodTemplateSpec("foo-2", "foo-node-2", map[string]string{}, map[string]string{apps.DefaultDeploymentUniqueLabelKey: "value-2", "something": "else"}),
   183  			false,
   184  		},
   185  		{
   186  			"Different spec, different labels",
   187  			generatePodTemplateSpec("foo", "foo-node-1", map[string]string{}, map[string]string{"something": "else"}),
   188  			generatePodTemplateSpec("foo", "foo-node-2", map[string]string{}, map[string]string{"nothing": "else"}),
   189  			false,
   190  		},
   191  	}
   192  
   193  	for _, test := range tests {
   194  		t.Run(test.Name, func(t *testing.T) {
   195  			runTest := func(t1, t2 *v1.PodTemplateSpec, reversed bool) {
   196  				reverseString := ""
   197  				if reversed {
   198  					reverseString = " (reverse order)"
   199  				}
   200  				// Run
   201  				equal := EqualIgnoreHash(t1, t2)
   202  				if equal != test.expected {
   203  					t.Errorf("%q%s: expected %v", test.Name, reverseString, test.expected)
   204  					return
   205  				}
   206  				if t1.Labels == nil || t2.Labels == nil {
   207  					t.Errorf("%q%s: unexpected labels becomes nil", test.Name, reverseString)
   208  				}
   209  			}
   210  
   211  			runTest(&test.former, &test.latter, false)
   212  			// Test the same case in reverse order
   213  			runTest(&test.latter, &test.former, true)
   214  		})
   215  	}
   216  }
   217  
   218  func TestFindNewReplicaSet(t *testing.T) {
   219  	now := metav1.Now()
   220  	later := metav1.Time{Time: now.Add(time.Minute)}
   221  
   222  	deployment := generateDeployment("nginx")
   223  	newRS := generateRS(deployment)
   224  	newRS.Labels[apps.DefaultDeploymentUniqueLabelKey] = "hash"
   225  	newRS.CreationTimestamp = later
   226  
   227  	newRSDup := generateRS(deployment)
   228  	newRSDup.Labels[apps.DefaultDeploymentUniqueLabelKey] = "different-hash"
   229  	newRSDup.CreationTimestamp = now
   230  
   231  	oldDeployment := generateDeployment("nginx")
   232  	oldDeployment.Spec.Template.Spec.Containers[0].Name = "nginx-old-1"
   233  	oldRS := generateRS(oldDeployment)
   234  	oldRS.Status.FullyLabeledReplicas = *(oldRS.Spec.Replicas)
   235  
   236  	tests := []struct {
   237  		Name       string
   238  		deployment apps.Deployment
   239  		rsList     []*apps.ReplicaSet
   240  		expected   *apps.ReplicaSet
   241  	}{
   242  		{
   243  			Name:       "Get new ReplicaSet with the same template as Deployment spec but different pod-template-hash value",
   244  			deployment: deployment,
   245  			rsList:     []*apps.ReplicaSet{&newRS, &oldRS},
   246  			expected:   &newRS,
   247  		},
   248  		{
   249  			Name:       "Get the oldest new ReplicaSet when there are more than one ReplicaSet with the same template",
   250  			deployment: deployment,
   251  			rsList:     []*apps.ReplicaSet{&newRS, &oldRS, &newRSDup},
   252  			expected:   &newRSDup,
   253  		},
   254  		{
   255  			Name:       "Get nil new ReplicaSet",
   256  			deployment: deployment,
   257  			rsList:     []*apps.ReplicaSet{&oldRS},
   258  			expected:   nil,
   259  		},
   260  	}
   261  
   262  	for _, test := range tests {
   263  		t.Run(test.Name, func(t *testing.T) {
   264  			if rs := FindNewReplicaSet(&test.deployment, test.rsList); !reflect.DeepEqual(rs, test.expected) {
   265  				t.Errorf("In test case %q, expected %#v, got %#v", test.Name, test.expected, rs)
   266  			}
   267  		})
   268  	}
   269  }
   270  
   271  func TestFindOldReplicaSets(t *testing.T) {
   272  	now := metav1.Now()
   273  	later := metav1.Time{Time: now.Add(time.Minute)}
   274  	before := metav1.Time{Time: now.Add(-time.Minute)}
   275  
   276  	deployment := generateDeployment("nginx")
   277  	newRS := generateRS(deployment)
   278  	*(newRS.Spec.Replicas) = 1
   279  	newRS.Labels[apps.DefaultDeploymentUniqueLabelKey] = "hash"
   280  	newRS.CreationTimestamp = later
   281  
   282  	newRSDup := generateRS(deployment)
   283  	newRSDup.Labels[apps.DefaultDeploymentUniqueLabelKey] = "different-hash"
   284  	newRSDup.CreationTimestamp = now
   285  
   286  	oldDeployment := generateDeployment("nginx")
   287  	oldDeployment.Spec.Template.Spec.Containers[0].Name = "nginx-old-1"
   288  	oldRS := generateRS(oldDeployment)
   289  	oldRS.Status.FullyLabeledReplicas = *(oldRS.Spec.Replicas)
   290  	oldRS.CreationTimestamp = before
   291  
   292  	tests := []struct {
   293  		Name            string
   294  		deployment      apps.Deployment
   295  		rsList          []*apps.ReplicaSet
   296  		expected        []*apps.ReplicaSet
   297  		expectedRequire []*apps.ReplicaSet
   298  	}{
   299  		{
   300  			Name:            "Get old ReplicaSets",
   301  			deployment:      deployment,
   302  			rsList:          []*apps.ReplicaSet{&newRS, &oldRS},
   303  			expected:        []*apps.ReplicaSet{&oldRS},
   304  			expectedRequire: nil,
   305  		},
   306  		{
   307  			Name:            "Get old ReplicaSets with no new ReplicaSet",
   308  			deployment:      deployment,
   309  			rsList:          []*apps.ReplicaSet{&oldRS},
   310  			expected:        []*apps.ReplicaSet{&oldRS},
   311  			expectedRequire: nil,
   312  		},
   313  		{
   314  			Name:            "Get old ReplicaSets with two new ReplicaSets, only the oldest new ReplicaSet is seen as new ReplicaSet",
   315  			deployment:      deployment,
   316  			rsList:          []*apps.ReplicaSet{&oldRS, &newRS, &newRSDup},
   317  			expected:        []*apps.ReplicaSet{&oldRS, &newRS},
   318  			expectedRequire: []*apps.ReplicaSet{&newRS},
   319  		},
   320  		{
   321  			Name:            "Get empty old ReplicaSets",
   322  			deployment:      deployment,
   323  			rsList:          []*apps.ReplicaSet{&newRS},
   324  			expected:        nil,
   325  			expectedRequire: nil,
   326  		},
   327  	}
   328  
   329  	for _, test := range tests {
   330  		t.Run(test.Name, func(t *testing.T) {
   331  			requireRS, allRS := FindOldReplicaSets(&test.deployment, test.rsList)
   332  			sort.Sort(controller.ReplicaSetsByCreationTimestamp(allRS))
   333  			sort.Sort(controller.ReplicaSetsByCreationTimestamp(test.expected))
   334  			if !reflect.DeepEqual(allRS, test.expected) {
   335  				t.Errorf("In test case %q, expected %#v, got %#v", test.Name, test.expected, allRS)
   336  			}
   337  			// RSs are getting filtered correctly by rs.spec.replicas
   338  			if !reflect.DeepEqual(requireRS, test.expectedRequire) {
   339  				t.Errorf("In test case %q, expected %#v, got %#v", test.Name, test.expectedRequire, requireRS)
   340  			}
   341  		})
   342  	}
   343  }
   344  
   345  func TestGetReplicaCountForReplicaSets(t *testing.T) {
   346  	rs1 := generateRS(generateDeployment("foo"))
   347  	*(rs1.Spec.Replicas) = 1
   348  	rs1.Status.Replicas = 2
   349  	rs2 := generateRS(generateDeployment("bar"))
   350  	*(rs2.Spec.Replicas) = 2
   351  	rs2.Status.Replicas = 3
   352  
   353  	tests := []struct {
   354  		Name           string
   355  		sets           []*apps.ReplicaSet
   356  		expectedCount  int32
   357  		expectedActual int32
   358  	}{
   359  		{
   360  			"1:2 Replicas",
   361  			[]*apps.ReplicaSet{&rs1},
   362  			1,
   363  			2,
   364  		},
   365  		{
   366  			"3:5 Replicas",
   367  			[]*apps.ReplicaSet{&rs1, &rs2},
   368  			3,
   369  			5,
   370  		},
   371  	}
   372  
   373  	for _, test := range tests {
   374  		t.Run(test.Name, func(t *testing.T) {
   375  			rs := GetReplicaCountForReplicaSets(test.sets)
   376  			if rs != test.expectedCount {
   377  				t.Errorf("In test case %s, expectedCount %+v, got %+v", test.Name, test.expectedCount, rs)
   378  			}
   379  			rs = GetActualReplicaCountForReplicaSets(test.sets)
   380  			if rs != test.expectedActual {
   381  				t.Errorf("In test case %s, expectedActual %+v, got %+v", test.Name, test.expectedActual, rs)
   382  			}
   383  		})
   384  	}
   385  }
   386  
   387  func TestResolveFenceposts(t *testing.T) {
   388  	tests := []struct {
   389  		maxSurge          *string
   390  		maxUnavailable    *string
   391  		desired           int32
   392  		expectSurge       int32
   393  		expectUnavailable int32
   394  		expectError       bool
   395  	}{
   396  		{
   397  			maxSurge:          ptr.To("0%"),
   398  			maxUnavailable:    ptr.To("0%"),
   399  			desired:           0,
   400  			expectSurge:       0,
   401  			expectUnavailable: 1,
   402  			expectError:       false,
   403  		},
   404  		{
   405  			maxSurge:          ptr.To("39%"),
   406  			maxUnavailable:    ptr.To("39%"),
   407  			desired:           10,
   408  			expectSurge:       4,
   409  			expectUnavailable: 3,
   410  			expectError:       false,
   411  		},
   412  		{
   413  			maxSurge:          ptr.To("oops"),
   414  			maxUnavailable:    ptr.To("39%"),
   415  			desired:           10,
   416  			expectSurge:       0,
   417  			expectUnavailable: 0,
   418  			expectError:       true,
   419  		},
   420  		{
   421  			maxSurge:          ptr.To("55%"),
   422  			maxUnavailable:    ptr.To("urg"),
   423  			desired:           10,
   424  			expectSurge:       0,
   425  			expectUnavailable: 0,
   426  			expectError:       true,
   427  		},
   428  		{
   429  			maxSurge:          nil,
   430  			maxUnavailable:    ptr.To("39%"),
   431  			desired:           10,
   432  			expectSurge:       0,
   433  			expectUnavailable: 3,
   434  			expectError:       false,
   435  		},
   436  		{
   437  			maxSurge:          ptr.To("39%"),
   438  			maxUnavailable:    nil,
   439  			desired:           10,
   440  			expectSurge:       4,
   441  			expectUnavailable: 0,
   442  			expectError:       false,
   443  		},
   444  		{
   445  			maxSurge:          nil,
   446  			maxUnavailable:    nil,
   447  			desired:           10,
   448  			expectSurge:       0,
   449  			expectUnavailable: 1,
   450  			expectError:       false,
   451  		},
   452  	}
   453  
   454  	for num, test := range tests {
   455  		t.Run(fmt.Sprintf("%d", num), func(t *testing.T) {
   456  			var maxSurge, maxUnavail *intstr.IntOrString
   457  			if test.maxSurge != nil {
   458  				maxSurge = ptr.To(intstr.FromString(*test.maxSurge))
   459  			}
   460  			if test.maxUnavailable != nil {
   461  				maxUnavail = ptr.To(intstr.FromString(*test.maxUnavailable))
   462  			}
   463  			surge, unavail, err := ResolveFenceposts(maxSurge, maxUnavail, test.desired)
   464  			if err != nil && !test.expectError {
   465  				t.Errorf("unexpected error %v", err)
   466  			}
   467  			if err == nil && test.expectError {
   468  				t.Error("expected error")
   469  			}
   470  			if surge != test.expectSurge || unavail != test.expectUnavailable {
   471  				t.Errorf("#%v got %v:%v, want %v:%v", num, surge, unavail, test.expectSurge, test.expectUnavailable)
   472  			}
   473  		})
   474  	}
   475  }
   476  
   477  func TestNewRSNewReplicas(t *testing.T) {
   478  	tests := []struct {
   479  		Name          string
   480  		strategyType  apps.DeploymentStrategyType
   481  		depReplicas   int32
   482  		newRSReplicas int32
   483  		maxSurge      int32
   484  		expected      int32
   485  	}{
   486  		{
   487  			"can not scale up - to newRSReplicas",
   488  			apps.RollingUpdateDeploymentStrategyType,
   489  			1, 5, 1, 5,
   490  		},
   491  		{
   492  			"scale up - to depReplicas",
   493  			apps.RollingUpdateDeploymentStrategyType,
   494  			6, 2, 10, 6,
   495  		},
   496  		{
   497  			"recreate - to depReplicas",
   498  			apps.RecreateDeploymentStrategyType,
   499  			3, 1, 1, 3,
   500  		},
   501  	}
   502  	newDeployment := generateDeployment("nginx")
   503  	newRC := generateRS(newDeployment)
   504  	rs5 := generateRS(newDeployment)
   505  	*(rs5.Spec.Replicas) = 5
   506  
   507  	for _, test := range tests {
   508  		t.Run(test.Name, func(t *testing.T) {
   509  			*(newDeployment.Spec.Replicas) = test.depReplicas
   510  			newDeployment.Spec.Strategy = apps.DeploymentStrategy{Type: test.strategyType}
   511  			newDeployment.Spec.Strategy.RollingUpdate = &apps.RollingUpdateDeployment{
   512  				MaxUnavailable: ptr.To(intstr.FromInt32(1)),
   513  				MaxSurge:       ptr.To(intstr.FromInt32(test.maxSurge)),
   514  			}
   515  			*(newRC.Spec.Replicas) = test.newRSReplicas
   516  			rs, err := NewRSNewReplicas(&newDeployment, []*apps.ReplicaSet{&rs5}, &newRC)
   517  			if err != nil {
   518  				t.Errorf("In test case %s, got unexpected error %v", test.Name, err)
   519  			}
   520  			if rs != test.expected {
   521  				t.Errorf("In test case %s, expected %+v, got %+v", test.Name, test.expected, rs)
   522  			}
   523  		})
   524  	}
   525  }
   526  
   527  var (
   528  	condProgressing = func() apps.DeploymentCondition {
   529  		return apps.DeploymentCondition{
   530  			Type:   apps.DeploymentProgressing,
   531  			Status: v1.ConditionFalse,
   532  			Reason: "ForSomeReason",
   533  		}
   534  	}
   535  
   536  	condProgressing2 = func() apps.DeploymentCondition {
   537  		return apps.DeploymentCondition{
   538  			Type:   apps.DeploymentProgressing,
   539  			Status: v1.ConditionTrue,
   540  			Reason: "BecauseItIs",
   541  		}
   542  	}
   543  
   544  	condAvailable = func() apps.DeploymentCondition {
   545  		return apps.DeploymentCondition{
   546  			Type:   apps.DeploymentAvailable,
   547  			Status: v1.ConditionTrue,
   548  			Reason: "AwesomeController",
   549  		}
   550  	}
   551  
   552  	status = func() *apps.DeploymentStatus {
   553  		return &apps.DeploymentStatus{
   554  			Conditions: []apps.DeploymentCondition{condProgressing(), condAvailable()},
   555  		}
   556  	}
   557  )
   558  
   559  func TestGetCondition(t *testing.T) {
   560  	exampleStatus := status()
   561  
   562  	tests := []struct {
   563  		name string
   564  
   565  		status   apps.DeploymentStatus
   566  		condType apps.DeploymentConditionType
   567  
   568  		expected bool
   569  	}{
   570  		{
   571  			name: "condition exists",
   572  
   573  			status:   *exampleStatus,
   574  			condType: apps.DeploymentAvailable,
   575  
   576  			expected: true,
   577  		},
   578  		{
   579  			name: "condition does not exist",
   580  
   581  			status:   *exampleStatus,
   582  			condType: apps.DeploymentReplicaFailure,
   583  
   584  			expected: false,
   585  		},
   586  	}
   587  
   588  	for _, test := range tests {
   589  		t.Run(test.name, func(t *testing.T) {
   590  			cond := GetDeploymentCondition(test.status, test.condType)
   591  			exists := cond != nil
   592  			if exists != test.expected {
   593  				t.Errorf("%s: expected condition to exist: %t, got: %t", test.name, test.expected, exists)
   594  			}
   595  		})
   596  	}
   597  }
   598  
   599  func TestSetCondition(t *testing.T) {
   600  	tests := []struct {
   601  		name string
   602  
   603  		status *apps.DeploymentStatus
   604  		cond   apps.DeploymentCondition
   605  
   606  		expectedStatus *apps.DeploymentStatus
   607  	}{
   608  		{
   609  			name: "set for the first time",
   610  
   611  			status: &apps.DeploymentStatus{},
   612  			cond:   condAvailable(),
   613  
   614  			expectedStatus: &apps.DeploymentStatus{Conditions: []apps.DeploymentCondition{condAvailable()}},
   615  		},
   616  		{
   617  			name: "simple set",
   618  
   619  			status: &apps.DeploymentStatus{Conditions: []apps.DeploymentCondition{condProgressing()}},
   620  			cond:   condAvailable(),
   621  
   622  			expectedStatus: status(),
   623  		},
   624  		{
   625  			name: "overwrite",
   626  
   627  			status: &apps.DeploymentStatus{Conditions: []apps.DeploymentCondition{condProgressing()}},
   628  			cond:   condProgressing2(),
   629  
   630  			expectedStatus: &apps.DeploymentStatus{Conditions: []apps.DeploymentCondition{condProgressing2()}},
   631  		},
   632  	}
   633  
   634  	for _, test := range tests {
   635  		t.Run(test.name, func(t *testing.T) {
   636  			SetDeploymentCondition(test.status, test.cond)
   637  			if !reflect.DeepEqual(test.status, test.expectedStatus) {
   638  				t.Errorf("%s: expected status: %v, got: %v", test.name, test.expectedStatus, test.status)
   639  			}
   640  		})
   641  	}
   642  }
   643  
   644  func TestRemoveCondition(t *testing.T) {
   645  	tests := []struct {
   646  		name string
   647  
   648  		status   *apps.DeploymentStatus
   649  		condType apps.DeploymentConditionType
   650  
   651  		expectedStatus *apps.DeploymentStatus
   652  	}{
   653  		{
   654  			name: "remove from empty status",
   655  
   656  			status:   &apps.DeploymentStatus{},
   657  			condType: apps.DeploymentProgressing,
   658  
   659  			expectedStatus: &apps.DeploymentStatus{},
   660  		},
   661  		{
   662  			name: "simple remove",
   663  
   664  			status:   &apps.DeploymentStatus{Conditions: []apps.DeploymentCondition{condProgressing()}},
   665  			condType: apps.DeploymentProgressing,
   666  
   667  			expectedStatus: &apps.DeploymentStatus{},
   668  		},
   669  		{
   670  			name: "doesn't remove anything",
   671  
   672  			status:   status(),
   673  			condType: apps.DeploymentReplicaFailure,
   674  
   675  			expectedStatus: status(),
   676  		},
   677  	}
   678  
   679  	for _, test := range tests {
   680  		t.Run(test.name, func(t *testing.T) {
   681  			RemoveDeploymentCondition(test.status, test.condType)
   682  			if !reflect.DeepEqual(test.status, test.expectedStatus) {
   683  				t.Errorf("%s: expected status: %v, got: %v", test.name, test.expectedStatus, test.status)
   684  			}
   685  		})
   686  	}
   687  }
   688  
   689  func TestDeploymentComplete(t *testing.T) {
   690  	deployment := func(desired, current, updated, available, maxUnavailable, maxSurge int32) *apps.Deployment {
   691  		return &apps.Deployment{
   692  			Spec: apps.DeploymentSpec{
   693  				Replicas: &desired,
   694  				Strategy: apps.DeploymentStrategy{
   695  					RollingUpdate: &apps.RollingUpdateDeployment{
   696  						MaxUnavailable: ptr.To(intstr.FromInt32(maxUnavailable)),
   697  						MaxSurge:       ptr.To(intstr.FromInt32(maxSurge)),
   698  					},
   699  					Type: apps.RollingUpdateDeploymentStrategyType,
   700  				},
   701  			},
   702  			Status: apps.DeploymentStatus{
   703  				Replicas:          current,
   704  				UpdatedReplicas:   updated,
   705  				AvailableReplicas: available,
   706  			},
   707  		}
   708  	}
   709  
   710  	tests := []struct {
   711  		name string
   712  
   713  		d *apps.Deployment
   714  
   715  		expected bool
   716  	}{
   717  		{
   718  			name: "not complete: min but not all pods become available",
   719  
   720  			d:        deployment(5, 5, 5, 4, 1, 0),
   721  			expected: false,
   722  		},
   723  		{
   724  			name: "not complete: min availability is not honored",
   725  
   726  			d:        deployment(5, 5, 5, 3, 1, 0),
   727  			expected: false,
   728  		},
   729  		{
   730  			name: "complete",
   731  
   732  			d:        deployment(5, 5, 5, 5, 0, 0),
   733  			expected: true,
   734  		},
   735  		{
   736  			name: "not complete: all pods are available but not updated",
   737  
   738  			d:        deployment(5, 5, 4, 5, 0, 0),
   739  			expected: false,
   740  		},
   741  		{
   742  			name: "not complete: still running old pods",
   743  
   744  			// old replica set: spec.replicas=1, status.replicas=1, status.availableReplicas=1
   745  			// new replica set: spec.replicas=1, status.replicas=1, status.availableReplicas=0
   746  			d:        deployment(1, 2, 1, 1, 0, 1),
   747  			expected: false,
   748  		},
   749  		{
   750  			name: "not complete: one replica deployment never comes up",
   751  
   752  			d:        deployment(1, 1, 1, 0, 1, 1),
   753  			expected: false,
   754  		},
   755  	}
   756  
   757  	for _, test := range tests {
   758  		t.Run(test.name, func(t *testing.T) {
   759  			if got, exp := DeploymentComplete(test.d, &test.d.Status), test.expected; got != exp {
   760  				t.Errorf("expected complete: %t, got: %t", exp, got)
   761  			}
   762  		})
   763  	}
   764  }
   765  
   766  func TestDeploymentProgressing(t *testing.T) {
   767  	deployment := func(current, updated, ready, available int32) *apps.Deployment {
   768  		return &apps.Deployment{
   769  			Status: apps.DeploymentStatus{
   770  				Replicas:          current,
   771  				UpdatedReplicas:   updated,
   772  				ReadyReplicas:     ready,
   773  				AvailableReplicas: available,
   774  			},
   775  		}
   776  	}
   777  	newStatus := func(current, updated, ready, available int32) apps.DeploymentStatus {
   778  		return apps.DeploymentStatus{
   779  			Replicas:          current,
   780  			UpdatedReplicas:   updated,
   781  			ReadyReplicas:     ready,
   782  			AvailableReplicas: available,
   783  		}
   784  	}
   785  
   786  	tests := []struct {
   787  		name string
   788  
   789  		d         *apps.Deployment
   790  		newStatus apps.DeploymentStatus
   791  
   792  		expected bool
   793  	}{
   794  		{
   795  			name: "progressing: updated pods",
   796  
   797  			d:         deployment(10, 4, 4, 4),
   798  			newStatus: newStatus(10, 6, 4, 4),
   799  
   800  			expected: true,
   801  		},
   802  		{
   803  			name: "not progressing",
   804  
   805  			d:         deployment(10, 4, 4, 4),
   806  			newStatus: newStatus(10, 4, 4, 4),
   807  
   808  			expected: false,
   809  		},
   810  		{
   811  			name: "progressing: old pods removed",
   812  
   813  			d:         deployment(10, 4, 6, 6),
   814  			newStatus: newStatus(8, 4, 6, 6),
   815  
   816  			expected: true,
   817  		},
   818  		{
   819  			name: "not progressing: less new pods",
   820  
   821  			d:         deployment(10, 7, 3, 3),
   822  			newStatus: newStatus(10, 6, 3, 3),
   823  
   824  			expected: false,
   825  		},
   826  		{
   827  			name: "progressing: less overall but more new pods",
   828  
   829  			d:         deployment(10, 4, 7, 7),
   830  			newStatus: newStatus(8, 8, 5, 5),
   831  
   832  			expected: true,
   833  		},
   834  		{
   835  			name: "progressing: more ready pods",
   836  
   837  			d:         deployment(10, 10, 9, 8),
   838  			newStatus: newStatus(10, 10, 10, 8),
   839  
   840  			expected: true,
   841  		},
   842  		{
   843  			name: "progressing: more available pods",
   844  
   845  			d:         deployment(10, 10, 10, 9),
   846  			newStatus: newStatus(10, 10, 10, 10),
   847  
   848  			expected: true,
   849  		},
   850  	}
   851  
   852  	for _, test := range tests {
   853  		t.Run(test.name, func(t *testing.T) {
   854  			if got, exp := DeploymentProgressing(test.d, &test.newStatus), test.expected; got != exp {
   855  				t.Errorf("expected progressing: %t, got: %t", exp, got)
   856  			}
   857  		})
   858  	}
   859  }
   860  
   861  func TestDeploymentTimedOut(t *testing.T) {
   862  	var (
   863  		null     *int32
   864  		ten      = int32(10)
   865  		infinite = int32(math.MaxInt32)
   866  	)
   867  
   868  	timeFn := func(min, sec int) time.Time {
   869  		return time.Date(2016, 1, 1, 0, min, sec, 0, time.UTC)
   870  	}
   871  	deployment := func(condType apps.DeploymentConditionType, status v1.ConditionStatus, reason string, pds *int32, from time.Time) apps.Deployment {
   872  		return apps.Deployment{
   873  			Spec: apps.DeploymentSpec{
   874  				ProgressDeadlineSeconds: pds,
   875  			},
   876  			Status: apps.DeploymentStatus{
   877  				Conditions: []apps.DeploymentCondition{
   878  					{
   879  						Type:           condType,
   880  						Status:         status,
   881  						Reason:         reason,
   882  						LastUpdateTime: metav1.Time{Time: from},
   883  					},
   884  				},
   885  			},
   886  		}
   887  	}
   888  
   889  	tests := []struct {
   890  		name string
   891  
   892  		d     apps.Deployment
   893  		nowFn func() time.Time
   894  
   895  		expected bool
   896  	}{
   897  		{
   898  			name: "nil progressDeadlineSeconds specified - no timeout",
   899  
   900  			d:        deployment(apps.DeploymentProgressing, v1.ConditionTrue, "", null, timeFn(1, 9)),
   901  			nowFn:    func() time.Time { return timeFn(1, 20) },
   902  			expected: false,
   903  		},
   904  		{
   905  			name: "infinite progressDeadlineSeconds specified - no timeout",
   906  
   907  			d:        deployment(apps.DeploymentProgressing, v1.ConditionTrue, "", &infinite, timeFn(1, 9)),
   908  			nowFn:    func() time.Time { return timeFn(1, 20) },
   909  			expected: false,
   910  		},
   911  		{
   912  			name: "progressDeadlineSeconds: 10s, now - started => 00:01:20 - 00:01:09 => 11s",
   913  
   914  			d:        deployment(apps.DeploymentProgressing, v1.ConditionTrue, "", &ten, timeFn(1, 9)),
   915  			nowFn:    func() time.Time { return timeFn(1, 20) },
   916  			expected: true,
   917  		},
   918  		{
   919  			name: "progressDeadlineSeconds: 10s, now - started => 00:01:20 - 00:01:11 => 9s",
   920  
   921  			d:        deployment(apps.DeploymentProgressing, v1.ConditionTrue, "", &ten, timeFn(1, 11)),
   922  			nowFn:    func() time.Time { return timeFn(1, 20) },
   923  			expected: false,
   924  		},
   925  		{
   926  			name: "previous status was a complete deployment",
   927  
   928  			d:        deployment(apps.DeploymentProgressing, v1.ConditionTrue, NewRSAvailableReason, nil, time.Time{}),
   929  			expected: false,
   930  		},
   931  	}
   932  
   933  	for _, test := range tests {
   934  		t.Run(test.name, func(t *testing.T) {
   935  			nowFn = test.nowFn
   936  			_, ctx := ktesting.NewTestContext(t)
   937  			if got, exp := DeploymentTimedOut(ctx, &test.d, &test.d.Status), test.expected; got != exp {
   938  				t.Errorf("expected timeout: %t, got: %t", exp, got)
   939  			}
   940  		})
   941  	}
   942  }
   943  
   944  func TestMaxUnavailable(t *testing.T) {
   945  	deployment := func(replicas int32, maxUnavailable intstr.IntOrString) apps.Deployment {
   946  		return apps.Deployment{
   947  			Spec: apps.DeploymentSpec{
   948  				Replicas: func(i int32) *int32 { return &i }(replicas),
   949  				Strategy: apps.DeploymentStrategy{
   950  					RollingUpdate: &apps.RollingUpdateDeployment{
   951  						MaxSurge:       ptr.To(intstr.FromInt32(1)),
   952  						MaxUnavailable: &maxUnavailable,
   953  					},
   954  					Type: apps.RollingUpdateDeploymentStrategyType,
   955  				},
   956  			},
   957  		}
   958  	}
   959  	tests := []struct {
   960  		name       string
   961  		deployment apps.Deployment
   962  		expected   int32
   963  	}{
   964  		{
   965  			name:       "maxUnavailable less than replicas",
   966  			deployment: deployment(10, intstr.FromInt32(5)),
   967  			expected:   int32(5),
   968  		},
   969  		{
   970  			name:       "maxUnavailable equal replicas",
   971  			deployment: deployment(10, intstr.FromInt32(10)),
   972  			expected:   int32(10),
   973  		},
   974  		{
   975  			name:       "maxUnavailable greater than replicas",
   976  			deployment: deployment(5, intstr.FromInt32(10)),
   977  			expected:   int32(5),
   978  		},
   979  		{
   980  			name:       "maxUnavailable with replicas is 0",
   981  			deployment: deployment(0, intstr.FromInt32(10)),
   982  			expected:   int32(0),
   983  		},
   984  		{
   985  			name: "maxUnavailable with Recreate deployment strategy",
   986  			deployment: apps.Deployment{
   987  				Spec: apps.DeploymentSpec{
   988  					Strategy: apps.DeploymentStrategy{
   989  						Type: apps.RecreateDeploymentStrategyType,
   990  					},
   991  				},
   992  			},
   993  			expected: int32(0),
   994  		},
   995  		{
   996  			name:       "maxUnavailable less than replicas with percents",
   997  			deployment: deployment(10, intstr.FromString("50%")),
   998  			expected:   int32(5),
   999  		},
  1000  		{
  1001  			name:       "maxUnavailable equal replicas with percents",
  1002  			deployment: deployment(10, intstr.FromString("100%")),
  1003  			expected:   int32(10),
  1004  		},
  1005  		{
  1006  			name:       "maxUnavailable greater than replicas with percents",
  1007  			deployment: deployment(5, intstr.FromString("100%")),
  1008  			expected:   int32(5),
  1009  		},
  1010  	}
  1011  
  1012  	for _, test := range tests {
  1013  		t.Log(test.name)
  1014  		t.Run(test.name, func(t *testing.T) {
  1015  			maxUnavailable := MaxUnavailable(test.deployment)
  1016  			if test.expected != maxUnavailable {
  1017  				t.Fatalf("expected:%v, got:%v", test.expected, maxUnavailable)
  1018  			}
  1019  		})
  1020  	}
  1021  }
  1022  
  1023  // Set of simple tests for annotation related util functions
  1024  func TestAnnotationUtils(t *testing.T) {
  1025  
  1026  	//Setup
  1027  	tDeployment := generateDeployment("nginx")
  1028  	tRS := generateRS(tDeployment)
  1029  	tDeployment.Annotations[RevisionAnnotation] = "1"
  1030  
  1031  	//Test Case 1: Check if anotations are copied properly from deployment to RS
  1032  	t.Run("SetNewReplicaSetAnnotations", func(t *testing.T) {
  1033  		_, ctx := ktesting.NewTestContext(t)
  1034  
  1035  		//Try to set the increment revision from 11 through 20
  1036  		for i := 10; i < 20; i++ {
  1037  
  1038  			nextRevision := fmt.Sprintf("%d", i+1)
  1039  			SetNewReplicaSetAnnotations(ctx, &tDeployment, &tRS, nextRevision, true, 5)
  1040  			//Now the ReplicaSets Revision Annotation should be i+1
  1041  
  1042  			if i >= 12 {
  1043  				expectedHistoryAnnotation := fmt.Sprintf("%d,%d", i-1, i)
  1044  				if tRS.Annotations[RevisionHistoryAnnotation] != expectedHistoryAnnotation {
  1045  					t.Errorf("Revision History Expected=%s Obtained=%s", expectedHistoryAnnotation, tRS.Annotations[RevisionHistoryAnnotation])
  1046  				}
  1047  			}
  1048  			if tRS.Annotations[RevisionAnnotation] != nextRevision {
  1049  				t.Errorf("Revision Expected=%s Obtained=%s", nextRevision, tRS.Annotations[RevisionAnnotation])
  1050  			}
  1051  		}
  1052  	})
  1053  
  1054  	//Test Case 2:  Check if annotations are set properly
  1055  	t.Run("SetReplicasAnnotations", func(t *testing.T) {
  1056  		updated := SetReplicasAnnotations(&tRS, 10, 11)
  1057  		if !updated {
  1058  			t.Errorf("SetReplicasAnnotations() failed")
  1059  		}
  1060  		value, ok := tRS.Annotations[DesiredReplicasAnnotation]
  1061  		if !ok {
  1062  			t.Errorf("SetReplicasAnnotations did not set DesiredReplicasAnnotation")
  1063  		}
  1064  		if value != "10" {
  1065  			t.Errorf("SetReplicasAnnotations did not set DesiredReplicasAnnotation correctly value=%s", value)
  1066  		}
  1067  		if value, ok = tRS.Annotations[MaxReplicasAnnotation]; !ok {
  1068  			t.Errorf("SetReplicasAnnotations did not set DesiredReplicasAnnotation")
  1069  		}
  1070  		if value != "11" {
  1071  			t.Errorf("SetReplicasAnnotations did not set MaxReplicasAnnotation correctly value=%s", value)
  1072  		}
  1073  	})
  1074  
  1075  	//Test Case 3:  Check if annotations reflect deployments state
  1076  	tRS.Annotations[DesiredReplicasAnnotation] = "1"
  1077  	tRS.Status.AvailableReplicas = 1
  1078  	tRS.Spec.Replicas = new(int32)
  1079  	*tRS.Spec.Replicas = 1
  1080  
  1081  	t.Run("IsSaturated", func(t *testing.T) {
  1082  		saturated := IsSaturated(&tDeployment, &tRS)
  1083  		if !saturated {
  1084  			t.Errorf("SetReplicasAnnotations Expected=true Obtained=false")
  1085  		}
  1086  	})
  1087  	//Tear Down
  1088  }
  1089  
  1090  func TestReplicasAnnotationsNeedUpdate(t *testing.T) {
  1091  
  1092  	desiredReplicas := fmt.Sprintf("%d", int32(10))
  1093  	maxReplicas := fmt.Sprintf("%d", int32(20))
  1094  
  1095  	tests := []struct {
  1096  		name       string
  1097  		replicaSet *apps.ReplicaSet
  1098  		expected   bool
  1099  	}{
  1100  		{
  1101  			name: "test Annotations nil",
  1102  			replicaSet: &apps.ReplicaSet{
  1103  				ObjectMeta: metav1.ObjectMeta{Name: "hello", Namespace: "test"},
  1104  				Spec: apps.ReplicaSetSpec{
  1105  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  1106  				},
  1107  			},
  1108  			expected: true,
  1109  		},
  1110  		{
  1111  			name: "test desiredReplicas update",
  1112  			replicaSet: &apps.ReplicaSet{
  1113  				ObjectMeta: metav1.ObjectMeta{
  1114  					Name:        "hello",
  1115  					Namespace:   "test",
  1116  					Annotations: map[string]string{DesiredReplicasAnnotation: "8", MaxReplicasAnnotation: maxReplicas},
  1117  				},
  1118  				Spec: apps.ReplicaSetSpec{
  1119  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  1120  				},
  1121  			},
  1122  			expected: true,
  1123  		},
  1124  		{
  1125  			name: "test maxReplicas update",
  1126  			replicaSet: &apps.ReplicaSet{
  1127  				ObjectMeta: metav1.ObjectMeta{
  1128  					Name:        "hello",
  1129  					Namespace:   "test",
  1130  					Annotations: map[string]string{DesiredReplicasAnnotation: desiredReplicas, MaxReplicasAnnotation: "16"},
  1131  				},
  1132  				Spec: apps.ReplicaSetSpec{
  1133  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  1134  				},
  1135  			},
  1136  			expected: true,
  1137  		},
  1138  		{
  1139  			name: "test needn't update",
  1140  			replicaSet: &apps.ReplicaSet{
  1141  				ObjectMeta: metav1.ObjectMeta{
  1142  					Name:        "hello",
  1143  					Namespace:   "test",
  1144  					Annotations: map[string]string{DesiredReplicasAnnotation: desiredReplicas, MaxReplicasAnnotation: maxReplicas},
  1145  				},
  1146  				Spec: apps.ReplicaSetSpec{
  1147  					Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  1148  				},
  1149  			},
  1150  			expected: false,
  1151  		},
  1152  	}
  1153  
  1154  	for i, test := range tests {
  1155  		t.Run(test.name, func(t *testing.T) {
  1156  			result := ReplicasAnnotationsNeedUpdate(test.replicaSet, 10, 20)
  1157  			if result != test.expected {
  1158  				t.Errorf("case[%d]:%s Expected %v, Got: %v", i, test.name, test.expected, result)
  1159  			}
  1160  		})
  1161  	}
  1162  }
  1163  
  1164  func TestGetDeploymentsForReplicaSet(t *testing.T) {
  1165  	fakeInformerFactory := informers.NewSharedInformerFactory(&fake.Clientset{}, 0*time.Second)
  1166  	var deployments []*apps.Deployment
  1167  	for i := 0; i < 3; i++ {
  1168  		deployment := &apps.Deployment{
  1169  			ObjectMeta: metav1.ObjectMeta{
  1170  				Name:      fmt.Sprintf("deployment-%d", i),
  1171  				Namespace: "test",
  1172  			},
  1173  			Spec: apps.DeploymentSpec{
  1174  				Selector: &metav1.LabelSelector{
  1175  					MatchLabels: map[string]string{
  1176  						"app": fmt.Sprintf("test-%d", i),
  1177  					},
  1178  				},
  1179  			},
  1180  		}
  1181  		deployments = append(deployments, deployment)
  1182  		fakeInformerFactory.Apps().V1().Deployments().Informer().GetStore().Add(deployment)
  1183  	}
  1184  	var rss []*apps.ReplicaSet
  1185  	for i := 0; i < 5; i++ {
  1186  		rs := &apps.ReplicaSet{
  1187  			ObjectMeta: metav1.ObjectMeta{
  1188  				Namespace: "test",
  1189  				Name:      fmt.Sprintf("test-replicaSet-%d", i),
  1190  				Labels: map[string]string{
  1191  					"app":   fmt.Sprintf("test-%d", i),
  1192  					"label": fmt.Sprintf("label-%d", i),
  1193  				},
  1194  			},
  1195  		}
  1196  		rss = append(rss, rs)
  1197  	}
  1198  	tests := []struct {
  1199  		name   string
  1200  		rs     *apps.ReplicaSet
  1201  		err    error
  1202  		expect []*apps.Deployment
  1203  	}{
  1204  		{
  1205  			name:   "GetDeploymentsForReplicaSet for rs-0",
  1206  			rs:     rss[0],
  1207  			expect: []*apps.Deployment{deployments[0]},
  1208  		},
  1209  		{
  1210  			name:   "GetDeploymentsForReplicaSet for rs-1",
  1211  			rs:     rss[1],
  1212  			expect: []*apps.Deployment{deployments[1]},
  1213  		},
  1214  		{
  1215  			name:   "GetDeploymentsForReplicaSet for rs-2",
  1216  			rs:     rss[2],
  1217  			expect: []*apps.Deployment{deployments[2]},
  1218  		},
  1219  		{
  1220  			name: "GetDeploymentsForReplicaSet for rs-3",
  1221  			rs:   rss[3],
  1222  			err:  fmt.Errorf("could not find deployments set for ReplicaSet %s in namespace %s with labels: %v", rss[3].Name, rss[3].Namespace, rss[3].Labels),
  1223  		},
  1224  		{
  1225  			name: "GetDeploymentsForReplicaSet for rs-4",
  1226  			rs:   rss[4],
  1227  			err:  fmt.Errorf("could not find deployments set for ReplicaSet %s in namespace %s with labels: %v", rss[4].Name, rss[4].Namespace, rss[4].Labels),
  1228  		},
  1229  	}
  1230  	for _, test := range tests {
  1231  		t.Run(test.name, func(t *testing.T) {
  1232  			get, err := GetDeploymentsForReplicaSet(fakeInformerFactory.Apps().V1().Deployments().Lister(), test.rs)
  1233  			if err != nil {
  1234  				if err.Error() != test.err.Error() {
  1235  					t.Errorf("Error from GetDeploymentsForReplicaSet: %v", err)
  1236  				}
  1237  			} else if !reflect.DeepEqual(get, test.expect) {
  1238  				t.Errorf("Expect deployments %v, but got %v", test.expect, get)
  1239  			}
  1240  		})
  1241  	}
  1242  
  1243  }
  1244  
  1245  func TestMinAvailable(t *testing.T) {
  1246  	maxSurge := ptr.To(intstr.FromInt32(1))
  1247  	deployment := func(replicas int32, maxUnavailable intstr.IntOrString) *apps.Deployment {
  1248  		return &apps.Deployment{
  1249  			Spec: apps.DeploymentSpec{
  1250  				Replicas: ptr.To(replicas),
  1251  				Strategy: apps.DeploymentStrategy{
  1252  					RollingUpdate: &apps.RollingUpdateDeployment{
  1253  						MaxSurge:       maxSurge,
  1254  						MaxUnavailable: &maxUnavailable,
  1255  					},
  1256  					Type: apps.RollingUpdateDeploymentStrategyType,
  1257  				},
  1258  			},
  1259  		}
  1260  	}
  1261  	tests := []struct {
  1262  		name       string
  1263  		deployment *apps.Deployment
  1264  		expected   int32
  1265  	}{
  1266  		{
  1267  			name:       "replicas greater than maxUnavailable",
  1268  			deployment: deployment(10, intstr.FromInt32(5)),
  1269  			expected:   5,
  1270  		},
  1271  		{
  1272  			name:       "replicas equal maxUnavailable",
  1273  			deployment: deployment(10, intstr.FromInt32(10)),
  1274  			expected:   0,
  1275  		},
  1276  		{
  1277  			name:       "replicas less than maxUnavailable",
  1278  			deployment: deployment(5, intstr.FromInt32(10)),
  1279  			expected:   0,
  1280  		},
  1281  		{
  1282  			name:       "replicas is 0",
  1283  			deployment: deployment(0, intstr.FromInt32(10)),
  1284  			expected:   0,
  1285  		},
  1286  		{
  1287  			name: "minAvailable with Recreate deployment strategy",
  1288  			deployment: &apps.Deployment{
  1289  				Spec: apps.DeploymentSpec{
  1290  					Replicas: ptr.To[int32](10),
  1291  					Strategy: apps.DeploymentStrategy{
  1292  						Type: apps.RecreateDeploymentStrategyType,
  1293  					},
  1294  				},
  1295  			},
  1296  			expected: 0,
  1297  		},
  1298  		{
  1299  			name:       "replicas greater than maxUnavailable with percents",
  1300  			deployment: deployment(10, intstr.FromString("60%")),
  1301  			expected:   4,
  1302  		},
  1303  		{
  1304  			name:       "replicas equal maxUnavailable with percents",
  1305  			deployment: deployment(10, intstr.FromString("100%")),
  1306  			expected:   int32(0),
  1307  		},
  1308  		{
  1309  			name:       "replicas less than maxUnavailable with percents",
  1310  			deployment: deployment(5, intstr.FromString("100%")),
  1311  			expected:   0,
  1312  		},
  1313  	}
  1314  	for _, tt := range tests {
  1315  		t.Run(tt.name, func(t *testing.T) {
  1316  			if got := MinAvailable(tt.deployment); got != tt.expected {
  1317  				t.Errorf("MinAvailable() = %v, want %v", got, tt.expected)
  1318  			}
  1319  		})
  1320  	}
  1321  }
  1322  

View as plain text