Merge pull request #70633 from leblancd/kubeadm_etcd_v6_fix

Fix kubeadm etcd manifests to use brackets around IPv6 addrs
pull/58/head
k8s-ci-robot 2018-11-20 15:34:24 -08:00 committed by GitHub
commit f8983a8988
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 178 additions and 64 deletions

View File

@ -90,7 +90,7 @@ func CreateStackedEtcdStaticPodManifestFile(client clientset.Interface, manifest
}
// notifies the other members of the etcd cluster about the joining member
etcdPeerAddress := fmt.Sprintf("https://%s:%d", cfg.LocalAPIEndpoint.AdvertiseAddress, kubeadmconstants.EtcdListenPeerPort)
etcdPeerAddress := etcdutil.GetPeerURL(cfg)
klog.V(1).Infof("Adding etcd member: %s", etcdPeerAddress)
initialCluster, err := etcdClient.AddMember(cfg.NodeRegistration.Name, etcdPeerAddress)
@ -141,10 +141,10 @@ func GetEtcdPodSpec(cfg *kubeadmapi.InitConfiguration, initialCluster []etcdutil
func getEtcdCommand(cfg *kubeadmapi.InitConfiguration, initialCluster []etcdutil.Member) []string {
defaultArguments := map[string]string{
"name": cfg.GetNodeName(),
"listen-client-urls": fmt.Sprintf("https://127.0.0.1:%d,https://%s:%d", kubeadmconstants.EtcdListenClientPort, cfg.LocalAPIEndpoint.AdvertiseAddress, kubeadmconstants.EtcdListenClientPort),
"advertise-client-urls": fmt.Sprintf("https://%s:%d", cfg.LocalAPIEndpoint.AdvertiseAddress, kubeadmconstants.EtcdListenClientPort),
"listen-peer-urls": fmt.Sprintf("https://%s:%d", cfg.LocalAPIEndpoint.AdvertiseAddress, kubeadmconstants.EtcdListenPeerPort),
"initial-advertise-peer-urls": fmt.Sprintf("https://%s:%d", cfg.LocalAPIEndpoint.AdvertiseAddress, kubeadmconstants.EtcdListenPeerPort),
"listen-client-urls": fmt.Sprintf("%s,%s", etcdutil.GetClientURLByIP("127.0.0.1"), etcdutil.GetClientURL(cfg)),
"advertise-client-urls": etcdutil.GetClientURL(cfg),
"listen-peer-urls": etcdutil.GetPeerURL(cfg),
"initial-advertise-peer-urls": etcdutil.GetPeerURL(cfg),
"data-dir": cfg.Etcd.Local.DataDir,
"cert-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdServerCertName),
"key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdServerKeyName),
@ -158,7 +158,7 @@ func getEtcdCommand(cfg *kubeadmapi.InitConfiguration, initialCluster []etcdutil
}
if len(initialCluster) == 0 {
defaultArguments["initial-cluster"] = fmt.Sprintf("%s=https://%s:%d", cfg.GetNodeName(), cfg.LocalAPIEndpoint.AdvertiseAddress, kubeadmconstants.EtcdListenPeerPort)
defaultArguments["initial-cluster"] = fmt.Sprintf("%s=%s", cfg.GetNodeName(), etcdutil.GetPeerURL(cfg))
} else {
// NB. the joining etcd instance should be part of the initialCluster list
endpoints := []string{}

View File

@ -114,28 +114,17 @@ func TestCreateLocalEtcdStaticPodManifestFile(t *testing.T) {
func TestGetEtcdCommand(t *testing.T) {
var tests = []struct {
name string
cfg *kubeadmapi.InitConfiguration
initialCluster []etcdutil.Member
expected []string
name string
advertiseAddress string
nodeName string
extraArgs map[string]string
initialCluster []etcdutil.Member
expected []string
}{
{
name: "Default args - with empty etcd initial cluster",
cfg: &kubeadmapi.InitConfiguration{
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
AdvertiseAddress: "1.2.3.4",
},
NodeRegistration: kubeadmapi.NodeRegistrationOptions{
Name: "foo",
},
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
Etcd: kubeadmapi.Etcd{
Local: &kubeadmapi.LocalEtcd{
DataDir: "/var/lib/etcd",
},
},
},
},
name: "Default args - with empty etcd initial cluster",
advertiseAddress: "1.2.3.4",
nodeName: "foo",
expected: []string{
"etcd",
"--name=foo",
@ -157,22 +146,9 @@ func TestGetEtcdCommand(t *testing.T) {
},
},
{
name: "Default args - With an existing etcd cluster",
cfg: &kubeadmapi.InitConfiguration{
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
AdvertiseAddress: "1.2.3.4",
},
NodeRegistration: kubeadmapi.NodeRegistrationOptions{
Name: "foo",
},
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
Etcd: kubeadmapi.Etcd{
Local: &kubeadmapi.LocalEtcd{
DataDir: "/var/lib/etcd",
},
},
},
},
name: "Default args - With an existing etcd cluster",
advertiseAddress: "1.2.3.4",
nodeName: "foo",
initialCluster: []etcdutil.Member{
{Name: "foo", PeerURL: fmt.Sprintf("https://1.2.3.4:%d", kubeadmconstants.EtcdListenPeerPort)}, // NB. the joining etcd instance should be part of the initialCluster list
{Name: "bar", PeerURL: fmt.Sprintf("https://5.6.7.8:%d", kubeadmconstants.EtcdListenPeerPort)},
@ -199,25 +175,12 @@ func TestGetEtcdCommand(t *testing.T) {
},
},
{
name: "Extra args",
cfg: &kubeadmapi.InitConfiguration{
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
AdvertiseAddress: "1.2.3.4",
},
NodeRegistration: kubeadmapi.NodeRegistrationOptions{
Name: "bar",
},
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
Etcd: kubeadmapi.Etcd{
Local: &kubeadmapi.LocalEtcd{
DataDir: "/var/lib/etcd",
ExtraArgs: map[string]string{
"listen-client-urls": "https://10.0.1.10:2379",
"advertise-client-urls": "https://10.0.1.10:2379",
},
},
},
},
name: "Extra args",
advertiseAddress: "1.2.3.4",
nodeName: "bar",
extraArgs: map[string]string{
"listen-client-urls": "https://10.0.1.10:2379",
"advertise-client-urls": "https://10.0.1.10:2379",
},
expected: []string{
"etcd",
@ -239,11 +202,51 @@ func TestGetEtcdCommand(t *testing.T) {
fmt.Sprintf("--initial-cluster=bar=https://1.2.3.4:%d", kubeadmconstants.EtcdListenPeerPort),
},
},
{
name: "IPv6 advertise address",
advertiseAddress: "2001:db8::3",
nodeName: "foo",
expected: []string{
"etcd",
"--name=foo",
fmt.Sprintf("--listen-client-urls=https://127.0.0.1:%d,https://[2001:db8::3]:%d", kubeadmconstants.EtcdListenClientPort, kubeadmconstants.EtcdListenClientPort),
fmt.Sprintf("--advertise-client-urls=https://[2001:db8::3]:%d", kubeadmconstants.EtcdListenClientPort),
fmt.Sprintf("--listen-peer-urls=https://[2001:db8::3]:%d", kubeadmconstants.EtcdListenPeerPort),
fmt.Sprintf("--initial-advertise-peer-urls=https://[2001:db8::3]:%d", kubeadmconstants.EtcdListenPeerPort),
"--data-dir=/var/lib/etcd",
"--cert-file=" + kubeadmconstants.EtcdServerCertName,
"--key-file=" + kubeadmconstants.EtcdServerKeyName,
"--trusted-ca-file=" + kubeadmconstants.EtcdCACertName,
"--client-cert-auth=true",
"--peer-cert-file=" + kubeadmconstants.EtcdPeerCertName,
"--peer-key-file=" + kubeadmconstants.EtcdPeerKeyName,
"--peer-trusted-ca-file=" + kubeadmconstants.EtcdCACertName,
"--snapshot-count=10000",
"--peer-client-cert-auth=true",
fmt.Sprintf("--initial-cluster=foo=https://[2001:db8::3]:%d", kubeadmconstants.EtcdListenPeerPort),
},
},
}
for _, rt := range tests {
t.Run(rt.name, func(t *testing.T) {
actual := getEtcdCommand(rt.cfg, rt.initialCluster)
cfg := &kubeadmapi.InitConfiguration{
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
AdvertiseAddress: rt.advertiseAddress,
},
NodeRegistration: kubeadmapi.NodeRegistrationOptions{
Name: rt.nodeName,
},
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
Etcd: kubeadmapi.Etcd{
Local: &kubeadmapi.LocalEtcd{
DataDir: "/var/lib/etcd",
ExtraArgs: rt.extraArgs,
},
},
},
}
actual := getEtcdCommand(cfg, rt.initialCluster)
sort.Strings(actual)
sort.Strings(rt.expected)
if !reflect.DeepEqual(actual, rt.expected) {

View File

@ -282,7 +282,7 @@ func performEtcdStaticPodUpgrade(client clientset.Interface, waiter apiclient.Wa
if err != nil {
return true, errors.Wrap(err, "failed to retrieve the current etcd version")
}
currentEtcdVersionStr, ok := currentEtcdVersions[fmt.Sprintf("https://%s:%d", cfg.LocalAPIEndpoint.AdvertiseAddress, constants.EtcdListenClientPort)]
currentEtcdVersionStr, ok := currentEtcdVersions[etcdutil.GetClientURL(cfg)]
if !ok {
fmt.Println(currentEtcdVersions)
return true, errors.Wrap(err, "failed to retrieve the current etcd version")

View File

@ -24,6 +24,7 @@ go_test(
embed = [":go_default_library"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/test:go_default_library",
],
)

View File

@ -20,7 +20,9 @@ import (
"context"
"crypto/tls"
"fmt"
"net"
"path/filepath"
"strconv"
"strings"
"time"
@ -171,7 +173,7 @@ func NewFromCluster(client clientset.Interface, certificatesDir string) (*Client
// Get the list of etcd endpoints from cluster status
endpoints := []string{}
for _, e := range clusterStatus.APIEndpoints {
endpoints = append(endpoints, fmt.Sprintf("https://%s:%d", e.AdvertiseAddress, constants.EtcdListenClientPort))
endpoints = append(endpoints, GetClientURLByIP(e.AdvertiseAddress))
}
klog.V(1).Infof("etcd endpoints read from pods: %s", strings.Join(endpoints, ","))
@ -358,3 +360,21 @@ func (c Client) WaitForClusterAvailable(delay time.Duration, retries int, retryI
func CheckConfigurationIsHA(cfg *kubeadmapi.Etcd) bool {
return cfg.External != nil && len(cfg.External.Endpoints) > 1
}
// GetClientURL creates an HTTPS URL that uses the configured advertise
// address and client port for the API controller
func GetClientURL(cfg *kubeadmapi.InitConfiguration) string {
return "https://" + net.JoinHostPort(cfg.LocalAPIEndpoint.AdvertiseAddress, strconv.Itoa(constants.EtcdListenClientPort))
}
// GetPeerURL creates an HTTPS URL that uses the configured advertise
// address and peer port for the API controller
func GetPeerURL(cfg *kubeadmapi.InitConfiguration) string {
return "https://" + net.JoinHostPort(cfg.LocalAPIEndpoint.AdvertiseAddress, strconv.Itoa(constants.EtcdListenPeerPort))
}
// GetClientURLByIP creates an HTTPS URL based on an IP address
// and the client listening port.
func GetClientURLByIP(ip string) string {
return "https://" + net.JoinHostPort(ip, strconv.Itoa(constants.EtcdListenClientPort))
}

View File

@ -17,12 +17,15 @@ limitations under the License.
package etcd
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"testing"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
)
@ -308,3 +311,90 @@ func TestCheckConfigurationIsHA(t *testing.T) {
})
}
}
func testGetURL(t *testing.T, getURLFunc func(*kubeadmapi.InitConfiguration) string, port int) {
portStr := strconv.Itoa(port)
var tests = []struct {
name string
advertiseAddress string
expectedURL string
}{
{
name: "IPv4",
advertiseAddress: "10.10.10.10",
expectedURL: fmt.Sprintf("https://10.10.10.10:%s", portStr),
},
{
name: "IPv6",
advertiseAddress: "2001:db8::2",
expectedURL: fmt.Sprintf("https://[2001:db8::2]:%s", portStr),
},
{
name: "IPv4 localhost",
advertiseAddress: "127.0.0.1",
expectedURL: fmt.Sprintf("https://127.0.0.1:%s", portStr),
},
{
name: "IPv6 localhost",
advertiseAddress: "::1",
expectedURL: fmt.Sprintf("https://[::1]:%s", portStr),
},
}
for _, test := range tests {
cfg := &kubeadmapi.InitConfiguration{
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
AdvertiseAddress: test.advertiseAddress,
},
}
url := getURLFunc(cfg)
if url != test.expectedURL {
t.Errorf("expected %s, got %s", test.expectedURL, url)
}
}
}
func TestGetClientURL(t *testing.T) {
testGetURL(t, GetClientURL, constants.EtcdListenClientPort)
}
func TestGetPeerURL(t *testing.T) {
testGetURL(t, GetClientURL, constants.EtcdListenClientPort)
}
func TestGetClientURLByIP(t *testing.T) {
portStr := strconv.Itoa(constants.EtcdListenClientPort)
var tests = []struct {
name string
ip string
expectedURL string
}{
{
name: "IPv4",
ip: "10.10.10.10",
expectedURL: fmt.Sprintf("https://10.10.10.10:%s", portStr),
},
{
name: "IPv6",
ip: "2001:db8::2",
expectedURL: fmt.Sprintf("https://[2001:db8::2]:%s", portStr),
},
{
name: "IPv4 localhost",
ip: "127.0.0.1",
expectedURL: fmt.Sprintf("https://127.0.0.1:%s", portStr),
},
{
name: "IPv6 localhost",
ip: "::1",
expectedURL: fmt.Sprintf("https://[::1]:%s", portStr),
},
}
for _, test := range tests {
url := GetClientURLByIP(test.ip)
if url != test.expectedURL {
t.Errorf("expected %s, got %s", test.expectedURL, url)
}
}
}