diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index aa9047fa605..286a64533ce 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -37,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" "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" @@ -126,6 +127,9 @@ type initData struct { skipTokenPrint bool dryRun bool ignorePreflightErrors sets.String + certificatesDir string + dryRunDir string + client clientset.Interface } // NewCmdInit returns "kubeadm init" command. @@ -160,7 +164,8 @@ func NewCmdInit(out io.Writer) *cobra.Command { 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) + initRunner.AppendPhase(phases.NewPreflightMasterPhase()) + // TODO: add other phases to the runner. // sets the data builder function, that will be used by the runner // both when running the entire workflow or single phases @@ -297,31 +302,113 @@ func newInitData(cmd *cobra.Command, options *initOptions) (initData, error) { return initData{}, err } + // if dry running creates a temporary folder for saving kubeadm generated files + dryRunDir := "" + if options.dryRun { + if dryRunDir, err = ioutil.TempDir("", "kubeadm-init-dryrun"); err != nil { + return initData{}, fmt.Errorf("couldn't create a temporary directory: %v", err) + } + } + return initData{ cfg: cfg, + certificatesDir: cfg.CertificatesDir, skipTokenPrint: options.skipTokenPrint, dryRun: options.dryRun, + dryRunDir: dryRunDir, ignorePreflightErrors: ignorePreflightErrorsSet, }, nil } +// Cfg returns initConfiguration. +func (d initData) Cfg() *kubeadmapi.InitConfiguration { + return d.cfg +} + +// DryRun returns the DryRun flag. +func (d initData) DryRun() bool { + return d.dryRun +} + +// SkipTokenPrint returns the SkipTokenPrint flag. +func (d initData) SkipTokenPrint() bool { + return d.skipTokenPrint +} + +// IgnorePreflightErrors returns the IgnorePreflightErrors flag. +func (d initData) IgnorePreflightErrors() sets.String { + return d.ignorePreflightErrors +} + +// CertificateWriteDir returns the path to the certificate folder or the temporary folder path in case of DryRun. +func (d initData) CertificateWriteDir() string { + if d.dryRun { + return d.dryRunDir + } + return d.certificatesDir +} + +// CertificateDir returns the CertificateDir as originally specified by the user. +func (d initData) CertificateDir() string { + return d.certificatesDir +} + +// KubeConfigDir returns the path of the kubernetes configuration folder or the temporary folder path in case of DryRun. +func (d initData) KubeConfigDir() string { + if d.dryRun { + return d.dryRunDir + } + return kubeadmconstants.KubernetesDir +} + +// KubeConfigDir returns the path where manifest should be stored or the temporary folder path in case of DryRun. +func (d initData) ManifestDir() string { + if d.dryRun { + return d.dryRunDir + } + return kubeadmconstants.GetStaticPodDirectory() +} + +// KubeletDir returns path of the kubelet configuration folder or the temporary folder in case of DryRun. +func (d initData) KubeletDir() string { + if d.dryRun { + return d.dryRunDir + } + return kubeadmconstants.KubeletRunDirectory +} + +// Client returns a Kubernetes client to be used by kubeadm. +// This function is implemented as a singleton, thus avoiding to recreate the client when it is used by different phases. +// Important. This function must be called after the admin.conf kubeconfig file is created. +func (d initData) Client() (clientset.Interface, error) { + if d.client == nil { + if d.dryRun { + // If we're dry-running; we should create a faked client that answers some GETs in order to be able to do the full init flow and just logs the rest of requests + dryRunGetter := apiclient.NewInitDryRunGetter(d.cfg.NodeRegistration.Name, d.cfg.Networking.ServiceSubnet) + d.client = apiclient.NewDryRunClient(dryRunGetter, os.Stdout) + } else { + // If we're acting for real, we should create a connection to the API server and wait for it to come up + var err error + d.client, err = kubeconfigutil.ClientSetFromFile(kubeadmconstants.GetAdminKubeConfigPath()) + if err != nil { + return nil, err + } + } + } + return d.client, nil +} + +// Tokens returns an array of token strings. +func (d initData) Tokens() []string { + tokens := []string{} + for _, bt := range d.cfg.BootstrapTokens { + tokens = append(tokens, bt.Token.String()) + } + return tokens +} + // 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(), i.cfg, i.ignorePreflightErrors); err != nil { - return err - } - - 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(), i.cfg, i.ignorePreflightErrors); err != nil { - return err - } - } else { - fmt.Println("[preflight/images] Would pull the required images (like 'kubeadm config images pull')") - } // Get directories to write files to; can be faked if we're dry-running glog.V(1).Infof("[init] Getting certificates directory from configuration") diff --git a/cmd/kubeadm/app/cmd/phases/preflight.go b/cmd/kubeadm/app/cmd/phases/preflight.go index 662499b8e33..ac55b9a3796 100644 --- a/cmd/kubeadm/app/cmd/phases/preflight.go +++ b/cmd/kubeadm/app/cmd/phases/preflight.go @@ -21,11 +21,13 @@ import ( "fmt" "github.com/spf13/cobra" - + "k8s.io/apimachinery/pkg/util/sets" + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" 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" "k8s.io/kubernetes/cmd/kubeadm/app/preflight" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" @@ -35,13 +37,9 @@ import ( ) var ( - masterPreflightLongDesc = normalizer.LongDesc(` - Run master pre-flight checks, functionally equivalent to what implemented by kubeadm init. - ` + cmdutil.AlphaDisclaimer) - masterPreflightExample = normalizer.Examples(` - # Run master pre-flight checks. - kubeadm alpha phase preflight master + # Run master pre-flight checks using a config file. + kubeadm init phase preflight --config kubeadm-config.yml `) nodePreflightLongDesc = normalizer.LongDesc(` @@ -56,6 +54,52 @@ var ( errorMissingConfigFlag = errors.New("the --config flag is mandatory") ) +// preflightMasterData defines the behavior that a runtime data struct passed to the PreflightMaster master phase +// should have. Please note that we are using an interface in order to make this phase reusable in different workflows +// (and thus with different runtime data struct, all of them requested to be compliant to this interface) +type preflightMasterData interface { + Cfg() *kubeadmapi.InitConfiguration + DryRun() bool + IgnorePreflightErrors() sets.String +} + +// NewPreflightMasterPhase creates a kubeadm workflow phase that implements preflight checks for a new master node. +func NewPreflightMasterPhase() workflow.Phase { + return workflow.Phase{ + Name: "preflight", + Short: "Run master pre-flight checks", + Long: "Run master pre-flight checks, functionally equivalent to what implemented by kubeadm init.", + Example: masterPreflightExample, + Run: runPreflightMaster, + } +} + +// runPreflightMaster executes preflight checks logic. +func runPreflightMaster(c workflow.RunData) error { + data, ok := c.(preflightMasterData) + if !ok { + return fmt.Errorf("preflight phase invoked with an invalid data struct") + } + + fmt.Println("[preflight] running pre-flight checks") + if err := preflight.RunInitMasterChecks(utilsexec.New(), data.Cfg(), data.IgnorePreflightErrors()); err != nil { + return nil + } + + if !data.DryRun() { + fmt.Println("[preflight] Pulling images required for setting up a Kubernetes cluster") + fmt.Println("[preflight] This might take a minute or two, depending on the speed of your internet connection") + fmt.Println("[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'") + if err := preflight.RunPullImagesCheck(utilsexec.New(), data.Cfg(), data.IgnorePreflightErrors()); err != nil { + return err + } + } else { + fmt.Println("[preflight] Would pull the required images (like 'kubeadm config images pull')") + } + + return nil +} + // NewCmdPreFlight calls cobra.Command for preflight checks func NewCmdPreFlight() *cobra.Command { var cfgPath string @@ -70,47 +114,11 @@ func NewCmdPreFlight() *cobra.Command { options.AddConfigFlag(cmd.PersistentFlags(), &cfgPath) options.AddIgnorePreflightErrorsFlag(cmd.PersistentFlags(), &ignorePreflightErrors) - cmd.AddCommand(NewCmdPreFlightMaster(&cfgPath, &ignorePreflightErrors)) cmd.AddCommand(NewCmdPreFlightNode(&cfgPath, &ignorePreflightErrors)) return cmd } -// NewCmdPreFlightMaster calls cobra.Command for master preflight checks -func NewCmdPreFlightMaster(cfgPath *string, ignorePreflightErrors *[]string) *cobra.Command { - - cmd := &cobra.Command{ - Use: "master", - Short: "Run master pre-flight checks", - Long: masterPreflightLongDesc, - Example: masterPreflightExample, - Run: func(cmd *cobra.Command, args []string) { - if len(*cfgPath) == 0 { - kubeadmutil.CheckErr(errorMissingConfigFlag) - } - ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(*ignorePreflightErrors) - kubeadmutil.CheckErr(err) - - cfg := &kubeadmapiv1beta1.InitConfiguration{} - kubeadmscheme.Scheme.Default(cfg) - - internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(*cfgPath, cfg) - kubeadmutil.CheckErr(err) - err = configutil.VerifyAPIServerBindAddress(internalcfg.APIEndpoint.AdvertiseAddress) - kubeadmutil.CheckErr(err) - - fmt.Println("[preflight] running pre-flight checks") - - err = preflight.RunInitMasterChecks(utilsexec.New(), internalcfg, ignorePreflightErrorsSet) - kubeadmutil.CheckErr(err) - - fmt.Println("[preflight] pre-flight checks passed") - }, - } - - return cmd -} - // NewCmdPreFlightNode calls cobra.Command for node preflight checks func NewCmdPreFlightNode(cfgPath *string, ignorePreflightErrors *[]string) *cobra.Command { cmd := &cobra.Command{