Skip to content

Commit a735e77

Browse files
author
JochemTSR
committed
Initial support for IPv6 load balancer address
1 parent b6a2dec commit a735e77

6 files changed

+203
-5
lines changed

api/v1beta1/types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ 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+
// Whether to use the LoadBalancer's IPv6 address as the cluster endpoint instead of IPv4.
163+
// Useful if nodes are provisioned without IPv4 address. Defaults to 'false'.
164+
// +optional
165+
// +kubebuilder:default=false
166+
UseIPv6Endpoint bool `json:"useIPv6Endpoint,omitempty"`
161167
}
162168

163169
// LoadBalancerServiceSpec defines a Loadbalancer Target.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ spec:
155155
- lb21
156156
- lb31
157157
type: string
158+
useIPv6Endpoint:
159+
default: false
160+
description: Whether to use the LoadBalancer's IPv6 address as
161+
the cluster endpoint instead of IPv4. Useful if nodes are provisioned
162+
without IPv4 address. Defaults to 'false'.
163+
type: boolean
158164
type: object
159165
controlPlaneRegions:
160166
description: ControlPlaneRegion consists of a list of HCloud Regions

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,12 @@ spec:
177177
- lb21
178178
- lb31
179179
type: string
180+
useIPv6Endpoint:
181+
default: false
182+
description: Whether to use the LoadBalancer's IPv6 address
183+
as the cluster endpoint instead of IPv4. Useful if nodes
184+
are provisioned without IPv4 address. Defaults to 'false'.
185+
type: boolean
180186
type: object
181187
controlPlaneRegions:
182188
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
}

0 commit comments

Comments
 (0)