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/cmd/options: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/util:go_default_library",
"//cmd/kubeadm/app/componentconfigs:go_default_library",

View File

@ -30,7 +30,6 @@ import (
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
@ -38,6 +37,7 @@ import (
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/cmd/options"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"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.
func NewCmdInit(out io.Writer) *cobra.Command {
externalcfg := &kubeadmapiv1beta1.InitConfiguration{}
kubeadmscheme.Scheme.Default(externalcfg)
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'."
options := newInitOptions()
initRunner := workflow.NewRunner()
cmd := &cobra.Command{
Use: "init",
Short: "Run this command in order to set up the Kubernetes master.",
Run: func(cmd *cobra.Command, args []string) {
var err error
if externalcfg.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, featureGatesString); err != nil {
kubeadmutil.CheckErr(err)
}
ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(ignorePreflightErrors)
c, err := initRunner.InitData()
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)
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)
i, err := NewInit(cfgPath, externalcfg, ignorePreflightErrorsSet, skipTokenPrint, dryRun)
kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(i.Run(out))
},
}
AddInitConfigFlags(cmd.PersistentFlags(), externalcfg, &featureGatesString)
AddInitOtherFlags(cmd.PersistentFlags(), &cfgPath, &skipTokenPrint, &dryRun, &ignorePreflightErrors)
bto.AddTokenFlag(cmd.PersistentFlags())
bto.AddTTLFlag(cmd.PersistentFlags())
// adds command flags
AddInitConfigFlags(cmd.PersistentFlags(), options.externalcfg, &options.featureGatesString)
AddInitOtherFlags(cmd.PersistentFlags(), &options.cfgPath, &options.skipTokenPrint, &options.dryRun, &options.ignorePreflightErrors)
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
}
@ -220,56 +243,86 @@ func AddInitOtherFlags(flagSet *flag.FlagSet, cfgPath *string, skipTokenPrint, d
)
}
// NewInit validates given arguments and instantiates Init struct with provided information.
func NewInit(cfgPath string, externalcfg *kubeadmapiv1beta1.InitConfiguration, ignorePreflightErrors sets.String, skipTokenPrint, dryRun bool) (*Init, error) {
// newInitOptions returns a struct ready for being used for creating cmd init flags.
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
cfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, externalcfg)
// 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'."
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 {
return nil, err
return initData{}, err
}
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 {
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")
if err := preflight.RunInitMasterChecks(utilsexec.New(), cfg, ignorePreflightErrors); err != nil {
return nil, err
if err := preflight.RunInitMasterChecks(utilsexec.New(), i.cfg, i.ignorePreflightErrors); err != nil {
return err
}
if !dryRun {
if !i.dryRun {
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] You can also perform this action in beforehand using 'kubeadm config images pull'")
if err := preflight.RunPullImagesCheck(utilsexec.New(), cfg, ignorePreflightErrors); err != nil {
return nil, err
if err := preflight.RunPullImagesCheck(utilsexec.New(), i.cfg, i.ignorePreflightErrors); err != nil {
return err
}
} else {
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
glog.V(1).Infof("[init] Getting certificates directory from configuration")
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
func getWaiter(i *Init, client clientset.Interface) apiclient.Waiter {
if i.dryRun {
func getWaiter(ctx *initData, client clientset.Interface) apiclient.Waiter {
if ctx.dryRun {
return dryrunutil.NewWaiter()
}