Skip to content

Commit 375fdf0

Browse files
JochemTSRDjokkum
authored andcommitted
support for IPv6 load balancer address
1 parent b6a2dec commit 375fdf0

7 files changed

+209
-7
lines changed

api/v1beta1/types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,13 @@ type LoadBalancerSpec struct {
158158

159159
// Region contains the name of the HCloud location the load balancer is running.
160160
Region Region `json:"region,omitempty"`
161+
162+
// UseIPv6Endpoint defines whether to use the LoadBalancer's IPv6 address as
163+
// the cluster endpoint instead of IPv4. This is useful if nodes are provisioned
164+
// without IPv4 address. Defaults to 'false'.
165+
// +optional
166+
// +kubebuilder:default=false
167+
UseIPv6Endpoint bool `json:"useIPv6Endpoint,omitempty"`
161168
}
162169

163170
// LoadBalancerServiceSpec defines a Loadbalancer Target.

config/crd/bases/infrastructure.cluster.x-k8s.io_hetznerclusters.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ spec:
155155
- lb21
156156
- lb31
157157
type: string
158+
useIPv6Endpoint:
159+
default: false
160+
description: UseIPv6Endpoint defines whether to use the LoadBalancer's
161+
IPv6 address as the cluster endpoint instead of IPv4. This is
162+
useful if nodes are provisioned without IPv4 address. Defaults
163+
to 'false'.
164+
type: boolean
158165
type: object
159166
controlPlaneRegions:
160167
description: ControlPlaneRegion consists of a list of HCloud Regions

config/crd/bases/infrastructure.cluster.x-k8s.io_hetznerclustertemplates.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,13 @@ spec:
177177
- lb21
178178
- lb31
179179
type: string
180+
useIPv6Endpoint:
181+
default: false
182+
description: UseIPv6Endpoint defines whether to use the
183+
LoadBalancer's IPv6 address as the cluster endpoint
184+
instead of IPv4. This is useful if nodes are provisioned
185+
without IPv4 address. Defaults to 'false'.
186+
type: boolean
180187
type: object
181188
controlPlaneRegions:
182189
description: ControlPlaneRegion consists of a list of HCloud

controllers/controllers_suite_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,10 @@ func getDefaultHetznerClusterSpec() infrav1.HetznerClusterSpec {
154154
Protocol: "tcp",
155155
},
156156
},
157-
Port: 6443,
158-
Region: "fsn1",
159-
Type: "lb11",
157+
Port: 6443,
158+
Region: "fsn1",
159+
Type: "lb11",
160+
UseIPv6Endpoint: false,
160161
},
161162
ControlPlaneEndpoint: &clusterv1.APIEndpoint{},
162163
ControlPlaneRegions: []infrav1.Region{"fsn1"},

controllers/hetznercluster_controller.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,13 @@ func (r *HetznerClusterReconciler) reconcileNormal(ctx context.Context, clusterS
243243

244244
func processControlPlaneEndpoint(hetznerCluster *infrav1.HetznerCluster) {
245245
if hetznerCluster.Spec.ControlPlaneLoadBalancer.Enabled {
246-
if hetznerCluster.Status.ControlPlaneLoadBalancer.IPv4 != "<nil>" {
247-
defaultHost := hetznerCluster.Status.ControlPlaneLoadBalancer.IPv4
246+
ip := hetznerCluster.Status.ControlPlaneLoadBalancer.IPv4
247+
if hetznerCluster.Spec.ControlPlaneLoadBalancer.UseIPv6Endpoint {
248+
ip = hetznerCluster.Status.ControlPlaneLoadBalancer.IPv6
249+
}
250+
251+
if ip != "<nil>" {
252+
defaultHost := ip
248253
defaultPort := int32(hetznerCluster.Spec.ControlPlaneLoadBalancer.Port)
249254

250255
if hetznerCluster.Spec.ControlPlaneEndpoint == nil {

controllers/hetznercluster_controller_test.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,4 +1208,178 @@ func TestSetControlPlaneEndpoint(t *testing.T) {
12081208
t.Fatalf("return value should be true")
12091209
}
12101210
})
1211+
1212+
t.Run("return false if load balancer is enabled with UseIPv6Endpoint and IPv6 is 'nil'. ControlPlaneEndpoint should not change", func(t *testing.T) {
1213+
hetznerCluster := &infrav1.HetznerCluster{
1214+
Spec: infrav1.HetznerClusterSpec{
1215+
ControlPlaneLoadBalancer: infrav1.LoadBalancerSpec{
1216+
UseIPv6Endpoint: true,
1217+
Enabled: true,
1218+
},
1219+
ControlPlaneEndpoint: nil,
1220+
},
1221+
Status: infrav1.HetznerClusterStatus{
1222+
ControlPlaneLoadBalancer: &infrav1.LoadBalancerStatus{
1223+
IPv4: "xyz",
1224+
IPv6: "<nil>",
1225+
},
1226+
},
1227+
}
1228+
1229+
processControlPlaneEndpoint(hetznerCluster)
1230+
1231+
if hetznerCluster.Spec.ControlPlaneEndpoint != nil {
1232+
t.Fatalf("ControlPlaneEndpoint should not change. It should remain nil")
1233+
}
1234+
1235+
if hetznerCluster.Status.Ready != false {
1236+
t.Fatalf("return value should be false")
1237+
}
1238+
1239+
if !conditions.Has(hetznerCluster, infrav1.ControlPlaneEndpointSetCondition) {
1240+
t.Fatalf("ControlPlaneEndpointSetCondition should exist")
1241+
}
1242+
1243+
condition := conditions.Get(hetznerCluster, infrav1.ControlPlaneEndpointSetCondition)
1244+
if condition.Status != corev1.ConditionFalse {
1245+
t.Fatalf("condition status should be false")
1246+
}
1247+
})
1248+
1249+
t.Run("return true if load balancer is enabled with UseIPv6Endpoint, IPv6 is not nil and ControlPlaneEndpoint is nil. Values of ControlPlaneEndpoint.Host and ControlPlaneEndpoint.Port will get updated", func(t *testing.T) {
1250+
hetznerCluster := &infrav1.HetznerCluster{
1251+
Spec: infrav1.HetznerClusterSpec{
1252+
ControlPlaneLoadBalancer: infrav1.LoadBalancerSpec{
1253+
UseIPv6Endpoint: true,
1254+
Enabled: true,
1255+
Port: 11,
1256+
},
1257+
ControlPlaneEndpoint: nil,
1258+
},
1259+
Status: infrav1.HetznerClusterStatus{
1260+
ControlPlaneLoadBalancer: &infrav1.LoadBalancerStatus{
1261+
IPv6: "abc",
1262+
},
1263+
},
1264+
}
1265+
1266+
processControlPlaneEndpoint(hetznerCluster)
1267+
1268+
if hetznerCluster.Spec.ControlPlaneEndpoint.Host != "abc" {
1269+
t.Fatalf("Wrong value for Host set. Got: %s, Want: 'abc'", hetznerCluster.Spec.ControlPlaneEndpoint.Host)
1270+
}
1271+
1272+
if hetznerCluster.Spec.ControlPlaneEndpoint.Port != 11 {
1273+
t.Fatalf("Wrong value for Port set. Got: %d, Want: 11", hetznerCluster.Spec.ControlPlaneEndpoint.Port)
1274+
}
1275+
1276+
if hetznerCluster.Status.Ready != true {
1277+
t.Fatalf("return value should be true")
1278+
}
1279+
})
1280+
1281+
t.Run("return true if load balancer is enabled with UseIPv6Endopint, IPv6 is not nil, ControlPlaneEndpoint.Host is an empty string and ControlPlaneEndpoint.Port is 0. Values of ControlPlaneEndpoint.Host and ControlPlaneEndpoint.Port should update", func(t *testing.T) {
1282+
hetznerCluster := &infrav1.HetznerCluster{
1283+
Spec: infrav1.HetznerClusterSpec{
1284+
ControlPlaneLoadBalancer: infrav1.LoadBalancerSpec{
1285+
UseIPv6Endpoint: true,
1286+
Enabled: true,
1287+
Port: 11,
1288+
},
1289+
ControlPlaneEndpoint: &clusterv1.APIEndpoint{
1290+
Host: "",
1291+
Port: 0,
1292+
},
1293+
},
1294+
Status: infrav1.HetznerClusterStatus{
1295+
ControlPlaneLoadBalancer: &infrav1.LoadBalancerStatus{
1296+
IPv6: "abc",
1297+
},
1298+
},
1299+
}
1300+
1301+
processControlPlaneEndpoint(hetznerCluster)
1302+
1303+
if hetznerCluster.Spec.ControlPlaneEndpoint.Host != "abc" {
1304+
t.Fatalf("Wrong value for Host set. Got: %s, Want: 'abc'", hetznerCluster.Spec.ControlPlaneEndpoint.Host)
1305+
}
1306+
1307+
if hetznerCluster.Spec.ControlPlaneEndpoint.Port != 11 {
1308+
t.Fatalf("Wrong value for Port set. Got: %d, Want: 11", hetznerCluster.Spec.ControlPlaneEndpoint.Port)
1309+
}
1310+
1311+
if hetznerCluster.Status.Ready != true {
1312+
t.Fatalf("return value should be true")
1313+
}
1314+
})
1315+
1316+
t.Run("return true if load balancer is enabled with UseIPv6Endpoint, IPv6 is not nil, ControlPlaneEndpoint.Host is an empty string and ControlPlaneEndpoint.Port is 21. Value of ControlPlaneEndpoint.Host will change and ControlPlaneEndpoint.Port should remain same", func(t *testing.T) {
1317+
hetznerCluster := &infrav1.HetznerCluster{
1318+
Spec: infrav1.HetznerClusterSpec{
1319+
ControlPlaneLoadBalancer: infrav1.LoadBalancerSpec{
1320+
UseIPv6Endpoint: true,
1321+
Enabled: true,
1322+
Port: 11,
1323+
},
1324+
ControlPlaneEndpoint: &clusterv1.APIEndpoint{
1325+
Host: "",
1326+
Port: 21,
1327+
},
1328+
},
1329+
Status: infrav1.HetznerClusterStatus{
1330+
ControlPlaneLoadBalancer: &infrav1.LoadBalancerStatus{
1331+
IPv6: "abc",
1332+
},
1333+
},
1334+
}
1335+
1336+
processControlPlaneEndpoint(hetznerCluster)
1337+
1338+
if hetznerCluster.Spec.ControlPlaneEndpoint.Host != "abc" {
1339+
t.Fatalf("Wrong value for Host set. Got: %s, Want: 'abc'", hetznerCluster.Spec.ControlPlaneEndpoint.Host)
1340+
}
1341+
1342+
if hetznerCluster.Spec.ControlPlaneEndpoint.Port != 21 {
1343+
t.Fatalf("Wrong value for Port set. Got: %d, Want: 21", hetznerCluster.Spec.ControlPlaneEndpoint.Port)
1344+
}
1345+
1346+
if hetznerCluster.Status.Ready != true {
1347+
t.Fatalf("return value should be true")
1348+
}
1349+
})
1350+
1351+
t.Run("return true if load balancer is enabled with UseIPv6Endpoint, IPv6 is not nil, ControlPlaneEndpoint.Host is 'xyz' and ControlPlaneEndpoint.Port is 21. Value of ControlPlaneEndpoint.Host and ControlPlaneEndpoint.Port should remain unchanged", func(t *testing.T) {
1352+
hetznerCluster := &infrav1.HetznerCluster{
1353+
Spec: infrav1.HetznerClusterSpec{
1354+
ControlPlaneLoadBalancer: infrav1.LoadBalancerSpec{
1355+
UseIPv6Endpoint: true,
1356+
Enabled: true,
1357+
Port: 11,
1358+
},
1359+
ControlPlaneEndpoint: &clusterv1.APIEndpoint{
1360+
Host: "xyz",
1361+
Port: 21,
1362+
},
1363+
},
1364+
Status: infrav1.HetznerClusterStatus{
1365+
ControlPlaneLoadBalancer: &infrav1.LoadBalancerStatus{
1366+
IPv6: "abc",
1367+
},
1368+
},
1369+
}
1370+
1371+
processControlPlaneEndpoint(hetznerCluster)
1372+
1373+
if hetznerCluster.Spec.ControlPlaneEndpoint.Host != "xyz" {
1374+
t.Fatalf("Wrong value for Host set. Got: %s, Want: 'xyz'", hetznerCluster.Spec.ControlPlaneEndpoint.Host)
1375+
}
1376+
1377+
if hetznerCluster.Spec.ControlPlaneEndpoint.Port != 21 {
1378+
t.Fatalf("Wrong value for Port set. Got: %d, Want: 21", hetznerCluster.Spec.ControlPlaneEndpoint.Port)
1379+
}
1380+
1381+
if hetznerCluster.Status.Ready != true {
1382+
t.Fatalf("return value should be true")
1383+
}
1384+
})
12111385
}

hack/kind-dev.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,9 @@ kindV1Alpha4Cluster:
4545
- role: control-plane
4646
image: kindest/node:${CLUSTER_VERSION}
4747
networking:
48-
podSubnet: "10.244.0.0/16"
49-
serviceSubnet: "10.96.0.0/12"
48+
podSubnet: "10.244.0.0/16,fd00:10:244::/56"
49+
serviceSubnet: "10.96.0.0/12,fd00:10:96::/112"
50+
ipFamily: dual
5051
EOF
5152
}
5253

0 commit comments

Comments
 (0)