mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-01 15:58:37 +00:00
support kubeadm join dry-run
This commit is contained in:
parent
6a043332be
commit
95e000fd65
@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
@ -132,6 +133,7 @@ type joinOptions struct {
|
||||
externalcfg *kubeadmapiv1.JoinConfiguration
|
||||
joinControlPlane *kubeadmapiv1.JoinControlPlane
|
||||
patchesDir string
|
||||
dryRun bool
|
||||
}
|
||||
|
||||
// compile-time assert that the local data object satisfies the phases data interface.
|
||||
@ -147,6 +149,8 @@ type joinData struct {
|
||||
ignorePreflightErrors sets.String
|
||||
outputWriter io.Writer
|
||||
patchesDir string
|
||||
dryRun bool
|
||||
dryRunDir string
|
||||
}
|
||||
|
||||
// newCmdJoin returns "kubeadm join" command.
|
||||
@ -295,6 +299,10 @@ func addJoinOtherFlags(flagSet *flag.FlagSet, joinOptions *joinOptions) {
|
||||
&joinOptions.controlPlane, options.ControlPlane, joinOptions.controlPlane,
|
||||
"Create a new control plane instance on this node",
|
||||
)
|
||||
flagSet.BoolVar(
|
||||
&joinOptions.dryRun, options.DryRun, joinOptions.dryRun,
|
||||
"Don't apply any changes; just output what would be done.",
|
||||
)
|
||||
options.AddPatchesFlag(flagSet, &joinOptions.patchesDir)
|
||||
}
|
||||
|
||||
@ -445,12 +453,22 @@ func newJoinData(cmd *cobra.Command, args []string, opt *joinOptions, out io.Wri
|
||||
}
|
||||
}
|
||||
|
||||
// if dry running, creates a temporary folder to save kubeadm generated files
|
||||
dryRunDir := ""
|
||||
if opt.dryRun {
|
||||
if dryRunDir, err = kubeadmconstants.CreateTempDirForKubeadm("", "kubeadm-join-dryrun"); err != nil {
|
||||
return nil, errors.Wrap(err, "couldn't create a temporary directory on dryrun")
|
||||
}
|
||||
}
|
||||
|
||||
return &joinData{
|
||||
cfg: cfg,
|
||||
tlsBootstrapCfg: tlsBootstrapCfg,
|
||||
ignorePreflightErrors: ignorePreflightErrorsSet,
|
||||
outputWriter: out,
|
||||
patchesDir: opt.patchesDir,
|
||||
dryRun: opt.dryRun,
|
||||
dryRunDir: dryRunDir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -467,6 +485,43 @@ func (j *joinData) Cfg() *kubeadmapi.JoinConfiguration {
|
||||
return j.cfg
|
||||
}
|
||||
|
||||
// DryRun returns the DryRun flag.
|
||||
func (j *joinData) DryRun() bool {
|
||||
return j.dryRun
|
||||
}
|
||||
|
||||
// KubeConfigDir returns the path of the Kubernetes configuration folder or the temporary folder path in case of DryRun.
|
||||
func (j *joinData) KubeConfigDir() string {
|
||||
if j.dryRun {
|
||||
return j.dryRunDir
|
||||
}
|
||||
return kubeadmconstants.KubernetesDir
|
||||
}
|
||||
|
||||
// KubeletDir returns the path of the kubelet configuration folder or the temporary folder in case of DryRun.
|
||||
func (j *joinData) KubeletDir() string {
|
||||
if j.dryRun {
|
||||
return j.dryRunDir
|
||||
}
|
||||
return kubeadmconstants.KubeletRunDirectory
|
||||
}
|
||||
|
||||
// ManifestDir returns the path where manifest should be stored or the temporary folder path in case of DryRun.
|
||||
func (j *joinData) ManifestDir() string {
|
||||
if j.dryRun {
|
||||
return j.dryRunDir
|
||||
}
|
||||
return kubeadmconstants.GetStaticPodDirectory()
|
||||
}
|
||||
|
||||
// CertificateWriteDir returns the path where certs should be stored or the temporary folder path in case of DryRun.
|
||||
func (j *joinData) CertificateWriteDir() string {
|
||||
if j.dryRun {
|
||||
return j.dryRunDir
|
||||
}
|
||||
return j.initCfg.CertificatesDir
|
||||
}
|
||||
|
||||
// TLSBootstrapCfg returns the cluster-info (kubeconfig).
|
||||
func (j *joinData) TLSBootstrapCfg() (*clientcmdapi.Config, error) {
|
||||
if j.tlsBootstrapCfg != nil {
|
||||
@ -497,7 +552,8 @@ func (j *joinData) ClientSet() (*clientset.Clientset, error) {
|
||||
if j.clientSet != nil {
|
||||
return j.clientSet, nil
|
||||
}
|
||||
path := kubeadmconstants.GetAdminKubeConfigPath()
|
||||
path := filepath.Join(j.KubeConfigDir(), kubeadmconstants.AdminKubeConfigFileName)
|
||||
|
||||
client, err := kubeconfigutil.ClientSetFromFile(path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "[preflight] couldn't create Kubernetes client")
|
||||
|
@ -19,7 +19,6 @@ package phases
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
@ -80,9 +79,12 @@ func runWaitControlPlanePhase(c workflow.RunData) error {
|
||||
return errors.New("wait-control-plane phase invoked with an invalid data struct")
|
||||
}
|
||||
|
||||
// If we're dry-running, print the generated manifests
|
||||
if err := printFilesIfDryRunning(data); err != nil {
|
||||
return errors.Wrap(err, "error printing files on dryrun")
|
||||
// If we're dry-running, print the generated manifests.
|
||||
// TODO: think of a better place to move this call - e.g. a hidden phase.
|
||||
if data.DryRun() {
|
||||
if err := dryrunutil.PrintFilesIfDryRunning(true /* needPrintManifest */, data.ManifestDir(), data.OutputWriter()); err != nil {
|
||||
return errors.Wrap(err, "error printing files on dryrun")
|
||||
}
|
||||
}
|
||||
|
||||
// waiter holds the apiclient.Waiter implementation of choice, responsible for querying the API server in various ways and waiting for conditions to be fulfilled
|
||||
@ -119,36 +121,6 @@ func runWaitControlPlanePhase(c workflow.RunData) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// printFilesIfDryRunning prints the Static Pod manifests to stdout and informs about the temporary directory to go and lookup
|
||||
func printFilesIfDryRunning(data InitData) error {
|
||||
if !data.DryRun() {
|
||||
return nil
|
||||
}
|
||||
manifestDir := data.ManifestDir()
|
||||
|
||||
fmt.Printf("[dryrun] Wrote certificates, kubeconfig files and control plane manifests to the %q directory\n", manifestDir)
|
||||
fmt.Println("[dryrun] The certificates or kubeconfig files would not be printed due to their sensitive nature")
|
||||
fmt.Printf("[dryrun] Please examine the %q directory for details about what would be written\n", manifestDir)
|
||||
|
||||
// Print the contents of the upgraded manifests and pretend like they were in /etc/kubernetes/manifests
|
||||
files := []dryrunutil.FileToPrint{}
|
||||
// Print static pod manifests
|
||||
for _, component := range kubeadmconstants.ControlPlaneComponents {
|
||||
realPath := kubeadmconstants.GetStaticPodFilepath(component, manifestDir)
|
||||
outputPath := kubeadmconstants.GetStaticPodFilepath(component, kubeadmconstants.GetStaticPodDirectory())
|
||||
files = append(files, dryrunutil.NewFileToPrint(realPath, outputPath))
|
||||
}
|
||||
// Print kubelet config manifests
|
||||
kubeletConfigFiles := []string{kubeadmconstants.KubeletConfigurationFileName, kubeadmconstants.KubeletEnvFileName}
|
||||
for _, filename := range kubeletConfigFiles {
|
||||
realPath := filepath.Join(manifestDir, filename)
|
||||
outputPath := filepath.Join(kubeadmconstants.KubeletRunDirectory, filename)
|
||||
files = append(files, dryrunutil.NewFileToPrint(realPath, outputPath))
|
||||
}
|
||||
|
||||
return dryrunutil.PrintDryRunFiles(files, data.OutputWriter())
|
||||
}
|
||||
|
||||
// newControlPlaneWaiter returns a new waiter that is used to wait on the control plane to boot up.
|
||||
func newControlPlaneWaiter(dryRun bool, timeout time.Duration, client clientset.Interface, out io.Writer) (apiclient.Waiter, error) {
|
||||
if dryRun {
|
||||
|
@ -66,5 +66,5 @@ func runCheckEtcdPhase(c workflow.RunData) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return etcdphase.CheckLocalEtcdClusterStatus(client, &cfg.ClusterConfiguration)
|
||||
return etcdphase.CheckLocalEtcdClusterStatus(client, data.CertificateWriteDir())
|
||||
}
|
||||
|
@ -132,9 +132,13 @@ func runEtcdPhase(c workflow.RunData) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create the etcd data directory
|
||||
if err := etcdutil.CreateDataDirectory(cfg.Etcd.Local.DataDir); err != nil {
|
||||
return err
|
||||
if !data.DryRun() {
|
||||
// Create the etcd data directory
|
||||
if err := etcdutil.CreateDataDirectory(cfg.Etcd.Local.DataDir); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("[dryrun] Would ensure that %q directory is present\n", cfg.Etcd.Local.DataDir)
|
||||
}
|
||||
|
||||
// Adds a new etcd instance; in order to do this the new etcd instance should be "announced" to
|
||||
@ -147,8 +151,7 @@ func runEtcdPhase(c workflow.RunData) error {
|
||||
// because it needs two members as majority to agree on the consensus. You will only see this behavior between the time
|
||||
// etcdctl member add informs the cluster about the new member and the new member successfully establishing a connection to the
|
||||
// existing one."
|
||||
// TODO: add support for join dry-run: https://github.com/kubernetes/kubeadm/issues/2505
|
||||
if err := etcdphase.CreateStackedEtcdStaticPodManifestFile(client, kubeadmconstants.GetStaticPodDirectory(), data.PatchesDir(), cfg.NodeRegistration.Name, &cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, false /* isDryRun */); err != nil {
|
||||
if err := etcdphase.CreateStackedEtcdStaticPodManifestFile(client, data.ManifestDir(), data.PatchesDir(), cfg.NodeRegistration.Name, &cfg.ClusterConfiguration, &cfg.LocalAPIEndpoint, data.DryRun(), data.CertificateWriteDir()); err != nil {
|
||||
return errors.Wrap(err, "error creating local etcd static pod manifest file")
|
||||
}
|
||||
|
||||
@ -188,8 +191,12 @@ func runMarkControlPlanePhase(c workflow.RunData) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := markcontrolplanephase.MarkControlPlane(client, cfg.NodeRegistration.Name, cfg.NodeRegistration.Taints); err != nil {
|
||||
return errors.Wrap(err, "error applying control-plane label and taints")
|
||||
if !data.DryRun() {
|
||||
if err := markcontrolplanephase.MarkControlPlane(client, cfg.NodeRegistration.Name, cfg.NodeRegistration.Taints); err != nil {
|
||||
return errors.Wrap(err, "error applying control-plane label and taints")
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("[dryrun] Would mark node %s as a control-plane\n", cfg.NodeRegistration.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -18,6 +18,7 @@ package phases
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
|
||||
@ -189,17 +190,21 @@ func runControlPlanePrepareControlPlaneSubphase(c workflow.RunData) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("[control-plane] Using manifest folder %q\n", kubeadmconstants.GetStaticPodDirectory())
|
||||
fmt.Printf("[control-plane] Using manifest folder %q\n", data.ManifestDir())
|
||||
|
||||
// If we're dry-running, set CertificatesDir to default value to get the right cert path in static pod yaml
|
||||
if data.DryRun() {
|
||||
cfg.CertificatesDir = filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.DefaultCertificateDir)
|
||||
}
|
||||
|
||||
for _, component := range kubeadmconstants.ControlPlaneComponents {
|
||||
fmt.Printf("[control-plane] Creating static Pod manifest for %q\n", component)
|
||||
err := controlplane.CreateStaticPodFiles(
|
||||
kubeadmconstants.GetStaticPodDirectory(),
|
||||
data.ManifestDir(),
|
||||
data.PatchesDir(),
|
||||
&cfg.ClusterConfiguration,
|
||||
&cfg.LocalAPIEndpoint,
|
||||
// TODO: add support for join dry-run:
|
||||
// https://github.com/kubernetes/kubeadm/issues/2505
|
||||
false,
|
||||
data.DryRun(),
|
||||
component,
|
||||
)
|
||||
if err != nil {
|
||||
@ -225,6 +230,11 @@ func runControlPlanePrepareDownloadCertsPhaseLocal(c workflow.RunData) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we're dry-running, download certs to tmp dir
|
||||
if data.DryRun() {
|
||||
cfg.CertificatesDir = data.CertificateWriteDir()
|
||||
}
|
||||
|
||||
client, err := bootstrapClient(data)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -275,12 +285,12 @@ func runControlPlanePrepareKubeconfigPhaseLocal(c workflow.RunData) error {
|
||||
}
|
||||
|
||||
fmt.Println("[kubeconfig] Generating kubeconfig files")
|
||||
fmt.Printf("[kubeconfig] Using kubeconfig folder %q\n", kubeadmconstants.KubernetesDir)
|
||||
fmt.Printf("[kubeconfig] Using kubeconfig folder %q\n", data.KubeConfigDir())
|
||||
|
||||
// Generate kubeconfig files for controller manager, scheduler and for the admin/kubeadm itself
|
||||
// NB. The kubeconfig file for kubelet will be generated by the TLS bootstrap process in
|
||||
// following steps of the join --control-plane workflow
|
||||
if err := kubeconfigphase.CreateJoinControlPlaneKubeConfigFiles(kubeadmconstants.KubernetesDir, cfg); err != nil {
|
||||
if err := kubeconfigphase.CreateJoinControlPlaneKubeConfigFiles(data.KubeConfigDir(), cfg); err != nil {
|
||||
return errors.Wrap(err, "error generating kubeconfig files")
|
||||
}
|
||||
|
||||
|
@ -37,4 +37,9 @@ type JoinData interface {
|
||||
IgnorePreflightErrors() sets.String
|
||||
OutputWriter() io.Writer
|
||||
PatchesDir() string
|
||||
DryRun() bool
|
||||
KubeConfigDir() string
|
||||
KubeletDir() string
|
||||
ManifestDir() string
|
||||
CertificateWriteDir() string
|
||||
}
|
||||
|
@ -40,3 +40,8 @@ func (j *testJoinData) ClientSet() (*clientset.Clientset, error) { return
|
||||
func (j *testJoinData) IgnorePreflightErrors() sets.String { return nil }
|
||||
func (j *testJoinData) OutputWriter() io.Writer { return nil }
|
||||
func (j *testJoinData) PatchesDir() string { return "" }
|
||||
func (t *testJoinData) DryRun() bool { return false }
|
||||
func (t *testJoinData) KubeConfigDir() string { return "" }
|
||||
func (t *testJoinData) KubeletDir() string { return "" }
|
||||
func (t *testJoinData) ManifestDir() string { return "" }
|
||||
func (t *testJoinData) CertificateWriteDir() string { return "" }
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
|
||||
@ -28,6 +29,7 @@ import (
|
||||
kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet"
|
||||
patchnodephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/patchnode"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||
dryrunutil "k8s.io/kubernetes/cmd/kubeadm/app/util/dryrun"
|
||||
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@ -103,7 +105,12 @@ func runKubeletStartJoinPhase(c workflow.RunData) (returnErr error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bootstrapKubeConfigFile := kubeadmconstants.GetBootstrapKubeletKubeConfigPath()
|
||||
|
||||
data, ok := c.(JoinData)
|
||||
if !ok {
|
||||
return errors.New("kubelet-start phase invoked with an invalid data struct")
|
||||
}
|
||||
bootstrapKubeConfigFile := filepath.Join(data.KubeConfigDir(), kubeadmconstants.KubeletBootstrapKubeConfigFileName)
|
||||
|
||||
// Deletes the bootstrapKubeConfigFile, so the credential used for TLS bootstrap is removed from disk
|
||||
defer os.Remove(bootstrapKubeConfigFile)
|
||||
@ -116,9 +123,16 @@ func runKubeletStartJoinPhase(c workflow.RunData) (returnErr error) {
|
||||
|
||||
// Write the ca certificate to disk so kubelet can use it for authentication
|
||||
cluster := tlsBootstrapCfg.Contexts[tlsBootstrapCfg.CurrentContext].Cluster
|
||||
if _, err := os.Stat(cfg.CACertPath); os.IsNotExist(err) {
|
||||
klog.V(1).Infof("[kubelet-start] writing CA certificate at %s", cfg.CACertPath)
|
||||
if err := certutil.WriteCert(cfg.CACertPath, tlsBootstrapCfg.Clusters[cluster].CertificateAuthorityData); err != nil {
|
||||
|
||||
// If we're dry-running, write ca cert in tmp
|
||||
caPath := cfg.CACertPath
|
||||
if data.DryRun() {
|
||||
caPath = filepath.Join(data.CertificateWriteDir(), kubeadmconstants.CACertName)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(caPath); os.IsNotExist(err) {
|
||||
klog.V(1).Infof("[kubelet-start] writing CA certificate at %s", caPath)
|
||||
if err := certutil.WriteCert(caPath, tlsBootstrapCfg.Clusters[cluster].CertificateAuthorityData); err != nil {
|
||||
return errors.Wrap(err, "couldn't save the CA certificate to disk")
|
||||
}
|
||||
}
|
||||
@ -152,11 +166,15 @@ func runKubeletStartJoinPhase(c workflow.RunData) (returnErr error) {
|
||||
|
||||
// Configure the kubelet. In this short timeframe, kubeadm is trying to stop/restart the kubelet
|
||||
// Try to stop the kubelet service so no race conditions occur when configuring it
|
||||
klog.V(1).Infoln("[kubelet-start] Stopping the kubelet")
|
||||
kubeletphase.TryStopKubelet()
|
||||
if !data.DryRun() {
|
||||
klog.V(1).Infoln("[kubelet-start] Stopping the kubelet")
|
||||
kubeletphase.TryStopKubelet()
|
||||
} else {
|
||||
fmt.Println("[dryrun] Would stop the kubelet")
|
||||
}
|
||||
|
||||
// Write the configuration for the kubelet (using the bootstrap token credentials) to disk so the kubelet can start
|
||||
if err := kubeletphase.WriteConfigToDisk(&initCfg.ClusterConfiguration, kubeadmconstants.KubeletRunDirectory); err != nil {
|
||||
if err := kubeletphase.WriteConfigToDisk(&initCfg.ClusterConfiguration, data.KubeletDir()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -164,10 +182,20 @@ func runKubeletStartJoinPhase(c workflow.RunData) (returnErr error) {
|
||||
// register the joining node with the specified taints if the node
|
||||
// is not a control-plane. The mark-control-plane phase will register the taints otherwise.
|
||||
registerTaintsUsingFlags := cfg.ControlPlane == nil
|
||||
if err := kubeletphase.WriteKubeletDynamicEnvFile(&initCfg.ClusterConfiguration, &initCfg.NodeRegistration, registerTaintsUsingFlags, kubeadmconstants.KubeletRunDirectory); err != nil {
|
||||
if err := kubeletphase.WriteKubeletDynamicEnvFile(&initCfg.ClusterConfiguration, &initCfg.NodeRegistration, registerTaintsUsingFlags, data.KubeletDir()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if data.DryRun() {
|
||||
fmt.Println("[dryrun] Would start the kubelet")
|
||||
// If we're dry-running, print the kubelet config manifests and print static pod manifests if joining a control plane.
|
||||
// TODO: think of a better place to move this call - e.g. a hidden phase.
|
||||
if err := dryrunutil.PrintFilesIfDryRunning(cfg.ControlPlane != nil, data.ManifestDir(), data.OutputWriter()); err != nil {
|
||||
return errors.Wrap(err, "error printing files on dryrun")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Try to start the kubelet service in case it's inactive
|
||||
fmt.Println("[kubelet-start] Starting the kubelet")
|
||||
kubeletphase.TryStartKubelet()
|
||||
|
@ -123,6 +123,11 @@ func runPreflight(c workflow.RunData) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if j.DryRun() {
|
||||
fmt.Println("[preflight] Would pull the required images (like 'kubeadm config images pull')")
|
||||
return nil
|
||||
}
|
||||
|
||||
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'")
|
||||
|
@ -47,6 +47,9 @@ const (
|
||||
// CertificateValidity defines the validity for all the signed certificates generated by kubeadm
|
||||
CertificateValidity = time.Hour * 24 * 365
|
||||
|
||||
// DefaultCertificateDir defines default certificate directory
|
||||
DefaultCertificateDir = "pki"
|
||||
|
||||
// CACertAndKeyBaseName defines certificate authority base name
|
||||
CACertAndKeyBaseName = "ca"
|
||||
// CACertName defines certificate name
|
||||
|
@ -67,12 +67,12 @@ func CreateLocalEtcdStaticPodManifestFile(manifestDir, patchesDir string, nodeNa
|
||||
}
|
||||
|
||||
// CheckLocalEtcdClusterStatus verifies health state of local/stacked etcd cluster before installing a new etcd member
|
||||
func CheckLocalEtcdClusterStatus(client clientset.Interface, cfg *kubeadmapi.ClusterConfiguration) error {
|
||||
func CheckLocalEtcdClusterStatus(client clientset.Interface, certificatesDir string) error {
|
||||
klog.V(1).Info("[etcd] Checking etcd cluster health")
|
||||
|
||||
// creates an etcd client that connects to all the local/stacked etcd members
|
||||
klog.V(1).Info("creating etcd client that connects to etcd pods")
|
||||
etcdClient, err := etcdutil.NewFromCluster(client, cfg.CertificatesDir)
|
||||
etcdClient, err := etcdutil.NewFromCluster(client, certificatesDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -134,32 +134,40 @@ func RemoveStackedEtcdMemberFromCluster(client clientset.Interface, cfg *kubeadm
|
||||
// CreateStackedEtcdStaticPodManifestFile will write local etcd static pod manifest file
|
||||
// for an additional etcd member that is joining an existing local/stacked etcd cluster.
|
||||
// Other members of the etcd cluster will be notified of the joining node in beforehand as well.
|
||||
func CreateStackedEtcdStaticPodManifestFile(client clientset.Interface, manifestDir, patchesDir string, nodeName string, cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.APIEndpoint, isDryRun bool) error {
|
||||
func CreateStackedEtcdStaticPodManifestFile(client clientset.Interface, manifestDir, patchesDir string, nodeName string, cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.APIEndpoint, isDryRun bool, certificatesDir string) error {
|
||||
// creates an etcd client that connects to all the local/stacked etcd members
|
||||
klog.V(1).Info("creating etcd client that connects to etcd pods")
|
||||
etcdClient, err := etcdutil.NewFromCluster(client, cfg.CertificatesDir)
|
||||
etcdClient, err := etcdutil.NewFromCluster(client, certificatesDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
etcdPeerAddress := etcdutil.GetPeerURL(endpoint)
|
||||
|
||||
klog.V(1).Infof("[etcd] Adding etcd member: %s", etcdPeerAddress)
|
||||
var cluster []etcdutil.Member
|
||||
cluster, err = etcdClient.AddMember(nodeName, etcdPeerAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
if isDryRun {
|
||||
fmt.Printf("[dryrun] Would add etcd member: %s\n", etcdPeerAddress)
|
||||
} else {
|
||||
klog.V(1).Infof("[etcd] Adding etcd member: %s", etcdPeerAddress)
|
||||
cluster, err = etcdClient.AddMember(nodeName, etcdPeerAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("[etcd] Announced new etcd member joining to the existing etcd cluster")
|
||||
klog.V(1).Infof("Updated etcd member list: %v", cluster)
|
||||
}
|
||||
|
||||
fmt.Println("[etcd] Announced new etcd member joining to the existing etcd cluster")
|
||||
klog.V(1).Infof("Updated etcd member list: %v", cluster)
|
||||
|
||||
fmt.Printf("[etcd] Creating static Pod manifest for %q\n", kubeadmconstants.Etcd)
|
||||
|
||||
if err := prepareAndWriteEtcdStaticPod(manifestDir, patchesDir, cfg, endpoint, nodeName, cluster, isDryRun); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isDryRun {
|
||||
fmt.Println("[dryrun] Would wait for the new etcd member to join the cluster")
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Printf("[etcd] Waiting for the new etcd member to join the cluster. This can take up to %v\n", etcdHealthyCheckInterval*etcdHealthyCheckRetries)
|
||||
if _, err := etcdClient.WaitForClusterAvailable(etcdHealthyCheckRetries, etcdHealthyCheckInterval); err != nil {
|
||||
return err
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@ -139,3 +140,32 @@ func (w *Waiter) WaitForStaticPodSingleHash(_ string, _ string) (string, error)
|
||||
func (w *Waiter) WaitForStaticPodHashChange(_, _, _ string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintFilesIfDryRunning prints the static pod manifests to stdout and informs about the temporary directory to go and lookup when dry running
|
||||
func PrintFilesIfDryRunning(needPrintManifest bool, manifestDir string, outputWriter io.Writer) error {
|
||||
var files []FileToPrint
|
||||
// Print static pod manifests if it is a control plane
|
||||
if needPrintManifest {
|
||||
fmt.Printf("[dryrun] Wrote certificates, kubeconfig files and control plane manifests to the %q directory\n", manifestDir)
|
||||
for _, component := range kubeadmconstants.ControlPlaneComponents {
|
||||
realPath := kubeadmconstants.GetStaticPodFilepath(component, manifestDir)
|
||||
outputPath := kubeadmconstants.GetStaticPodFilepath(component, kubeadmconstants.GetStaticPodDirectory())
|
||||
files = append(files, NewFileToPrint(realPath, outputPath))
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("[dryrun] Wrote certificates and kubeconfig files to the %q directory\n", manifestDir)
|
||||
}
|
||||
|
||||
fmt.Println("[dryrun] The certificates or kubeconfig files would not be printed due to their sensitive nature")
|
||||
fmt.Printf("[dryrun] Please examine the %q directory for details about what would be written\n", manifestDir)
|
||||
|
||||
// Print kubelet config manifests
|
||||
kubeletConfigFiles := []string{kubeadmconstants.KubeletConfigurationFileName, kubeadmconstants.KubeletEnvFileName}
|
||||
for _, filename := range kubeletConfigFiles {
|
||||
realPath := filepath.Join(manifestDir, filename)
|
||||
outputPath := filepath.Join(kubeadmconstants.KubeletRunDirectory, filename)
|
||||
files = append(files, NewFileToPrint(realPath, outputPath))
|
||||
}
|
||||
|
||||
return PrintDryRunFiles(files, outputWriter)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user