diff --git a/cmd/kubeadm/app/util/etcd/etcd.go b/cmd/kubeadm/app/util/etcd/etcd.go index 8d41809c58..c41b0ccdad 100644 --- a/cmd/kubeadm/app/util/etcd/etcd.go +++ b/cmd/kubeadm/app/util/etcd/etcd.go @@ -113,27 +113,55 @@ func New(endpoints []string, ca, cert, key string) (*Client, error) { return &client, nil } -// NewFromStaticPod creates a GenericClient from the given endpoints, manifestDir, and certificatesDir -func NewFromStaticPod(endpoints []string, manifestDir string, certificatesDir string) (*Client, error) { - hasTLS, err := PodManifestsHaveTLS(manifestDir) +// NewFromCluster creates an etcd client for the the etcd endpoints defined in the ClusterStatus value stored in +// the kubeadm-config ConfigMap in kube-system namespace. +// Once created, the client synchronizes client's endpoints with the known endpoints from the etcd membership API (reality check). +func NewFromCluster(client clientset.Interface, certificatesDir string) (*Client, error) { + + // Kubeadm v1.13 should manage v1.12 clusters and v1.13 clusters + // v1.12 clusters can be have etcd listening on localhost only (if the cluster was created with kubeadm v1.12) + // or etcd listening on localhost and API server advertise address (if the cluster was created with kubeadm v1.13). + // The first case should be dropped in v1.14 when support for v1.12 clusters can be removed from the codebase. + + // Detect which type of etcd we are dealing with + oldManifest := false + klog.V(1).Infoln("checking etcd manifest") + + etcdManifestFile := constants.GetStaticPodFilepath(constants.Etcd, constants.GetStaticPodDirectory()) + etcdPod, err := staticpod.ReadStaticPodFromDisk(etcdManifestFile) if err != nil { - return nil, errors.Wrapf(err, "could not read manifests from: %s, error", manifestDir) + return nil, errors.Wrap(err, "error reading etcd manifest file") } - if hasTLS { - return New( + etcdContainer := etcdPod.Spec.Containers[0] + for _, arg := range etcdContainer.Command { + if arg == "--listen-client-urls=https://127.0.0.1:2379" { + klog.V(1).Infoln("etcd manifest created by kubeadm v1.12") + oldManifest = true + } + } + + // if etcd is listening on localhost only + if oldManifest == true { + // etcd cluster has a single member "by design" + endpoints := []string{fmt.Sprintf("localhost:%d", constants.EtcdListenClientPort)} + + etcdClient, err := New( endpoints, filepath.Join(certificatesDir, constants.EtcdCACertName), filepath.Join(certificatesDir, constants.EtcdHealthcheckClientCertName), filepath.Join(certificatesDir, constants.EtcdHealthcheckClientKeyName), ) - } - return New(endpoints, "", "", "") -} + if err != nil { + return nil, errors.Wrapf(err, "error creating etcd client for %v endpoint", endpoints) + } + + return etcdClient, nil + } + + // etcd is listening on localhost and API server advertise address, and + // the etcd cluster can have more than one etcd members, so it is necessary to get the + // list of endpoints from kubeadm cluster status before connecting -// NewFromCluster creates an etcd client for the the etcd endpoints defined in the ClusterStatus value stored in -// the kubeadm-config ConfigMap in kube-system namespace. -// Once created, the client synchronizes client's endpoints with the known endpoints from the etcd membership API (reality check). -func NewFromCluster(client clientset.Interface, certificatesDir string) (*Client, error) { // Gets the cluster status clusterStatus, err := config.GetClusterStatus(client) if err != nil { @@ -155,13 +183,13 @@ func NewFromCluster(client clientset.Interface, certificatesDir string) (*Client filepath.Join(certificatesDir, constants.EtcdHealthcheckClientKeyName), ) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "error creating etcd client for %v endpoints", endpoints) } // synchronizes client's endpoints with the known endpoints from the etcd membership. err = etcdClient.Sync() if err != nil { - return nil, err + return nil, errors.Wrap(err, "error syncing endpoints with etc") } return etcdClient, nil