mirror of https://github.com/k3s-io/k3s
Merge pull request #73029 from neolit123/join-phases
kubeadm: include a phase runner for `join`pull/564/head
commit
3ec18a5aed
|
@ -38,6 +38,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"
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/discovery"
|
"k8s.io/kubernetes/cmd/kubeadm/app/discovery"
|
||||||
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
||||||
|
@ -157,6 +158,7 @@ var (
|
||||||
// Please note that this structure includes the public kubeadm config API, but only a subset of the options
|
// 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.
|
// supported by this api will be exposed as a flag.
|
||||||
type joinOptions struct {
|
type joinOptions struct {
|
||||||
|
args *[]string
|
||||||
cfgPath string
|
cfgPath string
|
||||||
token string
|
token string
|
||||||
controlPlane bool
|
controlPlane bool
|
||||||
|
@ -168,6 +170,8 @@ type joinOptions struct {
|
||||||
// this data is shared across all the phases that are included in the workflow.
|
// this data is shared across all the phases that are included in the workflow.
|
||||||
type joinData struct {
|
type joinData struct {
|
||||||
cfg *kubeadmapi.JoinConfiguration
|
cfg *kubeadmapi.JoinConfiguration
|
||||||
|
initCfg *kubeadmapi.InitConfiguration
|
||||||
|
tlsBootstrapCfg *clientcmdapi.Config
|
||||||
ignorePreflightErrors sets.String
|
ignorePreflightErrors sets.String
|
||||||
outputWriter io.Writer
|
outputWriter io.Writer
|
||||||
}
|
}
|
||||||
|
@ -179,16 +183,25 @@ func NewCmdJoin(out io.Writer, joinOptions *joinOptions) *cobra.Command {
|
||||||
if joinOptions == nil {
|
if joinOptions == nil {
|
||||||
joinOptions = newJoinOptions()
|
joinOptions = newJoinOptions()
|
||||||
}
|
}
|
||||||
|
joinRunner := workflow.NewRunner()
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "join",
|
Use: "join",
|
||||||
Short: "Run this on any machine you wish to join an existing cluster",
|
Short: "Run this on any machine you wish to join an existing cluster",
|
||||||
Long: joinLongDescription,
|
Long: joinLongDescription,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
joinData, err := newJoinData(cmd, args, joinOptions, out)
|
joinOptions.args = &args
|
||||||
|
|
||||||
|
c, err := joinRunner.InitData()
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
err = joinData.Run()
|
err = joinRunner.Run()
|
||||||
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
|
// TODO: remove this once we have all phases in place.
|
||||||
|
// the method joinData.Run() itself should be removed too.
|
||||||
|
data := c.(joinData)
|
||||||
|
err = data.Run()
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -196,6 +209,20 @@ func NewCmdJoin(out io.Writer, joinOptions *joinOptions) *cobra.Command {
|
||||||
AddJoinConfigFlags(cmd.Flags(), joinOptions.externalcfg)
|
AddJoinConfigFlags(cmd.Flags(), joinOptions.externalcfg)
|
||||||
AddJoinOtherFlags(cmd.Flags(), &joinOptions.cfgPath, &joinOptions.ignorePreflightErrors, &joinOptions.controlPlane, &joinOptions.token)
|
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())
|
||||||
|
|
||||||
|
// sets the data builder function, that will be used by the runner
|
||||||
|
// both when running the entire workflow or single phases
|
||||||
|
joinRunner.SetDataInitializer(func(cmd *cobra.Command) (workflow.RunData, error) {
|
||||||
|
return newJoinData(cmd, joinOptions, out)
|
||||||
|
})
|
||||||
|
|
||||||
|
// binds the Runner to kubeadm join command by altering
|
||||||
|
// command help, adding --skip-phases flag and by adding phases subcommands
|
||||||
|
joinRunner.BindToCommand(cmd)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,7 +323,7 @@ func newJoinOptions() *joinOptions {
|
||||||
// newJoinData returns a new joinData struct to be used for the execution of the kubeadm join workflow.
|
// newJoinData returns a new joinData struct to be used for the execution of the kubeadm join workflow.
|
||||||
// This func takes care of validating joinOptions passed to the command, and then it converts
|
// This func takes care of validating joinOptions passed to the command, and then it converts
|
||||||
// options into the internal JoinConfiguration type that is used as input all the phases in the kubeadm join workflow
|
// options into the internal JoinConfiguration type that is used as input all the phases in the kubeadm join workflow
|
||||||
func newJoinData(cmd *cobra.Command, args []string, options *joinOptions, out io.Writer) (joinData, error) {
|
func newJoinData(cmd *cobra.Command, options *joinOptions, out io.Writer) (joinData, error) {
|
||||||
// Re-apply defaults to the public kubeadm API (this will set only values not exposed/not set as a flags)
|
// 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)
|
kubeadmscheme.Scheme.Default(options.externalcfg)
|
||||||
|
|
||||||
|
@ -319,13 +346,13 @@ func newJoinData(cmd *cobra.Command, args []string, options *joinOptions, out io
|
||||||
}
|
}
|
||||||
|
|
||||||
// if an APIServerEndpoint from which to retrive cluster information was not provided, unset the Discovery.BootstrapToken object
|
// if an APIServerEndpoint from which to retrive cluster information was not provided, unset the Discovery.BootstrapToken object
|
||||||
if len(args) == 0 {
|
if len(*options.args) == 0 {
|
||||||
options.externalcfg.Discovery.BootstrapToken = nil
|
options.externalcfg.Discovery.BootstrapToken = nil
|
||||||
} else {
|
} else {
|
||||||
if len(options.cfgPath) == 0 && len(args) > 1 {
|
if len(options.cfgPath) == 0 && len(*options.args) > 1 {
|
||||||
klog.Warningf("[join] WARNING: More than one API server endpoint supplied on command line %v. Using the first one.", args)
|
klog.Warningf("[join] WARNING: More than one API server endpoint supplied on command line %v. Using the first one.", *options.args)
|
||||||
}
|
}
|
||||||
options.externalcfg.Discovery.BootstrapToken.APIServerEndpoint = args[0]
|
options.externalcfg.Discovery.BootstrapToken.APIServerEndpoint = (*options.args)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// if not joining a control plane, unset the ControlPlane object
|
// if not joining a control plane, unset the ControlPlane object
|
||||||
|
@ -378,6 +405,46 @@ func newJoinData(cmd *cobra.Command, args []string, options *joinOptions, out io
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cfg returns the JoinConfiguration.
|
||||||
|
func (j *joinData) Cfg() *kubeadmapi.JoinConfiguration {
|
||||||
|
return j.cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSBootstrapCfg returns the cluster-info (kubeconfig).
|
||||||
|
func (j *joinData) TLSBootstrapCfg() (*clientcmdapi.Config, error) {
|
||||||
|
if j.tlsBootstrapCfg != nil {
|
||||||
|
return j.tlsBootstrapCfg, nil
|
||||||
|
}
|
||||||
|
klog.V(1).Infoln("[join] Discovering cluster-info")
|
||||||
|
tlsBootstrapCfg, err := discovery.For(j.cfg)
|
||||||
|
j.tlsBootstrapCfg = tlsBootstrapCfg
|
||||||
|
return tlsBootstrapCfg, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCfg returns the InitConfiguration.
|
||||||
|
func (j *joinData) InitCfg() (*kubeadmapi.InitConfiguration, error) {
|
||||||
|
if j.initCfg != nil {
|
||||||
|
return j.initCfg, nil
|
||||||
|
}
|
||||||
|
if _, err := j.TLSBootstrapCfg(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
klog.V(1).Infoln("[join] Fetching init configuration")
|
||||||
|
initCfg, err := fetchInitConfigurationFromJoinConfiguration(j.cfg, j.tlsBootstrapCfg)
|
||||||
|
j.initCfg = initCfg
|
||||||
|
return initCfg, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IgnorePreflightErrors returns the list of preflight errors to ignore.
|
||||||
|
func (j *joinData) IgnorePreflightErrors() sets.String {
|
||||||
|
return j.ignorePreflightErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
// OutputWriter returns the io.Writer used to write messages such as the "join done" message.
|
||||||
|
func (j *joinData) OutputWriter() io.Writer {
|
||||||
|
return j.outputWriter
|
||||||
|
}
|
||||||
|
|
||||||
// Run executes worker node provisioning and tries to join an existing cluster.
|
// Run executes worker node provisioning and tries to join an existing cluster.
|
||||||
func (j *joinData) Run() error {
|
func (j *joinData) Run() error {
|
||||||
fmt.Println("[preflight] Running pre-flight checks")
|
fmt.Println("[preflight] Running pre-flight checks")
|
||||||
|
@ -388,9 +455,15 @@ func (j *joinData) Run() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the init configuration based on the join configuration
|
// Fetch the init configuration based on the join configuration.
|
||||||
klog.V(1).Infoln("[preflight] Fetching init configuration")
|
// TODO: individual phases should call these:
|
||||||
initCfg, tlsBootstrapCfg, err := fetchInitConfigurationFromJoinConfiguration(j.cfg)
|
// - phases that need initCfg should call joinData.InitCfg().
|
||||||
|
// - phases that need tlsBootstrapCfg should call joinData.TLSBootstrapCfg().
|
||||||
|
tlsBootstrapCfg, err := j.TLSBootstrapCfg()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
initCfg, err := j.InitCfg()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -654,20 +727,12 @@ func waitForTLSBootstrappedClient() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchInitConfigurationFromJoinConfiguration retrieves the init configuration from a join configuration, performing the discovery
|
// fetchInitConfigurationFromJoinConfiguration retrieves the init configuration from a join configuration, performing the discovery
|
||||||
func fetchInitConfigurationFromJoinConfiguration(cfg *kubeadmapi.JoinConfiguration) (*kubeadmapi.InitConfiguration, *clientcmdapi.Config, error) {
|
func fetchInitConfigurationFromJoinConfiguration(cfg *kubeadmapi.JoinConfiguration, tlsBootstrapCfg *clientcmdapi.Config) (*kubeadmapi.InitConfiguration, error) {
|
||||||
// Perform the Discovery, which turns a Bootstrap Token and optionally (and preferably) a CA cert hash into a KubeConfig
|
|
||||||
// file that may be used for the TLS Bootstrapping process the kubelet performs using the Certificates API.
|
|
||||||
klog.V(1).Infoln("[join] Discovering cluster-info")
|
|
||||||
tlsBootstrapCfg, err := discovery.For(cfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieves the kubeadm configuration
|
// Retrieves the kubeadm configuration
|
||||||
klog.V(1).Infoln("[join] Retrieving KubeConfig objects")
|
klog.V(1).Infoln("[join] Retrieving KubeConfig objects")
|
||||||
initConfiguration, err := fetchInitConfiguration(tlsBootstrapCfg)
|
initConfiguration, err := fetchInitConfiguration(tlsBootstrapCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the final KubeConfig file with the cluster name discovered after fetching the cluster configuration
|
// Create the final KubeConfig file with the cluster name discovered after fetching the cluster configuration
|
||||||
|
@ -683,7 +748,7 @@ func fetchInitConfigurationFromJoinConfiguration(cfg *kubeadmapi.JoinConfigurati
|
||||||
initConfiguration.LocalAPIEndpoint = cfg.ControlPlane.LocalAPIEndpoint
|
initConfiguration.LocalAPIEndpoint = cfg.ControlPlane.LocalAPIEndpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
return initConfiguration, tlsBootstrapCfg, nil
|
return initConfiguration, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchInitConfiguration reads the cluster configuration from the kubeadm-admin configMap
|
// fetchInitConfiguration reads the cluster configuration from the kubeadm-admin configMap
|
||||||
|
|
|
@ -220,6 +220,7 @@ func TestNewJoinData(t *testing.T) {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
// initialize an external join option and inject it to the join cmd
|
// initialize an external join option and inject it to the join cmd
|
||||||
joinOptions := newJoinOptions()
|
joinOptions := newJoinOptions()
|
||||||
|
joinOptions.args = &tc.args
|
||||||
cmd := NewCmdJoin(nil, joinOptions)
|
cmd := NewCmdJoin(nil, joinOptions)
|
||||||
|
|
||||||
// sets cmd flags (that will be reflected on the join options)
|
// sets cmd flags (that will be reflected on the join options)
|
||||||
|
@ -228,7 +229,7 @@ func TestNewJoinData(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// test newJoinData method
|
// test newJoinData method
|
||||||
data, err := newJoinData(cmd, tc.args, joinOptions, nil)
|
data, err := newJoinData(cmd, joinOptions, nil)
|
||||||
if err != nil && !tc.expectError {
|
if err != nil && !tc.expectError {
|
||||||
t.Fatalf("newJoinData returned unexpected error: %v", err)
|
t.Fatalf("newJoinData returned unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,6 +295,10 @@ func (e *Runner) SetAdditionalFlags(fn func(*pflag.FlagSet)) {
|
||||||
// command help, adding phase related flags and by adding phases subcommands
|
// command help, adding phase related flags and by adding phases subcommands
|
||||||
// Please note that this command needs to be done once all the phases are added to the Runner.
|
// Please note that this command needs to be done once all the phases are added to the Runner.
|
||||||
func (e *Runner) BindToCommand(cmd *cobra.Command) {
|
func (e *Runner) BindToCommand(cmd *cobra.Command) {
|
||||||
|
// keep track of the command triggering the runner
|
||||||
|
e.runCmd = cmd
|
||||||
|
|
||||||
|
// return early if no phases were added
|
||||||
if len(e.Phases) == 0 {
|
if len(e.Phases) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -387,9 +391,6 @@ func (e *Runner) BindToCommand(cmd *cobra.Command) {
|
||||||
|
|
||||||
// adds phase related flags to the main command
|
// adds phase related flags to the main command
|
||||||
cmd.Flags().StringSliceVar(&e.Options.SkipPhases, "skip-phases", nil, "List of phases to be skipped")
|
cmd.Flags().StringSliceVar(&e.Options.SkipPhases, "skip-phases", nil, "List of phases to be skipped")
|
||||||
|
|
||||||
// keep tracks of the command triggering the runner
|
|
||||||
e.runCmd = cmd
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func inheritsFlags(sourceFlags, targetFlags *pflag.FlagSet, cmdFlags []string) {
|
func inheritsFlags(sourceFlags, targetFlags *pflag.FlagSet, cmdFlags []string) {
|
||||||
|
|
Loading…
Reference in New Issue