Implement individual control for kubeadm preflight checks

With new flag `--ignore-checks-errors` user is able to
decrease severity of each individual check to warning.

Old flag `--skip-preflight-checks` now acts as
`--ignore-checks-errors=all` and will produce warnings.

Fixes: kubernetes/kubeadm#480
pull/6/head
Alexander Kanevskiy 2017-11-01 16:40:13 +02:00
parent bf32170dca
commit e42eb28500
17 changed files with 332 additions and 78 deletions

View File

@ -33,6 +33,7 @@ go_library(
"//pkg/registry/core/service/ipallocator:go_default_library", "//pkg/registry/core/service/ipallocator:go_default_library",
"//pkg/util/node:go_default_library", "//pkg/util/node:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library", "//vendor/github.com/spf13/pflag:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
], ],

View File

@ -26,6 +26,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
@ -289,7 +290,7 @@ func ValidateMixedArguments(flag *pflag.FlagSet) error {
mixedInvalidFlags := []string{} mixedInvalidFlags := []string{}
flag.Visit(func(f *pflag.Flag) { flag.Visit(func(f *pflag.Flag) {
if f.Name == "config" || strings.HasPrefix(f.Name, "skip-") || f.Name == "dry-run" || f.Name == "kubeconfig" { if f.Name == "config" || strings.HasPrefix(f.Name, "ignore-checks-") || strings.HasPrefix(f.Name, "skip-") || f.Name == "dry-run" || f.Name == "kubeconfig" {
// "--skip-*" flags or other whitelisted flags can be set with --config // "--skip-*" flags or other whitelisted flags can be set with --config
return return
} }
@ -328,3 +329,27 @@ func ValidateAPIEndpoint(c *kubeadm.MasterConfiguration, fldPath *field.Path) fi
} }
return allErrs return allErrs
} }
// ValidateIgnoreChecksErrors validates duplicates in ignore-checks-errors flag.
func ValidateIgnoreChecksErrors(ignoreChecksErrors []string, skipPreflightChecks bool) (sets.String, error) {
ignoreErrors := sets.NewString()
allErrs := field.ErrorList{}
for _, item := range ignoreChecksErrors {
ignoreErrors.Insert(strings.ToLower(item)) // parameters are case insensitive
}
// TODO: remove once deprecated flag --skip-preflight-checks is removed.
if skipPreflightChecks {
if ignoreErrors.Has("all") {
allErrs = append(allErrs, field.Invalid(field.NewPath("ignore-checks-errors"), strings.Join(ignoreErrors.List(), ","), "'all' is used together with deprecated flag --skip-preflight-checks. Remove deprecated flag"))
}
ignoreErrors.Insert("all")
}
if ignoreErrors.Has("all") && ignoreErrors.Len() > 1 {
allErrs = append(allErrs, field.Invalid(field.NewPath("ignore-checks-errors"), strings.Join(ignoreErrors.List(), ","), "don't specify individual checks if 'all' is used"))
}
return ignoreErrors, allErrs.ToAggregate()
}

View File

@ -458,3 +458,32 @@ func TestValidateFeatureGates(t *testing.T) {
} }
} }
} }
func TestValidateIgnoreChecksErrors(t *testing.T) {
var tests = []struct {
ignoreChecksErrors []string
skipPreflightChecks bool
expectedLen int
expectedError bool
}{
{[]string{}, false, 0, false}, // empty list, no old skip-preflight-checks
{[]string{}, true, 1, false}, // empty list, old skip-preflight-checks
{[]string{"check1", "check2"}, false, 2, false}, // non-duplicate
{[]string{"check1", "check2"}, true, 3, true}, // non-duplicate, but skip-preflight-checks
{[]string{"check1", "check2", "check1"}, false, 2, false}, // duplicates
{[]string{"check1", "check2", "all"}, false, 3, true}, // non-duplicate, but 'all' present together wth individual checks
{[]string{"all"}, false, 1, false}, // skip all checks by using new flag
{[]string{"all"}, true, 1, true}, // skip all checks by using both old and new flags at the same time
}
for _, rt := range tests {
result, err := ValidateIgnoreChecksErrors(rt.ignoreChecksErrors, rt.skipPreflightChecks)
switch {
case err != nil && !rt.expectedError:
t.Errorf("ValidateIgnoreChecksErrors: unexpected error for input (%s, %v), error: %v", rt.ignoreChecksErrors, rt.skipPreflightChecks, err)
case err == nil && rt.expectedError:
t.Errorf("ValidateIgnoreChecksErrors: expected error for input (%s, %v) but got: %v", rt.ignoreChecksErrors, rt.skipPreflightChecks, result)
case result.Len() != rt.expectedLen:
t.Errorf("ValidateIgnoreChecksErrors: expected Len = %d for input (%s, %v) but got: %v, %v", rt.expectedLen, rt.ignoreChecksErrors, rt.skipPreflightChecks, result.Len(), result)
}
}
}

View File

@ -31,6 +31,7 @@ import (
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
@ -112,6 +113,7 @@ func NewCmdInit(out io.Writer) *cobra.Command {
var dryRun bool var dryRun bool
var featureGatesString string var featureGatesString string
var criSocket string var criSocket string
var ignoreChecksErrors []string
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "init", Use: "init",
@ -126,7 +128,10 @@ func NewCmdInit(out io.Writer) *cobra.Command {
internalcfg := &kubeadmapi.MasterConfiguration{} internalcfg := &kubeadmapi.MasterConfiguration{}
legacyscheme.Scheme.Convert(cfg, internalcfg, nil) legacyscheme.Scheme.Convert(cfg, internalcfg, nil)
i, err := NewInit(cfgPath, internalcfg, skipPreFlight, skipTokenPrint, dryRun, criSocket) ignoreChecksErrorsSet, err := validation.ValidateIgnoreChecksErrors(ignoreChecksErrors, skipPreFlight)
kubeadmutil.CheckErr(err)
i, err := NewInit(cfgPath, internalcfg, ignoreChecksErrorsSet, skipTokenPrint, dryRun, criSocket)
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(i.Validate(cmd)) kubeadmutil.CheckErr(i.Validate(cmd))
kubeadmutil.CheckErr(i.Run(out)) kubeadmutil.CheckErr(i.Run(out))
@ -134,7 +139,7 @@ func NewCmdInit(out io.Writer) *cobra.Command {
} }
AddInitConfigFlags(cmd.PersistentFlags(), cfg, &featureGatesString) AddInitConfigFlags(cmd.PersistentFlags(), cfg, &featureGatesString)
AddInitOtherFlags(cmd.PersistentFlags(), &cfgPath, &skipPreFlight, &skipTokenPrint, &dryRun, &criSocket) AddInitOtherFlags(cmd.PersistentFlags(), &cfgPath, &skipPreFlight, &skipTokenPrint, &dryRun, &criSocket, &ignoreChecksErrors)
return cmd return cmd
} }
@ -190,16 +195,21 @@ func AddInitConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiext.MasterConfigur
} }
// AddInitOtherFlags adds init flags that are not bound to a configuration file to the given flagset // AddInitOtherFlags adds init flags that are not bound to a configuration file to the given flagset
func AddInitOtherFlags(flagSet *flag.FlagSet, cfgPath *string, skipPreFlight, skipTokenPrint, dryRun *bool, criSocket *string) { func AddInitOtherFlags(flagSet *flag.FlagSet, cfgPath *string, skipPreFlight, skipTokenPrint, dryRun *bool, criSocket *string, ignoreChecksErrors *[]string) {
flagSet.StringVar( flagSet.StringVar(
cfgPath, "config", *cfgPath, cfgPath, "config", *cfgPath,
"Path to kubeadm config file. WARNING: Usage of a configuration file is experimental.", "Path to kubeadm config file. WARNING: Usage of a configuration file is experimental.",
) )
flagSet.StringSliceVar(
ignoreChecksErrors, "ignore-checks-errors", *ignoreChecksErrors,
"A list of checks whose errors will be shown as warnings. Example: 'IsPrivilegedUser,Swap'. Value 'all' ignores errors from all checks.",
)
// Note: All flags that are not bound to the cfg object should be whitelisted in cmd/kubeadm/app/apis/kubeadm/validation/validation.go // Note: All flags that are not bound to the cfg object should be whitelisted in cmd/kubeadm/app/apis/kubeadm/validation/validation.go
flagSet.BoolVar( flagSet.BoolVar(
skipPreFlight, "skip-preflight-checks", *skipPreFlight, skipPreFlight, "skip-preflight-checks", *skipPreFlight,
"Skip preflight checks which normally run before modifying the system.", "Skip preflight checks which normally run before modifying the system.",
) )
flagSet.MarkDeprecated("skip-preflight-checks", "it is now equivalent to --ignore-checks-errors=all")
// Note: All flags that are not bound to the cfg object should be whitelisted in cmd/kubeadm/app/apis/kubeadm/validation/validation.go // Note: All flags that are not bound to the cfg object should be whitelisted in cmd/kubeadm/app/apis/kubeadm/validation/validation.go
flagSet.BoolVar( flagSet.BoolVar(
skipTokenPrint, "skip-token-print", *skipTokenPrint, skipTokenPrint, "skip-token-print", *skipTokenPrint,
@ -217,7 +227,7 @@ func AddInitOtherFlags(flagSet *flag.FlagSet, cfgPath *string, skipPreFlight, sk
} }
// NewInit validates given arguments and instantiates Init struct with provided information. // NewInit validates given arguments and instantiates Init struct with provided information.
func NewInit(cfgPath string, cfg *kubeadmapi.MasterConfiguration, skipPreFlight, skipTokenPrint, dryRun bool, criSocket string) (*Init, error) { func NewInit(cfgPath string, cfg *kubeadmapi.MasterConfiguration, ignoreChecksErrors sets.String, skipTokenPrint, dryRun bool, criSocket string) (*Init, error) {
fmt.Println("[kubeadm] WARNING: kubeadm is currently in beta") fmt.Println("[kubeadm] WARNING: kubeadm is currently in beta")
if cfgPath != "" { if cfgPath != "" {
@ -249,19 +259,15 @@ func NewInit(cfgPath string, cfg *kubeadmapi.MasterConfiguration, skipPreFlight,
fmt.Println("\t(/etc/systemd/system/kubelet.service.d/10-kubeadm.conf should be edited for this purpose)") fmt.Println("\t(/etc/systemd/system/kubelet.service.d/10-kubeadm.conf should be edited for this purpose)")
} }
if !skipPreFlight { fmt.Println("[preflight] Running pre-flight checks.")
fmt.Println("[preflight] Running pre-flight checks.")
if err := preflight.RunInitMasterChecks(utilsexec.New(), cfg, criSocket); err != nil { if err := preflight.RunInitMasterChecks(utilsexec.New(), cfg, criSocket, ignoreChecksErrors); err != nil {
return nil, err return nil, err
}
// Try to start the kubelet service in case it's inactive
preflight.TryStartKubelet()
} else {
fmt.Println("[preflight] Skipping pre-flight checks.")
} }
// Try to start the kubelet service in case it's inactive
preflight.TryStartKubelet(ignoreChecksErrors)
return &Init{cfg: cfg, skipTokenPrint: skipTokenPrint, dryRun: dryRun}, nil return &Init{cfg: cfg, skipTokenPrint: skipTokenPrint, dryRun: dryRun}, nil
} }

View File

@ -29,6 +29,7 @@ import (
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
certutil "k8s.io/client-go/util/cert" certutil "k8s.io/client-go/util/cert"
@ -110,6 +111,7 @@ func NewCmdJoin(out io.Writer) *cobra.Command {
var cfgPath string var cfgPath string
var criSocket string var criSocket string
var featureGatesString string var featureGatesString string
var ignoreChecksErrors []string
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "join [flags]", Use: "join [flags]",
@ -127,7 +129,10 @@ func NewCmdJoin(out io.Writer) *cobra.Command {
internalcfg := &kubeadmapi.NodeConfiguration{} internalcfg := &kubeadmapi.NodeConfiguration{}
legacyscheme.Scheme.Convert(cfg, internalcfg, nil) legacyscheme.Scheme.Convert(cfg, internalcfg, nil)
j, err := NewJoin(cfgPath, args, internalcfg, skipPreFlight, criSocket) ignoreChecksErrorsSet, err := validation.ValidateIgnoreChecksErrors(ignoreChecksErrors, skipPreFlight)
kubeadmutil.CheckErr(err)
j, err := NewJoin(cfgPath, args, internalcfg, ignoreChecksErrorsSet, criSocket)
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(j.Validate(cmd)) kubeadmutil.CheckErr(j.Validate(cmd))
kubeadmutil.CheckErr(j.Run(out)) kubeadmutil.CheckErr(j.Run(out))
@ -135,7 +140,7 @@ func NewCmdJoin(out io.Writer) *cobra.Command {
} }
AddJoinConfigFlags(cmd.PersistentFlags(), cfg, &featureGatesString) AddJoinConfigFlags(cmd.PersistentFlags(), cfg, &featureGatesString)
AddJoinOtherFlags(cmd.PersistentFlags(), &cfgPath, &skipPreFlight, &criSocket) AddJoinOtherFlags(cmd.PersistentFlags(), &cfgPath, &skipPreFlight, &criSocket, &ignoreChecksErrors)
return cmd return cmd
} }
@ -170,15 +175,20 @@ func AddJoinConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiext.NodeConfigurat
} }
// AddJoinOtherFlags adds join flags that are not bound to a configuration file to the given flagset // AddJoinOtherFlags adds join flags that are not bound to a configuration file to the given flagset
func AddJoinOtherFlags(flagSet *flag.FlagSet, cfgPath *string, skipPreFlight *bool, criSocket *string) { func AddJoinOtherFlags(flagSet *flag.FlagSet, cfgPath *string, skipPreFlight *bool, criSocket *string, ignoreChecksErrors *[]string) {
flagSet.StringVar( flagSet.StringVar(
cfgPath, "config", *cfgPath, cfgPath, "config", *cfgPath,
"Path to kubeadm config file.") "Path to kubeadm config file.")
flagSet.StringSliceVar(
ignoreChecksErrors, "ignore-checks-errors", *ignoreChecksErrors,
"A list of checks whose errors will be shown as warnings. Example: 'IsPrivilegedUser,Swap'. Value 'all' ignores errors from all checks.",
)
flagSet.BoolVar( flagSet.BoolVar(
skipPreFlight, "skip-preflight-checks", false, skipPreFlight, "skip-preflight-checks", false,
"Skip preflight checks which normally run before modifying the system.", "Skip preflight checks which normally run before modifying the system.",
) )
flagSet.MarkDeprecated("skip-preflight-checks", "it is now equivalent to --ignore-checks-errors=all")
flagSet.StringVar( flagSet.StringVar(
criSocket, "cri-socket", "/var/run/dockershim.sock", criSocket, "cri-socket", "/var/run/dockershim.sock",
`Specify the CRI socket to connect to.`, `Specify the CRI socket to connect to.`,
@ -191,7 +201,7 @@ type Join struct {
} }
// NewJoin instantiates Join struct with given arguments // NewJoin instantiates Join struct with given arguments
func NewJoin(cfgPath string, args []string, cfg *kubeadmapi.NodeConfiguration, skipPreFlight bool, criSocket string) (*Join, error) { func NewJoin(cfgPath string, args []string, cfg *kubeadmapi.NodeConfiguration, ignoreChecksErrors sets.String, criSocket string) (*Join, error) {
fmt.Println("[kubeadm] WARNING: kubeadm is currently in beta") fmt.Println("[kubeadm] WARNING: kubeadm is currently in beta")
if cfg.NodeName == "" { if cfg.NodeName == "" {
@ -208,20 +218,16 @@ func NewJoin(cfgPath string, args []string, cfg *kubeadmapi.NodeConfiguration, s
} }
} }
if !skipPreFlight { fmt.Println("[preflight] Running pre-flight checks.")
fmt.Println("[preflight] Running pre-flight checks.")
// Then continue with the others... // Then continue with the others...
if err := preflight.RunJoinNodeChecks(utilsexec.New(), cfg, criSocket); err != nil { if err := preflight.RunJoinNodeChecks(utilsexec.New(), cfg, criSocket, ignoreChecksErrors); err != nil {
return nil, err return nil, err
}
// Try to start the kubelet service in case it's inactive
preflight.TryStartKubelet()
} else {
fmt.Println("[preflight] Skipping pre-flight checks.")
} }
// Try to start the kubelet service in case it's inactive
preflight.TryStartKubelet(ignoreChecksErrors)
return &Join{cfg: cfg}, nil return &Join{cfg: cfg}, nil
} }

View File

@ -50,6 +50,7 @@ go_library(
"//pkg/util/normalizer:go_default_library", "//pkg/util/normalizer:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library",
], ],

View File

@ -19,6 +19,7 @@ package phases
import ( import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/util/sets"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight" "k8s.io/kubernetes/cmd/kubeadm/app/preflight"
@ -70,7 +71,7 @@ func NewCmdPreFlightMaster() *cobra.Command {
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cfg := &kubeadmapi.MasterConfiguration{} cfg := &kubeadmapi.MasterConfiguration{}
criSocket := "" criSocket := ""
err := preflight.RunInitMasterChecks(utilsexec.New(), cfg, criSocket) err := preflight.RunInitMasterChecks(utilsexec.New(), cfg, criSocket, sets.NewString())
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
}, },
} }
@ -88,7 +89,7 @@ func NewCmdPreFlightNode() *cobra.Command {
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cfg := &kubeadmapi.NodeConfiguration{} cfg := &kubeadmapi.NodeConfiguration{}
criSocket := "" criSocket := ""
err := preflight.RunJoinNodeChecks(utilsexec.New(), cfg, criSocket) err := preflight.RunJoinNodeChecks(utilsexec.New(), cfg, criSocket, sets.NewString())
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
}, },
} }

View File

@ -26,7 +26,9 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/util/sets"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight" "k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
@ -45,20 +47,30 @@ func NewCmdReset(out io.Writer) *cobra.Command {
var skipPreFlight bool var skipPreFlight bool
var certsDir string var certsDir string
var criSocketPath string var criSocketPath string
var ignoreChecksErrors []string
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "reset", Use: "reset",
Short: "Run this to revert any changes made to this host by 'kubeadm init' or 'kubeadm join'.", Short: "Run this to revert any changes made to this host by 'kubeadm init' or 'kubeadm join'.",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
r, err := NewReset(skipPreFlight, certsDir, criSocketPath) ignoreChecksErrorsSet, err := validation.ValidateIgnoreChecksErrors(ignoreChecksErrors, skipPreFlight)
kubeadmutil.CheckErr(err)
r, err := NewReset(ignoreChecksErrorsSet, certsDir, criSocketPath)
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(r.Run(out)) kubeadmutil.CheckErr(r.Run(out))
}, },
} }
cmd.PersistentFlags().StringSliceVar(
&ignoreChecksErrors, "ignore-checks-errors", ignoreChecksErrors,
"A list of checks whose errors will be shown as warnings. Example: 'IsPrivilegedUser,Swap'. Value 'all' ignores errors from all checks.",
)
cmd.PersistentFlags().BoolVar( cmd.PersistentFlags().BoolVar(
&skipPreFlight, "skip-preflight-checks", false, &skipPreFlight, "skip-preflight-checks", false,
"Skip preflight checks which normally run before modifying the system.", "Skip preflight checks which normally run before modifying the system.",
) )
cmd.PersistentFlags().MarkDeprecated("skip-preflight-checks", "it is now equivalent to --ignore-checks-errors=all")
cmd.PersistentFlags().StringVar( cmd.PersistentFlags().StringVar(
&certsDir, "cert-dir", kubeadmapiext.DefaultCertificatesDir, &certsDir, "cert-dir", kubeadmapiext.DefaultCertificatesDir,
@ -80,15 +92,11 @@ type Reset struct {
} }
// NewReset instantiate Reset struct // NewReset instantiate Reset struct
func NewReset(skipPreFlight bool, certsDir, criSocketPath string) (*Reset, error) { func NewReset(ignoreChecksErrors sets.String, certsDir, criSocketPath string) (*Reset, error) {
if !skipPreFlight { fmt.Println("[preflight] Running pre-flight checks.")
fmt.Println("[preflight] Running pre-flight checks.")
if err := preflight.RunRootCheckOnly(); err != nil { if err := preflight.RunRootCheckOnly(ignoreChecksErrors); err != nil {
return nil, err return nil, err
}
} else {
fmt.Println("[preflight] Skipping pre-flight checks.")
} }
return &Reset{ return &Reset{

View File

@ -195,6 +195,10 @@ func (c *fakeDockerChecker) Check() (warnings, errors []error) {
return c.warnings, c.errors return c.warnings, c.errors
} }
func (c *fakeDockerChecker) Name() string {
return "FakeDocker"
}
func newFakeDockerChecker(warnings, errors []error) preflight.Checker { func newFakeDockerChecker(warnings, errors []error) preflight.Checker {
return &fakeDockerChecker{warnings: warnings, errors: errors} return &fakeDockerChecker{warnings: warnings, errors: errors}
} }

View File

@ -13,6 +13,7 @@ go_library(
deps = [ deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
"//cmd/kubeadm/app/cmd/util:go_default_library", "//cmd/kubeadm/app/cmd/util:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/phases/controlplane:go_default_library", "//cmd/kubeadm/app/phases/controlplane:go_default_library",
@ -27,6 +28,7 @@ go_library(
"//pkg/util/version:go_default_library", "//pkg/util/version:go_default_library",
"//vendor/github.com/ghodss/yaml:go_default_library", "//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/client-go/discovery/fake:go_default_library", "//vendor/k8s.io/client-go/discovery/fake:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library",
], ],

View File

@ -25,6 +25,7 @@ import (
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane" "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane"
@ -70,8 +71,12 @@ func NewCmdApply(parentFlags *cmdUpgradeFlags) *cobra.Command {
Use: "apply [version]", Use: "apply [version]",
Short: "Upgrade your Kubernetes cluster to the specified version.", Short: "Upgrade your Kubernetes cluster to the specified version.",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var err error
flags.parent.ignoreChecksErrorsSet, err = validation.ValidateIgnoreChecksErrors(flags.parent.ignoreChecksErrors, flags.parent.skipPreFlight)
kubeadmutil.CheckErr(err)
// Ensure the user is root // Ensure the user is root
err := runPreflightChecks(flags.parent.skipPreFlight) err = runPreflightChecks(flags.parent.ignoreChecksErrorsSet)
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
err = cmdutil.ValidateExactArgNumber(args, []string{"version"}) err = cmdutil.ValidateExactArgNumber(args, []string{"version"})

View File

@ -26,6 +26,7 @@ import (
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"k8s.io/apimachinery/pkg/util/sets"
fakediscovery "k8s.io/client-go/discovery/fake" fakediscovery "k8s.io/client-go/discovery/fake"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
@ -97,14 +98,9 @@ func printConfiguration(cfg *kubeadmapiext.MasterConfiguration, w io.Writer) {
} }
// runPreflightChecks runs the root preflight check // runPreflightChecks runs the root preflight check
func runPreflightChecks(skipPreFlight bool) error { func runPreflightChecks(ignoreChecksErrors sets.String) error {
if skipPreFlight {
fmt.Println("[preflight] Skipping pre-flight checks.")
return nil
}
fmt.Println("[preflight] Running pre-flight checks.") fmt.Println("[preflight] Running pre-flight checks.")
return preflight.RunRootCheckOnly() return preflight.RunRootCheckOnly(ignoreChecksErrors)
} }
// getClient gets a real or fake client depending on whether the user is dry-running or not // getClient gets a real or fake client depending on whether the user is dry-running or not

View File

@ -25,6 +25,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade" "k8s.io/kubernetes/cmd/kubeadm/app/phases/upgrade"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
) )
@ -35,8 +36,11 @@ func NewCmdPlan(parentFlags *cmdUpgradeFlags) *cobra.Command {
Use: "plan", Use: "plan",
Short: "Check which versions are available to upgrade to and validate whether your current cluster is upgradeable.", Short: "Check which versions are available to upgrade to and validate whether your current cluster is upgradeable.",
Run: func(_ *cobra.Command, _ []string) { Run: func(_ *cobra.Command, _ []string) {
var err error
parentFlags.ignoreChecksErrorsSet, err = validation.ValidateIgnoreChecksErrors(parentFlags.ignoreChecksErrors, parentFlags.skipPreFlight)
kubeadmutil.CheckErr(err)
// Ensure the user is root // Ensure the user is root
err := runPreflightChecks(parentFlags.skipPreFlight) err = runPreflightChecks(parentFlags.ignoreChecksErrorsSet)
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
err = RunPlan(parentFlags) err = RunPlan(parentFlags)

View File

@ -20,6 +20,7 @@ import (
"io" "io"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/util/sets"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
) )
@ -31,6 +32,8 @@ type cmdUpgradeFlags struct {
allowRCUpgrades bool allowRCUpgrades bool
printConfig bool printConfig bool
skipPreFlight bool skipPreFlight bool
ignoreChecksErrors []string
ignoreChecksErrorsSet sets.String
} }
// NewCmdUpgrade returns the cobra command for `kubeadm upgrade` // NewCmdUpgrade returns the cobra command for `kubeadm upgrade`
@ -42,6 +45,7 @@ func NewCmdUpgrade(out io.Writer) *cobra.Command {
allowRCUpgrades: false, allowRCUpgrades: false,
printConfig: false, printConfig: false,
skipPreFlight: false, skipPreFlight: false,
ignoreChecksErrorsSet: sets.NewString(),
} }
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -55,7 +59,9 @@ func NewCmdUpgrade(out io.Writer) *cobra.Command {
cmd.PersistentFlags().BoolVar(&flags.allowExperimentalUpgrades, "allow-experimental-upgrades", flags.allowExperimentalUpgrades, "Show unstable versions of Kubernetes as an upgrade alternative and allow upgrading to an alpha/beta/release candidate versions of Kubernetes.") cmd.PersistentFlags().BoolVar(&flags.allowExperimentalUpgrades, "allow-experimental-upgrades", flags.allowExperimentalUpgrades, "Show unstable versions of Kubernetes as an upgrade alternative and allow upgrading to an alpha/beta/release candidate versions of Kubernetes.")
cmd.PersistentFlags().BoolVar(&flags.allowRCUpgrades, "allow-release-candidate-upgrades", flags.allowRCUpgrades, "Show release candidate versions of Kubernetes as an upgrade alternative and allow upgrading to a release candidate versions of Kubernetes.") cmd.PersistentFlags().BoolVar(&flags.allowRCUpgrades, "allow-release-candidate-upgrades", flags.allowRCUpgrades, "Show release candidate versions of Kubernetes as an upgrade alternative and allow upgrading to a release candidate versions of Kubernetes.")
cmd.PersistentFlags().BoolVar(&flags.printConfig, "print-config", flags.printConfig, "Specifies whether the configuration file that will be used in the upgrade should be printed or not.") cmd.PersistentFlags().BoolVar(&flags.printConfig, "print-config", flags.printConfig, "Specifies whether the configuration file that will be used in the upgrade should be printed or not.")
cmd.PersistentFlags().StringSliceVar(&flags.ignoreChecksErrors, "ignore-checks-errors", flags.ignoreChecksErrors, "A list of checks whose errors will be shown as warnings. Example: 'IsPrivilegedUser,Swap'. Value 'all' ignores errors from all checks.")
cmd.PersistentFlags().BoolVar(&flags.skipPreFlight, "skip-preflight-checks", flags.skipPreFlight, "Skip preflight checks that normally run before modifying the system.") cmd.PersistentFlags().BoolVar(&flags.skipPreFlight, "skip-preflight-checks", flags.skipPreFlight, "Skip preflight checks that normally run before modifying the system.")
cmd.PersistentFlags().MarkDeprecated("skip-preflight-checks", "it is now equivalent to --ignore-checks-errors=all")
cmd.AddCommand(NewCmdApply(flags)) cmd.AddCommand(NewCmdApply(flags))
cmd.AddCommand(NewCmdPlan(flags)) cmd.AddCommand(NewCmdPlan(flags))

View File

@ -36,6 +36,7 @@ go_library(
"//vendor/github.com/blang/semver:go_default_library", "//vendor/github.com/blang/semver:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library", "//vendor/github.com/spf13/pflag:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library",
], ],
) )
@ -51,6 +52,7 @@ go_test(
deps = [ deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//vendor/github.com/renstrom/dedent:go_default_library", "//vendor/github.com/renstrom/dedent:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library",
], ],
) )

View File

@ -41,6 +41,7 @@ import (
"net/url" "net/url"
netutil "k8s.io/apimachinery/pkg/util/net" netutil "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets"
apiservoptions "k8s.io/kubernetes/cmd/kube-apiserver/app/options" apiservoptions "k8s.io/kubernetes/cmd/kube-apiserver/app/options"
cmoptions "k8s.io/kubernetes/cmd/kube-controller-manager/app/options" cmoptions "k8s.io/kubernetes/cmd/kube-controller-manager/app/options"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
@ -74,13 +75,14 @@ type Error struct {
} }
func (e *Error) Error() string { func (e *Error) Error() string {
return fmt.Sprintf("[preflight] Some fatal errors occurred:\n%s%s", e.Msg, "[preflight] If you know what you are doing, you can skip pre-flight checks with `--skip-preflight-checks`") return fmt.Sprintf("[preflight] Some fatal errors occurred:\n%s%s", e.Msg, "[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-checks-errors=...`")
} }
// Checker validates the state of the system to ensure kubeadm will be // Checker validates the state of the system to ensure kubeadm will be
// successful as often as possilble. // successful as often as possilble.
type Checker interface { type Checker interface {
Check() (warnings, errors []error) Check() (warnings, errors []error)
Name() string
} }
// CRICheck verifies the container runtime through the CRI. // CRICheck verifies the container runtime through the CRI.
@ -89,6 +91,11 @@ type CRICheck struct {
exec utilsexec.Interface exec utilsexec.Interface
} }
// Name returns label for CRICheck.
func (CRICheck) Name() string {
return "CRI"
}
// Check validates the container runtime through the CRI. // Check validates the container runtime through the CRI.
func (criCheck CRICheck) Check() (warnings, errors []error) { func (criCheck CRICheck) Check() (warnings, errors []error) {
if err := criCheck.exec.Command("sh", "-c", fmt.Sprintf("crictl -r %s info", criCheck.socket)).Run(); err != nil { if err := criCheck.exec.Command("sh", "-c", fmt.Sprintf("crictl -r %s info", criCheck.socket)).Run(); err != nil {
@ -104,6 +111,15 @@ func (criCheck CRICheck) Check() (warnings, errors []error) {
type ServiceCheck struct { type ServiceCheck struct {
Service string Service string
CheckIfActive bool CheckIfActive bool
Label string
}
// Name returns label for ServiceCheck. If not provided, will return based on the service parameter
func (sc ServiceCheck) Name() string {
if sc.Label != "" {
return sc.Label
}
return fmt.Sprintf("Service-%s", strings.Title(sc.Service))
} }
// Check validates if the service is enabled and active. // Check validates if the service is enabled and active.
@ -141,6 +157,11 @@ type FirewalldCheck struct {
ports []int ports []int
} }
// Name returns label for FirewalldCheck.
func (FirewalldCheck) Name() string {
return "Firewalld"
}
// Check validates if the firewall is enabled and active. // Check validates if the firewall is enabled and active.
func (fc FirewalldCheck) Check() (warnings, errors []error) { func (fc FirewalldCheck) Check() (warnings, errors []error) {
initSystem, err := initsystem.GetInitSystem() initSystem, err := initsystem.GetInitSystem()
@ -165,7 +186,16 @@ func (fc FirewalldCheck) Check() (warnings, errors []error) {
// PortOpenCheck ensures the given port is available for use. // PortOpenCheck ensures the given port is available for use.
type PortOpenCheck struct { type PortOpenCheck struct {
port int port int
label string
}
// Name returns name for PortOpenCheck. If not known, will return "PortXXXX" based on port number
func (poc PortOpenCheck) Name() string {
if poc.label != "" {
return poc.label
}
return fmt.Sprintf("Port-%d", poc.port)
} }
// Check validates if the particular port is available. // Check validates if the particular port is available.
@ -185,9 +215,23 @@ func (poc PortOpenCheck) Check() (warnings, errors []error) {
// IsPrivilegedUserCheck verifies user is privileged (linux - root, windows - Administrator) // IsPrivilegedUserCheck verifies user is privileged (linux - root, windows - Administrator)
type IsPrivilegedUserCheck struct{} type IsPrivilegedUserCheck struct{}
// Name returns name for IsPrivilegedUserCheck
func (IsPrivilegedUserCheck) Name() string {
return "IsPrivilegedUser"
}
// DirAvailableCheck checks if the given directory either does not exist, or is empty. // DirAvailableCheck checks if the given directory either does not exist, or is empty.
type DirAvailableCheck struct { type DirAvailableCheck struct {
Path string Path string
Label string
}
// Name returns label for individual DirAvailableChecks. If not known, will return based on path.
func (dac DirAvailableCheck) Name() string {
if dac.Label != "" {
return dac.Label
}
return fmt.Sprintf("DirAvailable-%s", strings.Replace(dac.Path, "/", "-", -1))
} }
// Check validates if a directory does not exist or empty. // Check validates if a directory does not exist or empty.
@ -215,7 +259,16 @@ func (dac DirAvailableCheck) Check() (warnings, errors []error) {
// FileAvailableCheck checks that the given file does not already exist. // FileAvailableCheck checks that the given file does not already exist.
type FileAvailableCheck struct { type FileAvailableCheck struct {
Path string Path string
Label string
}
// Name returns label for individual FileAvailableChecks. If not known, will return based on path.
func (fac FileAvailableCheck) Name() string {
if fac.Label != "" {
return fac.Label
}
return fmt.Sprintf("FileAvailable-%s", strings.Replace(fac.Path, "/", "-", -1))
} }
// Check validates if the given file does not already exist. // Check validates if the given file does not already exist.
@ -229,7 +282,16 @@ func (fac FileAvailableCheck) Check() (warnings, errors []error) {
// FileExistingCheck checks that the given file does not already exist. // FileExistingCheck checks that the given file does not already exist.
type FileExistingCheck struct { type FileExistingCheck struct {
Path string Path string
Label string
}
// Name returns label for individual FileExistingChecks. If not known, will return based on path.
func (fac FileExistingCheck) Name() string {
if fac.Label != "" {
return fac.Label
}
return fmt.Sprintf("FileExisting-%s", strings.Replace(fac.Path, "/", "-", -1))
} }
// Check validates if the given file already exists. // Check validates if the given file already exists.
@ -245,6 +307,15 @@ func (fac FileExistingCheck) Check() (warnings, errors []error) {
type FileContentCheck struct { type FileContentCheck struct {
Path string Path string
Content []byte Content []byte
Label string
}
// Name returns label for individual FileContentChecks. If not known, will return based on path.
func (fcc FileContentCheck) Name() string {
if fcc.Label != "" {
return fcc.Label
}
return fmt.Sprintf("FileContent-%s", strings.Replace(fcc.Path, "/", "-", -1))
} }
// Check validates if the given file contains the given content. // Check validates if the given file contains the given content.
@ -275,6 +346,15 @@ type InPathCheck struct {
executable string executable string
mandatory bool mandatory bool
exec utilsexec.Interface exec utilsexec.Interface
label string
}
// Name returns label for individual InPathCheck. If not known, will return based on path.
func (ipc InPathCheck) Name() string {
if ipc.label != "" {
return ipc.label
}
return fmt.Sprintf("FileExisting-%s", strings.Replace(ipc.executable, "/", "-", -1))
} }
// Check validates if the given executable is present in the path. // Check validates if the given executable is present in the path.
@ -297,6 +377,11 @@ type HostnameCheck struct {
nodeName string nodeName string
} }
// Name will return Hostname as name for HostnameCheck
func (HostnameCheck) Name() string {
return "Hostname"
}
// Check validates if hostname match dns sub domain regex. // Check validates if hostname match dns sub domain regex.
func (hc HostnameCheck) Check() (warnings, errors []error) { func (hc HostnameCheck) Check() (warnings, errors []error) {
errors = []error{} errors = []error{}
@ -322,6 +407,11 @@ type HTTPProxyCheck struct {
Port int Port int
} }
// Name returns HTTPProxy as name for HTTPProxyCheck
func (hst HTTPProxyCheck) Name() string {
return "HTTPProxy"
}
// Check validates http connectivity type, direct or via proxy. // Check validates http connectivity type, direct or via proxy.
func (hst HTTPProxyCheck) Check() (warnings, errors []error) { func (hst HTTPProxyCheck) Check() (warnings, errors []error) {
@ -352,6 +442,11 @@ type HTTPProxyCIDRCheck struct {
CIDR string CIDR string
} }
// Name will return HTTPProxyCIDR as name for HTTPProxyCIDRCheck
func (HTTPProxyCIDRCheck) Name() string {
return "HTTPProxyCIDR"
}
// Check validates http connectivity to first IP address in the CIDR. // Check validates http connectivity to first IP address in the CIDR.
// If it is not directly connected and goes via proxy it will produce warning. // If it is not directly connected and goes via proxy it will produce warning.
func (subnet HTTPProxyCIDRCheck) Check() (warnings, errors []error) { func (subnet HTTPProxyCIDRCheck) Check() (warnings, errors []error) {
@ -399,6 +494,11 @@ type ExtraArgsCheck struct {
SchedulerExtraArgs map[string]string SchedulerExtraArgs map[string]string
} }
// Name will return ExtraArgs as name for ExtraArgsCheck
func (ExtraArgsCheck) Name() string {
return "ExtraArgs"
}
// Check validates additional arguments of the control plane components. // Check validates additional arguments of the control plane components.
func (eac ExtraArgsCheck) Check() (warnings, errors []error) { func (eac ExtraArgsCheck) Check() (warnings, errors []error) {
argsCheck := func(name string, args map[string]string, f *pflag.FlagSet) []error { argsCheck := func(name string, args map[string]string, f *pflag.FlagSet) []error {
@ -438,6 +538,11 @@ type SystemVerificationCheck struct {
CRISocket string CRISocket string
} }
// Name will return SystemVerification as name for SystemVerificationCheck
func (SystemVerificationCheck) Name() string {
return "SystemVerification"
}
// Check runs all individual checks // Check runs all individual checks
func (sysver SystemVerificationCheck) Check() (warnings, errors []error) { func (sysver SystemVerificationCheck) Check() (warnings, errors []error) {
// Create a buffered writer and choose a quite large value (1M) and suppose the output from the system verification test won't exceed the limit // Create a buffered writer and choose a quite large value (1M) and suppose the output from the system verification test won't exceed the limit
@ -490,6 +595,11 @@ type KubernetesVersionCheck struct {
KubernetesVersion string KubernetesVersion string
} }
// Name will return KubernetesVersion as name for KubernetesVersionCheck
func (KubernetesVersionCheck) Name() string {
return "KubernetesVersion"
}
// Check validates kubernetes and kubeadm versions // Check validates kubernetes and kubeadm versions
func (kubever KubernetesVersionCheck) Check() (warnings, errors []error) { func (kubever KubernetesVersionCheck) Check() (warnings, errors []error) {
@ -525,6 +635,11 @@ type KubeletVersionCheck struct {
KubernetesVersion string KubernetesVersion string
} }
// Name will return KubeletVersion as name for KubeletVersionCheck
func (KubeletVersionCheck) Name() string {
return "KubeletVersion"
}
// Check validates kubelet version. It should be not less than minimal supported version // Check validates kubelet version. It should be not less than minimal supported version
func (kubever KubeletVersionCheck) Check() (warnings, errors []error) { func (kubever KubeletVersionCheck) Check() (warnings, errors []error) {
kubeletVersion, err := GetKubeletVersion() kubeletVersion, err := GetKubeletVersion()
@ -550,6 +665,11 @@ func (kubever KubeletVersionCheck) Check() (warnings, errors []error) {
// SwapCheck warns if swap is enabled // SwapCheck warns if swap is enabled
type SwapCheck struct{} type SwapCheck struct{}
// Name will return Swap as name for SwapCheck
func (SwapCheck) Name() string {
return "Swap"
}
// Check validates whether swap is enabled or not // Check validates whether swap is enabled or not
func (swc SwapCheck) Check() (warnings, errors []error) { func (swc SwapCheck) Check() (warnings, errors []error) {
f, err := os.Open("/proc/swaps") f, err := os.Open("/proc/swaps")
@ -584,6 +704,11 @@ type ExternalEtcdVersionCheck struct {
Etcd kubeadmapi.Etcd Etcd kubeadmapi.Etcd
} }
// Name will return ExternalEtcdVersion as name for ExternalEtcdVersionCheck
func (ExternalEtcdVersionCheck) Name() string {
return "ExternalEtcdVersion"
}
// Check validates external etcd version // Check validates external etcd version
func (evc ExternalEtcdVersionCheck) Check() (warnings, errors []error) { func (evc ExternalEtcdVersionCheck) Check() (warnings, errors []error) {
var config *tls.Config var config *tls.Config
@ -712,9 +837,9 @@ func getEtcdVersionResponse(client *http.Client, url string, target interface{})
} }
// RunInitMasterChecks executes all individual, applicable to Master node checks. // RunInitMasterChecks executes all individual, applicable to Master node checks.
func RunInitMasterChecks(execer utilsexec.Interface, cfg *kubeadmapi.MasterConfiguration, criSocket string) error { func RunInitMasterChecks(execer utilsexec.Interface, cfg *kubeadmapi.MasterConfiguration, criSocket string, ignoreChecksErrors sets.String) error {
// First, check if we're root separately from the other preflight checks and fail fast // First, check if we're root separately from the other preflight checks and fail fast
if err := RunRootCheckOnly(); err != nil { if err := RunRootCheckOnly(ignoreChecksErrors); err != nil {
return err return err
} }
@ -804,13 +929,13 @@ func RunInitMasterChecks(execer utilsexec.Interface, cfg *kubeadmapi.MasterConfi
) )
} }
} }
return RunChecks(checks, os.Stderr) return RunChecks(checks, os.Stderr, ignoreChecksErrors)
} }
// RunJoinNodeChecks executes all individual, applicable to node checks. // RunJoinNodeChecks executes all individual, applicable to node checks.
func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.NodeConfiguration, criSocket string) error { func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.NodeConfiguration, criSocket string, ignoreChecksErrors sets.String) error {
// First, check if we're root separately from the other preflight checks and fail fast // First, check if we're root separately from the other preflight checks and fail fast
if err := RunRootCheckOnly(); err != nil { if err := RunRootCheckOnly(ignoreChecksErrors); err != nil {
return err return err
} }
@ -862,33 +987,50 @@ func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.NodeConfigura
} }
} }
} }
return RunChecks(checks, os.Stderr) return RunChecks(checks, os.Stderr, ignoreChecksErrors)
} }
// RunRootCheckOnly initializes checks slice of structs and call RunChecks // RunRootCheckOnly initializes checks slice of structs and call RunChecks
func RunRootCheckOnly() error { func RunRootCheckOnly(ignoreChecksErrors sets.String) error {
checks := []Checker{ checks := []Checker{
IsPrivilegedUserCheck{}, IsPrivilegedUserCheck{},
} }
return RunChecks(checks, os.Stderr) return RunChecks(checks, os.Stderr, ignoreChecksErrors)
} }
// RunChecks runs each check, displays it's warnings/errors, and once all // RunChecks runs each check, displays it's warnings/errors, and once all
// are processed will exit if any errors occurred. // are processed will exit if any errors occurred.
func RunChecks(checks []Checker, ww io.Writer) error { func RunChecks(checks []Checker, ww io.Writer, ignoreChecksErrors sets.String) error {
found := []error{} type checkErrors struct {
Name string
Errors []error
}
found := []checkErrors{}
for _, c := range checks { for _, c := range checks {
name := c.Name()
warnings, errs := c.Check() warnings, errs := c.Check()
for _, w := range warnings {
io.WriteString(ww, fmt.Sprintf("[preflight] WARNING: %v\n", w)) if setHasItemOrAll(ignoreChecksErrors, name) {
// Decrease severity of errors to warnings for this check
warnings = append(warnings, errs...)
errs = []error{}
}
for _, w := range warnings {
io.WriteString(ww, fmt.Sprintf("\t[WARNING %s]: %v\n", name, w))
}
if len(errs) > 0 {
found = append(found, checkErrors{Name: name, Errors: errs})
} }
found = append(found, errs...)
} }
if len(found) > 0 { if len(found) > 0 {
var errs bytes.Buffer var errs bytes.Buffer
for _, i := range found { for _, c := range found {
errs.WriteString("\t" + i.Error() + "\n") for _, i := range c.Errors {
errs.WriteString(fmt.Sprintf("\t[ERROR %s]: %v\n", c.Name, i.Error()))
}
} }
return &Error{Msg: errs.String()} return &Error{Msg: errs.String()}
} }
@ -896,7 +1038,10 @@ func RunChecks(checks []Checker, ww io.Writer) error {
} }
// TryStartKubelet attempts to bring up kubelet service // TryStartKubelet attempts to bring up kubelet service
func TryStartKubelet() { func TryStartKubelet(ignoreChecksErrors sets.String) {
if setHasItemOrAll(ignoreChecksErrors, "StartKubelet") {
return
}
// If we notice that the kubelet service is inactive, try to start it // If we notice that the kubelet service is inactive, try to start it
initSystem, err := initsystem.GetInitSystem() initSystem, err := initsystem.GetInitSystem()
if err != nil { if err != nil {
@ -910,3 +1055,11 @@ func TryStartKubelet() {
} }
} }
} }
// setHasItemOrAll is helper function that return true if item is present in the set (case insensitive) or special key 'all' is present
func setHasItemOrAll(s sets.String, item string) bool {
if s.Has("all") || s.Has(strings.ToLower(item)) {
return true
}
return false
}

View File

@ -29,6 +29,7 @@ import (
"net/http" "net/http"
"os" "os"
"k8s.io/apimachinery/pkg/util/sets"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/utils/exec" "k8s.io/utils/exec"
) )
@ -166,6 +167,10 @@ type preflightCheckTest struct {
msg string msg string
} }
func (pfct preflightCheckTest) Name() string {
return "preflightCheckTest"
}
func (pfct preflightCheckTest) Check() (warning, errors []error) { func (pfct preflightCheckTest) Check() (warning, errors []error) {
if pfct.msg == "warning" { if pfct.msg == "warning" {
return []error{fmt.Errorf("warning")}, nil return []error{fmt.Errorf("warning")}, nil
@ -218,7 +223,7 @@ func TestRunInitMasterChecks(t *testing.T) {
} }
for _, rt := range tests { for _, rt := range tests {
actual := RunInitMasterChecks(exec.New(), rt.cfg, "") actual := RunInitMasterChecks(exec.New(), rt.cfg, "", sets.NewString())
if (actual == nil) != rt.expected { if (actual == nil) != rt.expected {
t.Errorf( t.Errorf(
"failed RunInitMasterChecks:\n\texpected: %t\n\t actual: %t\n\t error: %v", "failed RunInitMasterChecks:\n\texpected: %t\n\t actual: %t\n\t error: %v",
@ -254,7 +259,7 @@ func TestRunJoinNodeChecks(t *testing.T) {
} }
for _, rt := range tests { for _, rt := range tests {
actual := RunJoinNodeChecks(exec.New(), rt.cfg, "") actual := RunJoinNodeChecks(exec.New(), rt.cfg, "", sets.NewString())
if (actual == nil) != rt.expected { if (actual == nil) != rt.expected {
t.Errorf( t.Errorf(
"failed RunJoinNodeChecks:\n\texpected: %t\n\t actual: %t", "failed RunJoinNodeChecks:\n\texpected: %t\n\t actual: %t",
@ -272,7 +277,7 @@ func TestRunChecks(t *testing.T) {
output string output string
}{ }{
{[]Checker{}, true, ""}, {[]Checker{}, true, ""},
{[]Checker{preflightCheckTest{"warning"}}, true, "[preflight] WARNING: warning\n"}, // should just print warning {[]Checker{preflightCheckTest{"warning"}}, true, "\t[WARNING preflightCheckTest]: warning\n"}, // should just print warning
{[]Checker{preflightCheckTest{"error"}}, false, ""}, {[]Checker{preflightCheckTest{"error"}}, false, ""},
{[]Checker{preflightCheckTest{"test"}}, false, ""}, {[]Checker{preflightCheckTest{"test"}}, false, ""},
{[]Checker{DirAvailableCheck{Path: "/does/not/exist"}}, true, ""}, {[]Checker{DirAvailableCheck{Path: "/does/not/exist"}}, true, ""},
@ -281,7 +286,7 @@ func TestRunChecks(t *testing.T) {
{[]Checker{FileContentCheck{Path: "/does/not/exist"}}, false, ""}, {[]Checker{FileContentCheck{Path: "/does/not/exist"}}, false, ""},
{[]Checker{FileContentCheck{Path: "/"}}, true, ""}, {[]Checker{FileContentCheck{Path: "/"}}, true, ""},
{[]Checker{FileContentCheck{Path: "/", Content: []byte("does not exist")}}, false, ""}, {[]Checker{FileContentCheck{Path: "/", Content: []byte("does not exist")}}, false, ""},
{[]Checker{InPathCheck{executable: "foobarbaz", exec: exec.New()}}, true, "[preflight] WARNING: foobarbaz not found in system path\n"}, {[]Checker{InPathCheck{executable: "foobarbaz", exec: exec.New()}}, true, "\t[WARNING FileExisting-foobarbaz]: foobarbaz not found in system path\n"},
{[]Checker{InPathCheck{executable: "foobarbaz", mandatory: true, exec: exec.New()}}, false, ""}, {[]Checker{InPathCheck{executable: "foobarbaz", mandatory: true, exec: exec.New()}}, false, ""},
{[]Checker{ExtraArgsCheck{ {[]Checker{ExtraArgsCheck{
APIServerExtraArgs: map[string]string{"secure-port": "1234"}, APIServerExtraArgs: map[string]string{"secure-port": "1234"},
@ -290,14 +295,14 @@ func TestRunChecks(t *testing.T) {
}}, true, ""}, }}, true, ""},
{[]Checker{ExtraArgsCheck{ {[]Checker{ExtraArgsCheck{
APIServerExtraArgs: map[string]string{"secure-port": "foo"}, APIServerExtraArgs: map[string]string{"secure-port": "foo"},
}}, true, "[preflight] WARNING: kube-apiserver: failed to parse extra argument --secure-port=foo\n"}, }}, true, "\t[WARNING ExtraArgs]: kube-apiserver: failed to parse extra argument --secure-port=foo\n"},
{[]Checker{ExtraArgsCheck{ {[]Checker{ExtraArgsCheck{
APIServerExtraArgs: map[string]string{"invalid-argument": "foo"}, APIServerExtraArgs: map[string]string{"invalid-argument": "foo"},
}}, true, "[preflight] WARNING: kube-apiserver: failed to parse extra argument --invalid-argument=foo\n"}, }}, true, "\t[WARNING ExtraArgs]: kube-apiserver: failed to parse extra argument --invalid-argument=foo\n"},
} }
for _, rt := range tokenTest { for _, rt := range tokenTest {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
actual := RunChecks(rt.p, buf) actual := RunChecks(rt.p, buf, sets.NewString())
if (actual == nil) != rt.expected { if (actual == nil) != rt.expected {
t.Errorf( t.Errorf(
"failed RunChecks:\n\texpected: %t\n\t actual: %t", "failed RunChecks:\n\texpected: %t\n\t actual: %t",