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