...

Source file src/k8s.io/kubernetes/pkg/apis/apps/v1/defaults_test.go

Documentation: k8s.io/kubernetes/pkg/apis/apps/v1

     1  /*
     2  Copyright 2017 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 v1_test
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	appsv1 "k8s.io/api/apps/v1"
    24  	v1 "k8s.io/api/core/v1"
    25  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    26  	"k8s.io/apimachinery/pkg/api/resource"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/apimachinery/pkg/util/intstr"
    30  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    31  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    32  	"k8s.io/kubernetes/pkg/api/legacyscheme"
    33  	_ "k8s.io/kubernetes/pkg/apis/apps/install"
    34  	. "k8s.io/kubernetes/pkg/apis/apps/v1"
    35  	_ "k8s.io/kubernetes/pkg/apis/core/install"
    36  	"k8s.io/kubernetes/pkg/features"
    37  	"k8s.io/utils/ptr"
    38  )
    39  
    40  func TestSetDefaultDaemonSetSpec(t *testing.T) {
    41  	defaultLabels := map[string]string{"foo": "bar"}
    42  	maxUnavailable := intstr.FromInt32(1)
    43  	maxSurge := intstr.FromInt32(0)
    44  	period := int64(v1.DefaultTerminationGracePeriodSeconds)
    45  	defaultTemplate := v1.PodTemplateSpec{
    46  		Spec: v1.PodSpec{
    47  			DNSPolicy:                     v1.DNSClusterFirst,
    48  			RestartPolicy:                 v1.RestartPolicyAlways,
    49  			SecurityContext:               &v1.PodSecurityContext{},
    50  			TerminationGracePeriodSeconds: &period,
    51  			SchedulerName:                 v1.DefaultSchedulerName,
    52  		},
    53  		ObjectMeta: metav1.ObjectMeta{
    54  			Labels: defaultLabels,
    55  		},
    56  	}
    57  	templateNoLabel := v1.PodTemplateSpec{
    58  		Spec: v1.PodSpec{
    59  			DNSPolicy:                     v1.DNSClusterFirst,
    60  			RestartPolicy:                 v1.RestartPolicyAlways,
    61  			SecurityContext:               &v1.PodSecurityContext{},
    62  			TerminationGracePeriodSeconds: &period,
    63  			SchedulerName:                 v1.DefaultSchedulerName,
    64  		},
    65  	}
    66  	tests := []struct {
    67  		original *appsv1.DaemonSet
    68  		expected *appsv1.DaemonSet
    69  	}{
    70  		{ // Labels change/defaulting test.
    71  			original: &appsv1.DaemonSet{
    72  				Spec: appsv1.DaemonSetSpec{
    73  					Template: defaultTemplate,
    74  				},
    75  			},
    76  			expected: &appsv1.DaemonSet{
    77  				ObjectMeta: metav1.ObjectMeta{
    78  					Labels: defaultLabels,
    79  				},
    80  				Spec: appsv1.DaemonSetSpec{
    81  					Template: defaultTemplate,
    82  					UpdateStrategy: appsv1.DaemonSetUpdateStrategy{
    83  						Type: appsv1.RollingUpdateDaemonSetStrategyType,
    84  						RollingUpdate: &appsv1.RollingUpdateDaemonSet{
    85  							MaxUnavailable: &maxUnavailable,
    86  							MaxSurge:       &maxSurge,
    87  						},
    88  					},
    89  					RevisionHistoryLimit: ptr.To[int32](10),
    90  				},
    91  			},
    92  		},
    93  		{ // Labels change/defaulting test.
    94  			original: &appsv1.DaemonSet{
    95  				ObjectMeta: metav1.ObjectMeta{
    96  					Labels: map[string]string{
    97  						"bar": "foo",
    98  					},
    99  				},
   100  				Spec: appsv1.DaemonSetSpec{
   101  					Template:             defaultTemplate,
   102  					RevisionHistoryLimit: ptr.To[int32](1),
   103  				},
   104  			},
   105  			expected: &appsv1.DaemonSet{
   106  				ObjectMeta: metav1.ObjectMeta{
   107  					Labels: map[string]string{
   108  						"bar": "foo",
   109  					},
   110  				},
   111  				Spec: appsv1.DaemonSetSpec{
   112  					Template: defaultTemplate,
   113  					UpdateStrategy: appsv1.DaemonSetUpdateStrategy{
   114  						Type: appsv1.RollingUpdateDaemonSetStrategyType,
   115  						RollingUpdate: &appsv1.RollingUpdateDaemonSet{
   116  							MaxUnavailable: &maxUnavailable,
   117  							MaxSurge:       &maxSurge,
   118  						},
   119  					},
   120  					RevisionHistoryLimit: ptr.To[int32](1),
   121  				},
   122  			},
   123  		},
   124  		{ // OnDeleteDaemonSetStrategyType update strategy.
   125  			original: &appsv1.DaemonSet{
   126  				Spec: appsv1.DaemonSetSpec{
   127  					Template: templateNoLabel,
   128  					UpdateStrategy: appsv1.DaemonSetUpdateStrategy{
   129  						Type: appsv1.OnDeleteDaemonSetStrategyType,
   130  					},
   131  				},
   132  			},
   133  			expected: &appsv1.DaemonSet{
   134  				Spec: appsv1.DaemonSetSpec{
   135  					Template: templateNoLabel,
   136  					UpdateStrategy: appsv1.DaemonSetUpdateStrategy{
   137  						Type: appsv1.OnDeleteDaemonSetStrategyType,
   138  					},
   139  					RevisionHistoryLimit: ptr.To[int32](10),
   140  				},
   141  			},
   142  		},
   143  		{ // Custom unique label key.
   144  			original: &appsv1.DaemonSet{
   145  				Spec: appsv1.DaemonSetSpec{},
   146  			},
   147  			expected: &appsv1.DaemonSet{
   148  				Spec: appsv1.DaemonSetSpec{
   149  					Template: templateNoLabel,
   150  					UpdateStrategy: appsv1.DaemonSetUpdateStrategy{
   151  						Type: appsv1.RollingUpdateDaemonSetStrategyType,
   152  						RollingUpdate: &appsv1.RollingUpdateDaemonSet{
   153  							MaxUnavailable: &maxUnavailable,
   154  							MaxSurge:       &maxSurge,
   155  						},
   156  					},
   157  					RevisionHistoryLimit: ptr.To[int32](10),
   158  				},
   159  			},
   160  		},
   161  	}
   162  
   163  	for i, test := range tests {
   164  		original := test.original
   165  		expected := test.expected
   166  		obj2 := roundTrip(t, runtime.Object(original))
   167  		got, ok := obj2.(*appsv1.DaemonSet)
   168  		if !ok {
   169  			t.Errorf("(%d) unexpected object: %v", i, got)
   170  			t.FailNow()
   171  		}
   172  		if !apiequality.Semantic.DeepEqual(got.Spec, expected.Spec) {
   173  			t.Errorf("(%d) got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", i, got.Spec, expected.Spec)
   174  		}
   175  	}
   176  }
   177  
   178  func TestSetDefaultStatefulSet(t *testing.T) {
   179  	defaultLabels := map[string]string{"foo": "bar"}
   180  	var defaultPartition int32 = 0
   181  	var defaultReplicas int32 = 1
   182  	var notTheDefaultPartition int32 = 42
   183  
   184  	period := int64(v1.DefaultTerminationGracePeriodSeconds)
   185  	defaultTemplate := v1.PodTemplateSpec{
   186  		Spec: v1.PodSpec{
   187  			DNSPolicy:                     v1.DNSClusterFirst,
   188  			RestartPolicy:                 v1.RestartPolicyAlways,
   189  			SecurityContext:               &v1.PodSecurityContext{},
   190  			TerminationGracePeriodSeconds: &period,
   191  			SchedulerName:                 v1.DefaultSchedulerName,
   192  		},
   193  		ObjectMeta: metav1.ObjectMeta{
   194  			Labels: defaultLabels,
   195  		},
   196  	}
   197  
   198  	tests := []struct {
   199  		name                       string
   200  		original                   *appsv1.StatefulSet
   201  		expected                   *appsv1.StatefulSet
   202  		enablePVCDeletionPolicy    bool
   203  		enableMaxUnavailablePolicy bool
   204  	}{
   205  		{
   206  			name: "labels and default update strategy",
   207  			original: &appsv1.StatefulSet{
   208  				Spec: appsv1.StatefulSetSpec{
   209  					Template: defaultTemplate,
   210  				},
   211  			},
   212  			expected: &appsv1.StatefulSet{
   213  				ObjectMeta: metav1.ObjectMeta{
   214  					Labels: defaultLabels,
   215  				},
   216  				Spec: appsv1.StatefulSetSpec{
   217  					Replicas:            &defaultReplicas,
   218  					MinReadySeconds:     int32(0),
   219  					Template:            defaultTemplate,
   220  					PodManagementPolicy: appsv1.OrderedReadyPodManagement,
   221  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   222  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   223  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   224  							Partition: &defaultPartition,
   225  						},
   226  					},
   227  					RevisionHistoryLimit: ptr.To[int32](10),
   228  				},
   229  			},
   230  		},
   231  		{
   232  			name: "Alternate update strategy",
   233  			original: &appsv1.StatefulSet{
   234  				Spec: appsv1.StatefulSetSpec{
   235  					Template: defaultTemplate,
   236  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   237  						Type: appsv1.OnDeleteStatefulSetStrategyType,
   238  					},
   239  				},
   240  			},
   241  			expected: &appsv1.StatefulSet{
   242  				ObjectMeta: metav1.ObjectMeta{
   243  					Labels: defaultLabels,
   244  				},
   245  				Spec: appsv1.StatefulSetSpec{
   246  					Replicas:            &defaultReplicas,
   247  					MinReadySeconds:     int32(0),
   248  					Template:            defaultTemplate,
   249  					PodManagementPolicy: appsv1.OrderedReadyPodManagement,
   250  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   251  						Type: appsv1.OnDeleteStatefulSetStrategyType,
   252  					},
   253  					RevisionHistoryLimit: ptr.To[int32](10),
   254  				},
   255  			},
   256  		},
   257  		{
   258  			name: "Parallel pod management policy",
   259  			original: &appsv1.StatefulSet{
   260  				Spec: appsv1.StatefulSetSpec{
   261  					Template:            defaultTemplate,
   262  					PodManagementPolicy: appsv1.ParallelPodManagement,
   263  				},
   264  			},
   265  			expected: &appsv1.StatefulSet{
   266  				ObjectMeta: metav1.ObjectMeta{
   267  					Labels: defaultLabels,
   268  				},
   269  				Spec: appsv1.StatefulSetSpec{
   270  					Replicas:            &defaultReplicas,
   271  					MinReadySeconds:     int32(0),
   272  					Template:            defaultTemplate,
   273  					PodManagementPolicy: appsv1.ParallelPodManagement,
   274  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   275  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   276  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   277  							Partition: &defaultPartition,
   278  						},
   279  					},
   280  					RevisionHistoryLimit: ptr.To[int32](10),
   281  				},
   282  			},
   283  		},
   284  		{
   285  			name: "UpdateStrategy.RollingUpdate.Partition is not lost when UpdateStrategy.Type is not set",
   286  			original: &appsv1.StatefulSet{
   287  				Spec: appsv1.StatefulSetSpec{
   288  					Template: defaultTemplate,
   289  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   290  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   291  							Partition: &notTheDefaultPartition,
   292  						},
   293  					},
   294  				},
   295  			},
   296  			expected: &appsv1.StatefulSet{
   297  				ObjectMeta: metav1.ObjectMeta{
   298  					Labels: defaultLabels,
   299  				},
   300  				Spec: appsv1.StatefulSetSpec{
   301  					Replicas:            &defaultReplicas,
   302  					MinReadySeconds:     int32(0),
   303  					Template:            defaultTemplate,
   304  					PodManagementPolicy: appsv1.OrderedReadyPodManagement,
   305  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   306  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   307  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   308  							Partition: &notTheDefaultPartition,
   309  						},
   310  					},
   311  					RevisionHistoryLimit: ptr.To[int32](10),
   312  				},
   313  			},
   314  		},
   315  		{
   316  			name: "PVC delete policy enabled, no policy specified",
   317  			original: &appsv1.StatefulSet{
   318  				Spec: appsv1.StatefulSetSpec{
   319  					Template: defaultTemplate,
   320  				},
   321  			},
   322  			expected: &appsv1.StatefulSet{
   323  				ObjectMeta: metav1.ObjectMeta{
   324  					Labels: defaultLabels,
   325  				},
   326  				Spec: appsv1.StatefulSetSpec{
   327  					Replicas:            &defaultReplicas,
   328  					Template:            defaultTemplate,
   329  					PodManagementPolicy: appsv1.OrderedReadyPodManagement,
   330  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   331  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   332  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   333  							Partition: &defaultPartition,
   334  						},
   335  					},
   336  					PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
   337  						WhenDeleted: appsv1.RetainPersistentVolumeClaimRetentionPolicyType,
   338  						WhenScaled:  appsv1.RetainPersistentVolumeClaimRetentionPolicyType,
   339  					},
   340  					RevisionHistoryLimit: ptr.To[int32](10),
   341  				},
   342  			},
   343  			enablePVCDeletionPolicy: true,
   344  		},
   345  		{
   346  			name: "PVC delete policy enabled, with scaledown policy specified",
   347  			original: &appsv1.StatefulSet{
   348  				Spec: appsv1.StatefulSetSpec{
   349  					Template: defaultTemplate,
   350  					PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
   351  						WhenScaled: appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
   352  					},
   353  				},
   354  			},
   355  			expected: &appsv1.StatefulSet{
   356  				ObjectMeta: metav1.ObjectMeta{
   357  					Labels: defaultLabels,
   358  				},
   359  				Spec: appsv1.StatefulSetSpec{
   360  					Replicas:            &defaultReplicas,
   361  					Template:            defaultTemplate,
   362  					PodManagementPolicy: appsv1.OrderedReadyPodManagement,
   363  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   364  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   365  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   366  							Partition: &defaultPartition,
   367  						},
   368  					},
   369  					PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
   370  						WhenDeleted: appsv1.RetainPersistentVolumeClaimRetentionPolicyType,
   371  						WhenScaled:  appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
   372  					},
   373  					RevisionHistoryLimit: ptr.To[int32](10),
   374  				},
   375  			},
   376  			enablePVCDeletionPolicy: true,
   377  		},
   378  		{
   379  			name: "PVC delete policy disabled, with set deletion policy specified",
   380  			original: &appsv1.StatefulSet{
   381  				Spec: appsv1.StatefulSetSpec{
   382  					Template: defaultTemplate,
   383  					PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
   384  						WhenDeleted: appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
   385  					},
   386  				},
   387  			},
   388  			expected: &appsv1.StatefulSet{
   389  				ObjectMeta: metav1.ObjectMeta{
   390  					Labels: defaultLabels,
   391  				},
   392  				Spec: appsv1.StatefulSetSpec{
   393  					Replicas:            &defaultReplicas,
   394  					Template:            defaultTemplate,
   395  					PodManagementPolicy: appsv1.OrderedReadyPodManagement,
   396  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   397  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   398  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   399  							Partition: &defaultPartition,
   400  						},
   401  					},
   402  					PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
   403  						WhenDeleted: appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
   404  						WhenScaled:  appsv1.RetainPersistentVolumeClaimRetentionPolicyType,
   405  					},
   406  					RevisionHistoryLimit: ptr.To[int32](10),
   407  				},
   408  			},
   409  			enablePVCDeletionPolicy: true,
   410  		},
   411  		{
   412  			name: "PVC delete policy disabled, with policy specified",
   413  			original: &appsv1.StatefulSet{
   414  				Spec: appsv1.StatefulSetSpec{
   415  					Template: defaultTemplate,
   416  					PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
   417  						WhenScaled: appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
   418  					},
   419  				},
   420  			},
   421  			expected: &appsv1.StatefulSet{
   422  				ObjectMeta: metav1.ObjectMeta{
   423  					Labels: defaultLabels,
   424  				},
   425  				Spec: appsv1.StatefulSetSpec{
   426  					Replicas:            &defaultReplicas,
   427  					Template:            defaultTemplate,
   428  					PodManagementPolicy: appsv1.OrderedReadyPodManagement,
   429  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   430  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   431  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   432  							Partition: &defaultPartition,
   433  						},
   434  					},
   435  					PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{
   436  						WhenScaled: appsv1.DeletePersistentVolumeClaimRetentionPolicyType,
   437  					},
   438  					RevisionHistoryLimit: ptr.To[int32](10),
   439  				},
   440  			},
   441  			enablePVCDeletionPolicy: false,
   442  		},
   443  		{
   444  			name: "MaxUnavailable disabled, with maxUnavailable not specified",
   445  			original: &appsv1.StatefulSet{
   446  				Spec: appsv1.StatefulSetSpec{
   447  					Template: defaultTemplate,
   448  				},
   449  			},
   450  			expected: &appsv1.StatefulSet{
   451  				ObjectMeta: metav1.ObjectMeta{
   452  					Labels: defaultLabels,
   453  				},
   454  				Spec: appsv1.StatefulSetSpec{
   455  					Replicas:            &defaultReplicas,
   456  					Template:            defaultTemplate,
   457  					PodManagementPolicy: appsv1.OrderedReadyPodManagement,
   458  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   459  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   460  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   461  							Partition: ptr.To[int32](0),
   462  						},
   463  					},
   464  					RevisionHistoryLimit: ptr.To[int32](10),
   465  				},
   466  			},
   467  			enableMaxUnavailablePolicy: false,
   468  		},
   469  		{
   470  			name: "MaxUnavailable disabled, with default maxUnavailable specified",
   471  			original: &appsv1.StatefulSet{
   472  				Spec: appsv1.StatefulSetSpec{
   473  					Template: defaultTemplate,
   474  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   475  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   476  							Partition:      &defaultPartition,
   477  							MaxUnavailable: ptr.To(intstr.FromInt32(1)),
   478  						},
   479  					},
   480  				},
   481  			},
   482  			expected: &appsv1.StatefulSet{
   483  				ObjectMeta: metav1.ObjectMeta{
   484  					Labels: defaultLabels,
   485  				},
   486  				Spec: appsv1.StatefulSetSpec{
   487  					Replicas:            &defaultReplicas,
   488  					Template:            defaultTemplate,
   489  					PodManagementPolicy: appsv1.OrderedReadyPodManagement,
   490  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   491  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   492  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   493  							Partition:      ptr.To[int32](0),
   494  							MaxUnavailable: ptr.To(intstr.FromInt32(1)),
   495  						},
   496  					},
   497  					RevisionHistoryLimit: ptr.To[int32](10),
   498  				},
   499  			},
   500  			enableMaxUnavailablePolicy: false,
   501  		},
   502  		{
   503  			name: "MaxUnavailable disabled, with non default maxUnavailable specified",
   504  			original: &appsv1.StatefulSet{
   505  				Spec: appsv1.StatefulSetSpec{
   506  					Template: defaultTemplate,
   507  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   508  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   509  							Partition:      &notTheDefaultPartition,
   510  							MaxUnavailable: ptr.To(intstr.FromInt32(3)),
   511  						},
   512  					},
   513  				},
   514  			},
   515  			expected: &appsv1.StatefulSet{
   516  				ObjectMeta: metav1.ObjectMeta{
   517  					Labels: defaultLabels,
   518  				},
   519  				Spec: appsv1.StatefulSetSpec{
   520  					Replicas:            &defaultReplicas,
   521  					Template:            defaultTemplate,
   522  					PodManagementPolicy: appsv1.OrderedReadyPodManagement,
   523  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   524  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   525  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   526  							Partition:      ptr.To[int32](42),
   527  							MaxUnavailable: ptr.To(intstr.FromInt32(3)),
   528  						},
   529  					},
   530  					RevisionHistoryLimit: ptr.To[int32](10),
   531  				},
   532  			},
   533  			enableMaxUnavailablePolicy: false,
   534  		},
   535  		{
   536  			name: "MaxUnavailable enabled, with no maxUnavailable specified",
   537  			original: &appsv1.StatefulSet{
   538  				Spec: appsv1.StatefulSetSpec{
   539  					Template: defaultTemplate,
   540  				},
   541  			},
   542  			expected: &appsv1.StatefulSet{
   543  				ObjectMeta: metav1.ObjectMeta{
   544  					Labels: defaultLabels,
   545  				},
   546  				Spec: appsv1.StatefulSetSpec{
   547  					Replicas:            &defaultReplicas,
   548  					Template:            defaultTemplate,
   549  					PodManagementPolicy: appsv1.OrderedReadyPodManagement,
   550  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   551  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   552  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   553  							Partition:      ptr.To[int32](0),
   554  							MaxUnavailable: ptr.To(intstr.FromInt32(1)),
   555  						},
   556  					},
   557  					RevisionHistoryLimit: ptr.To[int32](10),
   558  				},
   559  			},
   560  			enableMaxUnavailablePolicy: true,
   561  		},
   562  		{
   563  			name: "MaxUnavailable enabled, with non default maxUnavailable specified",
   564  			original: &appsv1.StatefulSet{
   565  				Spec: appsv1.StatefulSetSpec{
   566  					Template: defaultTemplate,
   567  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   568  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   569  							Partition:      &notTheDefaultPartition,
   570  							MaxUnavailable: ptr.To(intstr.FromInt32(3)),
   571  						},
   572  					},
   573  				},
   574  			},
   575  			expected: &appsv1.StatefulSet{
   576  				ObjectMeta: metav1.ObjectMeta{
   577  					Labels: defaultLabels,
   578  				},
   579  				Spec: appsv1.StatefulSetSpec{
   580  					Replicas:            &defaultReplicas,
   581  					Template:            defaultTemplate,
   582  					PodManagementPolicy: appsv1.OrderedReadyPodManagement,
   583  					UpdateStrategy: appsv1.StatefulSetUpdateStrategy{
   584  						Type: appsv1.RollingUpdateStatefulSetStrategyType,
   585  						RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   586  							Partition:      ptr.To[int32](42),
   587  							MaxUnavailable: ptr.To(intstr.FromInt32(3)),
   588  						},
   589  					},
   590  					RevisionHistoryLimit: ptr.To[int32](10),
   591  				},
   592  			},
   593  			enableMaxUnavailablePolicy: true,
   594  		},
   595  	}
   596  
   597  	for _, test := range tests {
   598  		test := test
   599  		t.Run(test.name, func(t *testing.T) {
   600  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetAutoDeletePVC, test.enablePVCDeletionPolicy)()
   601  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MaxUnavailableStatefulSet, test.enableMaxUnavailablePolicy)()
   602  
   603  			obj2 := roundTrip(t, runtime.Object(test.original))
   604  			got, ok := obj2.(*appsv1.StatefulSet)
   605  			if !ok {
   606  				t.Errorf("unexpected object: %v", got)
   607  				t.FailNow()
   608  			}
   609  			if !apiequality.Semantic.DeepEqual(got.Spec, test.expected.Spec) {
   610  				t.Errorf("got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", got.Spec, test.expected.Spec)
   611  			}
   612  		})
   613  	}
   614  }
   615  
   616  func TestSetDefaultDeployment(t *testing.T) {
   617  	defaultIntOrString := intstr.FromString("25%")
   618  	differentIntOrString := intstr.FromInt32(5)
   619  	period := int64(v1.DefaultTerminationGracePeriodSeconds)
   620  	defaultTemplate := v1.PodTemplateSpec{
   621  		Spec: v1.PodSpec{
   622  			DNSPolicy:                     v1.DNSClusterFirst,
   623  			RestartPolicy:                 v1.RestartPolicyAlways,
   624  			SecurityContext:               &v1.PodSecurityContext{},
   625  			TerminationGracePeriodSeconds: &period,
   626  			SchedulerName:                 v1.DefaultSchedulerName,
   627  		},
   628  	}
   629  	tests := []struct {
   630  		original *appsv1.Deployment
   631  		expected *appsv1.Deployment
   632  	}{
   633  		{
   634  			original: &appsv1.Deployment{},
   635  			expected: &appsv1.Deployment{
   636  				Spec: appsv1.DeploymentSpec{
   637  					Replicas: ptr.To[int32](1),
   638  					Strategy: appsv1.DeploymentStrategy{
   639  						Type: appsv1.RollingUpdateDeploymentStrategyType,
   640  						RollingUpdate: &appsv1.RollingUpdateDeployment{
   641  							MaxSurge:       &defaultIntOrString,
   642  							MaxUnavailable: &defaultIntOrString,
   643  						},
   644  					},
   645  					RevisionHistoryLimit:    ptr.To[int32](10),
   646  					ProgressDeadlineSeconds: ptr.To[int32](600),
   647  					Template:                defaultTemplate,
   648  				},
   649  			},
   650  		},
   651  		{
   652  			original: &appsv1.Deployment{
   653  				Spec: appsv1.DeploymentSpec{
   654  					Replicas: ptr.To[int32](5),
   655  					Strategy: appsv1.DeploymentStrategy{
   656  						RollingUpdate: &appsv1.RollingUpdateDeployment{
   657  							MaxSurge: &differentIntOrString,
   658  						},
   659  					},
   660  				},
   661  			},
   662  			expected: &appsv1.Deployment{
   663  				Spec: appsv1.DeploymentSpec{
   664  					Replicas: ptr.To[int32](5),
   665  					Strategy: appsv1.DeploymentStrategy{
   666  						Type: appsv1.RollingUpdateDeploymentStrategyType,
   667  						RollingUpdate: &appsv1.RollingUpdateDeployment{
   668  							MaxSurge:       &differentIntOrString,
   669  							MaxUnavailable: &defaultIntOrString,
   670  						},
   671  					},
   672  					RevisionHistoryLimit:    ptr.To[int32](10),
   673  					ProgressDeadlineSeconds: ptr.To[int32](600),
   674  					Template:                defaultTemplate,
   675  				},
   676  			},
   677  		},
   678  		{
   679  			original: &appsv1.Deployment{
   680  				Spec: appsv1.DeploymentSpec{
   681  					Replicas: ptr.To[int32](3),
   682  					Strategy: appsv1.DeploymentStrategy{
   683  						Type:          appsv1.RollingUpdateDeploymentStrategyType,
   684  						RollingUpdate: nil,
   685  					},
   686  				},
   687  			},
   688  			expected: &appsv1.Deployment{
   689  				Spec: appsv1.DeploymentSpec{
   690  					Replicas: ptr.To[int32](3),
   691  					Strategy: appsv1.DeploymentStrategy{
   692  						Type: appsv1.RollingUpdateDeploymentStrategyType,
   693  						RollingUpdate: &appsv1.RollingUpdateDeployment{
   694  							MaxSurge:       &defaultIntOrString,
   695  							MaxUnavailable: &defaultIntOrString,
   696  						},
   697  					},
   698  					RevisionHistoryLimit:    ptr.To[int32](10),
   699  					ProgressDeadlineSeconds: ptr.To[int32](600),
   700  					Template:                defaultTemplate,
   701  				},
   702  			},
   703  		},
   704  		{
   705  			original: &appsv1.Deployment{
   706  				Spec: appsv1.DeploymentSpec{
   707  					Replicas: ptr.To[int32](5),
   708  					Strategy: appsv1.DeploymentStrategy{
   709  						Type: appsv1.RecreateDeploymentStrategyType,
   710  					},
   711  					RevisionHistoryLimit: ptr.To[int32](0),
   712  				},
   713  			},
   714  			expected: &appsv1.Deployment{
   715  				Spec: appsv1.DeploymentSpec{
   716  					Replicas: ptr.To[int32](5),
   717  					Strategy: appsv1.DeploymentStrategy{
   718  						Type: appsv1.RecreateDeploymentStrategyType,
   719  					},
   720  					RevisionHistoryLimit:    ptr.To[int32](0),
   721  					ProgressDeadlineSeconds: ptr.To[int32](600),
   722  					Template:                defaultTemplate,
   723  				},
   724  			},
   725  		},
   726  		{
   727  			original: &appsv1.Deployment{
   728  				Spec: appsv1.DeploymentSpec{
   729  					Replicas: ptr.To[int32](5),
   730  					Strategy: appsv1.DeploymentStrategy{
   731  						Type: appsv1.RecreateDeploymentStrategyType,
   732  					},
   733  					ProgressDeadlineSeconds: ptr.To[int32](30),
   734  					RevisionHistoryLimit:    ptr.To[int32](2),
   735  				},
   736  			},
   737  			expected: &appsv1.Deployment{
   738  				Spec: appsv1.DeploymentSpec{
   739  					Replicas: ptr.To[int32](5),
   740  					Strategy: appsv1.DeploymentStrategy{
   741  						Type: appsv1.RecreateDeploymentStrategyType,
   742  					},
   743  					ProgressDeadlineSeconds: ptr.To[int32](30),
   744  					RevisionHistoryLimit:    ptr.To[int32](2),
   745  					Template:                defaultTemplate,
   746  				},
   747  			},
   748  		},
   749  	}
   750  
   751  	for _, test := range tests {
   752  		original := test.original
   753  		expected := test.expected
   754  		obj2 := roundTrip(t, runtime.Object(original))
   755  		got, ok := obj2.(*appsv1.Deployment)
   756  		if !ok {
   757  			t.Errorf("unexpected object: %v", got)
   758  			t.FailNow()
   759  		}
   760  		if !apiequality.Semantic.DeepEqual(got.Spec, expected.Spec) {
   761  			t.Errorf("object mismatch!\nexpected:\n\t%+v\ngot:\n\t%+v", got.Spec, expected.Spec)
   762  		}
   763  	}
   764  }
   765  
   766  func TestDefaultDeploymentAvailability(t *testing.T) {
   767  	d := roundTrip(t, runtime.Object(&appsv1.Deployment{})).(*appsv1.Deployment)
   768  
   769  	maxUnavailable, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*(d.Spec.Replicas)), false)
   770  	if err != nil {
   771  		t.Fatalf("unexpected error: %v", err)
   772  	}
   773  
   774  	if *(d.Spec.Replicas)-int32(maxUnavailable) <= 0 {
   775  		t.Fatalf("the default value of maxUnavailable can lead to no active replicas during rolling update")
   776  	}
   777  }
   778  
   779  func TestSetDefaultReplicaSetReplicas(t *testing.T) {
   780  	tests := []struct {
   781  		rs             appsv1.ReplicaSet
   782  		expectReplicas int32
   783  	}{
   784  		{
   785  			rs: appsv1.ReplicaSet{
   786  				Spec: appsv1.ReplicaSetSpec{
   787  					Template: v1.PodTemplateSpec{
   788  						ObjectMeta: metav1.ObjectMeta{
   789  							Labels: map[string]string{
   790  								"foo": "bar",
   791  							},
   792  						},
   793  					},
   794  				},
   795  			},
   796  			expectReplicas: 1,
   797  		},
   798  		{
   799  			rs: appsv1.ReplicaSet{
   800  				Spec: appsv1.ReplicaSetSpec{
   801  					Replicas: ptr.To[int32](0),
   802  					Template: v1.PodTemplateSpec{
   803  						ObjectMeta: metav1.ObjectMeta{
   804  							Labels: map[string]string{
   805  								"foo": "bar",
   806  							},
   807  						},
   808  					},
   809  				},
   810  			},
   811  			expectReplicas: 0,
   812  		},
   813  		{
   814  			rs: appsv1.ReplicaSet{
   815  				Spec: appsv1.ReplicaSetSpec{
   816  					Replicas: ptr.To[int32](3),
   817  					Template: v1.PodTemplateSpec{
   818  						ObjectMeta: metav1.ObjectMeta{
   819  							Labels: map[string]string{
   820  								"foo": "bar",
   821  							},
   822  						},
   823  					},
   824  				},
   825  			},
   826  			expectReplicas: 3,
   827  		},
   828  	}
   829  
   830  	for _, test := range tests {
   831  		rs := &test.rs
   832  		obj2 := roundTrip(t, runtime.Object(rs))
   833  		rs2, ok := obj2.(*appsv1.ReplicaSet)
   834  		if !ok {
   835  			t.Errorf("unexpected object: %v", rs2)
   836  			t.FailNow()
   837  		}
   838  		if rs2.Spec.Replicas == nil {
   839  			t.Errorf("unexpected nil Replicas")
   840  		} else if test.expectReplicas != *rs2.Spec.Replicas {
   841  			t.Errorf("expected: %d replicas, got: %d", test.expectReplicas, *rs2.Spec.Replicas)
   842  		}
   843  	}
   844  }
   845  
   846  func TestDefaultRequestIsNotSetForReplicaSet(t *testing.T) {
   847  	s := v1.PodSpec{}
   848  	s.Containers = []v1.Container{
   849  		{
   850  			Resources: v1.ResourceRequirements{
   851  				Limits: v1.ResourceList{
   852  					v1.ResourceCPU: resource.MustParse("100m"),
   853  				},
   854  			},
   855  		},
   856  	}
   857  	rs := &appsv1.ReplicaSet{
   858  		Spec: appsv1.ReplicaSetSpec{
   859  			Replicas: ptr.To[int32](3),
   860  			Template: v1.PodTemplateSpec{
   861  				ObjectMeta: metav1.ObjectMeta{
   862  					Labels: map[string]string{
   863  						"foo": "bar",
   864  					},
   865  				},
   866  				Spec: s,
   867  			},
   868  		},
   869  	}
   870  	output := roundTrip(t, runtime.Object(rs))
   871  	rs2 := output.(*appsv1.ReplicaSet)
   872  	defaultRequest := rs2.Spec.Template.Spec.Containers[0].Resources.Requests
   873  	requestValue := defaultRequest[v1.ResourceCPU]
   874  	if requestValue.String() != "0" {
   875  		t.Errorf("Expected 0 request value, got: %s", requestValue.String())
   876  	}
   877  }
   878  
   879  func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
   880  	data, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(SchemeGroupVersion), obj)
   881  	if err != nil {
   882  		t.Errorf("%v\n %#v", err, obj)
   883  		return nil
   884  	}
   885  	obj2, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), data)
   886  	if err != nil {
   887  		t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj)
   888  		return nil
   889  	}
   890  	obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object)
   891  	err = legacyscheme.Scheme.Convert(obj2, obj3, nil)
   892  	if err != nil {
   893  		t.Errorf("%v\nSource: %#v", err, obj2)
   894  		return nil
   895  	}
   896  	return obj3
   897  }
   898  

View as plain text