k8s.io/kubernetes@v1.29.3/pkg/kubelet/apis/config/validation/validation.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 18 19 import ( 20 "fmt" 21 "time" 22 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 utilerrors "k8s.io/apimachinery/pkg/util/errors" 25 utilvalidation "k8s.io/apimachinery/pkg/util/validation" 26 "k8s.io/apimachinery/pkg/util/validation/field" 27 "k8s.io/component-base/featuregate" 28 logsapi "k8s.io/component-base/logs/api/v1" 29 "k8s.io/component-base/metrics" 30 tracingapi "k8s.io/component-base/tracing/api/v1" 31 "k8s.io/kubernetes/pkg/features" 32 kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config" 33 kubetypes "k8s.io/kubernetes/pkg/kubelet/types" 34 utiltaints "k8s.io/kubernetes/pkg/util/taints" 35 "k8s.io/utils/cpuset" 36 ) 37 38 var ( 39 defaultCFSQuota = metav1.Duration{Duration: 100 * time.Millisecond} 40 ) 41 42 // ValidateKubeletConfiguration validates `kc` and returns an error if it is invalid 43 func ValidateKubeletConfiguration(kc *kubeletconfig.KubeletConfiguration, featureGate featuregate.FeatureGate) error { 44 allErrors := []error{} 45 46 // Make a local copy of the feature gates and combine it with the gates set by this configuration. 47 // This allows us to validate the config against the set of gates it will actually run against. 48 localFeatureGate := featureGate.DeepCopy() 49 if err := localFeatureGate.SetFromMap(kc.FeatureGates); err != nil { 50 return err 51 } 52 53 if kc.NodeLeaseDurationSeconds <= 0 { 54 allErrors = append(allErrors, fmt.Errorf("invalid configuration: nodeLeaseDurationSeconds must be greater than 0")) 55 } 56 if !kc.CgroupsPerQOS && len(kc.EnforceNodeAllocatable) > 0 { 57 allErrors = append(allErrors, fmt.Errorf("invalid configuration: enforceNodeAllocatable (--enforce-node-allocatable) is not supported unless cgroupsPerQOS (--cgroups-per-qos) is set to true")) 58 } 59 if kc.SystemCgroups != "" && kc.CgroupRoot == "" { 60 allErrors = append(allErrors, fmt.Errorf("invalid configuration: systemCgroups (--system-cgroups) was specified and cgroupRoot (--cgroup-root) was not specified")) 61 } 62 if kc.EventBurst < 0 { 63 allErrors = append(allErrors, fmt.Errorf("invalid configuration: eventBurst (--event-burst) %v must not be a negative number", kc.EventBurst)) 64 } 65 if kc.EventRecordQPS < 0 { 66 allErrors = append(allErrors, fmt.Errorf("invalid configuration: eventRecordQPS (--event-qps) %v must not be a negative number", kc.EventRecordQPS)) 67 } 68 if kc.HealthzPort != 0 && utilvalidation.IsValidPortNum(int(kc.HealthzPort)) != nil { 69 allErrors = append(allErrors, fmt.Errorf("invalid configuration: healthzPort (--healthz-port) %v must be between 1 and 65535, inclusive", kc.HealthzPort)) 70 } 71 if !localFeatureGate.Enabled(features.CPUCFSQuotaPeriod) && kc.CPUCFSQuotaPeriod != defaultCFSQuota { 72 allErrors = append(allErrors, fmt.Errorf("invalid configuration: cpuCFSQuotaPeriod (--cpu-cfs-quota-period) %v requires feature gate CustomCPUCFSQuotaPeriod", kc.CPUCFSQuotaPeriod)) 73 } 74 if localFeatureGate.Enabled(features.CPUCFSQuotaPeriod) && utilvalidation.IsInRange(int(kc.CPUCFSQuotaPeriod.Duration), int(1*time.Millisecond), int(time.Second)) != nil { 75 allErrors = append(allErrors, fmt.Errorf("invalid configuration: cpuCFSQuotaPeriod (--cpu-cfs-quota-period) %v must be between 1ms and 1sec, inclusive", kc.CPUCFSQuotaPeriod)) 76 } 77 if utilvalidation.IsInRange(int(kc.ImageGCHighThresholdPercent), 0, 100) != nil { 78 allErrors = append(allErrors, fmt.Errorf("invalid configuration: imageGCHighThresholdPercent (--image-gc-high-threshold) %v must be between 0 and 100, inclusive", kc.ImageGCHighThresholdPercent)) 79 } 80 if utilvalidation.IsInRange(int(kc.ImageGCLowThresholdPercent), 0, 100) != nil { 81 allErrors = append(allErrors, fmt.Errorf("invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) %v must be between 0 and 100, inclusive", kc.ImageGCLowThresholdPercent)) 82 } 83 if kc.ImageGCLowThresholdPercent >= kc.ImageGCHighThresholdPercent { 84 allErrors = append(allErrors, fmt.Errorf("invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) %v must be less than imageGCHighThresholdPercent (--image-gc-high-threshold) %v", kc.ImageGCLowThresholdPercent, kc.ImageGCHighThresholdPercent)) 85 } 86 if kc.ImageMaximumGCAge.Duration != 0 && !localFeatureGate.Enabled(features.ImageMaximumGCAge) { 87 allErrors = append(allErrors, fmt.Errorf("invalid configuration: ImageMaximumGCAge feature gate is required for Kubelet configuration option ImageMaximumGCAge")) 88 } 89 if kc.ImageMaximumGCAge.Duration < 0 { 90 allErrors = append(allErrors, fmt.Errorf("invalid configuration: imageMaximumGCAge %v must not be negative", kc.ImageMaximumGCAge.Duration)) 91 } 92 if kc.ImageMaximumGCAge.Duration > 0 && kc.ImageMaximumGCAge.Duration <= kc.ImageMinimumGCAge.Duration { 93 allErrors = append(allErrors, fmt.Errorf("invalid configuration: imageMaximumGCAge %v must be greater than imageMinimumGCAge %v", kc.ImageMaximumGCAge.Duration, kc.ImageMinimumGCAge.Duration)) 94 } 95 if utilvalidation.IsInRange(int(kc.IPTablesDropBit), 0, 31) != nil { 96 allErrors = append(allErrors, fmt.Errorf("invalid configuration: iptablesDropBit (--iptables-drop-bit) %v must be between 0 and 31, inclusive", kc.IPTablesDropBit)) 97 } 98 if utilvalidation.IsInRange(int(kc.IPTablesMasqueradeBit), 0, 31) != nil { 99 allErrors = append(allErrors, fmt.Errorf("invalid configuration: iptablesMasqueradeBit (--iptables-masquerade-bit) %v must be between 0 and 31, inclusive", kc.IPTablesMasqueradeBit)) 100 } 101 if kc.KubeAPIBurst < 0 { 102 allErrors = append(allErrors, fmt.Errorf("invalid configuration: kubeAPIBurst (--kube-api-burst) %v must not be a negative number", kc.KubeAPIBurst)) 103 } 104 if kc.KubeAPIQPS < 0 { 105 allErrors = append(allErrors, fmt.Errorf("invalid configuration: kubeAPIQPS (--kube-api-qps) %v must not be a negative number", kc.KubeAPIQPS)) 106 } 107 if kc.NodeStatusMaxImages < -1 { 108 allErrors = append(allErrors, fmt.Errorf("invalid configuration: nodeStatusMaxImages (--node-status-max-images) %v must be -1 or greater", kc.NodeStatusMaxImages)) 109 } 110 if kc.MaxOpenFiles < 0 { 111 allErrors = append(allErrors, fmt.Errorf("invalid configuration: maxOpenFiles (--max-open-files) %v must not be a negative number", kc.MaxOpenFiles)) 112 } 113 if kc.MaxPods < 0 { 114 allErrors = append(allErrors, fmt.Errorf("invalid configuration: maxPods (--max-pods) %v must not be a negative number", kc.MaxPods)) 115 } 116 if utilvalidation.IsInRange(int(kc.OOMScoreAdj), -1000, 1000) != nil { 117 allErrors = append(allErrors, fmt.Errorf("invalid configuration: oomScoreAdj (--oom-score-adj) %v must be between -1000 and 1000, inclusive", kc.OOMScoreAdj)) 118 } 119 if kc.PodsPerCore < 0 { 120 allErrors = append(allErrors, fmt.Errorf("invalid configuration: podsPerCore (--pods-per-core) %v must not be a negative number", kc.PodsPerCore)) 121 } 122 if utilvalidation.IsValidPortNum(int(kc.Port)) != nil { 123 allErrors = append(allErrors, fmt.Errorf("invalid configuration: port (--port) %v must be between 1 and 65535, inclusive", kc.Port)) 124 } 125 if kc.ReadOnlyPort != 0 && utilvalidation.IsValidPortNum(int(kc.ReadOnlyPort)) != nil { 126 allErrors = append(allErrors, fmt.Errorf("invalid configuration: readOnlyPort (--read-only-port) %v must be between 0 and 65535, inclusive", kc.ReadOnlyPort)) 127 } 128 if kc.RegistryBurst < 0 { 129 allErrors = append(allErrors, fmt.Errorf("invalid configuration: registryBurst (--registry-burst) %v must not be a negative number", kc.RegistryBurst)) 130 } 131 if kc.RegistryPullQPS < 0 { 132 allErrors = append(allErrors, fmt.Errorf("invalid configuration: registryPullQPS (--registry-qps) %v must not be a negative number", kc.RegistryPullQPS)) 133 } 134 if kc.MaxParallelImagePulls != nil && *kc.MaxParallelImagePulls < 1 { 135 allErrors = append(allErrors, fmt.Errorf("invalid configuration: maxParallelImagePulls %v must be a positive number", *kc.MaxParallelImagePulls)) 136 } 137 if kc.SerializeImagePulls && kc.MaxParallelImagePulls != nil && *kc.MaxParallelImagePulls > 1 { 138 allErrors = append(allErrors, fmt.Errorf("invalid configuration: maxParallelImagePulls cannot be larger than 1 unless SerializeImagePulls (--serialize-image-pulls) is set to false")) 139 } 140 if kc.ServerTLSBootstrap && !localFeatureGate.Enabled(features.RotateKubeletServerCertificate) { 141 allErrors = append(allErrors, fmt.Errorf("invalid configuration: serverTLSBootstrap %v requires feature gate RotateKubeletServerCertificate", kc.ServerTLSBootstrap)) 142 } 143 144 for _, nodeTaint := range kc.RegisterWithTaints { 145 if err := utiltaints.CheckTaintValidation(nodeTaint); err != nil { 146 allErrors = append(allErrors, fmt.Errorf("invalid taint: %v", nodeTaint)) 147 } 148 if nodeTaint.TimeAdded != nil { 149 allErrors = append(allErrors, fmt.Errorf("invalid configuration: taint.TimeAdded is not nil")) 150 } 151 } 152 153 switch kc.TopologyManagerPolicy { 154 case kubeletconfig.NoneTopologyManagerPolicy: 155 case kubeletconfig.BestEffortTopologyManagerPolicy: 156 case kubeletconfig.RestrictedTopologyManagerPolicy: 157 case kubeletconfig.SingleNumaNodeTopologyManagerPolicy: 158 default: 159 allErrors = append(allErrors, fmt.Errorf("invalid configuration: topologyManagerPolicy (--topology-manager-policy) %q must be one of: %q", kc.TopologyManagerPolicy, []string{kubeletconfig.NoneTopologyManagerPolicy, kubeletconfig.BestEffortTopologyManagerPolicy, kubeletconfig.RestrictedTopologyManagerPolicy, kubeletconfig.SingleNumaNodeTopologyManagerPolicy})) 160 } 161 162 switch kc.TopologyManagerScope { 163 case kubeletconfig.ContainerTopologyManagerScope: 164 case kubeletconfig.PodTopologyManagerScope: 165 default: 166 allErrors = append(allErrors, fmt.Errorf("invalid configuration: topologyManagerScope (--topology-manager-scope) %q must be one of: %q, or %q", kc.TopologyManagerScope, kubeletconfig.ContainerTopologyManagerScope, kubeletconfig.PodTopologyManagerScope)) 167 } 168 169 if localFeatureGate.Enabled(features.GracefulNodeShutdown) { 170 if kc.ShutdownGracePeriodCriticalPods.Duration > kc.ShutdownGracePeriod.Duration { 171 allErrors = append(allErrors, fmt.Errorf("invalid configuration: shutdownGracePeriodCriticalPods %v must be <= shutdownGracePeriod %v", kc.ShutdownGracePeriodCriticalPods, kc.ShutdownGracePeriod)) 172 } 173 if kc.ShutdownGracePeriod.Duration < 0 || (kc.ShutdownGracePeriod.Duration > 0 && kc.ShutdownGracePeriod.Duration < time.Second) { 174 allErrors = append(allErrors, fmt.Errorf("invalid configuration: shutdownGracePeriod %v must be either zero or otherwise >= 1 sec", kc.ShutdownGracePeriod)) 175 } 176 if kc.ShutdownGracePeriodCriticalPods.Duration < 0 || (kc.ShutdownGracePeriodCriticalPods.Duration > 0 && kc.ShutdownGracePeriodCriticalPods.Duration < time.Second) { 177 allErrors = append(allErrors, fmt.Errorf("invalid configuration: shutdownGracePeriodCriticalPods %v must be either zero or otherwise >= 1 sec", kc.ShutdownGracePeriodCriticalPods)) 178 } 179 } 180 if (kc.ShutdownGracePeriod.Duration > 0 || kc.ShutdownGracePeriodCriticalPods.Duration > 0) && !localFeatureGate.Enabled(features.GracefulNodeShutdown) { 181 allErrors = append(allErrors, fmt.Errorf("invalid configuration: specifying shutdownGracePeriod or shutdownGracePeriodCriticalPods requires feature gate GracefulNodeShutdown")) 182 } 183 if localFeatureGate.Enabled(features.GracefulNodeShutdownBasedOnPodPriority) { 184 if len(kc.ShutdownGracePeriodByPodPriority) != 0 && (kc.ShutdownGracePeriod.Duration > 0 || kc.ShutdownGracePeriodCriticalPods.Duration > 0) { 185 allErrors = append(allErrors, fmt.Errorf("invalid configuration: Cannot specify both shutdownGracePeriodByPodPriority and shutdownGracePeriod at the same time")) 186 } 187 } 188 if !localFeatureGate.Enabled(features.GracefulNodeShutdownBasedOnPodPriority) { 189 if len(kc.ShutdownGracePeriodByPodPriority) != 0 { 190 allErrors = append(allErrors, fmt.Errorf("invalid configuration: Specifying shutdownGracePeriodByPodPriority requires feature gate GracefulNodeShutdownBasedOnPodPriority")) 191 } 192 } 193 if localFeatureGate.Enabled(features.NodeSwap) { 194 switch kc.MemorySwap.SwapBehavior { 195 case "": 196 case kubetypes.LimitedSwap: 197 case kubetypes.UnlimitedSwap: 198 default: 199 allErrors = append(allErrors, fmt.Errorf("invalid configuration: memorySwap.swapBehavior %q must be one of: \"\", %q, or %q", kc.MemorySwap.SwapBehavior, kubetypes.LimitedSwap, kubetypes.UnlimitedSwap)) 200 } 201 } 202 if !localFeatureGate.Enabled(features.NodeSwap) && kc.MemorySwap != (kubeletconfig.MemorySwapConfiguration{}) { 203 allErrors = append(allErrors, fmt.Errorf("invalid configuration: memorySwap.swapBehavior cannot be set when NodeSwap feature flag is disabled")) 204 } 205 206 for _, val := range kc.EnforceNodeAllocatable { 207 switch val { 208 case kubetypes.NodeAllocatableEnforcementKey: 209 case kubetypes.SystemReservedEnforcementKey: 210 if kc.SystemReservedCgroup == "" { 211 allErrors = append(allErrors, fmt.Errorf("invalid configuration: systemReservedCgroup (--system-reserved-cgroup) must be specified when %q contained in enforceNodeAllocatable (--enforce-node-allocatable)", kubetypes.SystemReservedEnforcementKey)) 212 } 213 case kubetypes.KubeReservedEnforcementKey: 214 if kc.KubeReservedCgroup == "" { 215 allErrors = append(allErrors, fmt.Errorf("invalid configuration: kubeReservedCgroup (--kube-reserved-cgroup) must be specified when %q contained in enforceNodeAllocatable (--enforce-node-allocatable)", kubetypes.KubeReservedEnforcementKey)) 216 } 217 case kubetypes.NodeAllocatableNoneKey: 218 if len(kc.EnforceNodeAllocatable) > 1 { 219 allErrors = append(allErrors, fmt.Errorf("invalid configuration: enforceNodeAllocatable (--enforce-node-allocatable) may not contain additional enforcements when %q is specified", kubetypes.NodeAllocatableNoneKey)) 220 } 221 default: 222 allErrors = append(allErrors, fmt.Errorf("invalid configuration: option %q specified for enforceNodeAllocatable (--enforce-node-allocatable). Valid options are %q, %q, %q, or %q", 223 val, kubetypes.NodeAllocatableEnforcementKey, kubetypes.SystemReservedEnforcementKey, kubetypes.KubeReservedEnforcementKey, kubetypes.NodeAllocatableNoneKey)) 224 } 225 } 226 switch kc.HairpinMode { 227 case kubeletconfig.HairpinNone: 228 case kubeletconfig.HairpinVeth: 229 case kubeletconfig.PromiscuousBridge: 230 default: 231 allErrors = append(allErrors, fmt.Errorf("invalid configuration: option %q specified for hairpinMode (--hairpin-mode). Valid options are %q, %q or %q", 232 kc.HairpinMode, kubeletconfig.HairpinNone, kubeletconfig.HairpinVeth, kubeletconfig.PromiscuousBridge)) 233 } 234 if kc.ReservedSystemCPUs != "" { 235 // --reserved-cpus does not support --system-reserved-cgroup or --kube-reserved-cgroup 236 if kc.SystemReservedCgroup != "" || kc.KubeReservedCgroup != "" { 237 allErrors = append(allErrors, fmt.Errorf("invalid configuration: can't use reservedSystemCPUs (--reserved-cpus) with systemReservedCgroup (--system-reserved-cgroup) or kubeReservedCgroup (--kube-reserved-cgroup)")) 238 } 239 if _, err := cpuset.Parse(kc.ReservedSystemCPUs); err != nil { 240 allErrors = append(allErrors, fmt.Errorf("invalid configuration: unable to parse reservedSystemCPUs (--reserved-cpus) %v, error: %w", kc.ReservedSystemCPUs, err)) 241 } 242 } 243 244 allErrors = append(allErrors, validateReservedMemoryConfiguration(kc)...) 245 246 if err := validateKubeletOSConfiguration(kc); err != nil { 247 allErrors = append(allErrors, err) 248 } 249 allErrors = append(allErrors, metrics.ValidateShowHiddenMetricsVersion(kc.ShowHiddenMetricsForVersion)...) 250 251 if errs := logsapi.Validate(&kc.Logging, localFeatureGate, field.NewPath("logging")); len(errs) > 0 { 252 allErrors = append(allErrors, errs.ToAggregate().Errors()...) 253 } 254 255 if localFeatureGate.Enabled(features.KubeletTracing) { 256 if errs := tracingapi.ValidateTracingConfiguration(kc.Tracing, localFeatureGate, field.NewPath("tracing")); len(errs) > 0 { 257 allErrors = append(allErrors, errs.ToAggregate().Errors()...) 258 } 259 } else if kc.Tracing != nil { 260 allErrors = append(allErrors, fmt.Errorf("invalid configuration: tracing should not be configured if KubeletTracing feature flag is disabled.")) 261 } 262 263 if localFeatureGate.Enabled(features.MemoryQoS) && kc.MemoryThrottlingFactor == nil { 264 allErrors = append(allErrors, fmt.Errorf("invalid configuration: memoryThrottlingFactor is required when MemoryQoS feature flag is enabled")) 265 } 266 if kc.MemoryThrottlingFactor != nil && (*kc.MemoryThrottlingFactor <= 0 || *kc.MemoryThrottlingFactor > 1.0) { 267 allErrors = append(allErrors, fmt.Errorf("invalid configuration: memoryThrottlingFactor %v must be greater than 0 and less than or equal to 1.0", *kc.MemoryThrottlingFactor)) 268 } 269 270 if kc.ContainerRuntimeEndpoint == "" { 271 allErrors = append(allErrors, fmt.Errorf("invalid configuration: the containerRuntimeEndpoint was not specified or empty")) 272 } 273 274 if kc.EnableSystemLogQuery && !localFeatureGate.Enabled(features.NodeLogQuery) { 275 allErrors = append(allErrors, fmt.Errorf("invalid configuration: NodeLogQuery feature gate is required for enableSystemLogHandler")) 276 } 277 if kc.EnableSystemLogQuery && !kc.EnableSystemLogHandler { 278 allErrors = append(allErrors, 279 fmt.Errorf("invalid configuration: enableSystemLogHandler is required for enableSystemLogQuery")) 280 } 281 282 return utilerrors.NewAggregate(allErrors) 283 }