From 71494aa9ce0fd1d72e55d8637e1e2dda03c92d19 Mon Sep 17 00:00:00 2001 From: dmaiocchi Date: Wed, 30 Jan 2019 16:23:13 +0100 Subject: [PATCH] add preflight-phase --- cmd/kubeadm/app/cmd/join.go | 69 +------------ cmd/kubeadm/app/cmd/phases/preflight.go | 124 +++++++++++++++++++++++- 2 files changed, 124 insertions(+), 69 deletions(-) diff --git a/cmd/kubeadm/app/cmd/join.go b/cmd/kubeadm/app/cmd/join.go index 313a4a979e..1caed0c0b0 100644 --- a/cmd/kubeadm/app/cmd/join.go +++ b/cmd/kubeadm/app/cmd/join.go @@ -17,7 +17,6 @@ limitations under the License. package cmd import ( - "bytes" "fmt" "io" "os" @@ -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" "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" @@ -68,17 +68,6 @@ var ( `) - notReadyToJoinControPlaneTemp = template.Must(template.New("join").Parse(dedent.Dedent(` - One or more conditions for hosting a new control plane instance is not satisfied. - - {{.Error}} - - Please ensure that: - * The cluster has a stable controlPlaneEndpoint address. - * The certificates that must be shared among control plane instances are provided. - - `))) - joinControPlaneDoneTemp = template.Must(template.New("join").Parse(dedent.Dedent(` This node has joined the cluster and a new control plane instance was created: @@ -210,9 +199,7 @@ func NewCmdJoin(out io.Writer, joinOptions *joinOptions) *cobra.Command { AddJoinConfigFlags(cmd.Flags(), joinOptions.externalcfg) AddJoinOtherFlags(cmd.Flags(), &joinOptions.cfgPath, &joinOptions.ignorePreflightErrors, &joinOptions.controlPlane, &joinOptions.token) - // initialize the workflow runner with the list of phases - // TODO: append phases here like so: - // joinRunner.AppendPhase(phases.NewPreflightMasterPhase()) + joinRunner.AppendPhase(phases.NewPreflightJoinPhase()) // sets the data builder function, that will be used by the runner // both when running the entire workflow or single phases @@ -445,14 +432,6 @@ func (j *joinData) OutputWriter() io.Writer { // Run executes worker node provisioning and tries to join an existing cluster. func (j *joinData) Run() error { - fmt.Println("[preflight] Running pre-flight checks") - - // Start with general checks - klog.V(1).Infoln("[preflight] Running general checks") - if err := preflight.RunJoinNodeChecks(utilsexec.New(), j.cfg, j.ignorePreflightErrors); err != nil { - return err - } - // Fetch the init configuration based on the join configuration. // TODO: individual phases should call these: // - phases that need initCfg should call joinData.InitCfg(). @@ -465,35 +444,7 @@ func (j *joinData) Run() error { if err != nil { return err } - - // Continue with more specific checks based on the init configuration - klog.V(1).Infoln("[preflight] Running configuration dependant checks") - if err := preflight.RunOptionalJoinNodeChecks(utilsexec.New(), &initCfg.ClusterConfiguration, j.ignorePreflightErrors); err != nil { - return err - } - if j.cfg.ControlPlane != nil { - // Checks if the cluster configuration supports - // joining a new control plane instance and if all the necessary certificates are provided - if err := j.CheckIfReadyForAdditionalControlPlane(&initCfg.ClusterConfiguration); err != nil { - // outputs the not ready for hosting a new control plane instance message - ctx := map[string]string{ - "Error": err.Error(), - } - - var msg bytes.Buffer - notReadyToJoinControPlaneTemp.Execute(&msg, ctx) - return errors.New(msg.String()) - } - - // run kubeadm init preflight checks for checking all the prequisites - fmt.Println("[join] Running pre-flight checks before initializing the new control plane instance") - preflight.RunInitMasterChecks(utilsexec.New(), initCfg, j.ignorePreflightErrors) - - fmt.Println("[join] Pulling control-plane images") - if err := preflight.RunPullImagesCheck(utilsexec.New(), initCfg, j.ignorePreflightErrors); err != nil { - return err - } // Prepares the node for hosting a new control plane instance by writing necessary // kubeconfig files, and static pod manifests if err := j.PrepareForHostingControlPlane(initCfg); err != nil { @@ -539,22 +490,6 @@ func (j *joinData) Run() error { return nil } -// CheckIfReadyForAdditionalControlPlane ensures that the cluster is in a state that supports -// joining an additional control plane instance and if the node is ready to join -func (j *joinData) CheckIfReadyForAdditionalControlPlane(cfg *kubeadmapi.ClusterConfiguration) error { - // blocks if the cluster was created without a stable control plane endpoint - if cfg.ControlPlaneEndpoint == "" { - return errors.New("unable to add a new control plane instance a cluster that doesn't have a stable controlPlaneEndpoint address") - } - - // checks if the certificates that must be equal across contolplane instances are provided - if ret, err := certsphase.SharedCertificateExists(cfg); !ret { - return err - } - - return nil -} - // PrepareForHostingControlPlane makes all preparation activities require for a node hosting a new control plane instance func (j *joinData) PrepareForHostingControlPlane(initConfiguration *kubeadmapi.InitConfiguration) error { diff --git a/cmd/kubeadm/app/cmd/phases/preflight.go b/cmd/kubeadm/app/cmd/phases/preflight.go index 8884a9fe86..35c9e63e24 100644 --- a/cmd/kubeadm/app/cmd/phases/preflight.go +++ b/cmd/kubeadm/app/cmd/phases/preflight.go @@ -17,23 +17,43 @@ limitations under the License. package phases import ( + "bytes" "fmt" + "text/template" + "github.com/lithammer/dedent" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/klog" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/options" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow" + "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs" "k8s.io/kubernetes/cmd/kubeadm/app/preflight" "k8s.io/kubernetes/pkg/util/normalizer" utilsexec "k8s.io/utils/exec" ) var ( - masterPreflightExample = normalizer.Examples(` + initPreflightExample = normalizer.Examples(` # Run master pre-flight checks using a config file. kubeadm init phase preflight --config kubeadm-config.yml `) + joinPreflightExample = normalizer.Examples(` + # Run join pre-flight checks using a config file. + kubeadm join phase preflight --config kubeadm-config.yml + `) + + notReadyToJoinControPlaneTemp = template.Must(template.New("join").Parse(dedent.Dedent(` + One or more conditions for hosting a new control plane instance is not satisfied. + + {{.Error}} + + Please ensure that: + * The cluster has a stable controlPlaneEndpoint address. + * The certificates that must be shared among control plane instances are provided. + + `))) ) // preflightMasterData defines the behavior that a runtime data struct passed to the PreflightMaster master phase @@ -45,13 +65,19 @@ type preflightMasterData interface { IgnorePreflightErrors() sets.String } +type preflightJoinData interface { + Cfg() *kubeadmapi.JoinConfiguration + InitCfg() (*kubeadmapi.InitConfiguration, error) + 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, + Example: initPreflightExample, Run: runPreflightMaster, InheritFlags: []string{ options.CfgPath, @@ -60,6 +86,7 @@ func NewPreflightMasterPhase() workflow.Phase { } } +// TODO(dmaiocchi): rename all instances of master to controlPlane in this file. // runPreflightMaster executes preflight checks logic. func runPreflightMaster(c workflow.RunData) error { data, ok := c.(preflightMasterData) @@ -85,3 +112,96 @@ func runPreflightMaster(c workflow.RunData) error { return nil } + +// NewPreflightJoinPhase creates a kubeadm workflow phase that implements preflight checks for a new node join +func NewPreflightJoinPhase() workflow.Phase { + return workflow.Phase{ + Name: "preflight", + Short: "Run join pre-flight checks", + Long: "Run join pre-flight checks, functionally equivalent to what is implemented by kubeadm join.", + Example: joinPreflightExample, + Run: runPreflightJoin, + InheritFlags: []string{ + options.CfgPath, + options.IgnorePreflightErrors, + options.TLSBootstrapToken, + options.TokenStr, + options.ControlPlane, + options.APIServerAdvertiseAddress, + options.APIServerBindPort, + options.NodeCRISocket, + options.NodeName, + options.FileDiscovery, + options.TokenDiscovery, + options.TokenDiscoveryCAHash, + options.TokenDiscoverySkipCAHash, + }, + } +} + +// runPreflightJoin executes preflight checks logic. +func runPreflightJoin(c workflow.RunData) error { + j, ok := c.(preflightJoinData) + if !ok { + return errors.New("preflight phase invoked with an invalid data struct") + } + // Start with general checks + klog.V(1).Infoln("[preflight] Running general checks") + if err := preflight.RunJoinNodeChecks(utilsexec.New(), j.Cfg(), j.IgnorePreflightErrors()); err != nil { + return err + } + + initCfg, err := j.InitCfg() + if err != nil { + return err + } + + // Continue with more specific checks based on the init configuration + klog.V(1).Infoln("[preflight] Running configuration dependant checks") + if err := preflight.RunOptionalJoinNodeChecks(utilsexec.New(), &initCfg.ClusterConfiguration, j.IgnorePreflightErrors()); err != nil { + return err + } + + if j.Cfg().ControlPlane != nil { + // Checks if the cluster configuration supports + // joining a new control plane instance and if all the necessary certificates are provided + if err := checkIfReadyForAdditionalControlPlane(&initCfg.ClusterConfiguration); err != nil { + // outputs the not ready for hosting a new control plane instance message + ctx := map[string]string{ + "Error": err.Error(), + } + + var msg bytes.Buffer + notReadyToJoinControPlaneTemp.Execute(&msg, ctx) + return errors.New(msg.String()) + } + + // run kubeadm init preflight checks for checking all the prequisites + fmt.Println("[preflight] Running pre-flight checks before initializing the new control plane instance") + if err := preflight.RunInitMasterChecks(utilsexec.New(), initCfg, j.IgnorePreflightErrors()); err != nil { + return err + } + + fmt.Println("[preflight] Pulling control-plane images") + if err := preflight.RunPullImagesCheck(utilsexec.New(), initCfg, j.IgnorePreflightErrors()); err != nil { + return err + } + } + return nil +} + +// checkIfReadyForAdditionalControlPlane ensures that the cluster is in a state that supports +// joining an additional control plane instance and if the node is ready to preflight +func checkIfReadyForAdditionalControlPlane(initConfiguration *kubeadmapi.ClusterConfiguration) error { + // blocks if the cluster was created without a stable control plane endpoint + if initConfiguration.ControlPlaneEndpoint == "" { + return errors.New("unable to add a new control plane instance a cluster that doesn't have a stable controlPlaneEndpoint address") + } + + // checks if the certificates that must be equal across contolplane instances are provided + if ret, err := certs.SharedCertificateExists(initConfiguration); !ret { + return err + } + + return nil +}