mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
kubeadm: move show-join-command as a separate phase
This commit is contained in:
parent
6dd8b86124
commit
8e4cf3b8d2
@ -21,9 +21,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/lithammer/dedent"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
flag "github.com/spf13/pflag"
|
flag "github.com/spf13/pflag"
|
||||||
@ -48,46 +46,6 @@ import (
|
|||||||
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
initDoneTempl = template.Must(template.New("init").Parse(dedent.Dedent(`
|
|
||||||
Your Kubernetes control-plane has initialized successfully!
|
|
||||||
|
|
||||||
To start using your cluster, you need to run the following as a regular user:
|
|
||||||
|
|
||||||
mkdir -p $HOME/.kube
|
|
||||||
sudo cp -i {{.KubeConfigPath}} $HOME/.kube/config
|
|
||||||
sudo chown $(id -u):$(id -g) $HOME/.kube/config
|
|
||||||
|
|
||||||
Alternatively, if you are the root user, you can run:
|
|
||||||
|
|
||||||
export KUBECONFIG=/etc/kubernetes/admin.conf
|
|
||||||
|
|
||||||
You should now deploy a pod network to the cluster.
|
|
||||||
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
|
|
||||||
https://kubernetes.io/docs/concepts/cluster-administration/addons/
|
|
||||||
|
|
||||||
{{if .ControlPlaneEndpoint -}}
|
|
||||||
{{if .UploadCerts -}}
|
|
||||||
You can now join any number of the control-plane node running the following command on each as root:
|
|
||||||
|
|
||||||
{{.joinControlPlaneCommand}}
|
|
||||||
|
|
||||||
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
|
|
||||||
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
|
|
||||||
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.
|
|
||||||
|
|
||||||
{{else -}}
|
|
||||||
You can now join any number of control-plane nodes by copying certificate authorities
|
|
||||||
and service account keys on each node and then running the following as root:
|
|
||||||
|
|
||||||
{{.joinControlPlaneCommand}}
|
|
||||||
|
|
||||||
{{end}}{{end}}Then you can join any number of worker nodes by running the following on each as root:
|
|
||||||
|
|
||||||
{{.joinWorkerCommand}}
|
|
||||||
`)))
|
|
||||||
)
|
|
||||||
|
|
||||||
// initOptions defines all the init options exposed via flags by kubeadm init.
|
// 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
|
// 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.
|
||||||
@ -151,11 +109,7 @@ func newCmdInit(out io.Writer, initOptions *initOptions) *cobra.Command {
|
|||||||
data := c.(*initData)
|
data := c.(*initData)
|
||||||
fmt.Printf("[init] Using Kubernetes version: %s\n", data.cfg.KubernetesVersion)
|
fmt.Printf("[init] Using Kubernetes version: %s\n", data.cfg.KubernetesVersion)
|
||||||
|
|
||||||
if err := initRunner.Run(args); err != nil {
|
return initRunner.Run(args)
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return showJoinCommand(data, out)
|
|
||||||
},
|
},
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
}
|
}
|
||||||
@ -191,6 +145,7 @@ func newCmdInit(out io.Writer, initOptions *initOptions) *cobra.Command {
|
|||||||
initRunner.AppendPhase(phases.NewBootstrapTokenPhase())
|
initRunner.AppendPhase(phases.NewBootstrapTokenPhase())
|
||||||
initRunner.AppendPhase(phases.NewKubeletFinalizePhase())
|
initRunner.AppendPhase(phases.NewKubeletFinalizePhase())
|
||||||
initRunner.AppendPhase(phases.NewAddonPhase())
|
initRunner.AppendPhase(phases.NewAddonPhase())
|
||||||
|
initRunner.AppendPhase(phases.NewShowJoinCommandPhase())
|
||||||
|
|
||||||
// sets the data builder function, that will be used by the runner
|
// sets the data builder function, that will be used by the runner
|
||||||
// both when running the entire workflow or single phases
|
// both when running the entire workflow or single phases
|
||||||
@ -565,39 +520,3 @@ func (d *initData) PatchesDir() string {
|
|||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func printJoinCommand(out io.Writer, adminKubeConfigPath, token string, i *initData) error {
|
|
||||||
joinControlPlaneCommand, err := cmdutil.GetJoinControlPlaneCommand(adminKubeConfigPath, token, i.CertificateKey(), i.skipTokenPrint, i.skipCertificateKeyPrint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
joinWorkerCommand, err := cmdutil.GetJoinWorkerCommand(adminKubeConfigPath, token, i.skipTokenPrint)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := map[string]interface{}{
|
|
||||||
"KubeConfigPath": adminKubeConfigPath,
|
|
||||||
"ControlPlaneEndpoint": i.Cfg().ControlPlaneEndpoint,
|
|
||||||
"UploadCerts": i.uploadCerts,
|
|
||||||
"joinControlPlaneCommand": joinControlPlaneCommand,
|
|
||||||
"joinWorkerCommand": joinWorkerCommand,
|
|
||||||
}
|
|
||||||
|
|
||||||
return initDoneTempl.Execute(out, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// showJoinCommand prints the join command after all the phases in init have finished
|
|
||||||
func showJoinCommand(i *initData, out io.Writer) error {
|
|
||||||
adminKubeConfigPath := i.KubeConfigPath()
|
|
||||||
|
|
||||||
// Prints the join command, multiple times in case the user has multiple tokens
|
|
||||||
for _, token := range i.Tokens() {
|
|
||||||
if err := printJoinCommand(out, adminKubeConfigPath, token, i); err != nil {
|
|
||||||
return errors.Wrap(err, "failed to print join command")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
119
cmd/kubeadm/app/cmd/phases/init/showjoincommand.go
Normal file
119
cmd/kubeadm/app/cmd/phases/init/showjoincommand.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package phases
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/lithammer/dedent"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
|
||||||
|
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
initDoneTempl = template.Must(template.New("init").Parse(dedent.Dedent(`
|
||||||
|
Your Kubernetes control-plane has initialized successfully!
|
||||||
|
|
||||||
|
To start using your cluster, you need to run the following as a regular user:
|
||||||
|
|
||||||
|
mkdir -p $HOME/.kube
|
||||||
|
sudo cp -i {{.KubeConfigPath}} $HOME/.kube/config
|
||||||
|
sudo chown $(id -u):$(id -g) $HOME/.kube/config
|
||||||
|
|
||||||
|
Alternatively, if you are the root user, you can run:
|
||||||
|
|
||||||
|
export KUBECONFIG=/etc/kubernetes/admin.conf
|
||||||
|
|
||||||
|
You should now deploy a pod network to the cluster.
|
||||||
|
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
|
||||||
|
https://kubernetes.io/docs/concepts/cluster-administration/addons/
|
||||||
|
|
||||||
|
{{if .ControlPlaneEndpoint -}}
|
||||||
|
{{if .UploadCerts -}}
|
||||||
|
You can now join any number of the control-plane node running the following command on each as root:
|
||||||
|
|
||||||
|
{{.joinControlPlaneCommand}}
|
||||||
|
|
||||||
|
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
|
||||||
|
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
|
||||||
|
"kubeadm init phase upload-certs --upload-certs" to reload certs afterward.
|
||||||
|
|
||||||
|
{{else -}}
|
||||||
|
You can now join any number of control-plane nodes by copying certificate authorities
|
||||||
|
and service account keys on each node and then running the following as root:
|
||||||
|
|
||||||
|
{{.joinControlPlaneCommand}}
|
||||||
|
|
||||||
|
{{end}}{{end}}Then you can join any number of worker nodes by running the following on each as root:
|
||||||
|
|
||||||
|
{{.joinWorkerCommand}}
|
||||||
|
`)))
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewShowJoinCommandPhase creates a kubeadm workflow phase that implements showing the join command.
|
||||||
|
func NewShowJoinCommandPhase() workflow.Phase {
|
||||||
|
return workflow.Phase{
|
||||||
|
Name: "show-join-command",
|
||||||
|
Short: "Show the join command for control-plane and worker node",
|
||||||
|
Run: showJoinCommand,
|
||||||
|
Dependencies: []string{"bootstrap-token", "upload-certs"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// showJoinCommand prints the join command after all the phases in init have finished
|
||||||
|
func showJoinCommand(c workflow.RunData) error {
|
||||||
|
data, ok := c.(InitData)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("show-join-command phase invoked with an invalid data struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
adminKubeConfigPath := data.KubeConfigPath()
|
||||||
|
|
||||||
|
// Prints the join command, multiple times in case the user has multiple tokens
|
||||||
|
for _, token := range data.Tokens() {
|
||||||
|
if err := printJoinCommand(data.OutputWriter(), adminKubeConfigPath, token, data); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to print join command")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printJoinCommand(out io.Writer, adminKubeConfigPath, token string, i InitData) error {
|
||||||
|
joinControlPlaneCommand, err := cmdutil.GetJoinControlPlaneCommand(adminKubeConfigPath, token, i.CertificateKey(), i.SkipTokenPrint(), i.SkipCertificateKeyPrint())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
joinWorkerCommand, err := cmdutil.GetJoinWorkerCommand(adminKubeConfigPath, token, i.SkipTokenPrint())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := map[string]interface{}{
|
||||||
|
"KubeConfigPath": adminKubeConfigPath,
|
||||||
|
"ControlPlaneEndpoint": i.Cfg().ControlPlaneEndpoint,
|
||||||
|
"UploadCerts": i.UploadCerts(),
|
||||||
|
"joinControlPlaneCommand": joinControlPlaneCommand,
|
||||||
|
"joinWorkerCommand": joinWorkerCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
return initDoneTempl.Execute(out, ctx)
|
||||||
|
}
|
@ -78,6 +78,9 @@ type Phase struct {
|
|||||||
// ArgsValidator defines the positional arg function to be used for validating args for this phase
|
// ArgsValidator defines the positional arg function to be used for validating args for this phase
|
||||||
// If not set a phase will adopt the args of the top level command.
|
// If not set a phase will adopt the args of the top level command.
|
||||||
ArgsValidator cobra.PositionalArgs
|
ArgsValidator cobra.PositionalArgs
|
||||||
|
|
||||||
|
// Dependencies is a list of phases that the specific phase depends on.
|
||||||
|
Dependencies []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendPhase adds the given phase to the nested, ordered sequence of phases.
|
// AppendPhase adds the given phase to the nested, ordered sequence of phases.
|
||||||
|
@ -198,6 +198,29 @@ func (e *Runner) Run(args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// precheck phase dependencies before actual execution
|
||||||
|
missedDeps := make(map[string][]string)
|
||||||
|
visited := make(map[string]struct{})
|
||||||
|
for _, p := range e.phaseRunners {
|
||||||
|
if run, ok := phaseRunFlags[p.generatedName]; !run || !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, dep := range p.Phase.Dependencies {
|
||||||
|
if _, ok := visited[dep]; !ok {
|
||||||
|
missedDeps[p.Phase.Name] = append(missedDeps[p.Phase.Name], dep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visited[p.Phase.Name] = struct{}{}
|
||||||
|
}
|
||||||
|
if len(missedDeps) > 0 {
|
||||||
|
var msg strings.Builder
|
||||||
|
msg.WriteString("unresolved dependencies:")
|
||||||
|
for phase, missedPhases := range missedDeps {
|
||||||
|
msg.WriteString(fmt.Sprintf("\n\tmissing %v phase(s) needed by %q phase", missedPhases, phase))
|
||||||
|
}
|
||||||
|
return errors.New(msg.String())
|
||||||
|
}
|
||||||
|
|
||||||
// builds the runner data
|
// builds the runner data
|
||||||
var data RunData
|
var data RunData
|
||||||
if data, err = e.InitData(args); err != nil {
|
if data, err = e.InitData(args); err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user