diff --git a/cmd/kubeadm/app/cmd/join.go b/cmd/kubeadm/app/cmd/join.go index f5ea1e7f9b8..b9ea65c04fe 100644 --- a/cmd/kubeadm/app/cmd/join.go +++ b/cmd/kubeadm/app/cmd/join.go @@ -48,6 +48,7 @@ import ( kubeletphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubelet" markmasterphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/markmaster" patchnodephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/patchnode" + uploadconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadconfig" "k8s.io/kubernetes/cmd/kubeadm/app/preflight" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" @@ -300,21 +301,22 @@ func (j *Join) Run(out io.Writer) error { } // If the node should host a new control plane instance + var initConfiguration *kubeadmapi.InitConfiguration if j.cfg.ControlPlane == true { // Retrives the kubeadm configuration used during kubeadm init glog.V(1).Infoln("[join] retrieving KubeConfig objects") - clusterConfiguration, err := j.FetchInitClusterConfiguration(tlsBootstrapCfg) + initConfiguration, err = j.FetchInitConfiguration(tlsBootstrapCfg) if err != nil { return err } - // injects into the kubeadm configuration used for init the information about the joining node - clusterConfiguration.NodeRegistration = j.cfg.NodeRegistration - clusterConfiguration.APIEndpoint.AdvertiseAddress = j.cfg.APIEndpoint.AdvertiseAddress + // injects into the kubeadm configuration the information about the joining node + initConfiguration.NodeRegistration = j.cfg.NodeRegistration + initConfiguration.APIEndpoint = j.cfg.APIEndpoint // Checks if the cluster configuration supports // joining a new control plane instance and if all the necessary certificates are provided - if err = j.CheckIfReadyForAdditionalControlPlane(clusterConfiguration); err != nil { + if err = j.CheckIfReadyForAdditionalControlPlane(initConfiguration); err != nil { // outputs the not ready for hosting a new control plane instance message ctx := map[string]string{ "Error": err.Error(), @@ -327,11 +329,11 @@ func (j *Join) Run(out io.Writer) error { // run kubeadm init preflight checks for checking all the prequisites glog.Infoln("[join] running pre-flight checks before initializing the new control plane instance") - preflight.RunInitMasterChecks(utilsexec.New(), clusterConfiguration, j.ignorePreflightErrors) + preflight.RunInitMasterChecks(utilsexec.New(), initConfiguration, j.ignorePreflightErrors) // Prepares the node for hosting a new control plane instance by writing necessary // KubeConfig files, and static pod manifests - if err = j.PrepareForHostingControlPlane(clusterConfiguration); err != nil { + if err = j.PrepareForHostingControlPlane(initConfiguration); err != nil { return err } } @@ -348,8 +350,8 @@ func (j *Join) Run(out io.Writer) error { // if the node is hosting a new control plane instance if j.cfg.ControlPlane == true { - // Marks the node with master taint and label. - if err := j.MarkMaster(); err != nil { + // Completes the control plane setup + if err := j.PostInstallControlPlane(initConfiguration); err != nil { return err } @@ -367,52 +369,48 @@ func (j *Join) Run(out io.Writer) error { return nil } -// FetchInitClusterConfiguration reads the cluster configuration from the kubeadm-admin configMap, -func (j *Join) FetchInitClusterConfiguration(tlsBootstrapCfg *clientcmdapi.Config) (*kubeadmapi.InitConfiguration, error) { +// FetchInitConfiguration reads the cluster configuration from the kubeadm-admin configMap, +func (j *Join) FetchInitConfiguration(tlsBootstrapCfg *clientcmdapi.Config) (*kubeadmapi.InitConfiguration, error) { // creates a client to access the cluster using the bootstrap token identity tlsClient, err := kubeconfigutil.ToClientSet(tlsBootstrapCfg) if err != nil { return nil, errors.Wrap(err, "Unable to access the cluster") } - // Fetches the cluster configuration - kubeadmConfig, err := configutil.FetchConfigFromFileOrCluster(tlsClient, os.Stdout, "join", "") + // Fetches the init configuration + initConfiguration, err := configutil.FetchConfigFromFileOrCluster(tlsClient, os.Stdout, "join", "", true) if err != nil { return nil, errors.Wrap(err, "Unable to fetch the kubeadm-config ConfigMap") } - // Converts public API struct to internal API - clusterConfiguration := &kubeadmapi.InitConfiguration{} - kubeadmscheme.Scheme.Convert(kubeadmConfig, clusterConfiguration, nil) - - return clusterConfiguration, nil + return initConfiguration, 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 *Join) CheckIfReadyForAdditionalControlPlane(clusterConfiguration *kubeadmapi.InitConfiguration) error { +func (j *Join) CheckIfReadyForAdditionalControlPlane(initConfiguration *kubeadmapi.InitConfiguration) error { // blocks if the cluster was created without a stable control plane endpoint - if clusterConfiguration.ControlPlaneEndpoint == "" { + if initConfiguration.ControlPlaneEndpoint == "" { return fmt.Errorf("unable to add a new control plane instance a cluster that doesn't have a stable controlPlaneEndpoint address") } // blocks if the cluster was created without an external etcd cluster - if clusterConfiguration.Etcd.External == nil { + if initConfiguration.Etcd.External == nil { return fmt.Errorf("unable to add a new control plane instance on a cluster that doesn't use an external etcd") } // blocks if control plane is self-hosted - if features.Enabled(clusterConfiguration.FeatureGates, features.SelfHosting) { + if features.Enabled(initConfiguration.FeatureGates, features.SelfHosting) { return fmt.Errorf("self-hosted clusters are deprecated and won't be supported by `kubeadm join --experimental-control-plane`") } // blocks if the certificates for the control plane are stored in secrets (instead of the local pki folder) - if features.Enabled(clusterConfiguration.FeatureGates, features.StoreCertsInSecrets) { + if features.Enabled(initConfiguration.FeatureGates, features.StoreCertsInSecrets) { return fmt.Errorf("certificates stored in secrets, as well as self-hosted clusters are deprecated and won't be supported by `kubeadm join --experimental-control-plane`") } // checks if the certificates that must be equal across contolplane instances are provided - if ret, err := certsphase.SharedCertificateExists(clusterConfiguration); !ret { + if ret, err := certsphase.SharedCertificateExists(initConfiguration); !ret { return err } @@ -420,28 +418,28 @@ func (j *Join) CheckIfReadyForAdditionalControlPlane(clusterConfiguration *kubea } // PrepareForHostingControlPlane makes all preparation activities require for a node hosting a new control plane instance -func (j *Join) PrepareForHostingControlPlane(clusterConfiguration *kubeadmapi.InitConfiguration) error { +func (j *Join) PrepareForHostingControlPlane(initConfiguration *kubeadmapi.InitConfiguration) error { // Creates the admin kubeconfig file for the admin and for kubeadm itself. - if err := kubeconfigphase.CreateAdminKubeConfigFile(kubeadmconstants.KubernetesDir, clusterConfiguration); err != nil { + if err := kubeconfigphase.CreateAdminKubeConfigFile(kubeadmconstants.KubernetesDir, initConfiguration); err != nil { return errors.Wrap(err, "error generating the admin kubeconfig file") } // Generate missing certificates (if any) - if err := certsphase.CreatePKIAssets(clusterConfiguration); err != nil { + if err := certsphase.CreatePKIAssets(initConfiguration); err != nil { return err } // 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 --experimental-control plane workflow - if err := kubeconfigphase.CreateJoinControlPlaneKubeConfigFiles(kubeadmconstants.KubernetesDir, clusterConfiguration); err != nil { + if err := kubeconfigphase.CreateJoinControlPlaneKubeConfigFiles(kubeadmconstants.KubernetesDir, initConfiguration); err != nil { return errors.Wrap(err, "error generating kubeconfig files") } // Creates static pod manifests file for the control plane components to be deployed on this node // Static pods will be created and managed by the kubelet as soon as it starts - if err := controlplanephase.CreateInitStaticPodManifestFiles(kubeadmconstants.GetStaticPodDirectory(), clusterConfiguration); err != nil { + if err := controlplanephase.CreateInitStaticPodManifestFiles(kubeadmconstants.GetStaticPodDirectory(), initConfiguration); err != nil { return errors.Wrap(err, "error creating static pod manifest files for the control plane components") } @@ -488,8 +486,10 @@ func (j *Join) BootstrapKubelet(tlsBootstrapCfg *clientcmdapi.Config) error { return err } - // Write env file with flags for the kubelet to use. Also register taints - if err := kubeletphase.WriteKubeletDynamicEnvFile(&j.cfg.NodeRegistration, j.cfg.FeatureGates, true, kubeadmconstants.KubeletRunDirectory); err != nil { + // Write env file with flags for the kubelet to use. We do not need to write the --register-with-taints for the master, + // as we handle that ourselves in the markmaster phase + // TODO: Maybe we want to do that some time in the future, in order to remove some logic from the markmaster phase? + if err := kubeletphase.WriteKubeletDynamicEnvFile(&j.cfg.NodeRegistration, j.cfg.FeatureGates, false, kubeadmconstants.KubeletRunDirectory); err != nil { return err } @@ -527,8 +527,8 @@ func (j *Join) BootstrapKubelet(tlsBootstrapCfg *clientcmdapi.Config) error { return nil } -// MarkMaster marks the new node as master -func (j *Join) MarkMaster() error { +// PostInstallControlPlane marks the new node as master and update the cluster status with information about current node +func (j *Join) PostInstallControlPlane(initConfiguration *kubeadmapi.InitConfiguration) error { kubeConfigFile := filepath.Join(kubeadmconstants.KubernetesDir, kubeadmconstants.AdminKubeConfigFileName) client, err := kubeconfigutil.ClientSetFromFile(kubeConfigFile) @@ -536,8 +536,13 @@ func (j *Join) MarkMaster() error { return errors.Wrap(err, "couldn't create kubernetes client") } - err = markmasterphase.MarkMaster(client, j.cfg.NodeRegistration.Name, j.cfg.NodeRegistration.Taints) - if err != nil { + glog.V(1).Info("[join] uploading currently used configuration to the cluster") + if err := uploadconfigphase.UploadConfiguration(initConfiguration, client); err != nil { + return fmt.Errorf("error uploading configuration: %v", err) + } + + glog.V(1).Info("[join] marking the master with right label") + if err = markmasterphase.MarkMaster(client, initConfiguration.NodeRegistration.Name, initConfiguration.NodeRegistration.Taints); err != nil { return errors.Wrap(err, "error applying master label and taints") } diff --git a/cmd/kubeadm/app/cmd/upgrade/common.go b/cmd/kubeadm/app/cmd/upgrade/common.go index ef880874c0f..3698e832a98 100644 --- a/cmd/kubeadm/app/cmd/upgrade/common.go +++ b/cmd/kubeadm/app/cmd/upgrade/common.go @@ -64,7 +64,7 @@ func enforceRequirements(flags *applyPlanFlags, dryRun bool, newK8sVersion strin // Fetch the configuration from a file or ConfigMap and validate it fmt.Println("[upgrade/config] Making sure the configuration is correct:") - cfg, err := configutil.FetchConfigFromFileOrCluster(client, os.Stdout, "upgrade/config", flags.cfgPath) + cfg, err := configutil.FetchConfigFromFileOrCluster(client, os.Stdout, "upgrade/config", flags.cfgPath, false) if err != nil { if apierrors.IsNotFound(err) { fmt.Printf("[upgrade/config] In order to upgrade, a ConfigMap called %q in the %s namespace must exist.\n", constants.InitConfigurationConfigMap, metav1.NamespaceSystem) diff --git a/cmd/kubeadm/app/cmd/upgrade/node.go b/cmd/kubeadm/app/cmd/upgrade/node.go index 8758c7736cb..dca43e4f53f 100644 --- a/cmd/kubeadm/app/cmd/upgrade/node.go +++ b/cmd/kubeadm/app/cmd/upgrade/node.go @@ -150,10 +150,6 @@ func NewCmdUpgradeControlPlane() *cobra.Command { options.AddKubeConfigFlag(cmd.Flags(), &flags.kubeConfigPath) cmd.Flags().BoolVar(&flags.dryRun, "dry-run", flags.dryRun, "Do not change any state, just output the actions that would be performed.") - - //TODO: following values should retrieved form the kubeadm-config config map; remove as soon as the new config wil be in place - cmd.Flags().StringVar(&flags.advertiseAddress, "apiserver-advertise-address", flags.advertiseAddress, "If the node is joining as a master, the IP address the API Server will advertise it's listening on.") - cmd.Flags().StringVar(&flags.nodeName, "node-name", flags.nodeName, "Specify the node name.") return cmd } @@ -231,16 +227,11 @@ func RunUpgradeControlPlane(flags *controlplaneUpgradeFlags) error { waiter := apiclient.NewKubeWaiter(client, upgrade.UpgradeManifestTimeout, os.Stdout) // Fetches the cluster configuration - cfg, err := configutil.FetchConfigFromFileOrCluster(client, os.Stdout, "upgrade", "") + cfg, err := configutil.FetchConfigFromFileOrCluster(client, os.Stdout, "upgrade", "", false) if err != nil { return fmt.Errorf("Unable to fetch the kubeadm-config ConfigMap: %v", err) } - //TODO: as soon as the new config wil be in place check if the node is a known control plane instance - // and retrive corresponding infos (now are temporary managed as flag) - cfg.NodeRegistration.Name = flags.nodeName - cfg.APIEndpoint.AdvertiseAddress = flags.advertiseAddress - // Rotate API server certificate if needed if err := upgrade.BackupAPIServerCertIfNeeded(cfg, flags.dryRun); err != nil { return fmt.Errorf("Unable to rotate API server certificate: %v", err)