diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go index 3fad8dff3b7..df72c28ec73 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go @@ -36,6 +36,9 @@ import ( tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token" apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" + "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig" + kubeletscheme "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/scheme" + kubeletvalidation "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/validation" "k8s.io/kubernetes/pkg/proxy/apis/kubeproxyconfig" kubeproxyscheme "k8s.io/kubernetes/pkg/proxy/apis/kubeproxyconfig/scheme" proxyvalidation "k8s.io/kubernetes/pkg/proxy/apis/kubeproxyconfig/validation" @@ -75,6 +78,9 @@ func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList allErrs = append(allErrs, ValidateFeatureGates(c.FeatureGates, field.NewPath("feature-gates"))...) allErrs = append(allErrs, ValidateAPIEndpoint(c, field.NewPath("api-endpoint"))...) //allErrs = append(allErrs, ValidateProxy(c, field.NewPath("kube-proxy"))...) + if features.Enabled(c.FeatureGates, features.DynamicKubeletConfig) { + allErrs = append(allErrs, ValidateKubeletConfiguration(&c.KubeletConfiguration, field.NewPath("kubeletConfiguration"))...) + } return allErrs } @@ -369,3 +375,29 @@ func ValidateIgnorePreflightErrors(ignorePreflightErrors []string, skipPreflight return ignoreErrors, allErrs.ToAggregate() } + +// ValidateKubeletConfiguration validates kubelet configuration and collects all encountered errors +func ValidateKubeletConfiguration(c *kubeadm.KubeletConfiguration, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + scheme, _, err := kubeletscheme.NewSchemeAndCodecs() + if err != nil { + allErrs = append(allErrs, field.Invalid(fldPath, "kubeletConfiguration", err.Error())) + return allErrs + } + + // Convert versioned config to internal config + internalcfg := &kubeletconfig.KubeletConfiguration{} + err = scheme.Convert(c.BaseConfig, internalcfg, nil) + if err != nil { + allErrs = append(allErrs, field.Invalid(fldPath, "kubeletConfiguration", err.Error())) + return allErrs + } + + err = kubeletvalidation.ValidateKubeletConfiguration(internalcfg) + if err != nil { + allErrs = append(allErrs, field.Invalid(fldPath, "kubeletConfiguration", err.Error())) + } + + return allErrs +} diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index 37678de3757..826debfe426 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -25,8 +25,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + kubeletconfigv1alpha1 "k8s.io/kubernetes/pkg/kubelet/apis/kubeletconfig/v1alpha1" kubeproxyconfigv1alpha1 "k8s.io/kubernetes/pkg/proxy/apis/kubeproxyconfig/v1alpha1" - "k8s.io/kubernetes/pkg/util/pointer" + utilpointer "k8s.io/kubernetes/pkg/util/pointer" ) func TestValidateTokenDiscovery(t *testing.T) { @@ -338,9 +339,9 @@ func TestValidateMasterConfiguration(t *testing.T) { KubeProxy: kubeadm.KubeProxy{ Config: &kubeproxyconfigv1alpha1.KubeProxyConfiguration{ Conntrack: kubeproxyconfigv1alpha1.KubeProxyConntrackConfiguration{ - Max: pointer.Int32Ptr(2), - MaxPerCore: pointer.Int32Ptr(1), - Min: pointer.Int32Ptr(1), + Max: utilpointer.Int32Ptr(2), + MaxPerCore: utilpointer.Int32Ptr(1), + Min: utilpointer.Int32Ptr(1), TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second}, TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second}, }, @@ -502,3 +503,65 @@ func TestValidateIgnorePreflightErrors(t *testing.T) { } } } + +func TestValidateKubeletConfiguration(t *testing.T) { + successCase := &kubeadm.KubeletConfiguration{ + BaseConfig: &kubeletconfigv1alpha1.KubeletConfiguration{ + CgroupsPerQOS: utilpointer.BoolPtr(true), + EnforceNodeAllocatable: []string{"pods", "system-reserved", "kube-reserved"}, + SystemCgroups: "", + CgroupRoot: "", + CAdvisorPort: utilpointer.Int32Ptr(0), + EventBurst: 10, + EventRecordQPS: utilpointer.Int32Ptr(5), + HealthzPort: utilpointer.Int32Ptr(10248), + ImageGCHighThresholdPercent: utilpointer.Int32Ptr(85), + ImageGCLowThresholdPercent: utilpointer.Int32Ptr(80), + IPTablesDropBit: utilpointer.Int32Ptr(15), + IPTablesMasqueradeBit: utilpointer.Int32Ptr(14), + KubeAPIBurst: 10, + KubeAPIQPS: utilpointer.Int32Ptr(5), + MaxOpenFiles: 1000000, + MaxPods: 110, + OOMScoreAdj: utilpointer.Int32Ptr(-999), + PodsPerCore: 100, + Port: 65535, + ReadOnlyPort: utilpointer.Int32Ptr(0), + RegistryBurst: 10, + RegistryPullQPS: utilpointer.Int32Ptr(5), + }, + } + if allErrors := ValidateKubeletConfiguration(successCase, nil); len(allErrors) != 0 { + t.Errorf("failed ValidateKubeletConfiguration: expect no errors but got %v", allErrors) + } + + errorCase := &kubeadm.KubeletConfiguration{ + BaseConfig: &kubeletconfigv1alpha1.KubeletConfiguration{ + CgroupsPerQOS: utilpointer.BoolPtr(false), + EnforceNodeAllocatable: []string{"pods", "system-reserved", "kube-reserved", "illegal-key"}, + SystemCgroups: "/", + CgroupRoot: "", + CAdvisorPort: utilpointer.Int32Ptr(-10), + EventBurst: -10, + EventRecordQPS: utilpointer.Int32Ptr(-10), + HealthzPort: utilpointer.Int32Ptr(-10), + ImageGCHighThresholdPercent: utilpointer.Int32Ptr(101), + ImageGCLowThresholdPercent: utilpointer.Int32Ptr(101), + IPTablesDropBit: utilpointer.Int32Ptr(-10), + IPTablesMasqueradeBit: utilpointer.Int32Ptr(-10), + KubeAPIBurst: -10, + KubeAPIQPS: utilpointer.Int32Ptr(-10), + MaxOpenFiles: -10, + MaxPods: -10, + OOMScoreAdj: utilpointer.Int32Ptr(-1001), + PodsPerCore: -10, + Port: 0, + ReadOnlyPort: utilpointer.Int32Ptr(-10), + RegistryBurst: -10, + RegistryPullQPS: utilpointer.Int32Ptr(-10), + }, + } + if allErrors := ValidateKubeletConfiguration(errorCase, nil); len(allErrors) == 0 { + t.Errorf("failed ValidateKubeletConfiguration: expect errors but got no error") + } +}