k8s.io/kubernetes@v1.29.3/pkg/kubelet/apis/config/validation/validation_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package validation_test 18 19 import ( 20 "strings" 21 "testing" 22 "time" 23 24 v1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 utilfeature "k8s.io/apiserver/pkg/util/feature" 27 logsapi "k8s.io/component-base/logs/api/v1" 28 tracingapi "k8s.io/component-base/tracing/api/v1" 29 kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" 30 "k8s.io/kubernetes/pkg/kubelet/apis/config/validation" 31 kubetypes "k8s.io/kubernetes/pkg/kubelet/types" 32 utilpointer "k8s.io/utils/pointer" 33 ) 34 35 var ( 36 successConfig = kubeletconfig.KubeletConfiguration{ 37 CgroupsPerQOS: cgroupsPerQOS, 38 EnforceNodeAllocatable: enforceNodeAllocatable, 39 SystemReservedCgroup: "/system.slice", 40 KubeReservedCgroup: "/kubelet.service", 41 SystemCgroups: "", 42 CgroupRoot: "", 43 EventBurst: 10, 44 EventRecordQPS: 5, 45 HealthzPort: 10248, 46 ImageGCHighThresholdPercent: 85, 47 ImageGCLowThresholdPercent: 80, 48 IPTablesDropBit: 15, 49 IPTablesMasqueradeBit: 14, 50 KubeAPIBurst: 10, 51 KubeAPIQPS: 5, 52 MaxOpenFiles: 1000000, 53 MaxPods: 110, 54 OOMScoreAdj: -999, 55 PodsPerCore: 100, 56 Port: 65535, 57 ReadOnlyPort: 0, 58 RegistryBurst: 10, 59 RegistryPullQPS: 5, 60 MaxParallelImagePulls: nil, 61 HairpinMode: kubeletconfig.PromiscuousBridge, 62 NodeLeaseDurationSeconds: 1, 63 CPUCFSQuotaPeriod: metav1.Duration{Duration: 25 * time.Millisecond}, 64 TopologyManagerScope: kubeletconfig.PodTopologyManagerScope, 65 TopologyManagerPolicy: kubeletconfig.SingleNumaNodeTopologyManagerPolicy, 66 ShutdownGracePeriod: metav1.Duration{Duration: 30 * time.Second}, 67 ShutdownGracePeriodCriticalPods: metav1.Duration{Duration: 10 * time.Second}, 68 MemoryThrottlingFactor: utilpointer.Float64(0.9), 69 FeatureGates: map[string]bool{ 70 "CustomCPUCFSQuotaPeriod": true, 71 "GracefulNodeShutdown": true, 72 "MemoryQoS": true, 73 }, 74 Logging: logsapi.LoggingConfiguration{ 75 Format: "text", 76 }, 77 ContainerRuntimeEndpoint: "unix:///run/containerd/containerd.sock", 78 } 79 ) 80 81 func TestValidateKubeletConfiguration(t *testing.T) { 82 featureGate := utilfeature.DefaultFeatureGate.DeepCopy() 83 logsapi.AddFeatureGates(featureGate) 84 85 cases := []struct { 86 name string 87 configure func(config *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration 88 errMsg string 89 }{{ 90 name: "Success", 91 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 92 return conf 93 }, 94 }, { 95 name: "invalid NodeLeaseDurationSeconds", 96 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 97 conf.NodeLeaseDurationSeconds = 0 98 return conf 99 }, 100 errMsg: "invalid configuration: nodeLeaseDurationSeconds must be greater than 0", 101 }, { 102 name: "specify EnforceNodeAllocatable without enabling CgroupsPerQOS", 103 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 104 conf.CgroupsPerQOS = false 105 conf.EnforceNodeAllocatable = []string{"pods"} 106 return conf 107 }, 108 errMsg: "invalid configuration: enforceNodeAllocatable (--enforce-node-allocatable) is not supported unless cgroupsPerQOS (--cgroups-per-qos) is set to true", 109 }, { 110 name: "specify SystemCgroups without CgroupRoot", 111 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 112 conf.SystemCgroups = "/" 113 conf.CgroupRoot = "" 114 return conf 115 }, 116 errMsg: "invalid configuration: systemCgroups (--system-cgroups) was specified and cgroupRoot (--cgroup-root) was not specified", 117 }, { 118 name: "invalid EventBurst", 119 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 120 conf.EventBurst = -1 121 return conf 122 }, 123 errMsg: "invalid configuration: eventBurst (--event-burst) -1 must not be a negative number", 124 }, { 125 name: "invalid EventRecordQPS", 126 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 127 conf.EventRecordQPS = -1 128 return conf 129 }, 130 errMsg: "invalid configuration: eventRecordQPS (--event-qps) -1 must not be a negative number", 131 }, { 132 name: "invalid HealthzPort", 133 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 134 conf.HealthzPort = 65536 135 return conf 136 }, 137 errMsg: "invalid configuration: healthzPort (--healthz-port) 65536 must be between 1 and 65535, inclusive", 138 }, { 139 name: "specify CPUCFSQuotaPeriod without enabling CPUCFSQuotaPeriod", 140 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 141 conf.FeatureGates = map[string]bool{"CustomCPUCFSQuotaPeriod": false} 142 conf.CPUCFSQuotaPeriod = metav1.Duration{Duration: 200 * time.Millisecond} 143 return conf 144 }, 145 errMsg: "invalid configuration: cpuCFSQuotaPeriod (--cpu-cfs-quota-period) {200ms} requires feature gate CustomCPUCFSQuotaPeriod", 146 }, { 147 name: "invalid CPUCFSQuotaPeriod", 148 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 149 conf.FeatureGates = map[string]bool{"CustomCPUCFSQuotaPeriod": true} 150 conf.CPUCFSQuotaPeriod = metav1.Duration{Duration: 2 * time.Second} 151 return conf 152 }, 153 errMsg: "invalid configuration: cpuCFSQuotaPeriod (--cpu-cfs-quota-period) {2s} must be between 1ms and 1sec, inclusive", 154 }, { 155 name: "invalid ImageGCHighThresholdPercent", 156 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 157 conf.ImageGCHighThresholdPercent = 101 158 return conf 159 }, 160 errMsg: "invalid configuration: imageGCHighThresholdPercent (--image-gc-high-threshold) 101 must be between 0 and 100, inclusive", 161 }, { 162 name: "invalid ImageGCLowThresholdPercent", 163 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 164 conf.ImageGCLowThresholdPercent = -1 165 return conf 166 }, 167 errMsg: "invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) -1 must be between 0 and 100, inclusive", 168 }, { 169 name: "ImageGCLowThresholdPercent is equal to ImageGCHighThresholdPercent", 170 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 171 conf.ImageGCHighThresholdPercent = 0 172 conf.ImageGCLowThresholdPercent = 0 173 return conf 174 }, 175 errMsg: "invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) 0 must be less than imageGCHighThresholdPercent (--image-gc-high-threshold) 0", 176 }, { 177 name: "ImageGCLowThresholdPercent is greater than ImageGCHighThresholdPercent", 178 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 179 conf.ImageGCHighThresholdPercent = 0 180 conf.ImageGCLowThresholdPercent = 1 181 return conf 182 }, 183 errMsg: "invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) 1 must be less than imageGCHighThresholdPercent (--image-gc-high-threshold) 0", 184 }, { 185 name: "invalid IPTablesDropBit", 186 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 187 conf.IPTablesDropBit = 32 188 return conf 189 }, 190 errMsg: "invalid configuration: iptablesDropBit (--iptables-drop-bit) 32 must be between 0 and 31, inclusive", 191 }, { 192 name: "invalid IPTablesMasqueradeBit", 193 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 194 conf.IPTablesMasqueradeBit = 32 195 return conf 196 }, 197 errMsg: "invalid configuration: iptablesMasqueradeBit (--iptables-masquerade-bit) 32 must be between 0 and 31, inclusive", 198 }, { 199 name: "invalid KubeAPIBurst", 200 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 201 conf.KubeAPIBurst = -1 202 return conf 203 }, 204 errMsg: "invalid configuration: kubeAPIBurst (--kube-api-burst) -1 must not be a negative number", 205 }, { 206 name: "invalid KubeAPIQPS", 207 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 208 conf.KubeAPIQPS = -1 209 return conf 210 }, 211 errMsg: "invalid configuration: kubeAPIQPS (--kube-api-qps) -1 must not be a negative number", 212 }, { 213 name: "invalid NodeStatusMaxImages", 214 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 215 conf.NodeStatusMaxImages = -2 216 return conf 217 }, 218 errMsg: "invalid configuration: nodeStatusMaxImages (--node-status-max-images) -2 must be -1 or greater", 219 }, { 220 name: "invalid MaxOpenFiles", 221 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 222 conf.MaxOpenFiles = -1 223 return conf 224 }, 225 errMsg: "invalid configuration: maxOpenFiles (--max-open-files) -1 must not be a negative number", 226 }, { 227 name: "invalid MaxPods", 228 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 229 conf.MaxPods = -1 230 return conf 231 }, 232 errMsg: "invalid configuration: maxPods (--max-pods) -1 must not be a negative number", 233 }, { 234 name: "invalid OOMScoreAdj", 235 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 236 conf.OOMScoreAdj = 1001 237 return conf 238 }, 239 errMsg: "invalid configuration: oomScoreAdj (--oom-score-adj) 1001 must be between -1000 and 1000, inclusive", 240 }, { 241 name: "invalid PodsPerCore", 242 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 243 conf.PodsPerCore = -1 244 return conf 245 }, 246 errMsg: "invalid configuration: podsPerCore (--pods-per-core) -1 must not be a negative number", 247 }, { 248 name: "invalid Port", 249 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 250 conf.Port = 65536 251 return conf 252 }, 253 errMsg: "invalid configuration: port (--port) 65536 must be between 1 and 65535, inclusive", 254 }, { 255 name: "invalid ReadOnlyPort", 256 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 257 conf.ReadOnlyPort = 65536 258 return conf 259 }, 260 errMsg: "invalid configuration: readOnlyPort (--read-only-port) 65536 must be between 0 and 65535, inclusive", 261 }, { 262 name: "invalid RegistryBurst", 263 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 264 conf.RegistryBurst = -1 265 return conf 266 }, 267 errMsg: "invalid configuration: registryBurst (--registry-burst) -1 must not be a negative number", 268 }, { 269 name: "invalid RegistryPullQPS", 270 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 271 conf.RegistryPullQPS = -1 272 return conf 273 }, 274 errMsg: "invalid configuration: registryPullQPS (--registry-qps) -1 must not be a negative number", 275 }, { 276 name: "invalid MaxParallelImagePulls", 277 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 278 conf.MaxParallelImagePulls = utilpointer.Int32(0) 279 return conf 280 }, 281 errMsg: "invalid configuration: maxParallelImagePulls 0 must be a positive number", 282 }, { 283 name: "invalid MaxParallelImagePulls and SerializeImagePulls combination", 284 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 285 conf.MaxParallelImagePulls = utilpointer.Int32(3) 286 conf.SerializeImagePulls = true 287 return conf 288 }, 289 errMsg: "invalid configuration: maxParallelImagePulls cannot be larger than 1 unless SerializeImagePulls (--serialize-image-pulls) is set to false", 290 }, { 291 name: "valid MaxParallelImagePulls and SerializeImagePulls combination", 292 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 293 conf.MaxParallelImagePulls = utilpointer.Int32(1) 294 conf.SerializeImagePulls = true 295 return conf 296 }, 297 }, { 298 name: "specify ServerTLSBootstrap without enabling RotateKubeletServerCertificate", 299 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 300 conf.FeatureGates = map[string]bool{"RotateKubeletServerCertificate": false} 301 conf.ServerTLSBootstrap = true 302 return conf 303 }, 304 errMsg: "invalid configuration: serverTLSBootstrap true requires feature gate RotateKubeletServerCertificate", 305 }, { 306 name: "invalid TopologyManagerPolicy", 307 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 308 conf.TopologyManagerPolicy = "invalid-policy" 309 return conf 310 }, 311 errMsg: "invalid configuration: topologyManagerPolicy (--topology-manager-policy) \"invalid-policy\" must be one of: [\"none\" \"best-effort\" \"restricted\" \"single-numa-node\"]", 312 }, { 313 name: "invalid TopologyManagerScope", 314 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 315 conf.TopologyManagerScope = "invalid-scope" 316 return conf 317 }, 318 errMsg: "invalid configuration: topologyManagerScope (--topology-manager-scope) \"invalid-scope\" must be one of: \"container\", or \"pod\"", 319 }, { 320 name: "ShutdownGracePeriodCriticalPods is greater than ShutdownGracePeriod", 321 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 322 conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": true} 323 conf.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: 2 * time.Second} 324 conf.ShutdownGracePeriod = metav1.Duration{Duration: 1 * time.Second} 325 return conf 326 }, 327 errMsg: "invalid configuration: shutdownGracePeriodCriticalPods {2s} must be <= shutdownGracePeriod {1s}", 328 }, { 329 name: "ShutdownGracePeriod is less than 1 sec", 330 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 331 conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": true} 332 conf.ShutdownGracePeriod = metav1.Duration{Duration: 1 * time.Millisecond} 333 return conf 334 }, 335 errMsg: "invalid configuration: shutdownGracePeriod {1ms} must be either zero or otherwise >= 1 sec", 336 }, { 337 name: "ShutdownGracePeriodCriticalPods is less than 1 sec", 338 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 339 conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": true} 340 conf.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: 1 * time.Millisecond} 341 return conf 342 }, 343 errMsg: "invalid configuration: shutdownGracePeriodCriticalPods {1ms} must be either zero or otherwise >= 1 sec", 344 }, { 345 name: "specify ShutdownGracePeriod without enabling GracefulNodeShutdown", 346 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 347 conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": false} 348 conf.ShutdownGracePeriod = metav1.Duration{Duration: 1 * time.Second} 349 return conf 350 }, 351 errMsg: "invalid configuration: specifying shutdownGracePeriod or shutdownGracePeriodCriticalPods requires feature gate GracefulNodeShutdown", 352 }, { 353 name: "specify ShutdownGracePeriodCriticalPods without enabling GracefulNodeShutdown", 354 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 355 conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": false} 356 conf.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: 1 * time.Second} 357 return conf 358 }, 359 errMsg: "invalid configuration: specifying shutdownGracePeriod or shutdownGracePeriodCriticalPods requires feature gate GracefulNodeShutdown", 360 }, { 361 name: "invalid MemorySwap.SwapBehavior", 362 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 363 conf.FeatureGates = map[string]bool{"NodeSwap": true} 364 conf.MemorySwap.SwapBehavior = "invalid-behavior" 365 return conf 366 }, 367 errMsg: "invalid configuration: memorySwap.swapBehavior \"invalid-behavior\" must be one of: \"\", \"LimitedSwap\", or \"UnlimitedSwap\"", 368 }, { 369 name: "specify MemorySwap.SwapBehavior without enabling NodeSwap", 370 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 371 conf.FeatureGates = map[string]bool{"NodeSwap": false} 372 conf.MemorySwap.SwapBehavior = kubetypes.LimitedSwap 373 return conf 374 }, 375 errMsg: "invalid configuration: memorySwap.swapBehavior cannot be set when NodeSwap feature flag is disabled", 376 }, { 377 name: "specify SystemReservedEnforcementKey without specifying SystemReservedCgroup", 378 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 379 conf.EnforceNodeAllocatable = []string{kubetypes.SystemReservedEnforcementKey} 380 conf.SystemReservedCgroup = "" 381 return conf 382 }, 383 errMsg: "invalid configuration: systemReservedCgroup (--system-reserved-cgroup) must be specified when \"system-reserved\" contained in enforceNodeAllocatable (--enforce-node-allocatable)", 384 }, { 385 name: "specify KubeReservedEnforcementKey without specifying KubeReservedCgroup", 386 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 387 conf.EnforceNodeAllocatable = []string{kubetypes.KubeReservedEnforcementKey} 388 conf.KubeReservedCgroup = "" 389 return conf 390 }, 391 errMsg: "invalid configuration: kubeReservedCgroup (--kube-reserved-cgroup) must be specified when \"kube-reserved\" contained in enforceNodeAllocatable (--enforce-node-allocatable)", 392 }, { 393 name: "specify NodeAllocatableNoneKey with additional enforcements", 394 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 395 conf.EnforceNodeAllocatable = []string{kubetypes.NodeAllocatableNoneKey, kubetypes.KubeReservedEnforcementKey} 396 return conf 397 }, 398 errMsg: "invalid configuration: enforceNodeAllocatable (--enforce-node-allocatable) may not contain additional enforcements when \"none\" is specified", 399 }, { 400 name: "invalid EnforceNodeAllocatable", 401 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 402 conf.EnforceNodeAllocatable = []string{"invalid-enforce-node-allocatable"} 403 return conf 404 }, 405 errMsg: "invalid configuration: option \"invalid-enforce-node-allocatable\" specified for enforceNodeAllocatable (--enforce-node-allocatable). Valid options are \"pods\", \"system-reserved\", \"kube-reserved\", or \"none\"", 406 }, { 407 name: "invalid HairpinMode", 408 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 409 conf.HairpinMode = "invalid-hair-pin-mode" 410 return conf 411 }, 412 errMsg: "invalid configuration: option \"invalid-hair-pin-mode\" specified for hairpinMode (--hairpin-mode). Valid options are \"none\", \"hairpin-veth\" or \"promiscuous-bridge\"", 413 }, { 414 name: "specify ReservedSystemCPUs with SystemReservedCgroup", 415 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 416 conf.ReservedSystemCPUs = "0-3" 417 conf.SystemReservedCgroup = "/system.slice" 418 return conf 419 }, 420 errMsg: "invalid configuration: can't use reservedSystemCPUs (--reserved-cpus) with systemReservedCgroup (--system-reserved-cgroup) or kubeReservedCgroup (--kube-reserved-cgroup)", 421 }, { 422 name: "specify ReservedSystemCPUs with KubeReservedCgroup", 423 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 424 conf.ReservedSystemCPUs = "0-3" 425 conf.KubeReservedCgroup = "/system.slice" 426 return conf 427 }, 428 errMsg: "invalid configuration: can't use reservedSystemCPUs (--reserved-cpus) with systemReservedCgroup (--system-reserved-cgroup) or kubeReservedCgroup (--kube-reserved-cgroup)", 429 }, { 430 name: "invalid ReservedSystemCPUs", 431 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 432 conf.ReservedSystemCPUs = "invalid-reserved-system-cpus" 433 return conf 434 }, 435 errMsg: "invalid configuration: unable to parse reservedSystemCPUs (--reserved-cpus) invalid-reserved-system-cpus, error:", 436 }, { 437 name: "enable MemoryQoS without specifying MemoryThrottlingFactor", 438 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 439 conf.FeatureGates = map[string]bool{"MemoryQoS": true} 440 conf.MemoryThrottlingFactor = nil 441 return conf 442 }, 443 errMsg: "invalid configuration: memoryThrottlingFactor is required when MemoryQoS feature flag is enabled", 444 }, { 445 name: "invalid MemoryThrottlingFactor", 446 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 447 conf.MemoryThrottlingFactor = utilpointer.Float64(1.1) 448 return conf 449 }, 450 errMsg: "invalid configuration: memoryThrottlingFactor 1.1 must be greater than 0 and less than or equal to 1.0", 451 }, { 452 name: "invalid Taint.TimeAdded", 453 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 454 now := metav1.Now() 455 conf.RegisterWithTaints = []v1.Taint{{TimeAdded: &now}} 456 return conf 457 }, 458 errMsg: "invalid configuration: taint.TimeAdded is not nil", 459 }, { 460 name: "specify tracing with KubeletTracing disabled", 461 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 462 samplingRate := int32(99999) 463 conf.FeatureGates = map[string]bool{"KubeletTracing": false} 464 conf.Tracing = &tracingapi.TracingConfiguration{SamplingRatePerMillion: &samplingRate} 465 return conf 466 }, 467 errMsg: "invalid configuration: tracing should not be configured if KubeletTracing feature flag is disabled.", 468 }, { 469 name: "specify tracing invalid sampling rate", 470 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 471 samplingRate := int32(-1) 472 conf.FeatureGates = map[string]bool{"KubeletTracing": true} 473 conf.Tracing = &tracingapi.TracingConfiguration{SamplingRatePerMillion: &samplingRate} 474 return conf 475 }, 476 errMsg: "tracing.samplingRatePerMillion: Invalid value: -1: sampling rate must be positive", 477 }, { 478 name: "specify tracing invalid endpoint", 479 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 480 ep := "dn%2s://localhost:4317" 481 conf.FeatureGates = map[string]bool{"KubeletTracing": true} 482 conf.Tracing = &tracingapi.TracingConfiguration{Endpoint: &ep} 483 return conf 484 }, 485 errMsg: "tracing.endpoint: Invalid value: \"dn%2s://localhost:4317\": parse \"dn%2s://localhost:4317\": first path segment in URL cannot contain colon", 486 }, { 487 name: "invalid GracefulNodeShutdownBasedOnPodPriority", 488 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 489 conf.FeatureGates = map[string]bool{"GracefulNodeShutdownBasedOnPodPriority": true} 490 conf.ShutdownGracePeriodByPodPriority = []kubeletconfig.ShutdownGracePeriodByPodPriority{{ 491 Priority: 0, 492 ShutdownGracePeriodSeconds: 0, 493 }} 494 return conf 495 }, 496 errMsg: "invalid configuration: Cannot specify both shutdownGracePeriodByPodPriority and shutdownGracePeriod at the same time", 497 }, { 498 name: "Specifying shutdownGracePeriodByPodPriority without enable GracefulNodeShutdownBasedOnPodPriority", 499 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 500 conf.FeatureGates = map[string]bool{"GracefulNodeShutdownBasedOnPodPriority": false} 501 conf.ShutdownGracePeriodByPodPriority = []kubeletconfig.ShutdownGracePeriodByPodPriority{{ 502 Priority: 0, 503 ShutdownGracePeriodSeconds: 0, 504 }} 505 return conf 506 }, 507 errMsg: "invalid configuration: Specifying shutdownGracePeriodByPodPriority requires feature gate GracefulNodeShutdownBasedOnPodPriority", 508 }, { 509 name: "enableSystemLogQuery is enabled without NodeLogQuery feature gate", 510 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 511 conf.EnableSystemLogQuery = true 512 return conf 513 }, 514 errMsg: "invalid configuration: NodeLogQuery feature gate is required for enableSystemLogHandler", 515 }, { 516 name: "enableSystemLogQuery is enabled without enableSystemLogHandler", 517 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 518 conf.FeatureGates = map[string]bool{"NodeLogQuery": true} 519 conf.EnableSystemLogHandler = false 520 conf.EnableSystemLogQuery = true 521 return conf 522 }, 523 errMsg: "invalid configuration: enableSystemLogHandler is required for enableSystemLogQuery", 524 }, { 525 name: "imageMaximumGCAge should not be specified without feature gate", 526 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 527 conf.ImageMaximumGCAge = metav1.Duration{Duration: 1} 528 return conf 529 }, 530 errMsg: "invalid configuration: ImageMaximumGCAge feature gate is required for Kubelet configuration option ImageMaximumGCAge", 531 }, { 532 name: "imageMaximumGCAge should not be negative", 533 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 534 conf.FeatureGates = map[string]bool{"ImageMaximumGCAge": true} 535 conf.ImageMaximumGCAge = metav1.Duration{Duration: -1} 536 return conf 537 }, 538 errMsg: "invalid configuration: imageMaximumGCAge -1ns must not be negative", 539 }, { 540 name: "imageMaximumGCAge should not be less than imageMinimumGCAge", 541 configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration { 542 conf.FeatureGates = map[string]bool{"ImageMaximumGCAge": true} 543 conf.ImageMaximumGCAge = metav1.Duration{Duration: 1} 544 conf.ImageMinimumGCAge = metav1.Duration{Duration: 2} 545 return conf 546 }, 547 errMsg: "invalid configuration: imageMaximumGCAge 1ns must be greater than imageMinimumGCAge 2ns", 548 }} 549 550 for _, tc := range cases { 551 t.Run(tc.name, func(t *testing.T) { 552 errs := validation.ValidateKubeletConfiguration(tc.configure(successConfig.DeepCopy()), featureGate) 553 554 if len(tc.errMsg) == 0 { 555 if errs != nil { 556 t.Errorf("unexpected error: %s", errs) 557 } 558 559 return 560 } 561 562 if errs == nil { 563 t.Errorf("expected error: %s", tc.errMsg) 564 return 565 } 566 567 if got := errs.Error(); !strings.Contains(got, tc.errMsg) { 568 t.Errorf("unexpected error: %s expected to contain %s", got, tc.errMsg) 569 } 570 }) 571 } 572 }