From e706d272629979ffb4a125c5d330496d4b463137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Carr=C3=A9?= Date: Mon, 18 Mar 2019 21:01:17 +0900 Subject: [PATCH 1/2] kubeadm errors now ignorable via v1beta2 config files Specifically, IgnorePreflightErrors in {Init,Join}Configuration's NodeRegistrationOptions can be used to achieve this. See also: https://docs.google.com/document/d/1XnP67oO1i9VcDIpw42IzptnJsc5OQM-HTf8cVcjCR2w/edit --- cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go | 8 ++ cmd/kubeadm/app/apis/kubeadm/types.go | 3 + .../app/apis/kubeadm/v1beta1/conversion.go | 12 +++ .../apis/kubeadm/v1beta1/conversion_test.go | 68 +++++++++++++++ cmd/kubeadm/app/apis/kubeadm/v1beta2/types.go | 3 + .../app/apis/kubeadm/validation/validation.go | 19 ++++- .../kubeadm/validation/validation_test.go | 84 +++++++++++++++---- cmd/kubeadm/app/cmd/init.go | 12 +-- cmd/kubeadm/app/cmd/init_test.go | 40 +++++++++ cmd/kubeadm/app/cmd/join.go | 14 ++-- cmd/kubeadm/app/cmd/join_test.go | 40 +++++++++ cmd/kubeadm/app/cmd/reset.go | 21 ++++- cmd/kubeadm/app/cmd/upgrade/common.go | 34 ++++---- .../testdata/conversion/node/internal.yaml | 1 + 14 files changed, 311 insertions(+), 48 deletions(-) diff --git a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go index 3af7d3e3509..ffeabf041e2 100644 --- a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go +++ b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go @@ -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) diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index 11b9906a102..9e0f314431b 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -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. diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta1/conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta1/conversion.go index 2f8aa6eb5c5..cc50b68018c 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta1/conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta1/conversion.go @@ -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 +} diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta1/conversion_test.go b/cmd/kubeadm/app/apis/kubeadm/v1beta1/conversion_test.go index 0e7b73bbd73..0609c31e0a7 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta1/conversion_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta1/conversion_test.go @@ -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 diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta2/types.go b/cmd/kubeadm/app/apis/kubeadm/v1beta2/types.go index 327463430c6..d9d578b8108 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta2/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta2/types.go @@ -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 diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go index d61b4603377..37f62d026a0 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go @@ -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 } diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index 05be00ab90b..3dba33f0224 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -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) } } } diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index 9f4e3f1fdfc..bf1f0d9421a 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -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 diff --git a/cmd/kubeadm/app/cmd/init_test.go b/cmd/kubeadm/app/cmd/init_test.go index 79855fbca14..2daed1f77c1 100644 --- a/cmd/kubeadm/app/cmd/init_test.go +++ b/cmd/kubeadm/app/cmd/init_test.go @@ -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) + } + } +} diff --git a/cmd/kubeadm/app/cmd/join.go b/cmd/kubeadm/app/cmd/join.go index 0266ea46327..914fa154640 100644 --- a/cmd/kubeadm/app/cmd/join.go +++ b/cmd/kubeadm/app/cmd/join.go @@ -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 diff --git a/cmd/kubeadm/app/cmd/join_test.go b/cmd/kubeadm/app/cmd/join_test.go index 211fbcd2918..8a466a9f68b 100644 --- a/cmd/kubeadm/app/cmd/join_test.go +++ b/cmd/kubeadm/app/cmd/join_test.go @@ -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) + } + } +} diff --git a/cmd/kubeadm/app/cmd/reset.go b/cmd/kubeadm/app/cmd/reset.go index 1ef20d7ca3c..400d1e669ea 100644 --- a/cmd/kubeadm/app/cmd/reset.go +++ b/cmd/kubeadm/app/cmd/reset.go @@ -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( diff --git a/cmd/kubeadm/app/cmd/upgrade/common.go b/cmd/kubeadm/app/cmd/upgrade/common.go index 6c384337b64..c47115f6197 100644 --- a/cmd/kubeadm/app/cmd/upgrade/common.go +++ b/cmd/kubeadm/app/cmd/upgrade/common.go @@ -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) diff --git a/cmd/kubeadm/app/util/config/testdata/conversion/node/internal.yaml b/cmd/kubeadm/app/util/config/testdata/conversion/node/internal.yaml index f42d1cce4e4..e0af329be05 100644 --- a/cmd/kubeadm/app/util/config/testdata/conversion/node/internal.yaml +++ b/cmd/kubeadm/app/util/config/testdata/conversion/node/internal.yaml @@ -15,6 +15,7 @@ Discovery: Timeout: 5m0s NodeRegistration: CRISocket: /var/run/dockershim.sock + IgnorePreflightErrors: null KubeletExtraArgs: null Name: control-plane-1 Taints: From 7e2b4aa562ee45bd575f1078d6ed84f82aeb10aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Carr=C3=A9?= Date: Thu, 16 May 2019 17:23:12 +0900 Subject: [PATCH 2/2] kubeadm errors now ignorable via v1beta2 config files (generated diff) This commit contains only changes generated by the build process. Nothing here was manually changed. Changes made to: ``` cmd/kubeadm/app/apis/kubeadm/validation/BUILD cmd/kubeadm/app/cmd/BUILD ``` were generated by running: ```` ./hack/update-bazel.sh ``` --- .../apis/kubeadm/v1beta1/zz_generated.conversion.go | 11 ++++++----- .../apis/kubeadm/v1beta2/zz_generated.conversion.go | 2 ++ .../app/apis/kubeadm/v1beta2/zz_generated.deepcopy.go | 5 +++++ cmd/kubeadm/app/apis/kubeadm/validation/BUILD | 1 + cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go | 5 +++++ cmd/kubeadm/app/cmd/BUILD | 1 + 6 files changed, 20 insertions(+), 5 deletions(-) diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta1/zz_generated.conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta1/zz_generated.conversion.go index 4b2d3007945..012e3a71b63 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta1/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta1/zz_generated.conversion.go @@ -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) -} diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta2/zz_generated.conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta2/zz_generated.conversion.go index 368d760a47c..d7498eb1fa1 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta2/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta2/zz_generated.conversion.go @@ -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 } diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta2/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/kubeadm/v1beta2/zz_generated.deepcopy.go index 4f611d9a281..6e4945ce374 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta2/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta2/zz_generated.deepcopy.go @@ -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 } diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/BUILD b/cmd/kubeadm/app/apis/kubeadm/validation/BUILD index 84c37d0ab40..076ebdc9913 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/BUILD +++ b/cmd/kubeadm/app/apis/kubeadm/validation/BUILD @@ -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", diff --git a/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go index ebdf10bb2e2..164010242b0 100644 --- a/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go @@ -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 } diff --git a/cmd/kubeadm/app/cmd/BUILD b/cmd/kubeadm/app/cmd/BUILD index dcaed8131f3..45ab52a9dfc 100644 --- a/cmd/kubeadm/app/cmd/BUILD +++ b/cmd/kubeadm/app/cmd/BUILD @@ -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",