mirror of https://github.com/k3s-io/k3s
Merge pull request #75499 from marccarre/issues/74246-more-decl-kubeadm-cli-args
Add ability to configure kubeadm's ignored pre-flight errors via InitConfiguration and JoinConfigurationk3s-v1.15.3
commit
73b8011d47
|
@ -32,6 +32,7 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
|
|||
fuzzClusterConfiguration,
|
||||
fuzzComponentConfigs,
|
||||
fuzzDNS,
|
||||
fuzzNodeRegistration,
|
||||
fuzzLocalEtcd,
|
||||
fuzzNetworking,
|
||||
fuzzJoinConfiguration,
|
||||
|
@ -87,6 +88,13 @@ func fuzzInitConfiguration(obj *kubeadm.InitConfiguration, c fuzz.Continue) {
|
|||
obj.CertificateKey = ""
|
||||
}
|
||||
|
||||
func fuzzNodeRegistration(obj *kubeadm.NodeRegistrationOptions, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(obj)
|
||||
|
||||
// Pinning values for fields that get defaults if fuzz value is empty string or nil (thus making the round trip test fail)
|
||||
obj.IgnorePreflightErrors = nil
|
||||
}
|
||||
|
||||
func fuzzClusterConfiguration(obj *kubeadm.ClusterConfiguration, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(obj)
|
||||
|
||||
|
|
|
@ -229,6 +229,9 @@ type NodeRegistrationOptions struct {
|
|||
// kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap
|
||||
// Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on.
|
||||
KubeletExtraArgs map[string]string
|
||||
|
||||
// IgnorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered.
|
||||
IgnorePreflightErrors []string
|
||||
}
|
||||
|
||||
// Networking contains elements describing cluster's networking configuration.
|
||||
|
|
|
@ -45,3 +45,15 @@ func Convert_kubeadm_JoinControlPlane_To_v1beta1_JoinControlPlane(in *kubeadm.Jo
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_kubeadm_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in *kubeadm.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error {
|
||||
if err := autoConvert_kubeadm_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in, out, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(in.IgnorePreflightErrors) > 0 {
|
||||
return errors.New("ignorePreflightErrors field is not supported by v1beta1 config format")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -37,6 +37,14 @@ func TestInternalToVersionedInitConfigurationConversion(t *testing.T) {
|
|||
},
|
||||
expectedError: true,
|
||||
},
|
||||
"ignorePreflightErrors set causes an error": {
|
||||
in: kubeadm.InitConfiguration{
|
||||
NodeRegistration: kubeadm.NodeRegistrationOptions{
|
||||
IgnorePreflightErrors: []string{"SomeUndesirableError"},
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range testcases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
|
@ -51,6 +59,66 @@ func TestInternalToVersionedInitConfigurationConversion(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestInternalToVersionedJoinConfigurationConversion(t *testing.T) {
|
||||
testcases := map[string]struct {
|
||||
in kubeadm.JoinConfiguration
|
||||
expectedError bool
|
||||
}{
|
||||
"conversion succeeds": {
|
||||
in: kubeadm.JoinConfiguration{},
|
||||
expectedError: false,
|
||||
},
|
||||
"ignorePreflightErrors set causes an error": {
|
||||
in: kubeadm.JoinConfiguration{
|
||||
NodeRegistration: kubeadm.NodeRegistrationOptions{
|
||||
IgnorePreflightErrors: []string{"SomeUndesirableError"},
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range testcases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
versioned := &JoinConfiguration{}
|
||||
err := Convert_kubeadm_JoinConfiguration_To_v1beta1_JoinConfiguration(&tc.in, versioned, nil)
|
||||
if err == nil && tc.expectedError {
|
||||
t.Error("unexpected success")
|
||||
} else if err != nil && !tc.expectedError {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInternalToVersionedNodeRegistrationOptionsConversion(t *testing.T) {
|
||||
testcases := map[string]struct {
|
||||
in kubeadm.NodeRegistrationOptions
|
||||
expectedError bool
|
||||
}{
|
||||
"conversion succeeds": {
|
||||
in: kubeadm.NodeRegistrationOptions{},
|
||||
expectedError: false,
|
||||
},
|
||||
"ignorePreflightErrors set causes an error": {
|
||||
in: kubeadm.NodeRegistrationOptions{
|
||||
IgnorePreflightErrors: []string{"SomeUndesirableError"},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range testcases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
versioned := &NodeRegistrationOptions{}
|
||||
err := Convert_kubeadm_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(&tc.in, versioned, nil)
|
||||
if err == nil && tc.expectedError {
|
||||
t.Error("unexpected success")
|
||||
} else if err != nil && !tc.expectedError {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInternalToVersionedJoinControlPlaneConversion(t *testing.T) {
|
||||
testcases := map[string]struct {
|
||||
in kubeadm.JoinControlPlane
|
||||
|
|
|
@ -257,6 +257,11 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*kubeadm.NodeRegistrationOptions)(nil), (*NodeRegistrationOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_kubeadm_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(a.(*kubeadm.NodeRegistrationOptions), b.(*NodeRegistrationOptions), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -848,10 +853,6 @@ func autoConvert_kubeadm_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOpti
|
|||
out.CRISocket = in.CRISocket
|
||||
out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints))
|
||||
out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs))
|
||||
// WARNING: in.IgnorePreflightErrors requires manual conversion: does not exist in peer-type
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_kubeadm_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions is an autogenerated conversion function.
|
||||
func Convert_kubeadm_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in *kubeadm.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error {
|
||||
return autoConvert_kubeadm_NodeRegistrationOptions_To_v1beta1_NodeRegistrationOptions(in, out, s)
|
||||
}
|
||||
|
|
|
@ -215,6 +215,9 @@ type NodeRegistrationOptions struct {
|
|||
// kubeadm writes at runtime for the kubelet to source. This overrides the generic base-level configuration in the kubelet-config-1.X ConfigMap
|
||||
// Flags have higher priority when parsing. These values are local and specific to the node kubeadm is executing on.
|
||||
KubeletExtraArgs map[string]string `json:"kubeletExtraArgs,omitempty"`
|
||||
|
||||
// IgnorePreflightErrors provides a slice of pre-flight errors to be ignored when the current node is registered.
|
||||
IgnorePreflightErrors []string `json:"ignorePreflightErrors,omitempty"`
|
||||
}
|
||||
|
||||
// Networking contains elements describing cluster's networking configuration
|
||||
|
|
|
@ -821,6 +821,7 @@ func autoConvert_v1beta2_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOpti
|
|||
out.CRISocket = in.CRISocket
|
||||
out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints))
|
||||
out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs))
|
||||
out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -834,6 +835,7 @@ func autoConvert_kubeadm_NodeRegistrationOptions_To_v1beta2_NodeRegistrationOpti
|
|||
out.CRISocket = in.CRISocket
|
||||
out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints))
|
||||
out.KubeletExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.KubeletExtraArgs))
|
||||
out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -538,6 +538,11 @@ func (in *NodeRegistrationOptions) DeepCopyInto(out *NodeRegistrationOptions) {
|
|||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.IgnorePreflightErrors != nil {
|
||||
in, out := &in.IgnorePreflightErrors, &out.IgnorePreflightErrors
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ go_test(
|
|||
"//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library",
|
||||
"//pkg/proxy/apis/config:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||
|
|
|
@ -463,12 +463,25 @@ func ValidateAPIEndpoint(c *kubeadm.APIEndpoint, fldPath *field.Path) field.Erro
|
|||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateIgnorePreflightErrors validates duplicates in ignore-preflight-errors flag.
|
||||
func ValidateIgnorePreflightErrors(ignorePreflightErrors []string) (sets.String, error) {
|
||||
// ValidateIgnorePreflightErrors validates duplicates in:
|
||||
// - ignore-preflight-errors flag and
|
||||
// - ignorePreflightErrors field in {Init,Join}Configuration files.
|
||||
func ValidateIgnorePreflightErrors(ignorePreflightErrorsFromCLI, ignorePreflightErrorsFromConfigFile []string) (sets.String, error) {
|
||||
ignoreErrors := sets.NewString()
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
for _, item := range ignorePreflightErrors {
|
||||
for _, item := range ignorePreflightErrorsFromConfigFile {
|
||||
ignoreErrors.Insert(strings.ToLower(item)) // parameters are case insensitive
|
||||
}
|
||||
|
||||
if ignoreErrors.Has("all") {
|
||||
// "all" is forbidden in config files. Administrators should use an
|
||||
// explicit list of errors they want to ignore, as it can be risky to
|
||||
// mask all errors in such a way. Hence, we return an error:
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("ignorePreflightErrors"), "all", "'all' cannot be used in configuration file"))
|
||||
}
|
||||
|
||||
for _, item := range ignorePreflightErrorsFromCLI {
|
||||
ignoreErrors.Insert(strings.ToLower(item)) // parameters are case insensitive
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
"github.com/spf13/pflag"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
|
||||
|
@ -703,26 +704,81 @@ func TestValidateFeatureGates(t *testing.T) {
|
|||
|
||||
func TestValidateIgnorePreflightErrors(t *testing.T) {
|
||||
var tests = []struct {
|
||||
ignorePreflightErrors []string
|
||||
expectedLen int
|
||||
expectedError bool
|
||||
ignorePreflightErrorsFromCLI []string
|
||||
ignorePreflightErrorsFromConfigFile []string
|
||||
expectedSet sets.String
|
||||
expectedError bool
|
||||
}{
|
||||
{[]string{}, 0, false}, // empty list
|
||||
{[]string{"check1", "check2"}, 2, false}, // non-duplicate
|
||||
{[]string{"check1", "check2", "check1"}, 2, false}, // duplicates
|
||||
{[]string{"check1", "check2", "all"}, 3, true}, // non-duplicate, but 'all' present together wth individual checks
|
||||
{[]string{"all"}, 1, false}, // skip all checks by using new flag
|
||||
{[]string{"all"}, 1, false}, // skip all checks by using both old and new flags at the same time
|
||||
{ // empty lists in CLI and config file
|
||||
[]string{},
|
||||
[]string{},
|
||||
sets.NewString(),
|
||||
false,
|
||||
},
|
||||
{ // empty list in CLI only
|
||||
[]string{},
|
||||
[]string{"a"},
|
||||
sets.NewString("a"),
|
||||
false,
|
||||
},
|
||||
{ // empty list in config file only
|
||||
[]string{"a"},
|
||||
[]string{},
|
||||
sets.NewString("a"),
|
||||
false,
|
||||
},
|
||||
{ // no duplicates, no overlap
|
||||
[]string{"a", "b"},
|
||||
[]string{"c", "d"},
|
||||
sets.NewString("a", "b", "c", "d"),
|
||||
false,
|
||||
},
|
||||
{ // some duplicates, with some overlapping duplicates
|
||||
[]string{"a", "b", "a"},
|
||||
[]string{"c", "b"},
|
||||
sets.NewString("a", "b", "c"),
|
||||
false,
|
||||
},
|
||||
{ // non-duplicate, but 'all' present together with individual checks in CLI
|
||||
[]string{"a", "b", "all"},
|
||||
[]string{},
|
||||
sets.NewString(),
|
||||
true,
|
||||
},
|
||||
{ // empty list in CLI, but 'all' present in config file, which is forbidden
|
||||
[]string{},
|
||||
[]string{"all"},
|
||||
sets.NewString(),
|
||||
true,
|
||||
},
|
||||
{ // non-duplicate, but 'all' present in config file, which is forbidden
|
||||
[]string{"a", "b"},
|
||||
[]string{"all"},
|
||||
sets.NewString(),
|
||||
true,
|
||||
},
|
||||
{ // non-duplicate, but 'all' present in CLI, while values are in config file, which is forbidden
|
||||
[]string{"all"},
|
||||
[]string{"a", "b"},
|
||||
sets.NewString(),
|
||||
true,
|
||||
},
|
||||
{ // skip all checks
|
||||
[]string{"all"},
|
||||
[]string{},
|
||||
sets.NewString("all"),
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
result, err := ValidateIgnorePreflightErrors(rt.ignorePreflightErrors)
|
||||
result, err := ValidateIgnorePreflightErrors(rt.ignorePreflightErrorsFromCLI, rt.ignorePreflightErrorsFromConfigFile)
|
||||
switch {
|
||||
case err != nil && !rt.expectedError:
|
||||
t.Errorf("ValidateIgnorePreflightErrors: unexpected error for input (%s), error: %v", rt.ignorePreflightErrors, err)
|
||||
t.Errorf("ValidateIgnorePreflightErrors: unexpected error for input (%s, %s), error: %v", rt.ignorePreflightErrorsFromCLI, rt.ignorePreflightErrorsFromConfigFile, err)
|
||||
case err == nil && rt.expectedError:
|
||||
t.Errorf("ValidateIgnorePreflightErrors: expected error for input (%s) but got: %v", rt.ignorePreflightErrors, result)
|
||||
case result.Len() != rt.expectedLen:
|
||||
t.Errorf("ValidateIgnorePreflightErrors: expected Len = %d for input (%s) but got: %v, %v", rt.expectedLen, rt.ignorePreflightErrors, result.Len(), result)
|
||||
t.Errorf("ValidateIgnorePreflightErrors: expected error for input (%s, %s) but got: %v", rt.ignorePreflightErrorsFromCLI, rt.ignorePreflightErrorsFromConfigFile, result)
|
||||
case err == nil && !result.Equal(rt.expectedSet):
|
||||
t.Errorf("ValidateIgnorePreflightErrors: expected (%v) for input (%s, %s) but got: %v", rt.expectedSet, rt.ignorePreflightErrorsFromCLI, rt.ignorePreflightErrorsFromConfigFile, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -567,6 +567,11 @@ func (in *NodeRegistrationOptions) DeepCopyInto(out *NodeRegistrationOptions) {
|
|||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.IgnorePreflightErrors != nil {
|
||||
in, out := &in.IgnorePreflightErrors, &out.IgnorePreflightErrors
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -99,6 +99,7 @@ go_test(
|
|||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/testing:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
|
|
|
@ -296,11 +296,6 @@ func newInitData(cmd *cobra.Command, args []string, options *initOptions, out io
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(options.ignorePreflightErrors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = validation.ValidateMixedArguments(cmd.Flags()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -316,6 +311,13 @@ func newInitData(cmd *cobra.Command, args []string, options *initOptions, out io
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(options.ignorePreflightErrors, cfg.NodeRegistration.IgnorePreflightErrors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Also set the union of pre-flight errors to InitConfiguration, to provide a consistent view of the runtime configuration:
|
||||
cfg.NodeRegistration.IgnorePreflightErrors = ignorePreflightErrorsSet.List()
|
||||
|
||||
// override node name and CRI socket from the command line options
|
||||
if options.externalcfg.NodeRegistration.Name != "" {
|
||||
cfg.NodeRegistration.Name = options.externalcfg.NodeRegistration.Name
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/features"
|
||||
)
|
||||
|
@ -38,6 +39,9 @@ bootstrapTokens:
|
|||
nodeRegistration:
|
||||
criSocket: /run/containerd/containerd.sock
|
||||
name: someName
|
||||
ignorePreflightErrors:
|
||||
- c
|
||||
- d
|
||||
---
|
||||
apiVersion: kubeadm.k8s.io/v1beta2
|
||||
kind: ClusterConfiguration
|
||||
|
@ -129,6 +133,30 @@ func TestNewInitData(t *testing.T) {
|
|||
},
|
||||
expectError: true,
|
||||
},
|
||||
|
||||
// Pre-flight errors:
|
||||
{
|
||||
name: "pre-flights errors from CLI args only",
|
||||
flags: map[string]string{
|
||||
options.IgnorePreflightErrors: "a,b",
|
||||
},
|
||||
validate: expectedInitIgnorePreflightErrors("a", "b"),
|
||||
},
|
||||
{
|
||||
name: "pre-flights errors from InitConfiguration only",
|
||||
flags: map[string]string{
|
||||
options.CfgPath: configFilePath,
|
||||
},
|
||||
validate: expectedInitIgnorePreflightErrors("c", "d"),
|
||||
},
|
||||
{
|
||||
name: "pre-flights errors from both CLI args and InitConfiguration",
|
||||
flags: map[string]string{
|
||||
options.CfgPath: configFilePath,
|
||||
options.IgnorePreflightErrors: "a,b",
|
||||
},
|
||||
validate: expectedInitIgnorePreflightErrors("a", "b", "c", "d"),
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
@ -157,3 +185,15 @@ func TestNewInitData(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func expectedInitIgnorePreflightErrors(expectedItems ...string) func(t *testing.T, data *initData) {
|
||||
expected := sets.NewString(expectedItems...)
|
||||
return func(t *testing.T, data *initData) {
|
||||
if !expected.Equal(data.ignorePreflightErrors) {
|
||||
t.Errorf("Invalid ignore preflight errors. Expected: %v. Actual: %v", expected.List(), data.ignorePreflightErrors.List())
|
||||
}
|
||||
if !expected.HasAll(data.cfg.NodeRegistration.IgnorePreflightErrors...) {
|
||||
t.Errorf("Invalid ignore preflight errors in InitConfiguration. Expected: %v. Actual: %v", expected.List(), data.cfg.NodeRegistration.IgnorePreflightErrors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -349,12 +349,7 @@ func newJoinData(cmd *cobra.Command, args []string, opt *joinOptions, out io.Wri
|
|||
}
|
||||
}
|
||||
|
||||
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(opt.ignorePreflightErrors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = validation.ValidateMixedArguments(cmd.Flags()); err != nil {
|
||||
if err := validation.ValidateMixedArguments(cmd.Flags()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -383,6 +378,13 @@ func newJoinData(cmd *cobra.Command, args []string, opt *joinOptions, out io.Wri
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(opt.ignorePreflightErrors, cfg.NodeRegistration.IgnorePreflightErrors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Also set the union of pre-flight errors to JoinConfiguration, to provide a consistent view of the runtime configuration:
|
||||
cfg.NodeRegistration.IgnorePreflightErrors = ignorePreflightErrorsSet.List()
|
||||
|
||||
// override node name and CRI socket from the command line opt
|
||||
if opt.externalcfg.NodeRegistration.Name != "" {
|
||||
cfg.NodeRegistration.Name = opt.externalcfg.NodeRegistration.Name
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
|
||||
)
|
||||
|
||||
|
@ -36,6 +37,9 @@ discovery:
|
|||
nodeRegistration:
|
||||
criSocket: /run/containerd/containerd.sock
|
||||
name: someName
|
||||
ignorePreflightErrors:
|
||||
- c
|
||||
- d
|
||||
`
|
||||
)
|
||||
|
||||
|
@ -215,6 +219,31 @@ func TestNewJoinData(t *testing.T) {
|
|||
},
|
||||
expectError: true,
|
||||
},
|
||||
|
||||
// Pre-flight errors:
|
||||
{
|
||||
name: "pre-flights errors from CLI args only",
|
||||
flags: map[string]string{
|
||||
options.IgnorePreflightErrors: "a,b",
|
||||
options.FileDiscovery: "https://foo", //required only to pass discovery validation
|
||||
},
|
||||
validate: expectedJoinIgnorePreflightErrors(sets.NewString("a", "b")),
|
||||
},
|
||||
{
|
||||
name: "pre-flights errors from JoinConfiguration only",
|
||||
flags: map[string]string{
|
||||
options.CfgPath: configFilePath,
|
||||
},
|
||||
validate: expectedJoinIgnorePreflightErrors(sets.NewString("c", "d")),
|
||||
},
|
||||
{
|
||||
name: "pre-flights errors from both CLI args and JoinConfiguration",
|
||||
flags: map[string]string{
|
||||
options.CfgPath: configFilePath,
|
||||
options.IgnorePreflightErrors: "a,b",
|
||||
},
|
||||
validate: expectedJoinIgnorePreflightErrors(sets.NewString("a", "b", "c", "d")),
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
@ -243,3 +272,14 @@ func TestNewJoinData(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func expectedJoinIgnorePreflightErrors(expected sets.String) func(t *testing.T, data *joinData) {
|
||||
return func(t *testing.T, data *joinData) {
|
||||
if !expected.Equal(data.ignorePreflightErrors) {
|
||||
t.Errorf("Invalid ignore preflight errors. Expected: %v. Actual: %v", expected.List(), data.ignorePreflightErrors.List())
|
||||
}
|
||||
if !expected.HasAll(data.cfg.NodeRegistration.IgnorePreflightErrors...) {
|
||||
t.Errorf("Invalid ignore preflight errors in JoinConfiguration. Expected: %v. Actual: %v", expected.List(), data.cfg.NodeRegistration.IgnorePreflightErrors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,10 +84,6 @@ func newResetOptions() *resetOptions {
|
|||
// newResetData returns a new resetData struct to be used for the execution of the kubeadm reset workflow.
|
||||
func newResetData(cmd *cobra.Command, options *resetOptions, in io.Reader, out io.Writer) (*resetData, error) {
|
||||
var cfg *kubeadmapi.InitConfiguration
|
||||
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(options.ignorePreflightErrors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := getClientset(options.kubeconfigPath, false)
|
||||
if err == nil {
|
||||
|
@ -100,6 +96,16 @@ func newResetData(cmd *cobra.Command, options *resetOptions, in io.Reader, out i
|
|||
klog.V(1).Infof("[reset] Could not obtain a client set from the kubeconfig file: %s", options.kubeconfigPath)
|
||||
}
|
||||
|
||||
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(options.ignorePreflightErrors, ignorePreflightErrors(cfg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kubeadmutil.CheckErr(err)
|
||||
if cfg != nil {
|
||||
// Also set the union of pre-flight errors to InitConfiguration, to provide a consistent view of the runtime configuration:
|
||||
cfg.NodeRegistration.IgnorePreflightErrors = ignorePreflightErrorsSet.List()
|
||||
}
|
||||
|
||||
var criSocketPath string
|
||||
if options.criSocketPath == "" {
|
||||
criSocketPath, err = resetDetectCRISocket(cfg)
|
||||
|
@ -121,6 +127,13 @@ func newResetData(cmd *cobra.Command, options *resetOptions, in io.Reader, out i
|
|||
}, nil
|
||||
}
|
||||
|
||||
func ignorePreflightErrors(cfg *kubeadmapi.InitConfiguration) []string {
|
||||
if cfg == nil {
|
||||
return []string{}
|
||||
}
|
||||
return cfg.NodeRegistration.IgnorePreflightErrors
|
||||
}
|
||||
|
||||
// AddResetFlags adds reset flags
|
||||
func AddResetFlags(flagSet *flag.FlagSet, resetOptions *resetOptions) {
|
||||
flagSet.StringVar(
|
||||
|
|
|
@ -76,17 +76,6 @@ func getK8sVersionFromUserInput(flags *applyPlanFlags, args []string, versionIsM
|
|||
|
||||
// enforceRequirements verifies that it's okay to upgrade and then returns the variables needed for the rest of the procedure
|
||||
func enforceRequirements(flags *applyPlanFlags, dryRun bool, newK8sVersion string) (clientset.Interface, upgrade.VersionGetter, *kubeadmapi.InitConfiguration, error) {
|
||||
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(flags.ignorePreflightErrors)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Ensure the user is root
|
||||
klog.V(1).Info("running preflight checks")
|
||||
if err := runPreflightChecks(ignorePreflightErrorsSet); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
client, err := getClient(flags.kubeConfigPath, dryRun)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", flags.kubeConfigPath)
|
||||
|
@ -97,11 +86,6 @@ func enforceRequirements(flags *applyPlanFlags, dryRun bool, newK8sVersion strin
|
|||
return nil, nil, nil, errors.New("cannot upgrade a self-hosted control plane")
|
||||
}
|
||||
|
||||
// Run healthchecks against the cluster
|
||||
if err := upgrade.CheckClusterHealth(client, ignorePreflightErrorsSet); err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "[upgrade/health] FATAL")
|
||||
}
|
||||
|
||||
// Fetch the configuration from a file or ConfigMap and validate it
|
||||
fmt.Println("[upgrade/config] Making sure the configuration is correct:")
|
||||
|
||||
|
@ -112,6 +96,24 @@ func enforceRequirements(flags *applyPlanFlags, dryRun bool, newK8sVersion strin
|
|||
cfg, err = configutil.FetchInitConfigurationFromCluster(client, os.Stdout, "upgrade/config", false)
|
||||
}
|
||||
|
||||
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(flags.ignorePreflightErrors, cfg.NodeRegistration.IgnorePreflightErrors)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
// Also set the union of pre-flight errors to InitConfiguration, to provide a consistent view of the runtime configuration:
|
||||
cfg.NodeRegistration.IgnorePreflightErrors = ignorePreflightErrorsSet.List()
|
||||
|
||||
// Ensure the user is root
|
||||
klog.V(1).Info("running preflight checks")
|
||||
if err := runPreflightChecks(ignorePreflightErrorsSet); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Run healthchecks against the cluster
|
||||
if err := upgrade.CheckClusterHealth(client, ignorePreflightErrorsSet); err != nil {
|
||||
return nil, nil, nil, errors.Wrap(err, "[upgrade/health] FATAL")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
fmt.Printf("[upgrade/config] In order to upgrade, a ConfigMap called %q in the %s namespace must exist.\n", constants.KubeadmConfigConfigMap, metav1.NamespaceSystem)
|
||||
|
|
|
@ -15,6 +15,7 @@ Discovery:
|
|||
Timeout: 5m0s
|
||||
NodeRegistration:
|
||||
CRISocket: /var/run/dockershim.sock
|
||||
IgnorePreflightErrors: null
|
||||
KubeletExtraArgs: null
|
||||
Name: control-plane-1
|
||||
Taints:
|
||||
|
|
Loading…
Reference in New Issue