Merge pull request #69622 from fabriziopandini/kubeadm-add-phase-runner

kubeadm refactor cmd init
This commit is contained in:
k8s-ci-robot 2018-10-16 13:01:42 -07:00 committed by GitHub
commit d169696b2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 114 additions and 60 deletions

View File

@ -27,6 +27,7 @@ go_library(
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
"//cmd/kubeadm/app/cmd/options:go_default_library", "//cmd/kubeadm/app/cmd/options:go_default_library",
"//cmd/kubeadm/app/cmd/phases:go_default_library", "//cmd/kubeadm/app/cmd/phases:go_default_library",
"//cmd/kubeadm/app/cmd/phases/workflow:go_default_library",
"//cmd/kubeadm/app/cmd/upgrade:go_default_library", "//cmd/kubeadm/app/cmd/upgrade:go_default_library",
"//cmd/kubeadm/app/cmd/util:go_default_library", "//cmd/kubeadm/app/cmd/util:go_default_library",
"//cmd/kubeadm/app/componentconfigs:go_default_library", "//cmd/kubeadm/app/componentconfigs:go_default_library",

View File

@ -30,7 +30,6 @@ import (
"github.com/renstrom/dedent" "github.com/renstrom/dedent"
"github.com/spf13/cobra" "github.com/spf13/cobra"
flag "github.com/spf13/pflag" flag "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/sets" "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"
@ -38,6 +37,7 @@ import (
kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1" kubeadmapiv1beta1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features" "k8s.io/kubernetes/cmd/kubeadm/app/features"
@ -106,48 +106,71 @@ var (
`))) `)))
) )
// initOptions defines all the init options exposed via flags by kubeadm init.
// Please note that this structure includes the public kubeadm config API, but only a subset of the options
// supported by this api will be exposed as a flag.
type initOptions struct {
cfgPath string
skipTokenPrint bool
dryRun bool
featureGatesString string
ignorePreflightErrors []string
bto *options.BootstrapTokenOptions
externalcfg *kubeadmapiv1beta1.InitConfiguration
}
// initData defines all the runtime information used when running the kubeadm init worklow;
// this data is shared across all the phases that are included in the workflow.
type initData struct {
cfg *kubeadmapi.InitConfiguration
skipTokenPrint bool
dryRun bool
ignorePreflightErrors sets.String
}
// NewCmdInit returns "kubeadm init" command. // NewCmdInit returns "kubeadm init" command.
func NewCmdInit(out io.Writer) *cobra.Command { func NewCmdInit(out io.Writer) *cobra.Command {
externalcfg := &kubeadmapiv1beta1.InitConfiguration{} options := newInitOptions()
kubeadmscheme.Scheme.Default(externalcfg) initRunner := workflow.NewRunner()
var cfgPath string
var skipTokenPrint bool
var dryRun bool
var featureGatesString string
var ignorePreflightErrors []string
// Create the options object for the bootstrap token-related flags, and override the default value for .Description
bto := options.NewBootstrapTokenOptions()
bto.Description = "The default bootstrap token generated by 'kubeadm init'."
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "init", Use: "init",
Short: "Run this command in order to set up the Kubernetes master.", Short: "Run this command in order to set up the Kubernetes master.",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var err error c, err := initRunner.InitData()
if externalcfg.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, featureGatesString); err != nil {
kubeadmutil.CheckErr(err)
}
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(ignorePreflightErrors)
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
err = validation.ValidateMixedArguments(cmd.Flags()) data := c.(initData)
fmt.Printf("[init] using Kubernetes version: %s\n", data.cfg.KubernetesVersion)
err = initRunner.Run()
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
err = bto.ApplyTo(externalcfg) // TODO: the code in runInit should be progressively converted in phases; each phase will be exposed
// via the subcommands automatically created by initRunner.BindToCommand
err = runInit(&data, out)
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
i, err := NewInit(cfgPath, externalcfg, ignorePreflightErrorsSet, skipTokenPrint, dryRun)
kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(i.Run(out))
}, },
} }
AddInitConfigFlags(cmd.PersistentFlags(), externalcfg, &featureGatesString) // adds command flags
AddInitOtherFlags(cmd.PersistentFlags(), &cfgPath, &skipTokenPrint, &dryRun, &ignorePreflightErrors) AddInitConfigFlags(cmd.PersistentFlags(), options.externalcfg, &options.featureGatesString)
bto.AddTokenFlag(cmd.PersistentFlags()) AddInitOtherFlags(cmd.PersistentFlags(), &options.cfgPath, &options.skipTokenPrint, &options.dryRun, &options.ignorePreflightErrors)
bto.AddTTLFlag(cmd.PersistentFlags()) options.bto.AddTokenFlag(cmd.PersistentFlags())
options.bto.AddTTLFlag(cmd.PersistentFlags())
// initialize the workflow runner with the list of phases
// TODO: add the phases to the runner. e.g. initRunner.AppendPhase(phases.PreflightMaster)
// sets the data builder function, that will be used by the runner
// both when running the entire workflow or single phases
initRunner.SetDataInitializer(func() (workflow.RunData, error) {
return newInitData(cmd, options)
})
// binds the Runner to kubeadm init command by altering
// command help, adding --skip-phases flag and by adding phases subcommands
initRunner.BindToCommand(cmd)
return cmd return cmd
} }
@ -220,56 +243,86 @@ func AddInitOtherFlags(flagSet *flag.FlagSet, cfgPath *string, skipTokenPrint, d
) )
} }
// NewInit validates given arguments and instantiates Init struct with provided information. // newInitOptions returns a struct ready for being used for creating cmd init flags.
func NewInit(cfgPath string, externalcfg *kubeadmapiv1beta1.InitConfiguration, ignorePreflightErrors sets.String, skipTokenPrint, dryRun bool) (*Init, error) { func newInitOptions() *initOptions {
// initialize the public kubeadm config API by appling defaults
externalcfg := &kubeadmapiv1beta1.InitConfiguration{}
kubeadmscheme.Scheme.Default(externalcfg)
// Either use the config file if specified, or convert the defaults in the external to an internal cfg representation // Create the options object for the bootstrap token-related flags, and override the default value for .Description
cfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, externalcfg) bto := options.NewBootstrapTokenOptions()
bto.Description = "The default bootstrap token generated by 'kubeadm init'."
return &initOptions{
externalcfg: externalcfg,
bto: bto,
}
}
// newInitData returns a new initData struct to be used for the execution of the kubeadm init workflow.
// This func takes care of validating initOptions passed to the command, and then it converts
// options into the internal InitConfiguration type that is used as input all the phases in the kubeadm init workflow
func newInitData(cmd *cobra.Command, options *initOptions) (initData, error) {
// Re-apply defaults to the public kubeadm API (this will set only values not exposed/not set as a flags)
kubeadmscheme.Scheme.Default(options.externalcfg)
// Validate standalone flags values and/or combination of flags and then assigns
// validated values to the public kubeadm config API when applicable
var err error
if options.externalcfg.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, options.featureGatesString); err != nil {
return initData{}, err
}
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(options.ignorePreflightErrors)
kubeadmutil.CheckErr(err)
if err = validation.ValidateMixedArguments(cmd.Flags()); err != nil {
return initData{}, err
}
if err = options.bto.ApplyTo(options.externalcfg); err != nil {
return initData{}, err
}
// Either use the config file if specified, or convert public kubeadm API to the internal InitConfiguration
// and validates InitConfiguration
cfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(options.cfgPath, options.externalcfg)
if err != nil { if err != nil {
return nil, err return initData{}, err
} }
if err := configutil.VerifyAPIServerBindAddress(cfg.APIEndpoint.AdvertiseAddress); err != nil { if err := configutil.VerifyAPIServerBindAddress(cfg.APIEndpoint.AdvertiseAddress); err != nil {
return nil, err return initData{}, err
} }
glog.V(1).Infof("[init] validating feature gates")
if err := features.ValidateVersion(features.InitFeatureGates, cfg.FeatureGates, cfg.KubernetesVersion); err != nil { if err := features.ValidateVersion(features.InitFeatureGates, cfg.FeatureGates, cfg.KubernetesVersion); err != nil {
return nil, err return initData{}, err
} }
fmt.Printf("[init] using Kubernetes version: %s\n", cfg.KubernetesVersion) return initData{
cfg: cfg,
skipTokenPrint: options.skipTokenPrint,
dryRun: options.dryRun,
ignorePreflightErrors: ignorePreflightErrorsSet,
}, nil
}
// runInit executes master node provisioning
func runInit(i *initData, out io.Writer) error {
fmt.Println("[preflight] running pre-flight checks") fmt.Println("[preflight] running pre-flight checks")
if err := preflight.RunInitMasterChecks(utilsexec.New(), i.cfg, i.ignorePreflightErrors); err != nil {
if err := preflight.RunInitMasterChecks(utilsexec.New(), cfg, ignorePreflightErrors); err != nil { return err
return nil, err
} }
if !dryRun { if !i.dryRun {
fmt.Println("[preflight/images] Pulling images required for setting up a Kubernetes cluster") fmt.Println("[preflight/images] Pulling images required for setting up a Kubernetes cluster")
fmt.Println("[preflight/images] This might take a minute or two, depending on the speed of your internet connection") fmt.Println("[preflight/images] This might take a minute or two, depending on the speed of your internet connection")
fmt.Println("[preflight/images] You can also perform this action in beforehand using 'kubeadm config images pull'") fmt.Println("[preflight/images] You can also perform this action in beforehand using 'kubeadm config images pull'")
if err := preflight.RunPullImagesCheck(utilsexec.New(), cfg, ignorePreflightErrors); err != nil { if err := preflight.RunPullImagesCheck(utilsexec.New(), i.cfg, i.ignorePreflightErrors); err != nil {
return nil, err return err
} }
} else { } else {
fmt.Println("[preflight/images] Would pull the required images (like 'kubeadm config images pull')") fmt.Println("[preflight/images] Would pull the required images (like 'kubeadm config images pull')")
} }
return &Init{cfg: cfg, skipTokenPrint: skipTokenPrint, dryRun: dryRun, ignorePreflightErrors: ignorePreflightErrors}, nil
}
// Init defines struct used by "kubeadm init" command
type Init struct {
cfg *kubeadmapi.InitConfiguration
skipTokenPrint bool
dryRun bool
ignorePreflightErrors sets.String
}
// Run executes master node provisioning, including certificates, needed static pod manifests, etc.
func (i *Init) Run(out io.Writer) error {
// Get directories to write files to; can be faked if we're dry-running // Get directories to write files to; can be faked if we're dry-running
glog.V(1).Infof("[init] Getting certificates directory from configuration") glog.V(1).Infof("[init] Getting certificates directory from configuration")
realCertsDir := i.cfg.CertificatesDir realCertsDir := i.cfg.CertificatesDir
@ -579,8 +632,8 @@ func printFilesIfDryRunning(dryRun bool, manifestDir string) error {
} }
// getWaiter gets the right waiter implementation for the right occasion // getWaiter gets the right waiter implementation for the right occasion
func getWaiter(i *Init, client clientset.Interface) apiclient.Waiter { func getWaiter(ctx *initData, client clientset.Interface) apiclient.Waiter {
if i.dryRun { if ctx.dryRun {
return dryrunutil.NewWaiter() return dryrunutil.NewWaiter()
} }