...

Source file src/k8s.io/kubernetes/pkg/registry/policy/poddisruptionbudget/strategy_test.go

Documentation: k8s.io/kubernetes/pkg/registry/policy/poddisruptionbudget

     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 poddisruptionbudget
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/util/intstr"
    27  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    28  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    29  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    30  	"k8s.io/kubernetes/pkg/apis/policy"
    31  	"k8s.io/kubernetes/pkg/features"
    32  )
    33  
    34  type unhealthyPodEvictionPolicyStrategyTestCase struct {
    35  	name                                                       string
    36  	enableUnhealthyPodEvictionPolicy                           bool
    37  	disablePDBUnhealthyPodEvictionPolicyFeatureGateAfterCreate bool
    38  	unhealthyPodEvictionPolicy                                 *policy.UnhealthyPodEvictionPolicyType
    39  	expectedUnhealthyPodEvictionPolicy                         *policy.UnhealthyPodEvictionPolicyType
    40  	expectedValidationErr                                      bool
    41  	updateUnhealthyPodEvictionPolicy                           *policy.UnhealthyPodEvictionPolicyType
    42  	expectedUpdateUnhealthyPodEvictionPolicy                   *policy.UnhealthyPodEvictionPolicyType
    43  	expectedValidationUpdateErr                                bool
    44  }
    45  
    46  func TestPodDisruptionBudgetStrategy(t *testing.T) {
    47  	tests := map[string]bool{
    48  		"PodDisruptionBudget strategy with PDBUnhealthyPodEvictionPolicy feature gate disabled": false,
    49  		"PodDisruptionBudget strategy with PDBUnhealthyPodEvictionPolicy feature gate enabled":  true,
    50  	}
    51  
    52  	for name, enableUnhealthyPodEvictionPolicy := range tests {
    53  		t.Run(name, func(t *testing.T) {
    54  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PDBUnhealthyPodEvictionPolicy, enableUnhealthyPodEvictionPolicy)()
    55  			testPodDisruptionBudgetStrategy(t)
    56  		})
    57  	}
    58  
    59  	healthyPolicyTests := []unhealthyPodEvictionPolicyStrategyTestCase{
    60  		{
    61  			name:                             "PodDisruptionBudget strategy with FeatureGate disabled should remove unhealthyPodEvictionPolicy",
    62  			enableUnhealthyPodEvictionPolicy: false,
    63  			unhealthyPodEvictionPolicy:       unhealthyPolicyPtr(policy.IfHealthyBudget),
    64  			updateUnhealthyPodEvictionPolicy: unhealthyPolicyPtr(policy.IfHealthyBudget),
    65  		},
    66  		{
    67  			name:                             "PodDisruptionBudget strategy with FeatureGate disabled should remove invalid unhealthyPodEvictionPolicy",
    68  			enableUnhealthyPodEvictionPolicy: false,
    69  			unhealthyPodEvictionPolicy:       unhealthyPolicyPtr("Invalid"),
    70  			updateUnhealthyPodEvictionPolicy: unhealthyPolicyPtr("Invalid"),
    71  		},
    72  		{
    73  			name:                             "PodDisruptionBudget strategy with FeatureGate enabled",
    74  			enableUnhealthyPodEvictionPolicy: true,
    75  		},
    76  		{
    77  			name:                                     "PodDisruptionBudget strategy with FeatureGate enabled should respect unhealthyPodEvictionPolicy",
    78  			enableUnhealthyPodEvictionPolicy:         true,
    79  			unhealthyPodEvictionPolicy:               unhealthyPolicyPtr(policy.AlwaysAllow),
    80  			expectedUnhealthyPodEvictionPolicy:       unhealthyPolicyPtr(policy.AlwaysAllow),
    81  			updateUnhealthyPodEvictionPolicy:         unhealthyPolicyPtr(policy.IfHealthyBudget),
    82  			expectedUpdateUnhealthyPodEvictionPolicy: unhealthyPolicyPtr(policy.IfHealthyBudget),
    83  		},
    84  		{
    85  			name:                             "PodDisruptionBudget strategy with FeatureGate enabled should fail invalid unhealthyPodEvictionPolicy",
    86  			enableUnhealthyPodEvictionPolicy: true,
    87  			unhealthyPodEvictionPolicy:       unhealthyPolicyPtr("Invalid"),
    88  			expectedValidationErr:            true,
    89  		},
    90  		{
    91  			name:                             "PodDisruptionBudget strategy with FeatureGate enabled should fail invalid unhealthyPodEvictionPolicy when updated",
    92  			enableUnhealthyPodEvictionPolicy: true,
    93  			updateUnhealthyPodEvictionPolicy: unhealthyPolicyPtr("Invalid"),
    94  			expectedValidationUpdateErr:      true,
    95  		},
    96  		{
    97  			name:                             "PodDisruptionBudget strategy with unhealthyPodEvictionPolicy should be updated when feature gate is disabled",
    98  			enableUnhealthyPodEvictionPolicy: true,
    99  			disablePDBUnhealthyPodEvictionPolicyFeatureGateAfterCreate: true,
   100  			unhealthyPodEvictionPolicy:                                 unhealthyPolicyPtr(policy.AlwaysAllow),
   101  			expectedUnhealthyPodEvictionPolicy:                         unhealthyPolicyPtr(policy.AlwaysAllow),
   102  			updateUnhealthyPodEvictionPolicy:                           unhealthyPolicyPtr(policy.IfHealthyBudget),
   103  			expectedUpdateUnhealthyPodEvictionPolicy:                   unhealthyPolicyPtr(policy.IfHealthyBudget),
   104  		},
   105  		{
   106  			name:                             "PodDisruptionBudget strategy with unhealthyPodEvictionPolicy should not be updated to invalid when feature gate is disabled",
   107  			enableUnhealthyPodEvictionPolicy: true,
   108  			disablePDBUnhealthyPodEvictionPolicyFeatureGateAfterCreate: true,
   109  			unhealthyPodEvictionPolicy:                                 unhealthyPolicyPtr(policy.AlwaysAllow),
   110  			expectedUnhealthyPodEvictionPolicy:                         unhealthyPolicyPtr(policy.AlwaysAllow),
   111  			updateUnhealthyPodEvictionPolicy:                           unhealthyPolicyPtr("Invalid"),
   112  			expectedValidationUpdateErr:                                true,
   113  			expectedUpdateUnhealthyPodEvictionPolicy:                   unhealthyPolicyPtr(policy.AlwaysAllow),
   114  		},
   115  	}
   116  
   117  	for _, tc := range healthyPolicyTests {
   118  		t.Run(tc.name, func(t *testing.T) {
   119  			testPodDisruptionBudgetStrategyWithUnhealthyPodEvictionPolicy(t, tc)
   120  		})
   121  	}
   122  }
   123  
   124  func testPodDisruptionBudgetStrategyWithUnhealthyPodEvictionPolicy(t *testing.T, tc unhealthyPodEvictionPolicyStrategyTestCase) {
   125  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PDBUnhealthyPodEvictionPolicy, tc.enableUnhealthyPodEvictionPolicy)()
   126  	ctx := genericapirequest.NewDefaultContext()
   127  	if !Strategy.NamespaceScoped() {
   128  		t.Errorf("PodDisruptionBudget must be namespace scoped")
   129  	}
   130  	if Strategy.AllowCreateOnUpdate() {
   131  		t.Errorf("PodDisruptionBudget should not allow create on update")
   132  	}
   133  
   134  	validSelector := map[string]string{"a": "b"}
   135  	minAvailable := intstr.FromInt32(3)
   136  	pdb := &policy.PodDisruptionBudget{
   137  		ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
   138  		Spec: policy.PodDisruptionBudgetSpec{
   139  			MinAvailable:               &minAvailable,
   140  			Selector:                   &metav1.LabelSelector{MatchLabels: validSelector},
   141  			UnhealthyPodEvictionPolicy: tc.unhealthyPodEvictionPolicy,
   142  		},
   143  	}
   144  
   145  	Strategy.PrepareForCreate(ctx, pdb)
   146  	errs := Strategy.Validate(ctx, pdb)
   147  	if len(errs) != 0 {
   148  		if !tc.expectedValidationErr {
   149  			t.Errorf("Unexpected error validating %v", errs)
   150  		}
   151  		return // no point going further when we have invalid PDB
   152  	}
   153  	if len(errs) == 0 && tc.expectedValidationErr {
   154  		t.Errorf("Expected error validating")
   155  	}
   156  	if !reflect.DeepEqual(pdb.Spec.UnhealthyPodEvictionPolicy, tc.expectedUnhealthyPodEvictionPolicy) {
   157  		t.Errorf("Unexpected UnhealthyPodEvictionPolicy set: expected %v, got %v", tc.expectedUnhealthyPodEvictionPolicy, pdb.Spec.UnhealthyPodEvictionPolicy)
   158  	}
   159  	if tc.disablePDBUnhealthyPodEvictionPolicyFeatureGateAfterCreate {
   160  		defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PDBUnhealthyPodEvictionPolicy, false)()
   161  	}
   162  
   163  	newPdb := &policy.PodDisruptionBudget{
   164  		ObjectMeta: metav1.ObjectMeta{Name: pdb.Name, Namespace: pdb.Namespace},
   165  		Spec:       pdb.Spec,
   166  	}
   167  	if tc.updateUnhealthyPodEvictionPolicy != nil {
   168  		newPdb.Spec.UnhealthyPodEvictionPolicy = tc.updateUnhealthyPodEvictionPolicy
   169  	}
   170  
   171  	// Nothing in Spec changes: OK
   172  	Strategy.PrepareForUpdate(ctx, newPdb, pdb)
   173  	errs = Strategy.ValidateUpdate(ctx, newPdb, pdb)
   174  
   175  	if len(errs) != 0 {
   176  		if !tc.expectedValidationUpdateErr {
   177  			t.Errorf("Unexpected error updating PodDisruptionBudget %v", errs)
   178  		}
   179  		return // no point going further when we have invalid PDB
   180  	}
   181  	if len(errs) == 0 && tc.expectedValidationUpdateErr {
   182  		t.Errorf("Expected error updating PodDisruptionBudget")
   183  	}
   184  	if !reflect.DeepEqual(newPdb.Spec.UnhealthyPodEvictionPolicy, tc.expectedUpdateUnhealthyPodEvictionPolicy) {
   185  		t.Errorf("Unexpected UnhealthyPodEvictionPolicy set: expected %v, got %v", tc.expectedUpdateUnhealthyPodEvictionPolicy, newPdb.Spec.UnhealthyPodEvictionPolicy)
   186  	}
   187  }
   188  
   189  func testPodDisruptionBudgetStrategy(t *testing.T) {
   190  	ctx := genericapirequest.NewDefaultContext()
   191  	if !Strategy.NamespaceScoped() {
   192  		t.Errorf("PodDisruptionBudget must be namespace scoped")
   193  	}
   194  	if Strategy.AllowCreateOnUpdate() {
   195  		t.Errorf("PodDisruptionBudget should not allow create on update")
   196  	}
   197  
   198  	validSelector := map[string]string{"a": "b"}
   199  	minAvailable := intstr.FromInt32(3)
   200  	pdb := &policy.PodDisruptionBudget{
   201  		ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
   202  		Spec: policy.PodDisruptionBudgetSpec{
   203  			MinAvailable: &minAvailable,
   204  			Selector:     &metav1.LabelSelector{MatchLabels: validSelector},
   205  		},
   206  	}
   207  
   208  	Strategy.PrepareForCreate(ctx, pdb)
   209  	errs := Strategy.Validate(ctx, pdb)
   210  	if len(errs) != 0 {
   211  		t.Errorf("Unexpected error validating %v", errs)
   212  	}
   213  
   214  	newPdb := &policy.PodDisruptionBudget{
   215  		ObjectMeta: metav1.ObjectMeta{Name: pdb.Name, Namespace: pdb.Namespace},
   216  		Spec:       pdb.Spec,
   217  		Status: policy.PodDisruptionBudgetStatus{
   218  			DisruptionsAllowed: 1,
   219  			CurrentHealthy:     3,
   220  			DesiredHealthy:     3,
   221  			ExpectedPods:       3,
   222  		},
   223  	}
   224  
   225  	// Nothing in Spec changes: OK
   226  	Strategy.PrepareForUpdate(ctx, newPdb, pdb)
   227  	errs = Strategy.ValidateUpdate(ctx, newPdb, pdb)
   228  	if len(errs) != 0 {
   229  		t.Errorf("Unexpected error updating PodDisruptionBudget.")
   230  	}
   231  
   232  	// Changing the selector?  OK
   233  	newPdb.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{"a": "bar"}}
   234  	Strategy.PrepareForUpdate(ctx, newPdb, pdb)
   235  	errs = Strategy.ValidateUpdate(ctx, newPdb, pdb)
   236  	if len(errs) != 0 {
   237  		t.Errorf("Expected no error on changing selector on poddisruptionbudgets.")
   238  	}
   239  	newPdb.Spec.Selector = pdb.Spec.Selector
   240  
   241  	// Changing MinAvailable?  OK
   242  	newMinAvailable := intstr.FromString("28%")
   243  	newPdb.Spec.MinAvailable = &newMinAvailable
   244  	Strategy.PrepareForUpdate(ctx, newPdb, pdb)
   245  	errs = Strategy.ValidateUpdate(ctx, newPdb, pdb)
   246  	if len(errs) != 0 {
   247  		t.Errorf("Expected no error updating MinAvailable on poddisruptionbudgets.")
   248  	}
   249  
   250  	// Changing MinAvailable to MaxAvailable? OK
   251  	maxUnavailable := intstr.FromString("28%")
   252  	newPdb.Spec.MaxUnavailable = &maxUnavailable
   253  	newPdb.Spec.MinAvailable = nil
   254  	Strategy.PrepareForUpdate(ctx, newPdb, pdb)
   255  	errs = Strategy.ValidateUpdate(ctx, newPdb, pdb)
   256  	if len(errs) != 0 {
   257  		t.Errorf("Expected no error updating replacing MinAvailable with MaxUnavailable on poddisruptionbudgets.")
   258  	}
   259  }
   260  
   261  func TestPodDisruptionBudgetStatusStrategy(t *testing.T) {
   262  	ctx := genericapirequest.NewDefaultContext()
   263  	if !StatusStrategy.NamespaceScoped() {
   264  		t.Errorf("PodDisruptionBudgetStatus must be namespace scoped")
   265  	}
   266  	if StatusStrategy.AllowCreateOnUpdate() {
   267  		t.Errorf("PodDisruptionBudgetStatus should not allow create on update")
   268  	}
   269  
   270  	oldMinAvailable := intstr.FromInt32(3)
   271  	newMinAvailable := intstr.FromInt32(2)
   272  
   273  	validSelector := map[string]string{"a": "b"}
   274  	oldPdb := &policy.PodDisruptionBudget{
   275  		ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "10"},
   276  		Spec: policy.PodDisruptionBudgetSpec{
   277  			Selector:     &metav1.LabelSelector{MatchLabels: validSelector},
   278  			MinAvailable: &oldMinAvailable,
   279  		},
   280  		Status: policy.PodDisruptionBudgetStatus{
   281  			DisruptionsAllowed: 1,
   282  			CurrentHealthy:     3,
   283  			DesiredHealthy:     3,
   284  			ExpectedPods:       3,
   285  		},
   286  	}
   287  	newPdb := &policy.PodDisruptionBudget{
   288  		ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "9"},
   289  		Spec: policy.PodDisruptionBudgetSpec{
   290  			Selector:     &metav1.LabelSelector{MatchLabels: validSelector},
   291  			MinAvailable: &newMinAvailable,
   292  		},
   293  		Status: policy.PodDisruptionBudgetStatus{
   294  			DisruptionsAllowed: 0,
   295  			CurrentHealthy:     2,
   296  			DesiredHealthy:     3,
   297  			ExpectedPods:       3,
   298  		},
   299  	}
   300  	StatusStrategy.PrepareForUpdate(ctx, newPdb, oldPdb)
   301  	if newPdb.Status.CurrentHealthy != 2 {
   302  		t.Errorf("PodDisruptionBudget status updates should allow change of CurrentHealthy: %v", newPdb.Status.CurrentHealthy)
   303  	}
   304  	if newPdb.Spec.MinAvailable.IntValue() != 3 {
   305  		t.Errorf("PodDisruptionBudget status updates should not clobber spec: %v", newPdb.Spec)
   306  	}
   307  	errs := StatusStrategy.ValidateUpdate(ctx, newPdb, oldPdb)
   308  	if len(errs) != 0 {
   309  		t.Errorf("Unexpected error %v", errs)
   310  	}
   311  }
   312  
   313  func TestPodDisruptionBudgetStatusValidationByApiVersion(t *testing.T) {
   314  	testCases := map[string]struct {
   315  		apiVersion string
   316  		validation bool
   317  	}{
   318  		"policy/v1beta1 should not do update validation": {
   319  			apiVersion: "v1beta1",
   320  			validation: false,
   321  		},
   322  		"policy/v1 should do update validation": {
   323  			apiVersion: "v1",
   324  			validation: true,
   325  		},
   326  		"policy/some-version should do update validation": {
   327  			apiVersion: "some-version",
   328  			validation: true,
   329  		},
   330  	}
   331  
   332  	for tn, tc := range testCases {
   333  		t.Run(tn, func(t *testing.T) {
   334  			ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(),
   335  				&genericapirequest.RequestInfo{
   336  					APIGroup:   "policy",
   337  					APIVersion: tc.apiVersion,
   338  				})
   339  
   340  			oldMaxUnavailable := intstr.FromInt32(2)
   341  			newMaxUnavailable := intstr.FromInt32(3)
   342  			oldPdb := &policy.PodDisruptionBudget{
   343  				ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "10"},
   344  				Spec: policy.PodDisruptionBudgetSpec{
   345  					Selector:       &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
   346  					MaxUnavailable: &oldMaxUnavailable,
   347  				},
   348  				Status: policy.PodDisruptionBudgetStatus{
   349  					DisruptionsAllowed: 1,
   350  				},
   351  			}
   352  			newPdb := &policy.PodDisruptionBudget{
   353  				ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "9"},
   354  				Spec: policy.PodDisruptionBudgetSpec{
   355  					Selector:     &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
   356  					MinAvailable: &newMaxUnavailable,
   357  				},
   358  				Status: policy.PodDisruptionBudgetStatus{
   359  					DisruptionsAllowed: -1, // This is not allowed, so should trigger validation error.
   360  				},
   361  			}
   362  
   363  			errs := StatusStrategy.ValidateUpdate(ctx, newPdb, oldPdb)
   364  			hasErrors := len(errs) > 0
   365  			if !tc.validation && hasErrors {
   366  				t.Errorf("Validation failed when no validation should happen")
   367  			}
   368  			if tc.validation && !hasErrors {
   369  				t.Errorf("Expected validation errors but didn't get any")
   370  			}
   371  		})
   372  	}
   373  }
   374  
   375  func TestDropDisabledFields(t *testing.T) {
   376  	tests := map[string]struct {
   377  		oldSpec                          *policy.PodDisruptionBudgetSpec
   378  		newSpec                          *policy.PodDisruptionBudgetSpec
   379  		expectNewSpec                    *policy.PodDisruptionBudgetSpec
   380  		enableUnhealthyPodEvictionPolicy bool
   381  	}{
   382  		"disabled clears unhealthyPodEvictionPolicy": {
   383  			enableUnhealthyPodEvictionPolicy: false,
   384  			oldSpec:                          nil,
   385  			newSpec:                          specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   386  			expectNewSpec:                    specWithUnhealthyPodEvictionPolicy(nil),
   387  		},
   388  		"disabled does not allow updating unhealthyPodEvictionPolicy": {
   389  			enableUnhealthyPodEvictionPolicy: false,
   390  			oldSpec:                          specWithUnhealthyPodEvictionPolicy(nil),
   391  			newSpec:                          specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   392  			expectNewSpec:                    specWithUnhealthyPodEvictionPolicy(nil),
   393  		},
   394  		"disabled preserves old unhealthyPodEvictionPolicy when both old and new have it": {
   395  			enableUnhealthyPodEvictionPolicy: false,
   396  			oldSpec:                          specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   397  			newSpec:                          specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   398  			expectNewSpec:                    specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   399  		},
   400  		"disabled allows updating unhealthyPodEvictionPolicy": {
   401  			enableUnhealthyPodEvictionPolicy: false,
   402  			oldSpec:                          specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   403  			newSpec:                          specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.AlwaysAllow)),
   404  			expectNewSpec:                    specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.AlwaysAllow)),
   405  		},
   406  		"enabled preserve unhealthyPodEvictionPolicy": {
   407  			enableUnhealthyPodEvictionPolicy: true,
   408  			oldSpec:                          nil,
   409  			newSpec:                          specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   410  			expectNewSpec:                    specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   411  		},
   412  		"enabled allows updating unhealthyPodEvictionPolicy": {
   413  			enableUnhealthyPodEvictionPolicy: true,
   414  			oldSpec:                          specWithUnhealthyPodEvictionPolicy(nil),
   415  			newSpec:                          specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   416  			expectNewSpec:                    specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   417  		},
   418  		"enabled preserve unhealthyPodEvictionPolicy when both old and new have it": {
   419  			enableUnhealthyPodEvictionPolicy: true,
   420  			oldSpec:                          specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   421  			newSpec:                          specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   422  			expectNewSpec:                    specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   423  		},
   424  		"enabled updates unhealthyPodEvictionPolicy": {
   425  			enableUnhealthyPodEvictionPolicy: true,
   426  			oldSpec:                          specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.IfHealthyBudget)),
   427  			newSpec:                          specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.AlwaysAllow)),
   428  			expectNewSpec:                    specWithUnhealthyPodEvictionPolicy(unhealthyPolicyPtr(policy.AlwaysAllow)),
   429  		},
   430  	}
   431  
   432  	for name, tc := range tests {
   433  		t.Run(name, func(t *testing.T) {
   434  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PDBUnhealthyPodEvictionPolicy, tc.enableUnhealthyPodEvictionPolicy)()
   435  
   436  			oldSpecBefore := tc.oldSpec.DeepCopy()
   437  			dropDisabledFields(tc.newSpec, tc.oldSpec)
   438  			if !reflect.DeepEqual(tc.newSpec, tc.expectNewSpec) {
   439  				t.Error(cmp.Diff(tc.newSpec, tc.expectNewSpec))
   440  			}
   441  			if !reflect.DeepEqual(tc.oldSpec, oldSpecBefore) {
   442  				t.Error(cmp.Diff(tc.oldSpec, oldSpecBefore))
   443  			}
   444  		})
   445  	}
   446  }
   447  
   448  func unhealthyPolicyPtr(unhealthyPodEvictionPolicy policy.UnhealthyPodEvictionPolicyType) *policy.UnhealthyPodEvictionPolicyType {
   449  	return &unhealthyPodEvictionPolicy
   450  }
   451  
   452  func specWithUnhealthyPodEvictionPolicy(unhealthyPodEvictionPolicy *policy.UnhealthyPodEvictionPolicyType) *policy.PodDisruptionBudgetSpec {
   453  	return &policy.PodDisruptionBudgetSpec{
   454  		UnhealthyPodEvictionPolicy: unhealthyPodEvictionPolicy,
   455  	}
   456  }
   457  

View as plain text