Skip to content

Commit 3769de9

Browse files
authored
Merge pull request kubernetes#85745 from aojea/kubeadm_etcd_ipv6
kubeadm: default etcd address based on the IP family
2 parents 6e0ce9f + 335a3e9 commit 3769de9

File tree

7 files changed

+118
-14
lines changed

7 files changed

+118
-14
lines changed

cmd/kubeadm/app/phases/controlplane/manifests.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,12 @@ func getAPIServerCommand(cfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint
162162
}
163163
} else {
164164
// Default to etcd static pod on localhost
165-
defaultArguments["etcd-servers"] = fmt.Sprintf("https://127.0.0.1:%d", kubeadmconstants.EtcdListenClientPort)
165+
// localhost IP family should be the same that the AdvertiseAddress
166+
etcdLocalhostAddress := "127.0.0.1"
167+
if utilsnet.IsIPv6String(localAPIEndpoint.AdvertiseAddress) {
168+
etcdLocalhostAddress = "::1"
169+
}
170+
defaultArguments["etcd-servers"] = fmt.Sprintf("https://%s", net.JoinHostPort(etcdLocalhostAddress, strconv.Itoa(kubeadmconstants.EtcdListenClientPort)))
166171
defaultArguments["etcd-cafile"] = filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdCACertName)
167172
defaultArguments["etcd-certfile"] = filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerEtcdClientCertName)
168173
defaultArguments["etcd-keyfile"] = filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerEtcdClientKeyName)
@@ -175,7 +180,7 @@ func getAPIServerCommand(cfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint
175180
}
176181
}
177182

178-
// TODO: The following code should be remvoved after dual-stack is GA.
183+
// TODO: The following code should be removed after dual-stack is GA.
179184
// Note: The user still retains the ability to explicitly set feature-gates and that value will overwrite this base value.
180185
if enabled, present := cfg.FeatureGates[features.IPv6DualStack]; present {
181186
defaultArguments["feature-gates"] = fmt.Sprintf("%s=%t", features.IPv6DualStack, enabled)

cmd/kubeadm/app/phases/controlplane/manifests_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ func TestGetAPIServerCommand(t *testing.T) {
266266
"--requestheader-allowed-names=front-proxy-client",
267267
"--authorization-mode=Node,RBAC",
268268
"--advertise-address=2001:db8::1",
269-
fmt.Sprintf("--etcd-servers=https://127.0.0.1:%d", kubeadmconstants.EtcdListenClientPort),
269+
fmt.Sprintf("--etcd-servers=https://[::1]:%d", kubeadmconstants.EtcdListenClientPort),
270270
"--etcd-cafile=" + testCertsDir + "/etcd/ca.crt",
271271
"--etcd-certfile=" + testCertsDir + "/apiserver-etcd-client.crt",
272272
"--etcd-keyfile=" + testCertsDir + "/apiserver-etcd-client.key",
@@ -278,7 +278,7 @@ func TestGetAPIServerCommand(t *testing.T) {
278278
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
279279
Etcd: kubeadmapi.Etcd{
280280
External: &kubeadmapi.ExternalEtcd{
281-
Endpoints: []string{"https://8.6.4.1:2379", "https://8.6.4.2:2379"},
281+
Endpoints: []string{"https://[2001:abcd:bcda::1]:2379", "https://[2001:abcd:bcda::2]:2379"},
282282
CAFile: "fuz",
283283
CertFile: "fiz",
284284
KeyFile: "faz",
@@ -311,7 +311,7 @@ func TestGetAPIServerCommand(t *testing.T) {
311311
"--requestheader-allowed-names=front-proxy-client",
312312
"--authorization-mode=Node,RBAC",
313313
"--advertise-address=2001:db8::1",
314-
"--etcd-servers=https://8.6.4.1:2379,https://8.6.4.2:2379",
314+
"--etcd-servers=https://[2001:abcd:bcda::1]:2379,https://[2001:abcd:bcda::2]:2379",
315315
"--etcd-cafile=fuz",
316316
"--etcd-certfile=fiz",
317317
"--etcd-keyfile=faz",
@@ -323,7 +323,7 @@ func TestGetAPIServerCommand(t *testing.T) {
323323
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"},
324324
Etcd: kubeadmapi.Etcd{
325325
External: &kubeadmapi.ExternalEtcd{
326-
Endpoints: []string{"http://127.0.0.1:2379", "http://127.0.0.1:2380"},
326+
Endpoints: []string{"http://[::1]:2379", "http://[::1]:2380"},
327327
},
328328
},
329329
CertificatesDir: testCertsDir,
@@ -353,7 +353,7 @@ func TestGetAPIServerCommand(t *testing.T) {
353353
"--requestheader-allowed-names=front-proxy-client",
354354
"--authorization-mode=Node,RBAC",
355355
"--advertise-address=2001:db8::1",
356-
"--etcd-servers=http://127.0.0.1:2379,http://127.0.0.1:2380",
356+
"--etcd-servers=http://[::1]:2379,http://[::1]:2380",
357357
},
358358
},
359359
{

cmd/kubeadm/app/phases/etcd/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ go_library(
3535
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
3636
"//vendor/github.com/pkg/errors:go_default_library",
3737
"//vendor/k8s.io/klog:go_default_library",
38+
"//vendor/k8s.io/utils/net:go_default_library",
3839
],
3940
)
4041

cmd/kubeadm/app/phases/etcd/local.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ package etcd
1818

1919
import (
2020
"fmt"
21+
"net"
2122
"path/filepath"
23+
"strconv"
2224
"strings"
2325
"time"
2426

@@ -33,6 +35,7 @@ import (
3335
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
3436
etcdutil "k8s.io/kubernetes/cmd/kubeadm/app/util/etcd"
3537
staticpodutil "k8s.io/kubernetes/cmd/kubeadm/app/util/staticpod"
38+
utilsnet "k8s.io/utils/net"
3639
)
3740

3841
const (
@@ -176,7 +179,8 @@ func GetEtcdPodSpec(cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.A
176179
etcdVolumeName: staticpodutil.NewVolume(etcdVolumeName, cfg.Etcd.Local.DataDir, &pathType),
177180
certsVolumeName: staticpodutil.NewVolume(certsVolumeName, cfg.CertificatesDir+"/etcd", &pathType),
178181
}
179-
probeHostname, probePort, probeScheme := staticpodutil.GetEtcdProbeEndpoint(&cfg.Etcd)
182+
// probeHostname returns the correct localhost IP address family based on the endpoint AdvertiseAddress
183+
probeHostname, probePort, probeScheme := staticpodutil.GetEtcdProbeEndpoint(&cfg.Etcd, utilsnet.IsIPv6String(endpoint.AdvertiseAddress))
180184
return staticpodutil.ComponentPod(v1.Container{
181185
Name: kubeadmconstants.Etcd,
182186
Command: getEtcdCommand(cfg, endpoint, nodeName, initialCluster),
@@ -193,9 +197,14 @@ func GetEtcdPodSpec(cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.A
193197

194198
// getEtcdCommand builds the right etcd command from the given config object
195199
func getEtcdCommand(cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.APIEndpoint, nodeName string, initialCluster []etcdutil.Member) []string {
200+
// localhost IP family should be the same that the AdvertiseAddress
201+
etcdLocalhostAddress := "127.0.0.1"
202+
if utilsnet.IsIPv6String(endpoint.AdvertiseAddress) {
203+
etcdLocalhostAddress = "::1"
204+
}
196205
defaultArguments := map[string]string{
197206
"name": nodeName,
198-
"listen-client-urls": fmt.Sprintf("%s,%s", etcdutil.GetClientURLByIP("127.0.0.1"), etcdutil.GetClientURL(endpoint)),
207+
"listen-client-urls": fmt.Sprintf("%s,%s", etcdutil.GetClientURLByIP(etcdLocalhostAddress), etcdutil.GetClientURL(endpoint)),
199208
"advertise-client-urls": etcdutil.GetClientURL(endpoint),
200209
"listen-peer-urls": etcdutil.GetPeerURL(endpoint),
201210
"initial-advertise-peer-urls": etcdutil.GetPeerURL(endpoint),
@@ -209,7 +218,7 @@ func getEtcdCommand(cfg *kubeadmapi.ClusterConfiguration, endpoint *kubeadmapi.A
209218
"peer-trusted-ca-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.EtcdCACertName),
210219
"peer-client-cert-auth": "true",
211220
"snapshot-count": "10000",
212-
"listen-metrics-urls": fmt.Sprintf("http://127.0.0.1:%d", kubeadmconstants.EtcdMetricsPort),
221+
"listen-metrics-urls": fmt.Sprintf("http://%s", net.JoinHostPort(etcdLocalhostAddress, strconv.Itoa(kubeadmconstants.EtcdMetricsPort))),
213222
}
214223

215224
if len(initialCluster) == 0 {

cmd/kubeadm/app/phases/etcd/local_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ func TestGetEtcdCommand(t *testing.T) {
266266
expected: []string{
267267
"etcd",
268268
"--name=foo",
269-
fmt.Sprintf("--listen-client-urls=https://127.0.0.1:%d,https://[2001:db8::3]:%d", kubeadmconstants.EtcdListenClientPort, kubeadmconstants.EtcdListenClientPort),
270-
fmt.Sprintf("--listen-metrics-urls=http://127.0.0.1:%d", kubeadmconstants.EtcdMetricsPort),
269+
fmt.Sprintf("--listen-client-urls=https://[::1]:%d,https://[2001:db8::3]:%d", kubeadmconstants.EtcdListenClientPort, kubeadmconstants.EtcdListenClientPort),
270+
fmt.Sprintf("--listen-metrics-urls=http://[::1]:%d", kubeadmconstants.EtcdMetricsPort),
271271
fmt.Sprintf("--advertise-client-urls=https://[2001:db8::3]:%d", kubeadmconstants.EtcdListenClientPort),
272272
fmt.Sprintf("--listen-peer-urls=https://[2001:db8::3]:%d", kubeadmconstants.EtcdListenPeerPort),
273273
fmt.Sprintf("--initial-advertise-peer-urls=https://[2001:db8::3]:%d", kubeadmconstants.EtcdListenPeerPort),

cmd/kubeadm/app/util/staticpod/utils.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,11 @@ func GetSchedulerProbeAddress(cfg *kubeadmapi.ClusterConfiguration) string {
269269
// GetEtcdProbeEndpoint takes a kubeadm Etcd configuration object and attempts to parse
270270
// the first URL in the listen-metrics-urls argument, returning an etcd probe hostname,
271271
// port and scheme
272-
func GetEtcdProbeEndpoint(cfg *kubeadmapi.Etcd) (string, int, v1.URIScheme) {
272+
func GetEtcdProbeEndpoint(cfg *kubeadmapi.Etcd, isIPv6 bool) (string, int, v1.URIScheme) {
273273
localhost := "127.0.0.1"
274+
if isIPv6 {
275+
localhost = "::1"
276+
}
274277
if cfg.Local == nil || cfg.Local.ExtraArgs == nil {
275278
return localhost, kubeadmconstants.EtcdMetricsPort, v1.URISchemeHTTP
276279
}

cmd/kubeadm/app/util/staticpod/utils_test.go

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ func TestGetAPIServerProbeAddress(t *testing.T) {
6565
},
6666
expected: "10.10.10.10",
6767
},
68+
{
69+
desc: "filled in ipv6 AdvertiseAddress endpoint returns it",
70+
endpoint: &kubeadmapi.APIEndpoint{
71+
AdvertiseAddress: "2001:abcd:bcda::1",
72+
},
73+
expected: "2001:abcd:bcda::1",
74+
},
6875
}
6976

7077
for _, test := range tests {
@@ -103,6 +110,17 @@ func TestGetControllerManagerProbeAddress(t *testing.T) {
103110
},
104111
expected: "10.10.10.10",
105112
},
113+
{
114+
desc: "setting controller manager extra ipv6 address arg to something acknowledges it",
115+
cfg: &kubeadmapi.ClusterConfiguration{
116+
ControllerManager: kubeadmapi.ControlPlaneComponent{
117+
ExtraArgs: map[string]string{
118+
kubeControllerManagerAddressArg: "2001:abcd:bcda::1",
119+
},
120+
},
121+
},
122+
expected: "2001:abcd:bcda::1",
123+
},
106124
}
107125

108126
for _, test := range tests {
@@ -119,6 +137,7 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
119137
var tests = []struct {
120138
name string
121139
cfg *kubeadmapi.Etcd
140+
isIPv6 bool
122141
expectedHostname string
123142
expectedPort int
124143
expectedScheme v1.URIScheme
@@ -131,6 +150,7 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
131150
"listen-metrics-urls": "https://1.2.3.4:1234,https://4.3.2.1:2381"},
132151
},
133152
},
153+
isIPv6: false,
134154
expectedHostname: "1.2.3.4",
135155
expectedPort: 1234,
136156
expectedScheme: v1.URISchemeHTTPS,
@@ -143,6 +163,7 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
143163
"listen-metrics-urls": "http://1.2.3.4:1234"},
144164
},
145165
},
166+
isIPv6: false,
146167
expectedHostname: "1.2.3.4",
147168
expectedPort: 1234,
148169
expectedScheme: v1.URISchemeHTTP,
@@ -155,6 +176,7 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
155176
"listen-metrics-urls": "1.2.3.4"},
156177
},
157178
},
179+
isIPv6: false,
158180
expectedHostname: "127.0.0.1",
159181
expectedPort: kubeadmconstants.EtcdMetricsPort,
160182
expectedScheme: v1.URISchemeHTTP,
@@ -167,23 +189,87 @@ func TestGetEtcdProbeEndpoint(t *testing.T) {
167189
"listen-metrics-urls": "https://1.2.3.4"},
168190
},
169191
},
192+
isIPv6: false,
170193
expectedHostname: "1.2.3.4",
171194
expectedPort: kubeadmconstants.EtcdMetricsPort,
172195
expectedScheme: v1.URISchemeHTTPS,
173196
},
197+
{
198+
name: "etcd probe URL from two IPv6 URLs",
199+
cfg: &kubeadmapi.Etcd{
200+
Local: &kubeadmapi.LocalEtcd{
201+
ExtraArgs: map[string]string{
202+
"listen-metrics-urls": "https://[2001:abcd:bcda::1]:1234,https://[2001:abcd:bcda::2]:2381"},
203+
},
204+
},
205+
isIPv6: true,
206+
expectedHostname: "2001:abcd:bcda::1",
207+
expectedPort: 1234,
208+
expectedScheme: v1.URISchemeHTTPS,
209+
},
210+
{
211+
name: "etcd probe localhost IPv6 URL with HTTP scheme",
212+
cfg: &kubeadmapi.Etcd{
213+
Local: &kubeadmapi.LocalEtcd{
214+
ExtraArgs: map[string]string{
215+
"listen-metrics-urls": "http://[::1]:1234"},
216+
},
217+
},
218+
isIPv6: true,
219+
expectedHostname: "::1",
220+
expectedPort: 1234,
221+
expectedScheme: v1.URISchemeHTTP,
222+
},
223+
{
224+
name: "etcd probe IPv6 URL with HTTP scheme",
225+
cfg: &kubeadmapi.Etcd{
226+
Local: &kubeadmapi.LocalEtcd{
227+
ExtraArgs: map[string]string{
228+
"listen-metrics-urls": "http://[2001:abcd:bcda::1]:1234"},
229+
},
230+
},
231+
isIPv6: true,
232+
expectedHostname: "2001:abcd:bcda::1",
233+
expectedPort: 1234,
234+
expectedScheme: v1.URISchemeHTTP,
235+
},
236+
{
237+
name: "etcd probe IPv6 URL without port",
238+
cfg: &kubeadmapi.Etcd{
239+
Local: &kubeadmapi.LocalEtcd{
240+
ExtraArgs: map[string]string{
241+
"listen-metrics-urls": "https://[2001:abcd:bcda::1]"},
242+
},
243+
},
244+
isIPv6: true,
245+
expectedHostname: "2001:abcd:bcda::1",
246+
expectedPort: kubeadmconstants.EtcdMetricsPort,
247+
expectedScheme: v1.URISchemeHTTPS,
248+
},
174249
{
175250
name: "etcd probe URL from defaults",
176251
cfg: &kubeadmapi.Etcd{
177252
Local: &kubeadmapi.LocalEtcd{},
178253
},
254+
isIPv6: false,
179255
expectedHostname: "127.0.0.1",
180256
expectedPort: kubeadmconstants.EtcdMetricsPort,
181257
expectedScheme: v1.URISchemeHTTP,
182258
},
259+
{
260+
name: "etcd probe URL from defaults if IPv6",
261+
cfg: &kubeadmapi.Etcd{
262+
Local: &kubeadmapi.LocalEtcd{},
263+
},
264+
isIPv6: true,
265+
expectedHostname: "::1",
266+
expectedPort: kubeadmconstants.EtcdMetricsPort,
267+
expectedScheme: v1.URISchemeHTTP,
268+
},
183269
}
184270
for _, rt := range tests {
185271
t.Run(rt.name, func(t *testing.T) {
186-
hostname, port, scheme := GetEtcdProbeEndpoint(rt.cfg)
272+
hostname, port, scheme := GetEtcdProbeEndpoint(rt.cfg, rt.isIPv6)
187273
if hostname != rt.expectedHostname {
188274
t.Errorf("%q test case failed:\n\texpected hostname: %s\n\tgot: %s",
189275
rt.name, rt.expectedHostname, hostname)

0 commit comments

Comments
 (0)