
Making the LoggingConfiguration part of the versioned component-base/config API had the theoretic advantage that components could have offered different configuration APIs with experimental features limited to alpha versions (for example, sanitization offered only in a v1alpha1.KubeletConfiguration). Some components could have decided to only use stable logging options. In practice, this wasn't done. Furthermore, we don't want different components to make different choices regarding which logging features they offer to users. It should always be the same everywhere, for the sake of consistency. This can be achieved with a saner Go API by dropping the distinction between internal and external LoggingConfiguration types. Different stability levels of indidividual fields have to be covered by documentation (done) and potentially feature gates (not currently done). Advantages: - everything related to logging is under component-base/logs; previously this was scattered across different packages and different files under "logs" (why some code was in logs/config.go vs. logs/options.go vs. logs/logs.go always confused me again and again when coming back to the code): - long-term config and command line API are clearly separated into the "api" package underneath that - logs/logs.go itself only deals with legacy global flags and logging configuration - removal of separate Go APIs like logs.BindLoggingFlags and logs.Options - LogRegistry becomes an implementation detail, with less code and less exported functionality (only registration needs to be exported, querying is internal)
521 lines
22 KiB
Go
521 lines
22 KiB
Go
/*
|
|
Copyright 2017 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package validation_test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
v1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
logsapi "k8s.io/component-base/logs/api/v1"
|
|
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
|
"k8s.io/kubernetes/pkg/kubelet/apis/config/validation"
|
|
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
|
utilpointer "k8s.io/utils/pointer"
|
|
)
|
|
|
|
var (
|
|
successConfig = kubeletconfig.KubeletConfiguration{
|
|
CgroupsPerQOS: true,
|
|
EnforceNodeAllocatable: []string{"pods", "system-reserved", "kube-reserved"},
|
|
SystemReservedCgroup: "/system.slice",
|
|
KubeReservedCgroup: "/kubelet.service",
|
|
SystemCgroups: "",
|
|
CgroupRoot: "",
|
|
EventBurst: 10,
|
|
EventRecordQPS: 5,
|
|
HealthzPort: 10248,
|
|
ImageGCHighThresholdPercent: 85,
|
|
ImageGCLowThresholdPercent: 80,
|
|
IPTablesDropBit: 15,
|
|
IPTablesMasqueradeBit: 14,
|
|
KubeAPIBurst: 10,
|
|
KubeAPIQPS: 5,
|
|
MaxOpenFiles: 1000000,
|
|
MaxPods: 110,
|
|
OOMScoreAdj: -999,
|
|
PodsPerCore: 100,
|
|
Port: 65535,
|
|
ReadOnlyPort: 0,
|
|
RegistryBurst: 10,
|
|
RegistryPullQPS: 5,
|
|
HairpinMode: kubeletconfig.PromiscuousBridge,
|
|
NodeLeaseDurationSeconds: 1,
|
|
CPUCFSQuotaPeriod: metav1.Duration{Duration: 25 * time.Millisecond},
|
|
TopologyManagerScope: kubeletconfig.PodTopologyManagerScope,
|
|
TopologyManagerPolicy: kubeletconfig.SingleNumaNodeTopologyManagerPolicy,
|
|
ShutdownGracePeriod: metav1.Duration{Duration: 30 * time.Second},
|
|
ShutdownGracePeriodCriticalPods: metav1.Duration{Duration: 10 * time.Second},
|
|
MemoryThrottlingFactor: utilpointer.Float64Ptr(0.8),
|
|
FeatureGates: map[string]bool{
|
|
"CustomCPUCFSQuotaPeriod": true,
|
|
"GracefulNodeShutdown": true,
|
|
"MemoryQoS": true,
|
|
},
|
|
Logging: logsapi.LoggingConfiguration{
|
|
Format: "text",
|
|
},
|
|
}
|
|
)
|
|
|
|
func TestValidateKubeletConfiguration(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
configure func(config *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration
|
|
errMsg string
|
|
}{
|
|
{
|
|
name: "Success",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
return conf
|
|
},
|
|
},
|
|
{
|
|
name: "invalid NodeLeaseDurationSeconds",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.NodeLeaseDurationSeconds = 0
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: nodeLeaseDurationSeconds must be greater than 0",
|
|
},
|
|
{
|
|
name: "specify EnforceNodeAllocatable without enabling CgroupsPerQOS",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.CgroupsPerQOS = false
|
|
conf.EnforceNodeAllocatable = []string{"pods"}
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: enforceNodeAllocatable (--enforce-node-allocatable) is not supported unless cgroupsPerQOS (--cgroups-per-qos) is set to true",
|
|
},
|
|
{
|
|
name: "specify SystemCgroups without CgroupRoot",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.SystemCgroups = "/"
|
|
conf.CgroupRoot = ""
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: systemCgroups (--system-cgroups) was specified and cgroupRoot (--cgroup-root) was not specified",
|
|
},
|
|
{
|
|
name: "invalid EventBurst",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.EventBurst = -1
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: eventBurst (--event-burst) -1 must not be a negative number",
|
|
},
|
|
{
|
|
name: "invalid EventRecordQPS",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.EventRecordQPS = -1
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: eventRecordQPS (--event-qps) -1 must not be a negative number",
|
|
},
|
|
{
|
|
name: "invalid HealthzPort",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.HealthzPort = 65536
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: healthzPort (--healthz-port) 65536 must be between 1 and 65535, inclusive",
|
|
},
|
|
{
|
|
name: "specify CPUCFSQuotaPeriod without enabling CPUCFSQuotaPeriod",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"CustomCPUCFSQuotaPeriod": false}
|
|
conf.CPUCFSQuotaPeriod = metav1.Duration{Duration: 200 * time.Millisecond}
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: cpuCFSQuotaPeriod (--cpu-cfs-quota-period) {200ms} requires feature gate CustomCPUCFSQuotaPeriod",
|
|
},
|
|
{
|
|
name: "invalid CPUCFSQuotaPeriod",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"CustomCPUCFSQuotaPeriod": true}
|
|
conf.CPUCFSQuotaPeriod = metav1.Duration{Duration: 2 * time.Second}
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: cpuCFSQuotaPeriod (--cpu-cfs-quota-period) {2s} must be between 1usec and 1sec, inclusive",
|
|
},
|
|
{
|
|
name: "invalid ImageGCHighThresholdPercent",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.ImageGCHighThresholdPercent = 101
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: imageGCHighThresholdPercent (--image-gc-high-threshold) 101 must be between 0 and 100, inclusive",
|
|
},
|
|
{
|
|
name: "invalid ImageGCLowThresholdPercent",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.ImageGCLowThresholdPercent = -1
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) -1 must be between 0 and 100, inclusive",
|
|
},
|
|
{
|
|
name: "ImageGCLowThresholdPercent is equal to ImageGCHighThresholdPercent",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.ImageGCHighThresholdPercent = 0
|
|
conf.ImageGCLowThresholdPercent = 0
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) 0 must be less than imageGCHighThresholdPercent (--image-gc-high-threshold) 0",
|
|
},
|
|
{
|
|
name: "ImageGCLowThresholdPercent is greater than ImageGCHighThresholdPercent",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.ImageGCHighThresholdPercent = 0
|
|
conf.ImageGCLowThresholdPercent = 1
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) 1 must be less than imageGCHighThresholdPercent (--image-gc-high-threshold) 0",
|
|
},
|
|
{
|
|
name: "invalid IPTablesDropBit",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.IPTablesDropBit = 32
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: iptablesDropBit (--iptables-drop-bit) 32 must be between 0 and 31, inclusive",
|
|
},
|
|
{
|
|
name: "invalid IPTablesMasqueradeBit",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.IPTablesMasqueradeBit = 32
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: iptablesMasqueradeBit (--iptables-masquerade-bit) 32 must be between 0 and 31, inclusive",
|
|
},
|
|
{
|
|
name: "invalid KubeAPIBurst",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.KubeAPIBurst = -1
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: kubeAPIBurst (--kube-api-burst) -1 must not be a negative number",
|
|
},
|
|
{
|
|
name: "invalid KubeAPIQPS",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.KubeAPIQPS = -1
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: kubeAPIQPS (--kube-api-qps) -1 must not be a negative number",
|
|
},
|
|
{
|
|
name: "invalid NodeStatusMaxImages",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.NodeStatusMaxImages = -2
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: nodeStatusMaxImages (--node-status-max-images) -2 must be -1 or greater",
|
|
},
|
|
{
|
|
name: "invalid MaxOpenFiles",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.MaxOpenFiles = -1
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: maxOpenFiles (--max-open-files) -1 must not be a negative number",
|
|
},
|
|
{
|
|
name: "invalid MaxPods",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.MaxPods = -1
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: maxPods (--max-pods) -1 must not be a negative number",
|
|
},
|
|
{
|
|
name: "invalid OOMScoreAdj",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.OOMScoreAdj = 1001
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: oomScoreAdj (--oom-score-adj) 1001 must be between -1000 and 1000, inclusive",
|
|
},
|
|
{
|
|
name: "invalid PodsPerCore",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.PodsPerCore = -1
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: podsPerCore (--pods-per-core) -1 must not be a negative number",
|
|
},
|
|
{
|
|
name: "invalid Port",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.Port = 65536
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: port (--port) 65536 must be between 1 and 65535, inclusive",
|
|
},
|
|
{
|
|
name: "invalid ReadOnlyPort",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.ReadOnlyPort = 65536
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: readOnlyPort (--read-only-port) 65536 must be between 0 and 65535, inclusive",
|
|
},
|
|
{
|
|
name: "invalid RegistryBurst",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.RegistryBurst = -1
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: registryBurst (--registry-burst) -1 must not be a negative number",
|
|
},
|
|
{
|
|
name: "invalid RegistryPullQPS",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.RegistryPullQPS = -1
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: registryPullQPS (--registry-qps) -1 must not be a negative number",
|
|
},
|
|
{
|
|
name: "specify ServerTLSBootstrap without enabling RotateKubeletServerCertificate",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"RotateKubeletServerCertificate": false}
|
|
conf.ServerTLSBootstrap = true
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: serverTLSBootstrap true requires feature gate RotateKubeletServerCertificate",
|
|
},
|
|
{
|
|
name: "use SingleNumaNodeTopologyManagerPolicy without enabling TopologyManager",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"TopologyManager": false}
|
|
conf.TopologyManagerPolicy = kubeletconfig.SingleNumaNodeTopologyManagerPolicy
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: topologyManagerPolicy single-numa-node requires feature gate TopologyManager",
|
|
},
|
|
{
|
|
name: "invalid TopologyManagerPolicy",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.TopologyManagerPolicy = "invalid-policy"
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: topologyManagerPolicy (--topology-manager-policy) \"invalid-policy\" must be one of: [\"none\" \"best-effort\" \"restricted\" \"single-numa-node\"]",
|
|
},
|
|
{
|
|
name: "use PodTopologyManagerScope without enabling TopologyManager",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"TopologyManager": false}
|
|
conf.TopologyManagerScope = kubeletconfig.PodTopologyManagerScope
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: topologyManagerScope pod requires feature gate TopologyManager",
|
|
},
|
|
{
|
|
name: "invalid TopologyManagerScope",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.TopologyManagerScope = "invalid-scope"
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: topologyManagerScope (--topology-manager-scope) \"invalid-scope\" must be one of: \"container\", or \"pod\"",
|
|
},
|
|
{
|
|
name: "ShutdownGracePeriodCriticalPods is greater than ShutdownGracePeriod",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": true}
|
|
conf.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: 2 * time.Second}
|
|
conf.ShutdownGracePeriod = metav1.Duration{Duration: 1 * time.Second}
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: shutdownGracePeriodCriticalPods {2s} must be <= shutdownGracePeriod {1s}",
|
|
},
|
|
{
|
|
name: "ShutdownGracePeriod is less than 1 sec",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": true}
|
|
conf.ShutdownGracePeriod = metav1.Duration{Duration: 1 * time.Millisecond}
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: shutdownGracePeriod {1ms} must be either zero or otherwise >= 1 sec",
|
|
},
|
|
{
|
|
name: "ShutdownGracePeriodCriticalPods is less than 1 sec",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": true}
|
|
conf.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: 1 * time.Millisecond}
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: shutdownGracePeriodCriticalPods {1ms} must be either zero or otherwise >= 1 sec",
|
|
},
|
|
{
|
|
name: "specify ShutdownGracePeriod without enabling GracefulNodeShutdown",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": false}
|
|
conf.ShutdownGracePeriod = metav1.Duration{Duration: 1 * time.Second}
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: specifying shutdownGracePeriod or shutdownGracePeriodCriticalPods requires feature gate GracefulNodeShutdown",
|
|
},
|
|
{
|
|
name: "specify ShutdownGracePeriodCriticalPods without enabling GracefulNodeShutdown",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": false}
|
|
conf.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: 1 * time.Second}
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: specifying shutdownGracePeriod or shutdownGracePeriodCriticalPods requires feature gate GracefulNodeShutdown",
|
|
},
|
|
{
|
|
name: "invalid MemorySwap.SwapBehavior",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"NodeSwap": true}
|
|
conf.MemorySwap.SwapBehavior = "invalid-behavior"
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: memorySwap.swapBehavior \"invalid-behavior\" must be one of: \"\", \"LimitedSwap\", or \"UnlimitedSwap\"",
|
|
},
|
|
{
|
|
name: "specify MemorySwap.SwapBehavior without enabling NodeSwap",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"NodeSwap": false}
|
|
conf.MemorySwap.SwapBehavior = kubetypes.LimitedSwap
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: memorySwap.swapBehavior cannot be set when NodeSwap feature flag is disabled",
|
|
},
|
|
{
|
|
name: "specify SystemReservedEnforcementKey without specifying SystemReservedCgroup",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.EnforceNodeAllocatable = []string{kubetypes.SystemReservedEnforcementKey}
|
|
conf.SystemReservedCgroup = ""
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: systemReservedCgroup (--system-reserved-cgroup) must be specified when \"system-reserved\" contained in enforceNodeAllocatable (--enforce-node-allocatable)",
|
|
},
|
|
{
|
|
name: "specify KubeReservedEnforcementKey without specifying KubeReservedCgroup",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.EnforceNodeAllocatable = []string{kubetypes.KubeReservedEnforcementKey}
|
|
conf.KubeReservedCgroup = ""
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: kubeReservedCgroup (--kube-reserved-cgroup) must be specified when \"kube-reserved\" contained in enforceNodeAllocatable (--enforce-node-allocatable)",
|
|
},
|
|
{
|
|
name: "specify NodeAllocatableNoneKey with additional enforcements",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.EnforceNodeAllocatable = []string{kubetypes.NodeAllocatableNoneKey, kubetypes.KubeReservedEnforcementKey}
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: enforceNodeAllocatable (--enforce-node-allocatable) may not contain additional enforcements when \"none\" is specified",
|
|
},
|
|
{
|
|
name: "invalid EnforceNodeAllocatable",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.EnforceNodeAllocatable = []string{"invalid-enforce-node-allocatable"}
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: option \"invalid-enforce-node-allocatable\" specified for enforceNodeAllocatable (--enforce-node-allocatable). Valid options are \"pods\", \"system-reserved\", \"kube-reserved\", or \"none\"",
|
|
},
|
|
{
|
|
name: "invalid HairpinMode",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.HairpinMode = "invalid-hair-pin-mode"
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: option \"invalid-hair-pin-mode\" specified for hairpinMode (--hairpin-mode). Valid options are \"none\", \"hairpin-veth\" or \"promiscuous-bridge\"",
|
|
},
|
|
{
|
|
name: "specify ReservedSystemCPUs with SystemReservedCgroup",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.ReservedSystemCPUs = "0-3"
|
|
conf.SystemReservedCgroup = "/system.slice"
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: can't use reservedSystemCPUs (--reserved-cpus) with systemReservedCgroup (--system-reserved-cgroup) or kubeReservedCgroup (--kube-reserved-cgroup)",
|
|
},
|
|
{
|
|
name: "specify ReservedSystemCPUs with KubeReservedCgroup",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.ReservedSystemCPUs = "0-3"
|
|
conf.KubeReservedCgroup = "/system.slice"
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: can't use reservedSystemCPUs (--reserved-cpus) with systemReservedCgroup (--system-reserved-cgroup) or kubeReservedCgroup (--kube-reserved-cgroup)",
|
|
},
|
|
{
|
|
name: "invalid ReservedSystemCPUs",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.ReservedSystemCPUs = "invalid-reserved-system-cpus"
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: unable to parse reservedSystemCPUs (--reserved-cpus) invalid-reserved-system-cpus, error:",
|
|
},
|
|
{
|
|
name: "enable MemoryQoS without specifying MemoryThrottlingFactor",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.FeatureGates = map[string]bool{"MemoryQoS": true}
|
|
conf.MemoryThrottlingFactor = nil
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: memoryThrottlingFactor is required when MemoryQoS feature flag is enabled",
|
|
},
|
|
{
|
|
name: "invalid MemoryThrottlingFactor",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
conf.MemoryThrottlingFactor = utilpointer.Float64(1.1)
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: memoryThrottlingFactor 1.1 must be greater than 0 and less than or equal to 1.0",
|
|
},
|
|
{
|
|
name: "invalid Taint.TimeAdded",
|
|
configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
|
|
now := metav1.Now()
|
|
conf.RegisterWithTaints = []v1.Taint{{TimeAdded: &now}}
|
|
return conf
|
|
},
|
|
errMsg: "invalid configuration: taint.TimeAdded is not nil",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
errs := validation.ValidateKubeletConfiguration(tc.configure(successConfig.DeepCopy()))
|
|
|
|
if len(tc.errMsg) == 0 {
|
|
if errs != nil {
|
|
t.Errorf("unexpected error: %s", errs)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if errs == nil {
|
|
t.Errorf("expected error: %s", tc.errMsg)
|
|
return
|
|
}
|
|
|
|
if got := errs.Error(); !strings.Contains(got, tc.errMsg) {
|
|
t.Errorf("unexpected error: %s expected to contain %s", got, tc.errMsg)
|
|
}
|
|
})
|
|
}
|
|
}
|