k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/apis/core/v1/defaults_test.go (about) 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 v1_test 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "reflect" 23 "strings" 24 "testing" 25 26 "github.com/google/go-cmp/cmp" 27 v1 "k8s.io/api/core/v1" 28 "k8s.io/apimachinery/pkg/api/resource" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/runtime" 31 "k8s.io/apimachinery/pkg/util/intstr" 32 utilfeature "k8s.io/apiserver/pkg/util/feature" 33 featuregatetesting "k8s.io/component-base/featuregate/testing" 34 "k8s.io/kubernetes/pkg/api/legacyscheme" 35 corev1 "k8s.io/kubernetes/pkg/apis/core/v1" 36 "k8s.io/kubernetes/pkg/features" 37 utilpointer "k8s.io/utils/pointer" 38 39 // ensure types are installed 40 _ "k8s.io/kubernetes/pkg/apis/core/install" 41 ) 42 43 // TestWorkloadDefaults detects changes to defaults within PodTemplateSpec. 44 // Defaulting changes within this type can cause spurious rollouts of workloads on API server update. 45 func TestWorkloadDefaults(t *testing.T) { 46 t.Run("enabled_features", func(t *testing.T) { testWorkloadDefaults(t, true) }) 47 t.Run("disabled_features", func(t *testing.T) { testWorkloadDefaults(t, false) }) 48 } 49 func testWorkloadDefaults(t *testing.T, featuresEnabled bool) { 50 allFeatures := utilfeature.DefaultFeatureGate.DeepCopy().GetAll() 51 for feature, featureSpec := range allFeatures { 52 if !featureSpec.LockToDefault { 53 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, feature, featuresEnabled) 54 } 55 } 56 // New defaults under PodTemplateSpec are only acceptable if they would not be applied when reading data from a previous release. 57 // Forbidden: adding a new field `MyField *bool` and defaulting it to a non-nil value 58 // Forbidden: defaulting an existing field `MyField *bool` when it was previously not defaulted 59 // Forbidden: changing an existing default value 60 // Allowed: adding a new field `MyContainer *MyType` and defaulting a child of that type (e.g. `MyContainer.MyChildField`) if and only if MyContainer is non-nil 61 expectedDefaults := map[string]string{ 62 ".Spec.Containers[0].Env[0].ValueFrom.FieldRef.APIVersion": `"v1"`, 63 ".Spec.Containers[0].ImagePullPolicy": `"IfNotPresent"`, 64 ".Spec.Containers[0].Lifecycle.PostStart.HTTPGet.Path": `"/"`, 65 ".Spec.Containers[0].Lifecycle.PostStart.HTTPGet.Scheme": `"HTTP"`, 66 ".Spec.Containers[0].Lifecycle.PreStop.HTTPGet.Path": `"/"`, 67 ".Spec.Containers[0].Lifecycle.PreStop.HTTPGet.Scheme": `"HTTP"`, 68 ".Spec.Containers[0].LivenessProbe.FailureThreshold": `3`, 69 ".Spec.Containers[0].LivenessProbe.ProbeHandler.HTTPGet.Path": `"/"`, 70 ".Spec.Containers[0].LivenessProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 71 ".Spec.Containers[0].LivenessProbe.PeriodSeconds": `10`, 72 ".Spec.Containers[0].LivenessProbe.SuccessThreshold": `1`, 73 ".Spec.Containers[0].LivenessProbe.TimeoutSeconds": `1`, 74 ".Spec.Containers[0].Ports[0].Protocol": `"TCP"`, 75 ".Spec.Containers[0].ReadinessProbe.FailureThreshold": `3`, 76 ".Spec.Containers[0].ReadinessProbe.ProbeHandler.HTTPGet.Path": `"/"`, 77 ".Spec.Containers[0].ReadinessProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 78 ".Spec.Containers[0].ReadinessProbe.PeriodSeconds": `10`, 79 ".Spec.Containers[0].ReadinessProbe.SuccessThreshold": `1`, 80 ".Spec.Containers[0].ReadinessProbe.TimeoutSeconds": `1`, 81 ".Spec.Containers[0].StartupProbe.FailureThreshold": "3", 82 ".Spec.Containers[0].StartupProbe.ProbeHandler.HTTPGet.Path": `"/"`, 83 ".Spec.Containers[0].StartupProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 84 ".Spec.Containers[0].StartupProbe.PeriodSeconds": "10", 85 ".Spec.Containers[0].StartupProbe.SuccessThreshold": "1", 86 ".Spec.Containers[0].StartupProbe.TimeoutSeconds": "1", 87 ".Spec.Containers[0].TerminationMessagePath": `"/dev/termination-log"`, 88 ".Spec.Containers[0].TerminationMessagePolicy": `"File"`, 89 ".Spec.Containers[0].LivenessProbe.ProbeHandler.GRPC.Service": `""`, 90 ".Spec.Containers[0].ReadinessProbe.ProbeHandler.GRPC.Service": `""`, 91 ".Spec.Containers[0].StartupProbe.ProbeHandler.GRPC.Service": `""`, 92 ".Spec.DNSPolicy": `"ClusterFirst"`, 93 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.ProbeHandler.HTTPGet.Path": `"/"`, 94 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 95 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.ProbeHandler.HTTPGet.Path": `"/"`, 96 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 97 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.ProbeHandler.HTTPGet.Path": `"/"`, 98 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 99 ".Spec.InitContainers[0].LivenessProbe.ProbeHandler.HTTPGet.Path": `"/"`, 100 ".Spec.InitContainers[0].LivenessProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 101 ".Spec.InitContainers[0].ReadinessProbe.ProbeHandler.HTTPGet.Path": `"/"`, 102 ".Spec.InitContainers[0].ReadinessProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 103 ".Spec.InitContainers[0].StartupProbe.ProbeHandler.HTTPGet.Path": `"/"`, 104 ".Spec.InitContainers[0].StartupProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 105 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.Env[0].ValueFrom.FieldRef.APIVersion": `"v1"`, 106 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ImagePullPolicy": `"IfNotPresent"`, 107 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.Lifecycle.PostStart.HTTPGet.Path": `"/"`, 108 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.Lifecycle.PostStart.HTTPGet.Scheme": `"HTTP"`, 109 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.Lifecycle.PreStop.HTTPGet.Path": `"/"`, 110 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.Lifecycle.PreStop.HTTPGet.Scheme": `"HTTP"`, 111 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.FailureThreshold": "3", 112 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.ProbeHandler.GRPC.Service": `""`, 113 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.PeriodSeconds": "10", 114 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.SuccessThreshold": "1", 115 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.TimeoutSeconds": "1", 116 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.Ports[0].Protocol": `"TCP"`, 117 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.FailureThreshold": "3", 118 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.ProbeHandler.GRPC.Service": `""`, 119 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.PeriodSeconds": "10", 120 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.SuccessThreshold": "1", 121 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.TimeoutSeconds": "1", 122 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.FailureThreshold": "3", 123 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.ProbeHandler.GRPC.Service": `""`, 124 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.PeriodSeconds": "10", 125 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.SuccessThreshold": "1", 126 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.TimeoutSeconds": "1", 127 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.TerminationMessagePath": `"/dev/termination-log"`, 128 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.TerminationMessagePolicy": `"File"`, 129 ".Spec.InitContainers[0].Env[0].ValueFrom.FieldRef.APIVersion": `"v1"`, 130 ".Spec.InitContainers[0].ImagePullPolicy": `"IfNotPresent"`, 131 ".Spec.InitContainers[0].Lifecycle.PostStart.HTTPGet.Path": `"/"`, 132 ".Spec.InitContainers[0].Lifecycle.PostStart.HTTPGet.Scheme": `"HTTP"`, 133 ".Spec.InitContainers[0].Lifecycle.PreStop.HTTPGet.Path": `"/"`, 134 ".Spec.InitContainers[0].Lifecycle.PreStop.HTTPGet.Scheme": `"HTTP"`, 135 ".Spec.InitContainers[0].LivenessProbe.FailureThreshold": `3`, 136 ".Spec.InitContainers[0].LivenessProbe.ProbeHandler.GRPC.Service": `""`, 137 ".Spec.InitContainers[0].LivenessProbe.PeriodSeconds": `10`, 138 ".Spec.InitContainers[0].LivenessProbe.SuccessThreshold": `1`, 139 ".Spec.InitContainers[0].LivenessProbe.TimeoutSeconds": `1`, 140 ".Spec.InitContainers[0].Ports[0].Protocol": `"TCP"`, 141 ".Spec.InitContainers[0].ReadinessProbe.FailureThreshold": `3`, 142 ".Spec.InitContainers[0].ReadinessProbe.ProbeHandler.GRPC.Service": `""`, 143 ".Spec.InitContainers[0].ReadinessProbe.PeriodSeconds": `10`, 144 ".Spec.InitContainers[0].ReadinessProbe.SuccessThreshold": `1`, 145 ".Spec.InitContainers[0].ReadinessProbe.TimeoutSeconds": `1`, 146 ".Spec.InitContainers[0].StartupProbe.FailureThreshold": "3", 147 ".Spec.InitContainers[0].StartupProbe.ProbeHandler.GRPC.Service": `""`, 148 ".Spec.InitContainers[0].StartupProbe.PeriodSeconds": "10", 149 ".Spec.InitContainers[0].StartupProbe.SuccessThreshold": "1", 150 ".Spec.InitContainers[0].StartupProbe.TimeoutSeconds": "1", 151 ".Spec.InitContainers[0].TerminationMessagePath": `"/dev/termination-log"`, 152 ".Spec.InitContainers[0].TerminationMessagePolicy": `"File"`, 153 ".Spec.RestartPolicy": `"Always"`, 154 ".Spec.SchedulerName": `"default-scheduler"`, 155 ".Spec.SecurityContext": `{}`, 156 ".Spec.TerminationGracePeriodSeconds": `30`, 157 ".Spec.Volumes[0].VolumeSource.AzureDisk.CachingMode": `"ReadWrite"`, 158 ".Spec.Volumes[0].VolumeSource.AzureDisk.FSType": `"ext4"`, 159 ".Spec.Volumes[0].VolumeSource.AzureDisk.Kind": `"Shared"`, 160 ".Spec.Volumes[0].VolumeSource.AzureDisk.ReadOnly": `false`, 161 ".Spec.Volumes[0].VolumeSource.ConfigMap.DefaultMode": `420`, 162 ".Spec.Volumes[0].VolumeSource.DownwardAPI.DefaultMode": `420`, 163 ".Spec.Volumes[0].VolumeSource.DownwardAPI.Items[0].FieldRef.APIVersion": `"v1"`, 164 ".Spec.Volumes[0].VolumeSource.EmptyDir": `{}`, 165 ".Spec.Volumes[0].VolumeSource.Ephemeral.VolumeClaimTemplate.Spec.VolumeMode": `"Filesystem"`, 166 ".Spec.Volumes[0].VolumeSource.HostPath.Type": `""`, 167 ".Spec.Volumes[0].VolumeSource.ISCSI.ISCSIInterface": `"default"`, 168 ".Spec.Volumes[0].VolumeSource.Projected.DefaultMode": `420`, 169 ".Spec.Volumes[0].VolumeSource.Projected.Sources[0].DownwardAPI.Items[0].FieldRef.APIVersion": `"v1"`, 170 ".Spec.Volumes[0].VolumeSource.Projected.Sources[0].ServiceAccountToken.ExpirationSeconds": `3600`, 171 ".Spec.Volumes[0].VolumeSource.RBD.Keyring": `"/etc/ceph/keyring"`, 172 ".Spec.Volumes[0].VolumeSource.RBD.RBDPool": `"rbd"`, 173 ".Spec.Volumes[0].VolumeSource.RBD.RadosUser": `"admin"`, 174 ".Spec.Volumes[0].VolumeSource.ScaleIO.FSType": `"xfs"`, 175 ".Spec.Volumes[0].VolumeSource.ScaleIO.StorageMode": `"ThinProvisioned"`, 176 ".Spec.Volumes[0].VolumeSource.Secret.DefaultMode": `420`, 177 } 178 t.Run("empty PodTemplateSpec", func(t *testing.T) { 179 rc := &v1.ReplicationController{Spec: v1.ReplicationControllerSpec{Template: &v1.PodTemplateSpec{}}} 180 template := rc.Spec.Template 181 defaults := detectDefaults(t, rc, reflect.ValueOf(template)) 182 if !reflect.DeepEqual(expectedDefaults, defaults) { 183 t.Errorf("Defaults for PodTemplateSpec changed. This can cause spurious rollouts of workloads on API server upgrade.") 184 t.Logf(cmp.Diff(expectedDefaults, defaults)) 185 } 186 }) 187 t.Run("hostnet PodTemplateSpec with ports", func(t *testing.T) { 188 rc := &v1.ReplicationController{ 189 Spec: v1.ReplicationControllerSpec{ 190 Template: &v1.PodTemplateSpec{ 191 Spec: v1.PodSpec{ 192 HostNetwork: true, 193 Containers: []v1.Container{{ 194 Ports: []v1.ContainerPort{{ 195 ContainerPort: 12345, 196 Protocol: v1.ProtocolTCP, 197 }}, 198 }}, 199 }, 200 }, 201 }, 202 } 203 template := rc.Spec.Template 204 defaults := detectDefaults(t, rc, reflect.ValueOf(template)) 205 expected := func() map[string]string { 206 // Set values that are known inputs 207 m := map[string]string{ 208 ".Spec.HostNetwork": "true", 209 ".Spec.Containers[0].Ports[0].ContainerPort": "12345", 210 } 211 m[".Spec.Containers"] = `[{"name":"","ports":[{"containerPort":12345,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent"}]` 212 m[".Spec.Containers[0].Ports"] = `[{"containerPort":12345,"protocol":"TCP"}]` 213 for k, v := range expectedDefaults { 214 if _, found := m[k]; !found { 215 m[k] = v 216 } 217 } 218 return m 219 }() 220 if !reflect.DeepEqual(expected, defaults) { 221 t.Errorf("Defaults for PodTemplateSpec changed. This can cause spurious rollouts of workloads on API server upgrade.") 222 t.Logf(cmp.Diff(expected, defaults)) 223 } 224 }) 225 } 226 227 // TestPodDefaults detects changes to defaults within PodSpec. 228 // Defaulting changes within this type (*especially* within containers) can cause kubelets to restart containers on API server update. 229 func TestPodDefaults(t *testing.T) { 230 t.Run("enabled_features", func(t *testing.T) { testPodDefaults(t, true) }) 231 t.Run("disabled_features", func(t *testing.T) { testPodDefaults(t, false) }) 232 } 233 func testPodDefaults(t *testing.T, featuresEnabled bool) { 234 features := utilfeature.DefaultFeatureGate.DeepCopy().GetAll() 235 for feature, featureSpec := range features { 236 if !featureSpec.LockToDefault { 237 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, feature, featuresEnabled) 238 } 239 } 240 pod := &v1.Pod{} 241 // New defaults under PodSpec are only acceptable if they would not be applied when reading data from a previous release. 242 // Forbidden: adding a new field `MyField *bool` and defaulting it to a non-nil value 243 // Forbidden: defaulting an existing field `MyField *bool` when it was previously not defaulted 244 // Forbidden: changing an existing default value 245 // Allowed: adding a new field `MyContainer *MyType` and defaulting a child of that type (e.g. `MyContainer.MyChildField`) if and only if MyContainer is non-nil 246 expectedDefaults := map[string]string{ 247 ".Spec.Containers[0].Env[0].ValueFrom.FieldRef.APIVersion": `"v1"`, 248 ".Spec.Containers[0].ImagePullPolicy": `"IfNotPresent"`, 249 ".Spec.Containers[0].Lifecycle.PostStart.HTTPGet.Path": `"/"`, 250 ".Spec.Containers[0].Lifecycle.PostStart.HTTPGet.Scheme": `"HTTP"`, 251 ".Spec.Containers[0].Lifecycle.PreStop.HTTPGet.Path": `"/"`, 252 ".Spec.Containers[0].Lifecycle.PreStop.HTTPGet.Scheme": `"HTTP"`, 253 ".Spec.Containers[0].LivenessProbe.FailureThreshold": `3`, 254 ".Spec.Containers[0].LivenessProbe.ProbeHandler.HTTPGet.Path": `"/"`, 255 ".Spec.Containers[0].LivenessProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 256 ".Spec.Containers[0].LivenessProbe.PeriodSeconds": `10`, 257 ".Spec.Containers[0].LivenessProbe.SuccessThreshold": `1`, 258 ".Spec.Containers[0].LivenessProbe.TimeoutSeconds": `1`, 259 ".Spec.Containers[0].Ports[0].Protocol": `"TCP"`, 260 ".Spec.Containers[0].ReadinessProbe.FailureThreshold": `3`, 261 ".Spec.Containers[0].ReadinessProbe.ProbeHandler.HTTPGet.Path": `"/"`, 262 ".Spec.Containers[0].ReadinessProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 263 ".Spec.Containers[0].ReadinessProbe.PeriodSeconds": `10`, 264 ".Spec.Containers[0].ReadinessProbe.SuccessThreshold": `1`, 265 ".Spec.Containers[0].ReadinessProbe.TimeoutSeconds": `1`, 266 ".Spec.Containers[0].Resources.Requests": `{"":"0"}`, // this gets defaulted from the limits field 267 ".Spec.Containers[0].StartupProbe.FailureThreshold": "3", 268 ".Spec.Containers[0].StartupProbe.ProbeHandler.HTTPGet.Path": `"/"`, 269 ".Spec.Containers[0].StartupProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 270 ".Spec.Containers[0].StartupProbe.PeriodSeconds": "10", 271 ".Spec.Containers[0].StartupProbe.SuccessThreshold": "1", 272 ".Spec.Containers[0].StartupProbe.TimeoutSeconds": "1", 273 ".Spec.Containers[0].TerminationMessagePath": `"/dev/termination-log"`, 274 ".Spec.Containers[0].TerminationMessagePolicy": `"File"`, 275 ".Spec.Containers[0].LivenessProbe.ProbeHandler.GRPC.Service": `""`, 276 ".Spec.Containers[0].ReadinessProbe.ProbeHandler.GRPC.Service": `""`, 277 ".Spec.Containers[0].StartupProbe.ProbeHandler.GRPC.Service": `""`, 278 ".Spec.DNSPolicy": `"ClusterFirst"`, 279 ".Spec.EnableServiceLinks": `true`, 280 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.Env[0].ValueFrom.FieldRef.APIVersion": `"v1"`, 281 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ImagePullPolicy": `"IfNotPresent"`, 282 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.Lifecycle.PostStart.HTTPGet.Path": `"/"`, 283 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.Lifecycle.PostStart.HTTPGet.Scheme": `"HTTP"`, 284 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.Lifecycle.PreStop.HTTPGet.Path": `"/"`, 285 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.Lifecycle.PreStop.HTTPGet.Scheme": `"HTTP"`, 286 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.FailureThreshold": "3", 287 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.PeriodSeconds": "10", 288 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.SuccessThreshold": "1", 289 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.TimeoutSeconds": "1", 290 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.Ports[0].Protocol": `"TCP"`, 291 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.FailureThreshold": "3", 292 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.PeriodSeconds": "10", 293 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.SuccessThreshold": "1", 294 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.TimeoutSeconds": "1", 295 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.FailureThreshold": "3", 296 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.PeriodSeconds": "10", 297 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.SuccessThreshold": "1", 298 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.TimeoutSeconds": "1", 299 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.TerminationMessagePath": `"/dev/termination-log"`, 300 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.TerminationMessagePolicy": `"File"`, 301 ".Spec.InitContainers[0].Env[0].ValueFrom.FieldRef.APIVersion": `"v1"`, 302 ".Spec.InitContainers[0].ImagePullPolicy": `"IfNotPresent"`, 303 ".Spec.InitContainers[0].Lifecycle.PostStart.HTTPGet.Path": `"/"`, 304 ".Spec.InitContainers[0].Lifecycle.PostStart.HTTPGet.Scheme": `"HTTP"`, 305 ".Spec.InitContainers[0].Lifecycle.PreStop.HTTPGet.Path": `"/"`, 306 ".Spec.InitContainers[0].Lifecycle.PreStop.HTTPGet.Scheme": `"HTTP"`, 307 ".Spec.InitContainers[0].LivenessProbe.FailureThreshold": `3`, 308 ".Spec.InitContainers[0].LivenessProbe.PeriodSeconds": `10`, 309 ".Spec.InitContainers[0].LivenessProbe.SuccessThreshold": `1`, 310 ".Spec.InitContainers[0].LivenessProbe.TimeoutSeconds": `1`, 311 ".Spec.InitContainers[0].Ports[0].Protocol": `"TCP"`, 312 ".Spec.InitContainers[0].ReadinessProbe.FailureThreshold": `3`, 313 ".Spec.InitContainers[0].ReadinessProbe.PeriodSeconds": `10`, 314 ".Spec.InitContainers[0].ReadinessProbe.SuccessThreshold": `1`, 315 ".Spec.InitContainers[0].ReadinessProbe.TimeoutSeconds": `1`, 316 ".Spec.InitContainers[0].Resources.Requests": `{"":"0"}`, // this gets defaulted from the limits field 317 ".Spec.InitContainers[0].TerminationMessagePath": `"/dev/termination-log"`, 318 ".Spec.InitContainers[0].TerminationMessagePolicy": `"File"`, 319 ".Spec.InitContainers[0].StartupProbe.FailureThreshold": "3", 320 ".Spec.InitContainers[0].StartupProbe.PeriodSeconds": "10", 321 ".Spec.InitContainers[0].StartupProbe.SuccessThreshold": "1", 322 ".Spec.InitContainers[0].StartupProbe.TimeoutSeconds": "1", 323 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.ProbeHandler.GRPC.Service": `""`, 324 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.ProbeHandler.HTTPGet.Path": `"/"`, 325 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.LivenessProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 326 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.ProbeHandler.GRPC.Service": `""`, 327 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.ProbeHandler.HTTPGet.Path": `"/"`, 328 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.ReadinessProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 329 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.ProbeHandler.GRPC.Service": `""`, 330 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.ProbeHandler.HTTPGet.Path": `"/"`, 331 ".Spec.EphemeralContainers[0].EphemeralContainerCommon.StartupProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 332 ".Spec.InitContainers[0].LivenessProbe.ProbeHandler.GRPC.Service": `""`, 333 ".Spec.InitContainers[0].LivenessProbe.ProbeHandler.HTTPGet.Path": `"/"`, 334 ".Spec.InitContainers[0].LivenessProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 335 ".Spec.InitContainers[0].ReadinessProbe.ProbeHandler.GRPC.Service": `""`, 336 ".Spec.InitContainers[0].ReadinessProbe.ProbeHandler.HTTPGet.Path": `"/"`, 337 ".Spec.InitContainers[0].ReadinessProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 338 ".Spec.InitContainers[0].StartupProbe.ProbeHandler.GRPC.Service": `""`, 339 ".Spec.InitContainers[0].StartupProbe.ProbeHandler.HTTPGet.Path": `"/"`, 340 ".Spec.InitContainers[0].StartupProbe.ProbeHandler.HTTPGet.Scheme": `"HTTP"`, 341 ".Spec.RestartPolicy": `"Always"`, 342 ".Spec.SchedulerName": `"default-scheduler"`, 343 ".Spec.SecurityContext": `{}`, 344 ".Spec.TerminationGracePeriodSeconds": `30`, 345 ".Spec.Volumes[0].VolumeSource.AzureDisk.CachingMode": `"ReadWrite"`, 346 ".Spec.Volumes[0].VolumeSource.AzureDisk.FSType": `"ext4"`, 347 ".Spec.Volumes[0].VolumeSource.AzureDisk.Kind": `"Shared"`, 348 ".Spec.Volumes[0].VolumeSource.AzureDisk.ReadOnly": `false`, 349 ".Spec.Volumes[0].VolumeSource.ConfigMap.DefaultMode": `420`, 350 ".Spec.Volumes[0].VolumeSource.DownwardAPI.DefaultMode": `420`, 351 ".Spec.Volumes[0].VolumeSource.DownwardAPI.Items[0].FieldRef.APIVersion": `"v1"`, 352 ".Spec.Volumes[0].VolumeSource.EmptyDir": `{}`, 353 ".Spec.Volumes[0].VolumeSource.Ephemeral.VolumeClaimTemplate.Spec.VolumeMode": `"Filesystem"`, 354 ".Spec.Volumes[0].VolumeSource.HostPath.Type": `""`, 355 ".Spec.Volumes[0].VolumeSource.ISCSI.ISCSIInterface": `"default"`, 356 ".Spec.Volumes[0].VolumeSource.Projected.DefaultMode": `420`, 357 ".Spec.Volumes[0].VolumeSource.Projected.Sources[0].DownwardAPI.Items[0].FieldRef.APIVersion": `"v1"`, 358 ".Spec.Volumes[0].VolumeSource.Projected.Sources[0].ServiceAccountToken.ExpirationSeconds": `3600`, 359 ".Spec.Volumes[0].VolumeSource.RBD.Keyring": `"/etc/ceph/keyring"`, 360 ".Spec.Volumes[0].VolumeSource.RBD.RBDPool": `"rbd"`, 361 ".Spec.Volumes[0].VolumeSource.RBD.RadosUser": `"admin"`, 362 ".Spec.Volumes[0].VolumeSource.ScaleIO.FSType": `"xfs"`, 363 ".Spec.Volumes[0].VolumeSource.ScaleIO.StorageMode": `"ThinProvisioned"`, 364 ".Spec.Volumes[0].VolumeSource.Secret.DefaultMode": `420`, 365 } 366 defaults := detectDefaults(t, pod, reflect.ValueOf(pod)) 367 if !reflect.DeepEqual(expectedDefaults, defaults) { 368 t.Errorf("Defaults for PodSpec changed. This can cause spurious restarts of containers on API server upgrade.") 369 t.Logf(cmp.Diff(expectedDefaults, defaults)) 370 } 371 } 372 373 func TestPodHostNetworkDefaults(t *testing.T) { 374 cases := []struct { 375 name string 376 hostNet bool 377 expectPodDefault bool 378 expectPodSpecDefault bool 379 }{{ 380 name: "hostNetwork=false", 381 hostNet: false, 382 expectPodDefault: false, 383 expectPodSpecDefault: false, 384 }, { 385 name: "hostNetwork=true", 386 hostNet: true, 387 expectPodDefault: true, 388 expectPodSpecDefault: false, 389 }} 390 391 for _, tc := range cases { 392 t.Run(tc.name, func(t *testing.T) { 393 const portNum = 12345 394 spec := v1.PodSpec{ 395 HostNetwork: tc.hostNet, 396 Containers: []v1.Container{{ 397 Ports: []v1.ContainerPort{{ 398 ContainerPort: portNum, 399 Protocol: v1.ProtocolTCP, 400 // Note: HostPort is not set 401 }}, 402 }}, 403 } 404 405 // Test Pod defaulting. 406 p := v1.Pod{Spec: *spec.DeepCopy()} 407 corev1.SetDefaults_Pod(&p) 408 if got := p.Spec.Containers[0].Ports[0].HostPort; tc.expectPodDefault && got == 0 { 409 t.Errorf("expected Pod HostPort to be defaulted, got %v", got) 410 } 411 if got := p.Spec.Containers[0].Ports[0].HostPort; !tc.expectPodDefault && got != 0 { 412 t.Errorf("expected Pod HostPort to be 0, got %v", got) 413 } 414 415 // Test PodSpec defaulting. 416 s := spec.DeepCopy() 417 corev1.SetDefaults_PodSpec(s) 418 if got := s.Containers[0].Ports[0].HostPort; tc.expectPodSpecDefault && got == 0 { 419 t.Errorf("expected PodSpec HostPort to be defaulted, got %v", got) 420 } 421 if got := s.Containers[0].Ports[0].HostPort; !tc.expectPodSpecDefault && got != 0 { 422 t.Errorf("expected PodSpec HostPort to be 0, got %v", got) 423 } 424 }) 425 } 426 } 427 428 type testPath struct { 429 path string 430 value reflect.Value 431 } 432 433 func detectDefaults(t *testing.T, obj runtime.Object, v reflect.Value) map[string]string { 434 defaults := map[string]string{} 435 toVisit := []testPath{{path: "", value: v}} 436 437 for len(toVisit) > 0 { 438 visit := toVisit[0] 439 toVisit = toVisit[1:] 440 441 legacyscheme.Scheme.Default(obj) 442 defaultedV := visit.value 443 zeroV := reflect.Zero(visit.value.Type()) 444 445 switch { 446 case visit.value.Kind() == reflect.Struct: 447 for fi := 0; fi < visit.value.NumField(); fi++ { 448 structField := visit.value.Type().Field(fi) 449 valueField := visit.value.Field(fi) 450 if valueField.CanSet() { 451 toVisit = append(toVisit, testPath{path: visit.path + "." + structField.Name, value: valueField}) 452 } 453 } 454 455 case visit.value.Kind() == reflect.Slice: 456 if !visit.value.IsNil() { 457 // if we already have a value, we either got defaulted or there 458 // was a fixed input - flag it and see if we can descend 459 // anyway. 460 marshaled, _ := json.Marshal(defaultedV.Interface()) 461 defaults[visit.path] = string(marshaled) 462 toVisit = append(toVisit, testPath{path: visit.path + "[0]", value: visit.value.Index(0)}) 463 } else if visit.value.Type().Elem().Kind() == reflect.Struct { 464 if strings.HasPrefix(visit.path, ".ObjectMeta.ManagedFields[") { 465 break 466 } 467 // if we don't already have a value, and contain structs, add an empty item so we can recurse 468 item := reflect.New(visit.value.Type().Elem()).Elem() 469 visit.value.Set(reflect.Append(visit.value, item)) 470 toVisit = append(toVisit, testPath{path: visit.path + "[0]", value: visit.value.Index(0)}) 471 } else if !isPrimitive(visit.value.Type().Elem().Kind()) { 472 t.Logf("unhandled non-primitive slice type %s: %s", visit.path, visit.value.Type().Elem()) 473 } 474 475 case visit.value.Kind() == reflect.Map: 476 if !visit.value.IsNil() { 477 // if we already have a value, we got defaulted 478 marshaled, _ := json.Marshal(defaultedV.Interface()) 479 defaults[visit.path] = string(marshaled) 480 } else if visit.value.Type().Key().Kind() == reflect.String && visit.value.Type().Elem().Kind() == reflect.Struct { 481 if strings.HasPrefix(visit.path, ".ObjectMeta.ManagedFields[") { 482 break 483 } 484 // if we don't already have a value, and contain structs, add an empty item so we can recurse 485 item := reflect.New(visit.value.Type().Elem()).Elem() 486 visit.value.Set(reflect.MakeMap(visit.value.Type())) 487 visit.value.SetMapIndex(reflect.New(visit.value.Type().Key()).Elem(), item) 488 toVisit = append(toVisit, testPath{path: visit.path + "[*]", value: item}) 489 } else if !isPrimitive(visit.value.Type().Elem().Kind()) { 490 t.Logf("unhandled non-primitive map type %s: %s", visit.path, visit.value.Type().Elem()) 491 } 492 493 case visit.value.Kind() == reflect.Pointer: 494 if visit.value.IsNil() { 495 if visit.value.Type().Elem().Kind() == reflect.Struct { 496 visit.value.Set(reflect.New(visit.value.Type().Elem())) 497 toVisit = append(toVisit, testPath{path: visit.path, value: visit.value.Elem()}) 498 } else if !isPrimitive(visit.value.Type().Elem().Kind()) { 499 t.Errorf("unhandled non-primitive nil ptr: %s: %s", visit.path, visit.value.Type()) 500 } 501 } else { 502 if visit.path != "" { 503 marshaled, _ := json.Marshal(defaultedV.Interface()) 504 defaults[visit.path] = string(marshaled) 505 } 506 toVisit = append(toVisit, testPath{path: visit.path, value: visit.value.Elem()}) 507 } 508 509 case isPrimitive(visit.value.Kind()): 510 if !reflect.DeepEqual(defaultedV.Interface(), zeroV.Interface()) { 511 marshaled, _ := json.Marshal(defaultedV.Interface()) 512 defaults[visit.path] = string(marshaled) 513 } 514 515 default: 516 t.Errorf("unhandled kind: %s: %s", visit.path, visit.value.Type()) 517 } 518 519 } 520 return defaults 521 } 522 523 func isPrimitive(k reflect.Kind) bool { 524 switch k { 525 case reflect.String, reflect.Bool, reflect.Int32, reflect.Int64, reflect.Int: 526 return true 527 default: 528 return false 529 } 530 } 531 532 func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { 533 codec := legacyscheme.Codecs.LegacyCodec(corev1.SchemeGroupVersion) 534 data, err := runtime.Encode(codec, obj) 535 if err != nil { 536 t.Errorf("%v\n %#v", err, obj) 537 return nil 538 } 539 obj2, err := runtime.Decode(codec, data) 540 if err != nil { 541 t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) 542 return nil 543 } 544 obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object) 545 err = legacyscheme.Scheme.Convert(obj2, obj3, nil) 546 if err != nil { 547 t.Errorf("%v\nSource: %#v", err, obj2) 548 return nil 549 } 550 return obj3 551 } 552 553 func TestSetDefaultReplicationController(t *testing.T) { 554 tests := []struct { 555 rc *v1.ReplicationController 556 expectLabels bool 557 expectSelector bool 558 }{ 559 { 560 rc: &v1.ReplicationController{ 561 Spec: v1.ReplicationControllerSpec{ 562 Template: &v1.PodTemplateSpec{ 563 ObjectMeta: metav1.ObjectMeta{ 564 Labels: map[string]string{ 565 "foo": "bar", 566 }, 567 }, 568 }, 569 }, 570 }, 571 expectLabels: true, 572 expectSelector: true, 573 }, 574 { 575 rc: &v1.ReplicationController{ 576 ObjectMeta: metav1.ObjectMeta{ 577 Labels: map[string]string{ 578 "bar": "foo", 579 }, 580 }, 581 Spec: v1.ReplicationControllerSpec{ 582 Template: &v1.PodTemplateSpec{ 583 ObjectMeta: metav1.ObjectMeta{ 584 Labels: map[string]string{ 585 "foo": "bar", 586 }, 587 }, 588 }, 589 }, 590 }, 591 expectLabels: false, 592 expectSelector: true, 593 }, 594 { 595 rc: &v1.ReplicationController{ 596 ObjectMeta: metav1.ObjectMeta{ 597 Labels: map[string]string{ 598 "bar": "foo", 599 }, 600 }, 601 Spec: v1.ReplicationControllerSpec{ 602 Selector: map[string]string{ 603 "some": "other", 604 }, 605 Template: &v1.PodTemplateSpec{ 606 ObjectMeta: metav1.ObjectMeta{ 607 Labels: map[string]string{ 608 "foo": "bar", 609 }, 610 }, 611 }, 612 }, 613 }, 614 expectLabels: false, 615 expectSelector: false, 616 }, 617 { 618 rc: &v1.ReplicationController{ 619 Spec: v1.ReplicationControllerSpec{ 620 Selector: map[string]string{ 621 "some": "other", 622 }, 623 Template: &v1.PodTemplateSpec{ 624 ObjectMeta: metav1.ObjectMeta{ 625 Labels: map[string]string{ 626 "foo": "bar", 627 }, 628 }, 629 }, 630 }, 631 }, 632 expectLabels: true, 633 expectSelector: false, 634 }, 635 } 636 637 for _, test := range tests { 638 rc := test.rc 639 obj2 := roundTrip(t, runtime.Object(rc)) 640 rc2, ok := obj2.(*v1.ReplicationController) 641 if !ok { 642 t.Errorf("unexpected object: %v", rc2) 643 t.FailNow() 644 } 645 if test.expectSelector != reflect.DeepEqual(rc2.Spec.Selector, rc2.Spec.Template.Labels) { 646 if test.expectSelector { 647 t.Errorf("expected: %v, got: %v", rc2.Spec.Template.Labels, rc2.Spec.Selector) 648 } else { 649 t.Errorf("unexpected equality: %v", rc.Spec.Selector) 650 } 651 } 652 if test.expectLabels != reflect.DeepEqual(rc2.Labels, rc2.Spec.Template.Labels) { 653 if test.expectLabels { 654 t.Errorf("expected: %v, got: %v", rc2.Spec.Template.Labels, rc2.Labels) 655 } else { 656 t.Errorf("unexpected equality: %v", rc.Labels) 657 } 658 } 659 } 660 } 661 662 func TestSetDefaultReplicationControllerReplicas(t *testing.T) { 663 tests := []struct { 664 rc v1.ReplicationController 665 expectReplicas int32 666 }{ 667 { 668 rc: v1.ReplicationController{ 669 Spec: v1.ReplicationControllerSpec{ 670 Template: &v1.PodTemplateSpec{ 671 ObjectMeta: metav1.ObjectMeta{ 672 Labels: map[string]string{ 673 "foo": "bar", 674 }, 675 }, 676 }, 677 }, 678 }, 679 expectReplicas: 1, 680 }, 681 { 682 rc: v1.ReplicationController{ 683 Spec: v1.ReplicationControllerSpec{ 684 Replicas: utilpointer.Int32(0), 685 Template: &v1.PodTemplateSpec{ 686 ObjectMeta: metav1.ObjectMeta{ 687 Labels: map[string]string{ 688 "foo": "bar", 689 }, 690 }, 691 }, 692 }, 693 }, 694 expectReplicas: 0, 695 }, 696 { 697 rc: v1.ReplicationController{ 698 Spec: v1.ReplicationControllerSpec{ 699 Replicas: utilpointer.Int32(3), 700 Template: &v1.PodTemplateSpec{ 701 ObjectMeta: metav1.ObjectMeta{ 702 Labels: map[string]string{ 703 "foo": "bar", 704 }, 705 }, 706 }, 707 }, 708 }, 709 expectReplicas: 3, 710 }, 711 } 712 713 for _, test := range tests { 714 rc := &test.rc 715 obj2 := roundTrip(t, runtime.Object(rc)) 716 rc2, ok := obj2.(*v1.ReplicationController) 717 if !ok { 718 t.Errorf("unexpected object: %v", rc2) 719 t.FailNow() 720 } 721 if rc2.Spec.Replicas == nil { 722 t.Errorf("unexpected nil Replicas") 723 } else if test.expectReplicas != *rc2.Spec.Replicas { 724 t.Errorf("expected: %d replicas, got: %d", test.expectReplicas, *rc2.Spec.Replicas) 725 } 726 } 727 } 728 729 type InitContainerValidator func(got, expected *v1.Container) error 730 731 func TestSetDefaultReplicationControllerInitContainers(t *testing.T) { 732 assertEnvFieldRef := func(got, expected *v1.Container) error { 733 if len(got.Env) != len(expected.Env) { 734 return fmt.Errorf("different number of env: got <%v>, expected <%v>", len(got.Env), len(expected.Env)) 735 } 736 737 for j := range got.Env { 738 ge := &got.Env[j] 739 ee := &expected.Env[j] 740 741 if ge.Name != ee.Name { 742 return fmt.Errorf("different name of env: got <%v>, expected <%v>", ge.Name, ee.Name) 743 } 744 745 if ge.ValueFrom.FieldRef.APIVersion != ee.ValueFrom.FieldRef.APIVersion { 746 return fmt.Errorf("different api version of FieldRef <%v>: got <%v>, expected <%v>", 747 ge.Name, ge.ValueFrom.FieldRef.APIVersion, ee.ValueFrom.FieldRef.APIVersion) 748 } 749 } 750 return nil 751 } 752 753 assertImagePullPolicy := func(got, expected *v1.Container) error { 754 if got.ImagePullPolicy != expected.ImagePullPolicy { 755 return fmt.Errorf("different image pull policy: got <%v>, expected <%v>", got.ImagePullPolicy, expected.ImagePullPolicy) 756 } 757 return nil 758 } 759 760 assertContainerPort := func(got, expected *v1.Container) error { 761 if len(got.Ports) != len(expected.Ports) { 762 return fmt.Errorf("different number of ports: got <%v>, expected <%v>", len(got.Ports), len(expected.Ports)) 763 } 764 765 for i := range got.Ports { 766 gp := &got.Ports[i] 767 ep := &expected.Ports[i] 768 769 if gp.Name != ep.Name { 770 return fmt.Errorf("different name of port: got <%v>, expected <%v>", gp.Name, ep.Name) 771 } 772 773 if gp.Protocol != ep.Protocol { 774 return fmt.Errorf("different port protocol <%v>: got <%v>, expected <%v>", gp.Name, gp.Protocol, ep.Protocol) 775 } 776 } 777 778 return nil 779 } 780 781 assertResource := func(got, expected *v1.Container) error { 782 if len(got.Resources.Limits) != len(expected.Resources.Limits) { 783 return fmt.Errorf("different number of resources.Limits: got <%v>, expected <%v>", len(got.Resources.Limits), (expected.Resources.Limits)) 784 } 785 786 for k, v := range got.Resources.Limits { 787 if ev, found := expected.Resources.Limits[v1.ResourceName(k)]; !found { 788 return fmt.Errorf("failed to find resource <%v> in expected resources.Limits.", k) 789 } else { 790 if ev.Value() != v.Value() { 791 return fmt.Errorf("different resource.Limits: got <%v>, expected <%v>.", v.Value(), ev.Value()) 792 } 793 } 794 } 795 796 if len(got.Resources.Requests) != len(expected.Resources.Requests) { 797 return fmt.Errorf("different number of resources.Requests: got <%v>, expected <%v>", len(got.Resources.Requests), (expected.Resources.Requests)) 798 } 799 800 for k, v := range got.Resources.Requests { 801 if ev, found := expected.Resources.Requests[v1.ResourceName(k)]; !found { 802 return fmt.Errorf("failed to find resource <%v> in expected resources.Requests.", k) 803 } else { 804 if ev.Value() != v.Value() { 805 return fmt.Errorf("different resource.Requests: got <%v>, expected <%v>.", v.Value(), ev.Value()) 806 } 807 } 808 } 809 810 return nil 811 } 812 813 assertProb := func(got, expected *v1.Container) error { 814 // Assert LivenessProbe 815 if got.LivenessProbe.ProbeHandler.HTTPGet.Path != expected.LivenessProbe.ProbeHandler.HTTPGet.Path || 816 got.LivenessProbe.ProbeHandler.HTTPGet.Scheme != expected.LivenessProbe.ProbeHandler.HTTPGet.Scheme || 817 got.LivenessProbe.FailureThreshold != expected.LivenessProbe.FailureThreshold || 818 got.LivenessProbe.SuccessThreshold != expected.LivenessProbe.SuccessThreshold || 819 got.LivenessProbe.PeriodSeconds != expected.LivenessProbe.PeriodSeconds || 820 got.LivenessProbe.TimeoutSeconds != expected.LivenessProbe.TimeoutSeconds { 821 return fmt.Errorf("different LivenessProbe: got <%v>, expected <%v>", got.LivenessProbe, expected.LivenessProbe) 822 } 823 824 // Assert ReadinessProbe 825 if got.ReadinessProbe.ProbeHandler.HTTPGet.Path != expected.ReadinessProbe.ProbeHandler.HTTPGet.Path || 826 got.ReadinessProbe.ProbeHandler.HTTPGet.Scheme != expected.ReadinessProbe.ProbeHandler.HTTPGet.Scheme || 827 got.ReadinessProbe.FailureThreshold != expected.ReadinessProbe.FailureThreshold || 828 got.ReadinessProbe.SuccessThreshold != expected.ReadinessProbe.SuccessThreshold || 829 got.ReadinessProbe.PeriodSeconds != expected.ReadinessProbe.PeriodSeconds || 830 got.ReadinessProbe.TimeoutSeconds != expected.ReadinessProbe.TimeoutSeconds { 831 return fmt.Errorf("different ReadinessProbe: got <%v>, expected <%v>", got.ReadinessProbe, expected.ReadinessProbe) 832 } 833 834 return nil 835 } 836 837 assertLifeCycle := func(got, expected *v1.Container) error { 838 if got.Lifecycle.PostStart.HTTPGet.Path != expected.Lifecycle.PostStart.HTTPGet.Path || 839 got.Lifecycle.PostStart.HTTPGet.Scheme != expected.Lifecycle.PostStart.HTTPGet.Scheme { 840 return fmt.Errorf("different LifeCycle: got <%v>, expected <%v>", got.Lifecycle, expected.Lifecycle) 841 } 842 843 return nil 844 } 845 846 cpu, _ := resource.ParseQuantity("100m") 847 mem, _ := resource.ParseQuantity("100Mi") 848 849 tests := []struct { 850 name string 851 rc v1.ReplicationController 852 expected []v1.Container 853 validators []InitContainerValidator 854 }{ 855 { 856 name: "imagePullIPolicy", 857 rc: v1.ReplicationController{ 858 Spec: v1.ReplicationControllerSpec{ 859 Template: &v1.PodTemplateSpec{ 860 Spec: v1.PodSpec{ 861 InitContainers: []v1.Container{ 862 { 863 Name: "install", 864 Image: "busybox", 865 }, 866 }, 867 }, 868 }, 869 }, 870 }, 871 expected: []v1.Container{ 872 { 873 ImagePullPolicy: v1.PullAlways, 874 }, 875 }, 876 validators: []InitContainerValidator{assertImagePullPolicy}, 877 }, 878 { 879 name: "FieldRef", 880 rc: v1.ReplicationController{ 881 Spec: v1.ReplicationControllerSpec{ 882 Template: &v1.PodTemplateSpec{ 883 Spec: v1.PodSpec{ 884 InitContainers: []v1.Container{ 885 { 886 Name: "fun", 887 Image: "alpine", 888 Env: []v1.EnvVar{ 889 { 890 Name: "MY_POD_IP", 891 ValueFrom: &v1.EnvVarSource{ 892 FieldRef: &v1.ObjectFieldSelector{ 893 APIVersion: "", 894 FieldPath: "status.podIP", 895 }, 896 }, 897 }, 898 }, 899 }, 900 }, 901 }, 902 }, 903 }, 904 }, 905 expected: []v1.Container{ 906 { 907 Env: []v1.EnvVar{ 908 { 909 Name: "MY_POD_IP", 910 ValueFrom: &v1.EnvVarSource{ 911 FieldRef: &v1.ObjectFieldSelector{ 912 APIVersion: "v1", 913 FieldPath: "status.podIP", 914 }, 915 }, 916 }, 917 }, 918 }, 919 }, 920 validators: []InitContainerValidator{assertEnvFieldRef}, 921 }, 922 { 923 name: "ContainerPort", 924 rc: v1.ReplicationController{ 925 Spec: v1.ReplicationControllerSpec{ 926 Template: &v1.PodTemplateSpec{ 927 Spec: v1.PodSpec{ 928 InitContainers: []v1.Container{ 929 { 930 Name: "fun", 931 Image: "alpine", 932 Ports: []v1.ContainerPort{ 933 { 934 Name: "default", 935 }, 936 }, 937 }, 938 }, 939 }, 940 }, 941 }, 942 }, 943 expected: []v1.Container{ 944 { 945 Ports: []v1.ContainerPort{ 946 { 947 Name: "default", 948 Protocol: v1.ProtocolTCP, 949 }, 950 }, 951 }, 952 }, 953 validators: []InitContainerValidator{assertContainerPort}, 954 }, 955 { 956 name: "Resources", 957 rc: v1.ReplicationController{ 958 Spec: v1.ReplicationControllerSpec{ 959 Template: &v1.PodTemplateSpec{ 960 Spec: v1.PodSpec{ 961 InitContainers: []v1.Container{ 962 { 963 Name: "fun", 964 Image: "alpine", 965 Resources: v1.ResourceRequirements{ 966 Limits: v1.ResourceList{ 967 v1.ResourceCPU: resource.MustParse("100m"), 968 v1.ResourceMemory: resource.MustParse("100Mi"), 969 }, 970 Requests: v1.ResourceList{ 971 v1.ResourceCPU: resource.MustParse("100m"), 972 v1.ResourceMemory: resource.MustParse("100Mi"), 973 }, 974 }, 975 }, 976 }, 977 }, 978 }, 979 }, 980 }, 981 expected: []v1.Container{ 982 { 983 Resources: v1.ResourceRequirements{ 984 Limits: v1.ResourceList{ 985 v1.ResourceCPU: cpu, 986 v1.ResourceMemory: mem, 987 }, 988 Requests: v1.ResourceList{ 989 v1.ResourceCPU: cpu, 990 v1.ResourceMemory: mem, 991 }, 992 }, 993 }, 994 }, 995 validators: []InitContainerValidator{assertResource}, 996 }, 997 { 998 name: "Probe", 999 rc: v1.ReplicationController{ 1000 Spec: v1.ReplicationControllerSpec{ 1001 Template: &v1.PodTemplateSpec{ 1002 Spec: v1.PodSpec{ 1003 InitContainers: []v1.Container{ 1004 { 1005 Name: "fun", 1006 Image: "alpine", 1007 LivenessProbe: &v1.Probe{ 1008 ProbeHandler: v1.ProbeHandler{ 1009 HTTPGet: &v1.HTTPGetAction{ 1010 Host: "localhost", 1011 }, 1012 }, 1013 }, 1014 ReadinessProbe: &v1.Probe{ 1015 ProbeHandler: v1.ProbeHandler{ 1016 HTTPGet: &v1.HTTPGetAction{ 1017 Host: "localhost", 1018 }, 1019 }, 1020 }, 1021 }, 1022 }, 1023 }, 1024 }, 1025 }, 1026 }, 1027 expected: []v1.Container{ 1028 { 1029 LivenessProbe: &v1.Probe{ 1030 ProbeHandler: v1.ProbeHandler{ 1031 HTTPGet: &v1.HTTPGetAction{ 1032 Path: "/", 1033 Scheme: v1.URISchemeHTTP, 1034 }, 1035 }, 1036 TimeoutSeconds: 1, 1037 PeriodSeconds: 10, 1038 SuccessThreshold: 1, 1039 FailureThreshold: 3, 1040 }, 1041 ReadinessProbe: &v1.Probe{ 1042 ProbeHandler: v1.ProbeHandler{ 1043 HTTPGet: &v1.HTTPGetAction{ 1044 Path: "/", 1045 Scheme: v1.URISchemeHTTP, 1046 }, 1047 }, 1048 TimeoutSeconds: 1, 1049 PeriodSeconds: 10, 1050 SuccessThreshold: 1, 1051 FailureThreshold: 3, 1052 }, 1053 }, 1054 }, 1055 validators: []InitContainerValidator{assertProb}, 1056 }, 1057 { 1058 name: "LifeCycle", 1059 rc: v1.ReplicationController{ 1060 Spec: v1.ReplicationControllerSpec{ 1061 Template: &v1.PodTemplateSpec{ 1062 Spec: v1.PodSpec{ 1063 InitContainers: []v1.Container{ 1064 { 1065 Name: "fun", 1066 Image: "alpine", 1067 Ports: []v1.ContainerPort{ 1068 { 1069 Name: "default", 1070 }, 1071 }, 1072 Lifecycle: &v1.Lifecycle{ 1073 PostStart: &v1.LifecycleHandler{ 1074 HTTPGet: &v1.HTTPGetAction{ 1075 Host: "localhost", 1076 }, 1077 }, 1078 PreStop: &v1.LifecycleHandler{ 1079 HTTPGet: &v1.HTTPGetAction{ 1080 Host: "localhost", 1081 }, 1082 }, 1083 }, 1084 }, 1085 }, 1086 }, 1087 }, 1088 }, 1089 }, 1090 expected: []v1.Container{ 1091 { 1092 Lifecycle: &v1.Lifecycle{ 1093 PostStart: &v1.LifecycleHandler{ 1094 HTTPGet: &v1.HTTPGetAction{ 1095 Path: "/", 1096 Scheme: v1.URISchemeHTTP, 1097 }, 1098 }, 1099 PreStop: &v1.LifecycleHandler{ 1100 HTTPGet: &v1.HTTPGetAction{ 1101 Path: "/", 1102 Scheme: v1.URISchemeHTTP, 1103 }, 1104 }, 1105 }, 1106 }, 1107 }, 1108 validators: []InitContainerValidator{assertLifeCycle}, 1109 }, 1110 } 1111 1112 assertInitContainers := func(got, expected []v1.Container, validators []InitContainerValidator) error { 1113 if len(got) != len(expected) { 1114 return fmt.Errorf("different number of init container: got <%d>, expected <%d>", 1115 len(got), len(expected)) 1116 } 1117 1118 for i := range got { 1119 g := &got[i] 1120 e := &expected[i] 1121 1122 for _, validator := range validators { 1123 if err := validator(g, e); err != nil { 1124 return err 1125 } 1126 } 1127 } 1128 1129 return nil 1130 } 1131 1132 for _, test := range tests { 1133 rc := &test.rc 1134 obj2 := roundTrip(t, runtime.Object(rc)) 1135 rc2, ok := obj2.(*v1.ReplicationController) 1136 if !ok { 1137 t.Errorf("unexpected object: %v", rc2) 1138 t.FailNow() 1139 } 1140 1141 if err := assertInitContainers(rc2.Spec.Template.Spec.InitContainers, test.expected, test.validators); err != nil { 1142 t.Errorf("test %v failed: %v", test.name, err) 1143 } 1144 } 1145 } 1146 1147 func TestSetDefaultService(t *testing.T) { 1148 svc := &v1.Service{} 1149 obj2 := roundTrip(t, runtime.Object(svc)) 1150 svc2 := obj2.(*v1.Service) 1151 if svc2.Spec.SessionAffinity != v1.ServiceAffinityNone { 1152 t.Errorf("Expected default session affinity type:%s, got: %s", v1.ServiceAffinityNone, svc2.Spec.SessionAffinity) 1153 } 1154 if svc2.Spec.SessionAffinityConfig != nil { 1155 t.Errorf("Expected empty session affinity config when session affinity type: %s, got: %v", v1.ServiceAffinityNone, svc2.Spec.SessionAffinityConfig) 1156 } 1157 if svc2.Spec.Type != v1.ServiceTypeClusterIP { 1158 t.Errorf("Expected default type:%s, got: %s", v1.ServiceTypeClusterIP, svc2.Spec.Type) 1159 } 1160 } 1161 1162 func TestSetDefaultServiceSessionAffinityConfig(t *testing.T) { 1163 testCases := map[string]v1.Service{ 1164 "SessionAffinityConfig is empty": { 1165 Spec: v1.ServiceSpec{ 1166 SessionAffinity: v1.ServiceAffinityClientIP, 1167 SessionAffinityConfig: nil, 1168 }, 1169 }, 1170 "ClientIP is empty": { 1171 Spec: v1.ServiceSpec{ 1172 SessionAffinity: v1.ServiceAffinityClientIP, 1173 SessionAffinityConfig: &v1.SessionAffinityConfig{ 1174 ClientIP: nil, 1175 }, 1176 }, 1177 }, 1178 "TimeoutSeconds is empty": { 1179 Spec: v1.ServiceSpec{ 1180 SessionAffinity: v1.ServiceAffinityClientIP, 1181 SessionAffinityConfig: &v1.SessionAffinityConfig{ 1182 ClientIP: &v1.ClientIPConfig{ 1183 TimeoutSeconds: nil, 1184 }, 1185 }, 1186 }, 1187 }, 1188 } 1189 for name, test := range testCases { 1190 obj2 := roundTrip(t, runtime.Object(&test)) 1191 svc2 := obj2.(*v1.Service) 1192 if svc2.Spec.SessionAffinityConfig == nil || svc2.Spec.SessionAffinityConfig.ClientIP == nil || svc2.Spec.SessionAffinityConfig.ClientIP.TimeoutSeconds == nil { 1193 t.Fatalf("Case: %s, unexpected empty SessionAffinityConfig/ClientIP/TimeoutSeconds when session affinity type: %s, got: %v", name, v1.ServiceAffinityClientIP, svc2.Spec.SessionAffinityConfig) 1194 } 1195 if *svc2.Spec.SessionAffinityConfig.ClientIP.TimeoutSeconds != v1.DefaultClientIPServiceAffinitySeconds { 1196 t.Errorf("Case: %s, default TimeoutSeconds should be %d when session affinity type: %s, got: %d", name, v1.DefaultClientIPServiceAffinitySeconds, v1.ServiceAffinityClientIP, *svc2.Spec.SessionAffinityConfig.ClientIP.TimeoutSeconds) 1197 } 1198 } 1199 } 1200 1201 func TestSetDefaultServiceLoadbalancerIPMode(t *testing.T) { 1202 modeVIP := v1.LoadBalancerIPModeVIP 1203 modeProxy := v1.LoadBalancerIPModeProxy 1204 testCases := []struct { 1205 name string 1206 ipModeEnabled bool 1207 svc *v1.Service 1208 expectedIPMode []*v1.LoadBalancerIPMode 1209 }{ 1210 { 1211 name: "Set IP but not set IPMode with LoadbalancerIPMode disabled", 1212 ipModeEnabled: false, 1213 svc: &v1.Service{ 1214 Spec: v1.ServiceSpec{Type: v1.ServiceTypeLoadBalancer}, 1215 Status: v1.ServiceStatus{ 1216 LoadBalancer: v1.LoadBalancerStatus{ 1217 Ingress: []v1.LoadBalancerIngress{{ 1218 IP: "1.2.3.4", 1219 }}, 1220 }, 1221 }}, 1222 expectedIPMode: []*v1.LoadBalancerIPMode{nil}, 1223 }, { 1224 name: "Set IP but bot set IPMode with LoadbalancerIPMode enabled", 1225 ipModeEnabled: true, 1226 svc: &v1.Service{ 1227 Spec: v1.ServiceSpec{Type: v1.ServiceTypeLoadBalancer}, 1228 Status: v1.ServiceStatus{ 1229 LoadBalancer: v1.LoadBalancerStatus{ 1230 Ingress: []v1.LoadBalancerIngress{{ 1231 IP: "1.2.3.4", 1232 }}, 1233 }, 1234 }}, 1235 expectedIPMode: []*v1.LoadBalancerIPMode{&modeVIP}, 1236 }, { 1237 name: "Both IP and IPMode are set with LoadbalancerIPMode enabled", 1238 ipModeEnabled: true, 1239 svc: &v1.Service{ 1240 Spec: v1.ServiceSpec{Type: v1.ServiceTypeLoadBalancer}, 1241 Status: v1.ServiceStatus{ 1242 LoadBalancer: v1.LoadBalancerStatus{ 1243 Ingress: []v1.LoadBalancerIngress{{ 1244 IP: "1.2.3.4", 1245 IPMode: &modeProxy, 1246 }}, 1247 }, 1248 }}, 1249 expectedIPMode: []*v1.LoadBalancerIPMode{&modeProxy}, 1250 }, 1251 } 1252 1253 for _, tc := range testCases { 1254 t.Run(tc.name, func(t *testing.T) { 1255 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.LoadBalancerIPMode, tc.ipModeEnabled) 1256 obj := roundTrip(t, runtime.Object(tc.svc)) 1257 svc := obj.(*v1.Service) 1258 for i, s := range svc.Status.LoadBalancer.Ingress { 1259 got := s.IPMode 1260 expected := tc.expectedIPMode[i] 1261 if !reflect.DeepEqual(got, expected) { 1262 t.Errorf("Expected IPMode %v, got %v", tc.expectedIPMode[i], s.IPMode) 1263 } 1264 } 1265 }) 1266 } 1267 } 1268 1269 func TestSetDefaultSecretVolumeSource(t *testing.T) { 1270 s := v1.PodSpec{} 1271 s.Volumes = []v1.Volume{ 1272 { 1273 VolumeSource: v1.VolumeSource{ 1274 Secret: &v1.SecretVolumeSource{}, 1275 }, 1276 }, 1277 } 1278 pod := &v1.Pod{ 1279 Spec: s, 1280 } 1281 output := roundTrip(t, runtime.Object(pod)) 1282 pod2 := output.(*v1.Pod) 1283 defaultMode := pod2.Spec.Volumes[0].VolumeSource.Secret.DefaultMode 1284 expectedMode := v1.SecretVolumeSourceDefaultMode 1285 1286 if defaultMode == nil || *defaultMode != expectedMode { 1287 t.Errorf("Expected secret DefaultMode %v, got %v", expectedMode, defaultMode) 1288 } 1289 } 1290 1291 func TestSetDefaultConfigMapVolumeSource(t *testing.T) { 1292 s := v1.PodSpec{} 1293 s.Volumes = []v1.Volume{ 1294 { 1295 VolumeSource: v1.VolumeSource{ 1296 ConfigMap: &v1.ConfigMapVolumeSource{}, 1297 }, 1298 }, 1299 } 1300 pod := &v1.Pod{ 1301 Spec: s, 1302 } 1303 output := roundTrip(t, runtime.Object(pod)) 1304 pod2 := output.(*v1.Pod) 1305 defaultMode := pod2.Spec.Volumes[0].VolumeSource.ConfigMap.DefaultMode 1306 expectedMode := v1.ConfigMapVolumeSourceDefaultMode 1307 1308 if defaultMode == nil || *defaultMode != expectedMode { 1309 t.Errorf("Expected v1.ConfigMap DefaultMode %v, got %v", expectedMode, defaultMode) 1310 } 1311 } 1312 1313 func TestSetDefaultDownwardAPIVolumeSource(t *testing.T) { 1314 s := v1.PodSpec{} 1315 s.Volumes = []v1.Volume{ 1316 { 1317 VolumeSource: v1.VolumeSource{ 1318 DownwardAPI: &v1.DownwardAPIVolumeSource{}, 1319 }, 1320 }, 1321 } 1322 pod := &v1.Pod{ 1323 Spec: s, 1324 } 1325 output := roundTrip(t, runtime.Object(pod)) 1326 pod2 := output.(*v1.Pod) 1327 defaultMode := pod2.Spec.Volumes[0].VolumeSource.DownwardAPI.DefaultMode 1328 expectedMode := v1.DownwardAPIVolumeSourceDefaultMode 1329 1330 if defaultMode == nil || *defaultMode != expectedMode { 1331 t.Errorf("Expected DownwardAPI DefaultMode %v, got %v", expectedMode, defaultMode) 1332 } 1333 } 1334 1335 func TestSetDefaultProjectedVolumeSource(t *testing.T) { 1336 s := v1.PodSpec{} 1337 s.Volumes = []v1.Volume{ 1338 { 1339 VolumeSource: v1.VolumeSource{ 1340 Projected: &v1.ProjectedVolumeSource{}, 1341 }, 1342 }, 1343 } 1344 pod := &v1.Pod{ 1345 Spec: s, 1346 } 1347 output := roundTrip(t, runtime.Object(pod)) 1348 pod2 := output.(*v1.Pod) 1349 defaultMode := pod2.Spec.Volumes[0].VolumeSource.Projected.DefaultMode 1350 expectedMode := v1.ProjectedVolumeSourceDefaultMode 1351 1352 if defaultMode == nil || *defaultMode != expectedMode { 1353 t.Errorf("Expected v1.ProjectedVolumeSource DefaultMode %v, got %v", expectedMode, defaultMode) 1354 } 1355 } 1356 1357 func TestSetDefaultSecret(t *testing.T) { 1358 s := &v1.Secret{} 1359 obj2 := roundTrip(t, runtime.Object(s)) 1360 s2 := obj2.(*v1.Secret) 1361 1362 if s2.Type != v1.SecretTypeOpaque { 1363 t.Errorf("Expected secret type %v, got %v", v1.SecretTypeOpaque, s2.Type) 1364 } 1365 } 1366 1367 func TestSetDefaultPersistentVolume(t *testing.T) { 1368 fsMode := v1.PersistentVolumeFilesystem 1369 blockMode := v1.PersistentVolumeBlock 1370 1371 tests := []struct { 1372 name string 1373 volumeMode *v1.PersistentVolumeMode 1374 expectedVolumeMode v1.PersistentVolumeMode 1375 }{ 1376 { 1377 name: "volume mode nil", 1378 volumeMode: nil, 1379 expectedVolumeMode: v1.PersistentVolumeFilesystem, 1380 }, 1381 { 1382 name: "volume mode filesystem", 1383 volumeMode: &fsMode, 1384 expectedVolumeMode: v1.PersistentVolumeFilesystem, 1385 }, 1386 { 1387 name: "volume mode block", 1388 volumeMode: &blockMode, 1389 expectedVolumeMode: v1.PersistentVolumeBlock, 1390 }, 1391 } 1392 1393 for _, test := range tests { 1394 pv := &v1.PersistentVolume{ 1395 Spec: v1.PersistentVolumeSpec{ 1396 VolumeMode: test.volumeMode, 1397 }, 1398 } 1399 obj1 := roundTrip(t, runtime.Object(pv)) 1400 pv1 := obj1.(*v1.PersistentVolume) 1401 if pv1.Status.Phase != v1.VolumePending { 1402 t.Errorf("Expected claim phase %v, got %v", v1.ClaimPending, pv1.Status.Phase) 1403 } 1404 if pv1.Spec.PersistentVolumeReclaimPolicy != v1.PersistentVolumeReclaimRetain { 1405 t.Errorf("Expected pv reclaim policy %v, got %v", v1.PersistentVolumeReclaimRetain, pv1.Spec.PersistentVolumeReclaimPolicy) 1406 } 1407 if *pv1.Spec.VolumeMode != test.expectedVolumeMode { 1408 t.Errorf("Test %s failed, Expected VolumeMode: %v, but got %v", test.name, test.volumeMode, *pv1.Spec.VolumeMode) 1409 } 1410 } 1411 } 1412 1413 func TestSetDefaultPersistentVolumeClaim(t *testing.T) { 1414 fsMode := v1.PersistentVolumeFilesystem 1415 blockMode := v1.PersistentVolumeBlock 1416 1417 tests := []struct { 1418 name string 1419 volumeMode *v1.PersistentVolumeMode 1420 expectedVolumeMode v1.PersistentVolumeMode 1421 }{ 1422 { 1423 name: "volume mode nil", 1424 volumeMode: nil, 1425 expectedVolumeMode: v1.PersistentVolumeFilesystem, 1426 }, 1427 { 1428 name: "volume mode filesystem", 1429 volumeMode: &fsMode, 1430 expectedVolumeMode: v1.PersistentVolumeFilesystem, 1431 }, 1432 { 1433 name: "volume mode block", 1434 volumeMode: &blockMode, 1435 expectedVolumeMode: v1.PersistentVolumeBlock, 1436 }, 1437 } 1438 1439 for _, test := range tests { 1440 pvc := &v1.PersistentVolumeClaim{ 1441 Spec: v1.PersistentVolumeClaimSpec{ 1442 VolumeMode: test.volumeMode, 1443 }, 1444 } 1445 obj1 := roundTrip(t, runtime.Object(pvc)) 1446 pvc1 := obj1.(*v1.PersistentVolumeClaim) 1447 if pvc1.Status.Phase != v1.ClaimPending { 1448 t.Errorf("Expected claim phase %v, got %v", v1.ClaimPending, pvc1.Status.Phase) 1449 } 1450 if *pvc1.Spec.VolumeMode != test.expectedVolumeMode { 1451 t.Errorf("Test %s failed, Expected VolumeMode: %v, but got %v", test.name, test.volumeMode, *pvc1.Spec.VolumeMode) 1452 } 1453 } 1454 } 1455 1456 func TestSetDefaultEphemeral(t *testing.T) { 1457 fsMode := v1.PersistentVolumeFilesystem 1458 blockMode := v1.PersistentVolumeBlock 1459 1460 tests := []struct { 1461 name string 1462 volumeMode *v1.PersistentVolumeMode 1463 expectedVolumeMode v1.PersistentVolumeMode 1464 }{ 1465 { 1466 name: "volume mode nil", 1467 volumeMode: nil, 1468 expectedVolumeMode: v1.PersistentVolumeFilesystem, 1469 }, 1470 { 1471 name: "volume mode filesystem", 1472 volumeMode: &fsMode, 1473 expectedVolumeMode: v1.PersistentVolumeFilesystem, 1474 }, 1475 { 1476 name: "volume mode block", 1477 volumeMode: &blockMode, 1478 expectedVolumeMode: v1.PersistentVolumeBlock, 1479 }, 1480 } 1481 1482 for _, test := range tests { 1483 pod := &v1.Pod{ 1484 Spec: v1.PodSpec{ 1485 Volumes: []v1.Volume{ 1486 { 1487 VolumeSource: v1.VolumeSource{ 1488 Ephemeral: &v1.EphemeralVolumeSource{ 1489 VolumeClaimTemplate: &v1.PersistentVolumeClaimTemplate{ 1490 Spec: v1.PersistentVolumeClaimSpec{ 1491 VolumeMode: test.volumeMode, 1492 }, 1493 }, 1494 }, 1495 }, 1496 }, 1497 }, 1498 }, 1499 } 1500 obj1 := roundTrip(t, runtime.Object(pod)) 1501 pod1 := obj1.(*v1.Pod) 1502 if *pod1.Spec.Volumes[0].VolumeSource.Ephemeral.VolumeClaimTemplate.Spec.VolumeMode != test.expectedVolumeMode { 1503 t.Errorf("Test %s failed, Expected VolumeMode: %v, but got %v", test.name, test.volumeMode, *pod1.Spec.Volumes[0].VolumeSource.Ephemeral.VolumeClaimTemplate.Spec.VolumeMode) 1504 } 1505 } 1506 } 1507 1508 func TestSetDefaultEndpointsProtocol(t *testing.T) { 1509 in := &v1.Endpoints{Subsets: []v1.EndpointSubset{ 1510 {Ports: []v1.EndpointPort{{}, {Protocol: "UDP"}, {}}}, 1511 }} 1512 obj := roundTrip(t, runtime.Object(in)) 1513 out := obj.(*v1.Endpoints) 1514 1515 for i := range out.Subsets { 1516 for j := range out.Subsets[i].Ports { 1517 if in.Subsets[i].Ports[j].Protocol == "" { 1518 if out.Subsets[i].Ports[j].Protocol != v1.ProtocolTCP { 1519 t.Errorf("Expected protocol %s, got %s", v1.ProtocolTCP, out.Subsets[i].Ports[j].Protocol) 1520 } 1521 } else { 1522 if out.Subsets[i].Ports[j].Protocol != in.Subsets[i].Ports[j].Protocol { 1523 t.Errorf("Expected protocol %s, got %s", in.Subsets[i].Ports[j].Protocol, out.Subsets[i].Ports[j].Protocol) 1524 } 1525 } 1526 } 1527 } 1528 } 1529 1530 func TestSetDefaultServiceTargetPort(t *testing.T) { 1531 in := &v1.Service{Spec: v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 1234}}}} 1532 obj := roundTrip(t, runtime.Object(in)) 1533 out := obj.(*v1.Service) 1534 if out.Spec.Ports[0].TargetPort != intstr.FromInt32(1234) { 1535 t.Errorf("Expected TargetPort to be defaulted, got %v", out.Spec.Ports[0].TargetPort) 1536 } 1537 1538 in = &v1.Service{Spec: v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 1234, TargetPort: intstr.FromInt32(5678)}}}} 1539 obj = roundTrip(t, runtime.Object(in)) 1540 out = obj.(*v1.Service) 1541 if out.Spec.Ports[0].TargetPort != intstr.FromInt32(5678) { 1542 t.Errorf("Expected TargetPort to be unchanged, got %v", out.Spec.Ports[0].TargetPort) 1543 } 1544 } 1545 1546 func TestSetDefaultServicePort(t *testing.T) { 1547 // Unchanged if set. 1548 in := &v1.Service{Spec: v1.ServiceSpec{ 1549 Ports: []v1.ServicePort{ 1550 {Protocol: "UDP", Port: 9376, TargetPort: intstr.FromString("p")}, 1551 {Protocol: "UDP", Port: 8675, TargetPort: intstr.FromInt32(309)}, 1552 }, 1553 }} 1554 out := roundTrip(t, runtime.Object(in)).(*v1.Service) 1555 if out.Spec.Ports[0].Protocol != v1.ProtocolUDP { 1556 t.Errorf("Expected protocol %s, got %s", v1.ProtocolUDP, out.Spec.Ports[0].Protocol) 1557 } 1558 if out.Spec.Ports[0].TargetPort != intstr.FromString("p") { 1559 t.Errorf("Expected port %v, got %v", in.Spec.Ports[0].Port, out.Spec.Ports[0].TargetPort) 1560 } 1561 if out.Spec.Ports[1].Protocol != v1.ProtocolUDP { 1562 t.Errorf("Expected protocol %s, got %s", v1.ProtocolUDP, out.Spec.Ports[1].Protocol) 1563 } 1564 if out.Spec.Ports[1].TargetPort != intstr.FromInt32(309) { 1565 t.Errorf("Expected port %v, got %v", in.Spec.Ports[1].Port, out.Spec.Ports[1].TargetPort) 1566 } 1567 1568 // Defaulted. 1569 in = &v1.Service{Spec: v1.ServiceSpec{ 1570 Ports: []v1.ServicePort{ 1571 {Protocol: "", Port: 9376, TargetPort: intstr.FromString("")}, 1572 {Protocol: "", Port: 8675, TargetPort: intstr.FromInt32(0)}, 1573 }, 1574 }} 1575 out = roundTrip(t, runtime.Object(in)).(*v1.Service) 1576 if out.Spec.Ports[0].Protocol != v1.ProtocolTCP { 1577 t.Errorf("Expected protocol %s, got %s", v1.ProtocolTCP, out.Spec.Ports[0].Protocol) 1578 } 1579 if out.Spec.Ports[0].TargetPort != intstr.FromInt32(in.Spec.Ports[0].Port) { 1580 t.Errorf("Expected port %v, got %v", in.Spec.Ports[0].Port, out.Spec.Ports[0].TargetPort) 1581 } 1582 if out.Spec.Ports[1].Protocol != v1.ProtocolTCP { 1583 t.Errorf("Expected protocol %s, got %s", v1.ProtocolTCP, out.Spec.Ports[1].Protocol) 1584 } 1585 if out.Spec.Ports[1].TargetPort != intstr.FromInt32(in.Spec.Ports[1].Port) { 1586 t.Errorf("Expected port %v, got %v", in.Spec.Ports[1].Port, out.Spec.Ports[1].TargetPort) 1587 } 1588 } 1589 1590 func TestSetDefaultServiceExternalTraffic(t *testing.T) { 1591 in := &v1.Service{} 1592 obj := roundTrip(t, runtime.Object(in)) 1593 out := obj.(*v1.Service) 1594 if out.Spec.ExternalTrafficPolicy != "" { 1595 t.Errorf("Expected ExternalTrafficPolicy to be empty, got %v", out.Spec.ExternalTrafficPolicy) 1596 } 1597 1598 in = &v1.Service{Spec: v1.ServiceSpec{Type: v1.ServiceTypeNodePort}} 1599 obj = roundTrip(t, runtime.Object(in)) 1600 out = obj.(*v1.Service) 1601 if out.Spec.ExternalTrafficPolicy != v1.ServiceExternalTrafficPolicyCluster { 1602 t.Errorf("Expected ExternalTrafficPolicy to be %v, got %v", v1.ServiceExternalTrafficPolicyCluster, out.Spec.ExternalTrafficPolicy) 1603 } 1604 1605 in = &v1.Service{Spec: v1.ServiceSpec{Type: v1.ServiceTypeLoadBalancer}} 1606 obj = roundTrip(t, runtime.Object(in)) 1607 out = obj.(*v1.Service) 1608 if out.Spec.ExternalTrafficPolicy != v1.ServiceExternalTrafficPolicyCluster { 1609 t.Errorf("Expected ExternalTrafficPolicy to be %v, got %v", v1.ServiceExternalTrafficPolicyCluster, out.Spec.ExternalTrafficPolicy) 1610 } 1611 1612 in = &v1.Service{Spec: v1.ServiceSpec{Type: v1.ServiceTypeClusterIP, ExternalIPs: []string{"1.2.3.4"}}} 1613 obj = roundTrip(t, runtime.Object(in)) 1614 out = obj.(*v1.Service) 1615 if out.Spec.ExternalTrafficPolicy != v1.ServiceExternalTrafficPolicyCluster { 1616 t.Errorf("Expected ExternalTrafficPolicy to be %v, got %v", v1.ServiceExternalTrafficPolicyCluster, out.Spec.ExternalTrafficPolicy) 1617 } 1618 1619 in = &v1.Service{Spec: v1.ServiceSpec{Type: v1.ServiceTypeClusterIP}} 1620 obj = roundTrip(t, runtime.Object(in)) 1621 out = obj.(*v1.Service) 1622 if out.Spec.ExternalTrafficPolicy != "" { 1623 t.Errorf("Expected ExternalTrafficPolicy to be empty, got %v", out.Spec.ExternalTrafficPolicy) 1624 } 1625 1626 in = &v1.Service{Spec: v1.ServiceSpec{Type: v1.ServiceTypeExternalName}} 1627 obj = roundTrip(t, runtime.Object(in)) 1628 out = obj.(*v1.Service) 1629 if out.Spec.ExternalTrafficPolicy != "" { 1630 t.Errorf("Expected ExternalTrafficPolicy to be empty, got %v", out.Spec.ExternalTrafficPolicy) 1631 } 1632 } 1633 1634 func TestSetDefaultNamespace(t *testing.T) { 1635 s := &v1.Namespace{} 1636 obj2 := roundTrip(t, runtime.Object(s)) 1637 s2 := obj2.(*v1.Namespace) 1638 1639 if s2.Status.Phase != v1.NamespaceActive { 1640 t.Errorf("Expected phase %v, got %v", v1.NamespaceActive, s2.Status.Phase) 1641 } 1642 } 1643 1644 func TestSetDefaultNamespaceLabels(t *testing.T) { 1645 theNs := "default-ns-labels-are-great" 1646 s := &v1.Namespace{ 1647 ObjectMeta: metav1.ObjectMeta{ 1648 Name: theNs, 1649 }, 1650 } 1651 obj2 := roundTrip(t, runtime.Object(s)) 1652 s2 := obj2.(*v1.Namespace) 1653 1654 if s2.ObjectMeta.Labels[v1.LabelMetadataName] != theNs { 1655 t.Errorf("Expected default namespace label value of %v, but got %v", theNs, s2.ObjectMeta.Labels[v1.LabelMetadataName]) 1656 } 1657 } 1658 1659 func TestSetDefaultPodSpecHostNetwork(t *testing.T) { 1660 portNum := int32(8080) 1661 s := v1.PodSpec{} 1662 s.HostNetwork = true 1663 s.Containers = []v1.Container{ 1664 { 1665 Ports: []v1.ContainerPort{ 1666 { 1667 ContainerPort: portNum, 1668 }, 1669 }, 1670 }, 1671 } 1672 s.InitContainers = []v1.Container{ 1673 { 1674 Ports: []v1.ContainerPort{ 1675 { 1676 ContainerPort: portNum, 1677 }, 1678 }, 1679 }, 1680 } 1681 pod := &v1.Pod{ 1682 Spec: s, 1683 } 1684 obj2 := roundTrip(t, runtime.Object(pod)) 1685 pod2 := obj2.(*v1.Pod) 1686 s2 := pod2.Spec 1687 1688 hostPortNum := s2.Containers[0].Ports[0].HostPort 1689 if hostPortNum != portNum { 1690 t.Errorf("Expected container port to be defaulted, was made %d instead of %d", hostPortNum, portNum) 1691 } 1692 1693 hostPortNum = s2.InitContainers[0].Ports[0].HostPort 1694 if hostPortNum != portNum { 1695 t.Errorf("Expected container port to be defaulted, was made %d instead of %d", hostPortNum, portNum) 1696 } 1697 } 1698 1699 func TestSetDefaultNodeStatusAllocatable(t *testing.T) { 1700 capacity := v1.ResourceList{ 1701 v1.ResourceCPU: resource.MustParse("1000m"), 1702 v1.ResourceMemory: resource.MustParse("10G"), 1703 } 1704 allocatable := v1.ResourceList{ 1705 v1.ResourceCPU: resource.MustParse("500m"), 1706 v1.ResourceMemory: resource.MustParse("5G"), 1707 } 1708 tests := []struct { 1709 capacity v1.ResourceList 1710 allocatable v1.ResourceList 1711 expectedAllocatable v1.ResourceList 1712 }{{ // Everything set, no defaulting. 1713 capacity: capacity, 1714 allocatable: allocatable, 1715 expectedAllocatable: allocatable, 1716 }, { // Allocatable set, no defaulting. 1717 capacity: nil, 1718 allocatable: allocatable, 1719 expectedAllocatable: allocatable, 1720 }, { // Capacity set, allocatable defaults to capacity. 1721 capacity: capacity, 1722 allocatable: nil, 1723 expectedAllocatable: capacity, 1724 }, { // Nothing set, allocatable "defaults" to capacity. 1725 capacity: nil, 1726 allocatable: nil, 1727 expectedAllocatable: nil, 1728 }} 1729 1730 copyResourceList := func(rl v1.ResourceList) v1.ResourceList { 1731 if rl == nil { 1732 return nil 1733 } 1734 copy := make(v1.ResourceList, len(rl)) 1735 for k, v := range rl { 1736 copy[k] = v.DeepCopy() 1737 } 1738 return copy 1739 } 1740 1741 resourceListsEqual := func(a v1.ResourceList, b v1.ResourceList) bool { 1742 if len(a) != len(b) { 1743 return false 1744 } 1745 for k, v := range a { 1746 vb, found := b[k] 1747 if !found { 1748 return false 1749 } 1750 if v.Cmp(vb) != 0 { 1751 return false 1752 } 1753 } 1754 return true 1755 } 1756 1757 for i, testcase := range tests { 1758 node := v1.Node{ 1759 Status: v1.NodeStatus{ 1760 Capacity: copyResourceList(testcase.capacity), 1761 Allocatable: copyResourceList(testcase.allocatable), 1762 }, 1763 } 1764 node2 := roundTrip(t, runtime.Object(&node)).(*v1.Node) 1765 actual := node2.Status.Allocatable 1766 expected := testcase.expectedAllocatable 1767 if !resourceListsEqual(expected, actual) { 1768 t.Errorf("[%d] Expected v1.NodeStatus.Allocatable: %+v; Got: %+v", i, expected, actual) 1769 } 1770 } 1771 } 1772 1773 func TestSetDefaultObjectFieldSelectorAPIVersion(t *testing.T) { 1774 s := v1.PodSpec{ 1775 Containers: []v1.Container{ 1776 { 1777 Env: []v1.EnvVar{ 1778 { 1779 ValueFrom: &v1.EnvVarSource{ 1780 FieldRef: &v1.ObjectFieldSelector{}, 1781 }, 1782 }, 1783 }, 1784 }, 1785 }, 1786 } 1787 pod := &v1.Pod{ 1788 Spec: s, 1789 } 1790 obj2 := roundTrip(t, runtime.Object(pod)) 1791 pod2 := obj2.(*v1.Pod) 1792 s2 := pod2.Spec 1793 1794 apiVersion := s2.Containers[0].Env[0].ValueFrom.FieldRef.APIVersion 1795 if apiVersion != "v1" { 1796 t.Errorf("Expected default APIVersion v1, got: %v", apiVersion) 1797 } 1798 } 1799 1800 func TestSetMinimumScalePod(t *testing.T) { 1801 // verify we default if limits are specified (and that request=0 is preserved) 1802 s := v1.PodSpec{} 1803 s.Containers = []v1.Container{ 1804 { 1805 Resources: v1.ResourceRequirements{ 1806 Requests: v1.ResourceList{ 1807 v1.ResourceMemory: resource.MustParse("1n"), 1808 }, 1809 Limits: v1.ResourceList{ 1810 v1.ResourceCPU: resource.MustParse("2n"), 1811 }, 1812 }, 1813 }, 1814 } 1815 s.InitContainers = []v1.Container{ 1816 { 1817 Resources: v1.ResourceRequirements{ 1818 Requests: v1.ResourceList{ 1819 v1.ResourceMemory: resource.MustParse("1n"), 1820 }, 1821 Limits: v1.ResourceList{ 1822 v1.ResourceCPU: resource.MustParse("2n"), 1823 }, 1824 }, 1825 }, 1826 } 1827 pod := &v1.Pod{ 1828 Spec: s, 1829 } 1830 corev1.SetObjectDefaults_Pod(pod) 1831 1832 if expect := resource.MustParse("1m"); expect.Cmp(pod.Spec.Containers[0].Resources.Requests[v1.ResourceMemory]) != 0 { 1833 t.Errorf("did not round resources: %#v", pod.Spec.Containers[0].Resources) 1834 } 1835 if expect := resource.MustParse("1m"); expect.Cmp(pod.Spec.InitContainers[0].Resources.Requests[v1.ResourceMemory]) != 0 { 1836 t.Errorf("did not round resources: %#v", pod.Spec.InitContainers[0].Resources) 1837 } 1838 } 1839 1840 func TestSetDefaultRequestsPod(t *testing.T) { 1841 // verify we default if limits are specified (and that request=0 is preserved) 1842 s := v1.PodSpec{} 1843 s.Containers = []v1.Container{ 1844 { 1845 Resources: v1.ResourceRequirements{ 1846 Requests: v1.ResourceList{ 1847 v1.ResourceMemory: resource.MustParse("0"), 1848 }, 1849 Limits: v1.ResourceList{ 1850 v1.ResourceCPU: resource.MustParse("100m"), 1851 v1.ResourceMemory: resource.MustParse("1Gi"), 1852 }, 1853 }, 1854 }, 1855 } 1856 s.InitContainers = []v1.Container{ 1857 { 1858 Resources: v1.ResourceRequirements{ 1859 Requests: v1.ResourceList{ 1860 v1.ResourceMemory: resource.MustParse("0"), 1861 }, 1862 Limits: v1.ResourceList{ 1863 v1.ResourceCPU: resource.MustParse("100m"), 1864 v1.ResourceMemory: resource.MustParse("1Gi"), 1865 }, 1866 }, 1867 }, 1868 } 1869 pod := &v1.Pod{ 1870 Spec: s, 1871 } 1872 output := roundTrip(t, runtime.Object(pod)) 1873 pod2 := output.(*v1.Pod) 1874 defaultRequest := pod2.Spec.Containers[0].Resources.Requests 1875 if requestValue := defaultRequest[v1.ResourceCPU]; requestValue.String() != "100m" { 1876 t.Errorf("Expected request cpu: %s, got: %s", "100m", requestValue.String()) 1877 } 1878 if requestValue := defaultRequest[v1.ResourceMemory]; requestValue.String() != "0" { 1879 t.Errorf("Expected request memory: %s, got: %s", "0", requestValue.String()) 1880 } 1881 defaultRequest = pod2.Spec.InitContainers[0].Resources.Requests 1882 if requestValue := defaultRequest[v1.ResourceCPU]; requestValue.String() != "100m" { 1883 t.Errorf("Expected request cpu: %s, got: %s", "100m", requestValue.String()) 1884 } 1885 if requestValue := defaultRequest[v1.ResourceMemory]; requestValue.String() != "0" { 1886 t.Errorf("Expected request memory: %s, got: %s", "0", requestValue.String()) 1887 } 1888 1889 // verify we do nothing if no limits are specified 1890 s = v1.PodSpec{} 1891 s.Containers = []v1.Container{{}} 1892 s.InitContainers = []v1.Container{{}} 1893 pod = &v1.Pod{ 1894 Spec: s, 1895 } 1896 output = roundTrip(t, runtime.Object(pod)) 1897 pod2 = output.(*v1.Pod) 1898 defaultRequest = pod2.Spec.Containers[0].Resources.Requests 1899 if requestValue := defaultRequest[v1.ResourceCPU]; requestValue.String() != "0" { 1900 t.Errorf("Expected 0 request value, got: %s", requestValue.String()) 1901 } 1902 defaultRequest = pod2.Spec.InitContainers[0].Resources.Requests 1903 if requestValue := defaultRequest[v1.ResourceCPU]; requestValue.String() != "0" { 1904 t.Errorf("Expected 0 request value, got: %s", requestValue.String()) 1905 } 1906 } 1907 1908 func TestDefaultRequestIsNotSetForReplicationController(t *testing.T) { 1909 s := v1.PodSpec{} 1910 s.Containers = []v1.Container{ 1911 { 1912 Resources: v1.ResourceRequirements{ 1913 Limits: v1.ResourceList{ 1914 v1.ResourceCPU: resource.MustParse("100m"), 1915 }, 1916 }, 1917 }, 1918 } 1919 rc := &v1.ReplicationController{ 1920 Spec: v1.ReplicationControllerSpec{ 1921 Replicas: utilpointer.Int32(3), 1922 Template: &v1.PodTemplateSpec{ 1923 ObjectMeta: metav1.ObjectMeta{ 1924 Labels: map[string]string{ 1925 "foo": "bar", 1926 }, 1927 }, 1928 Spec: s, 1929 }, 1930 }, 1931 } 1932 output := roundTrip(t, runtime.Object(rc)) 1933 rc2 := output.(*v1.ReplicationController) 1934 defaultRequest := rc2.Spec.Template.Spec.Containers[0].Resources.Requests 1935 requestValue := defaultRequest[v1.ResourceCPU] 1936 if requestValue.String() != "0" { 1937 t.Errorf("Expected 0 request value, got: %s", requestValue.String()) 1938 } 1939 } 1940 1941 func TestSetDefaultLimitRangeItem(t *testing.T) { 1942 limitRange := &v1.LimitRange{ 1943 ObjectMeta: metav1.ObjectMeta{ 1944 Name: "test-defaults", 1945 }, 1946 Spec: v1.LimitRangeSpec{ 1947 Limits: []v1.LimitRangeItem{{ 1948 Type: v1.LimitTypeContainer, 1949 Max: v1.ResourceList{ 1950 v1.ResourceCPU: resource.MustParse("100m"), 1951 }, 1952 Min: v1.ResourceList{ 1953 v1.ResourceMemory: resource.MustParse("100Mi"), 1954 }, 1955 Default: v1.ResourceList{}, 1956 DefaultRequest: v1.ResourceList{}, 1957 }}, 1958 }, 1959 } 1960 1961 output := roundTrip(t, runtime.Object(limitRange)) 1962 limitRange2 := output.(*v1.LimitRange) 1963 defaultLimit := limitRange2.Spec.Limits[0].Default 1964 defaultRequest := limitRange2.Spec.Limits[0].DefaultRequest 1965 1966 // verify that default cpu was set to the max 1967 defaultValue := defaultLimit[v1.ResourceCPU] 1968 if defaultValue.String() != "100m" { 1969 t.Errorf("Expected default cpu: %s, got: %s", "100m", defaultValue.String()) 1970 } 1971 // verify that default request was set to the limit 1972 requestValue := defaultRequest[v1.ResourceCPU] 1973 if requestValue.String() != "100m" { 1974 t.Errorf("Expected request cpu: %s, got: %s", "100m", requestValue.String()) 1975 } 1976 // verify that if a min is provided, it will be the default if no limit is specified 1977 requestMinValue := defaultRequest[v1.ResourceMemory] 1978 if requestMinValue.String() != "100Mi" { 1979 t.Errorf("Expected request memory: %s, got: %s", "100Mi", requestMinValue.String()) 1980 } 1981 } 1982 1983 func TestSetDefaultProbe(t *testing.T) { 1984 originalProbe := v1.Probe{} 1985 expectedProbe := v1.Probe{ 1986 InitialDelaySeconds: 0, 1987 TimeoutSeconds: 1, 1988 PeriodSeconds: 10, 1989 SuccessThreshold: 1, 1990 FailureThreshold: 3, 1991 } 1992 1993 pod := &v1.Pod{ 1994 Spec: v1.PodSpec{ 1995 Containers: []v1.Container{{LivenessProbe: &originalProbe}}, 1996 }, 1997 } 1998 1999 output := roundTrip(t, runtime.Object(pod)).(*v1.Pod) 2000 actualProbe := *output.Spec.Containers[0].LivenessProbe 2001 if actualProbe != expectedProbe { 2002 t.Errorf("Expected probe: %+v\ngot: %+v\n", expectedProbe, actualProbe) 2003 } 2004 } 2005 2006 func TestSetDefaultSchedulerName(t *testing.T) { 2007 pod := &v1.Pod{} 2008 2009 output := roundTrip(t, runtime.Object(pod)).(*v1.Pod) 2010 if output.Spec.SchedulerName != v1.DefaultSchedulerName { 2011 t.Errorf("Expected scheduler name: %+v\ngot: %+v\n", v1.DefaultSchedulerName, output.Spec.SchedulerName) 2012 } 2013 } 2014 2015 func TestSetDefaultHostPathVolumeSource(t *testing.T) { 2016 s := v1.PodSpec{} 2017 s.Volumes = []v1.Volume{ 2018 { 2019 VolumeSource: v1.VolumeSource{ 2020 HostPath: &v1.HostPathVolumeSource{Path: "foo"}, 2021 }, 2022 }, 2023 } 2024 pod := &v1.Pod{ 2025 Spec: s, 2026 } 2027 output := roundTrip(t, runtime.Object(pod)) 2028 pod2 := output.(*v1.Pod) 2029 defaultType := pod2.Spec.Volumes[0].VolumeSource.HostPath.Type 2030 expectedType := v1.HostPathUnset 2031 2032 if defaultType == nil || *defaultType != expectedType { 2033 t.Errorf("Expected v1.HostPathVolumeSource default type %v, got %v", expectedType, defaultType) 2034 } 2035 } 2036 2037 func TestSetDefaultEnableServiceLinks(t *testing.T) { 2038 pod := &v1.Pod{} 2039 output := roundTrip(t, runtime.Object(pod)).(*v1.Pod) 2040 if output.Spec.EnableServiceLinks == nil || *output.Spec.EnableServiceLinks != v1.DefaultEnableServiceLinks { 2041 t.Errorf("Expected enableServiceLinks value: %+v\ngot: %+v\n", v1.DefaultEnableServiceLinks, *output.Spec.EnableServiceLinks) 2042 } 2043 } 2044 2045 func TestSetDefaultServiceInternalTrafficPolicy(t *testing.T) { 2046 cluster := v1.ServiceInternalTrafficPolicyCluster 2047 local := v1.ServiceInternalTrafficPolicyLocal 2048 testCases := []struct { 2049 name string 2050 expectedInternalTrafficPolicy *v1.ServiceInternalTrafficPolicy 2051 svc v1.Service 2052 }{ 2053 { 2054 name: "must set default internalTrafficPolicy", 2055 expectedInternalTrafficPolicy: &cluster, 2056 svc: v1.Service{}, 2057 }, 2058 { 2059 name: "must not set default internalTrafficPolicy when it's cluster", 2060 expectedInternalTrafficPolicy: &cluster, 2061 svc: v1.Service{ 2062 Spec: v1.ServiceSpec{ 2063 InternalTrafficPolicy: &cluster, 2064 }, 2065 }, 2066 }, 2067 { 2068 name: "must not set default internalTrafficPolicy when type is ExternalName", 2069 expectedInternalTrafficPolicy: nil, 2070 svc: v1.Service{ 2071 Spec: v1.ServiceSpec{ 2072 Type: v1.ServiceTypeExternalName, 2073 }, 2074 }, 2075 }, 2076 { 2077 name: "must not set default internalTrafficPolicy when it's local", 2078 expectedInternalTrafficPolicy: &local, 2079 svc: v1.Service{ 2080 Spec: v1.ServiceSpec{ 2081 InternalTrafficPolicy: &local, 2082 }, 2083 }, 2084 }, 2085 } 2086 for _, test := range testCases { 2087 t.Run(test.name, func(t *testing.T) { 2088 obj := roundTrip(t, runtime.Object(&test.svc)) 2089 svc := obj.(*v1.Service) 2090 2091 if !reflect.DeepEqual(svc.Spec.InternalTrafficPolicy, test.expectedInternalTrafficPolicy) { 2092 t.Errorf("expected .spec.internalTrafficPolicy: %v got %v", test.expectedInternalTrafficPolicy, svc.Spec.InternalTrafficPolicy) 2093 } 2094 }) 2095 } 2096 } 2097 2098 func TestSetDefaultResizePolicy(t *testing.T) { 2099 // verify we default to NotRequired restart policy for resize when resources are specified 2100 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true) 2101 2102 for desc, tc := range map[string]struct { 2103 testContainer v1.Container 2104 expectedResizePolicy []v1.ContainerResizePolicy 2105 }{ 2106 "CPU and memory limits are specified": { 2107 testContainer: v1.Container{ 2108 Resources: v1.ResourceRequirements{ 2109 Limits: v1.ResourceList{ 2110 v1.ResourceCPU: resource.MustParse("100m"), 2111 v1.ResourceMemory: resource.MustParse("200Mi"), 2112 }, 2113 }, 2114 }, 2115 expectedResizePolicy: []v1.ContainerResizePolicy{ 2116 { 2117 ResourceName: v1.ResourceCPU, 2118 RestartPolicy: v1.NotRequired, 2119 }, 2120 { 2121 ResourceName: v1.ResourceMemory, 2122 RestartPolicy: v1.NotRequired, 2123 }, 2124 }, 2125 }, 2126 "CPU requests are specified": { 2127 testContainer: v1.Container{ 2128 Resources: v1.ResourceRequirements{ 2129 Requests: v1.ResourceList{ 2130 v1.ResourceCPU: resource.MustParse("100m"), 2131 }, 2132 }, 2133 }, 2134 expectedResizePolicy: []v1.ContainerResizePolicy{ 2135 { 2136 ResourceName: v1.ResourceCPU, 2137 RestartPolicy: v1.NotRequired, 2138 }, 2139 }, 2140 }, 2141 "Memory limits are specified": { 2142 testContainer: v1.Container{ 2143 Resources: v1.ResourceRequirements{ 2144 Limits: v1.ResourceList{ 2145 v1.ResourceMemory: resource.MustParse("200Mi"), 2146 }, 2147 }, 2148 }, 2149 expectedResizePolicy: []v1.ContainerResizePolicy{ 2150 { 2151 ResourceName: v1.ResourceMemory, 2152 RestartPolicy: v1.NotRequired, 2153 }, 2154 }, 2155 }, 2156 "No resources are specified": { 2157 testContainer: v1.Container{Name: "besteffort"}, 2158 expectedResizePolicy: nil, 2159 }, 2160 "CPU and memory limits are specified with restartContainer resize policy for memory": { 2161 testContainer: v1.Container{ 2162 Resources: v1.ResourceRequirements{ 2163 Limits: v1.ResourceList{ 2164 v1.ResourceCPU: resource.MustParse("100m"), 2165 v1.ResourceMemory: resource.MustParse("200Mi"), 2166 }, 2167 }, 2168 ResizePolicy: []v1.ContainerResizePolicy{ 2169 { 2170 ResourceName: v1.ResourceMemory, 2171 RestartPolicy: v1.RestartContainer, 2172 }, 2173 }, 2174 }, 2175 expectedResizePolicy: []v1.ContainerResizePolicy{ 2176 { 2177 ResourceName: v1.ResourceMemory, 2178 RestartPolicy: v1.RestartContainer, 2179 }, 2180 { 2181 ResourceName: v1.ResourceCPU, 2182 RestartPolicy: v1.NotRequired, 2183 }, 2184 }, 2185 }, 2186 "CPU requests and memory limits are specified with restartContainer resize policy for CPU": { 2187 testContainer: v1.Container{ 2188 Resources: v1.ResourceRequirements{ 2189 Limits: v1.ResourceList{ 2190 v1.ResourceMemory: resource.MustParse("200Mi"), 2191 }, 2192 Requests: v1.ResourceList{ 2193 v1.ResourceCPU: resource.MustParse("100m"), 2194 }, 2195 }, 2196 ResizePolicy: []v1.ContainerResizePolicy{ 2197 { 2198 ResourceName: v1.ResourceCPU, 2199 RestartPolicy: v1.RestartContainer, 2200 }, 2201 }, 2202 }, 2203 expectedResizePolicy: []v1.ContainerResizePolicy{ 2204 { 2205 ResourceName: v1.ResourceCPU, 2206 RestartPolicy: v1.RestartContainer, 2207 }, 2208 { 2209 ResourceName: v1.ResourceMemory, 2210 RestartPolicy: v1.NotRequired, 2211 }, 2212 }, 2213 }, 2214 "CPU and memory requests are specified with restartContainer resize policy for both": { 2215 testContainer: v1.Container{ 2216 Resources: v1.ResourceRequirements{ 2217 Requests: v1.ResourceList{ 2218 v1.ResourceCPU: resource.MustParse("100m"), 2219 v1.ResourceMemory: resource.MustParse("200Mi"), 2220 }, 2221 }, 2222 ResizePolicy: []v1.ContainerResizePolicy{ 2223 { 2224 ResourceName: v1.ResourceCPU, 2225 RestartPolicy: v1.RestartContainer, 2226 }, 2227 { 2228 ResourceName: v1.ResourceMemory, 2229 RestartPolicy: v1.RestartContainer, 2230 }, 2231 }, 2232 }, 2233 expectedResizePolicy: []v1.ContainerResizePolicy{ 2234 { 2235 ResourceName: v1.ResourceCPU, 2236 RestartPolicy: v1.RestartContainer, 2237 }, 2238 { 2239 ResourceName: v1.ResourceMemory, 2240 RestartPolicy: v1.RestartContainer, 2241 }, 2242 }, 2243 }, 2244 "Ephemeral storage limits are specified": { 2245 testContainer: v1.Container{ 2246 Resources: v1.ResourceRequirements{ 2247 Limits: v1.ResourceList{ 2248 v1.ResourceEphemeralStorage: resource.MustParse("500Mi"), 2249 }, 2250 }, 2251 }, 2252 expectedResizePolicy: nil, 2253 }, 2254 "Ephemeral storage requests and CPU limits are specified": { 2255 testContainer: v1.Container{ 2256 Resources: v1.ResourceRequirements{ 2257 Limits: v1.ResourceList{ 2258 v1.ResourceCPU: resource.MustParse("100m"), 2259 }, 2260 Requests: v1.ResourceList{ 2261 v1.ResourceEphemeralStorage: resource.MustParse("500Mi"), 2262 }, 2263 }, 2264 }, 2265 expectedResizePolicy: []v1.ContainerResizePolicy{ 2266 { 2267 ResourceName: v1.ResourceCPU, 2268 RestartPolicy: v1.NotRequired, 2269 }, 2270 }, 2271 }, 2272 "Ephemeral storage requests and limits, memory requests with restartContainer policy are specified": { 2273 testContainer: v1.Container{ 2274 Resources: v1.ResourceRequirements{ 2275 Limits: v1.ResourceList{ 2276 v1.ResourceEphemeralStorage: resource.MustParse("500Mi"), 2277 }, 2278 Requests: v1.ResourceList{ 2279 v1.ResourceEphemeralStorage: resource.MustParse("500Mi"), 2280 v1.ResourceMemory: resource.MustParse("200Mi"), 2281 }, 2282 }, 2283 ResizePolicy: []v1.ContainerResizePolicy{ 2284 { 2285 ResourceName: v1.ResourceMemory, 2286 RestartPolicy: v1.RestartContainer, 2287 }, 2288 }, 2289 }, 2290 expectedResizePolicy: []v1.ContainerResizePolicy{ 2291 { 2292 ResourceName: v1.ResourceMemory, 2293 RestartPolicy: v1.RestartContainer, 2294 }, 2295 }, 2296 }, 2297 } { 2298 t.Run(desc, func(t *testing.T) { 2299 testPod := v1.Pod{} 2300 testPod.Spec.Containers = append(testPod.Spec.Containers, tc.testContainer) 2301 output := roundTrip(t, runtime.Object(&testPod)) 2302 pod2 := output.(*v1.Pod) 2303 if !cmp.Equal(pod2.Spec.Containers[0].ResizePolicy, tc.expectedResizePolicy) { 2304 t.Errorf("expected resize policy %+v, but got %+v", tc.expectedResizePolicy, pod2.Spec.Containers[0].ResizePolicy) 2305 } 2306 }) 2307 } 2308 }