Skip to content

Commit 38bde6d

Browse files
committed
Adding IPVersion to load balancer frontend config
Adding a version option for the users to create dualstack frontend configurations in the load balancer.
1 parent 67c29b1 commit 38bde6d

7 files changed

Lines changed: 146 additions & 5 deletions

File tree

api/v1beta1/types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,11 @@ const (
388388
Public = LBType("Public")
389389
)
390390

391+
const (
392+
// IPv6 is the value for IPv6 address version.
393+
IPv6 = "IPv6"
394+
)
395+
391396
// FrontendIP defines a load balancer frontend IP configuration.
392397
type FrontendIP struct {
393398
// +kubebuilder:validation:MinLength=1

api/v1beta1/types_class.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,4 +535,9 @@ type SecurityGroupClass struct {
535535
type FrontendIPClass struct {
536536
// +optional
537537
PrivateIPAddress string `json:"privateIP,omitempty"`
538+
// IPVersion specifies the IP version for this frontend IP. Valid values are "IPv4" and "IPv6".
539+
// Defaults to "IPv4" if not specified.
540+
// +kubebuilder:validation:Enum=IPv4;IPv6
541+
// +optional
542+
IPVersion string `json:"ipVersion,omitempty"`
538543
}

azure/scope/cluster.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ func (s *ClusterScope) PublicIPSpecs() []azure.ResourceSpecGetter {
163163
Name: ip.PublicIP.Name,
164164
ResourceGroup: s.ResourceGroup(),
165165
ClusterName: s.ClusterName(),
166-
DNSName: "", // Set to default value
167-
IsIPv6: false, // Set to default value
166+
DNSName: "", // Set to default value
167+
IsIPv6: ip.IPVersion == infrav1.IPv6,
168168
Location: s.Location(),
169169
ExtendedLocation: s.ExtendedLocation(),
170170
FailureDomains: s.FailureDomains(),
@@ -179,7 +179,7 @@ func (s *ClusterScope) PublicIPSpecs() []azure.ResourceSpecGetter {
179179
Name: s.APIServerPublicIP().Name,
180180
ResourceGroup: s.ResourceGroup(),
181181
DNSName: s.APIServerPublicIP().DNSName,
182-
IsIPv6: false, // Currently azure requires an IPv4 lb rule to enable IPv6
182+
IsIPv6: s.APIServerLB().FrontendIPs[0].IPVersion == infrav1.IPv6,
183183
ClusterName: s.ClusterName(),
184184
Location: s.Location(),
185185
ExtendedLocation: s.ExtendedLocation(),
@@ -199,8 +199,8 @@ func (s *ClusterScope) PublicIPSpecs() []azure.ResourceSpecGetter {
199199
Name: ip.PublicIP.Name,
200200
ResourceGroup: s.ResourceGroup(),
201201
ClusterName: s.ClusterName(),
202-
DNSName: "", // Set to default value
203-
IsIPv6: false, // Set to default value
202+
DNSName: "", // Set to default value
203+
IsIPv6: ip.IPVersion == infrav1.IPv6,
204204
Location: s.Location(),
205205
ExtendedLocation: s.ExtendedLocation(),
206206
FailureDomains: s.FailureDomains(),

azure/services/loadbalancers/loadbalancers_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,58 @@ var (
132132
},
133133
}
134134

135+
fakeInternalAPILBSpecIPv6 = LBSpec{
136+
Name: "my-private-lb",
137+
ResourceGroup: "my-rg",
138+
SubscriptionID: "123",
139+
ClusterName: "my-cluster",
140+
Location: "my-location",
141+
VNetName: "my-vnet",
142+
VNetResourceGroup: "my-rg",
143+
Role: infrav1.APIServerRole,
144+
Type: infrav1.Internal,
145+
SKU: infrav1.SKUStandard,
146+
SubnetName: "my-cp-subnet",
147+
BackendPoolName: "my-private-lb-backendPool",
148+
IdleTimeoutInMinutes: ptr.To[int32](4),
149+
FrontendIPConfigs: []infrav1.FrontendIP{
150+
{
151+
Name: "my-private-lb-frontEnd-ipv6",
152+
FrontendIPClass: infrav1.FrontendIPClass{
153+
IPVersion: infrav1.IPv6,
154+
},
155+
},
156+
},
157+
APIServerPort: 6443,
158+
}
159+
160+
fakePublicAPILBSpecIPv6 = LBSpec{
161+
Name: "my-publiclb",
162+
ResourceGroup: "my-rg",
163+
SubscriptionID: "123",
164+
ClusterName: "my-cluster",
165+
Location: "my-location",
166+
Role: infrav1.APIServerRole,
167+
Type: infrav1.Public,
168+
SKU: infrav1.SKUStandard,
169+
SubnetName: "my-cp-subnet",
170+
BackendPoolName: "my-publiclb-backendPool",
171+
IdleTimeoutInMinutes: ptr.To[int32](4),
172+
FrontendIPConfigs: []infrav1.FrontendIP{
173+
{
174+
Name: "my-publiclb-frontEnd-ipv6",
175+
FrontendIPClass: infrav1.FrontendIPClass{
176+
IPVersion: infrav1.IPv6,
177+
},
178+
PublicIP: &infrav1.PublicIPSpec{
179+
Name: "my-publicip-ipv6",
180+
DNSName: "my-cluster.12345.mydomain.com",
181+
},
182+
},
183+
},
184+
APIServerPort: 6443,
185+
}
186+
135187
internalError = &azcore.ResponseError{
136188
RawResponse: &http.Response{
137189
Body: io.NopCloser(strings.NewReader("#: Internal Server Error: StatusCode=500")),

azure/services/loadbalancers/spec.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@ func getFrontendIPConfigs(lbSpec LBSpec) ([]*armnetwork.FrontendIPConfiguration,
177177
},
178178
PrivateIPAddress: ptr.To(ipConfig.PrivateIPAddress),
179179
}
180+
if ipConfig.IPVersion == infrav1.IPv6 {
181+
properties.PrivateIPAddressVersion = ptr.To(armnetwork.IPVersionIPv6)
182+
properties.PrivateIPAllocationMethod = ptr.To(armnetwork.IPAllocationMethodDynamic)
183+
properties.PrivateIPAddress = nil
184+
}
180185
} else {
181186
properties = armnetwork.FrontendIPConfigurationPropertiesFormat{
182187
PublicIPAddress: &armnetwork.PublicIPAddress{

azure/services/loadbalancers/spec_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,36 @@ func TestParameters(t *testing.T) {
178178
},
179179
expectedError: "",
180180
},
181+
{
182+
name: "internal API load balancer with IPv6 frontend",
183+
spec: &fakeInternalAPILBSpecIPv6,
184+
existing: nil,
185+
expect: func(g *WithT, result any) {
186+
g.Expect(result).To(BeAssignableToTypeOf(armnetwork.LoadBalancer{}))
187+
lb := result.(armnetwork.LoadBalancer)
188+
g.Expect(lb.Properties.FrontendIPConfigurations).To(HaveLen(1))
189+
frontendIP := lb.Properties.FrontendIPConfigurations[0]
190+
g.Expect(*frontendIP.Name).To(Equal("my-private-lb-frontEnd-ipv6"))
191+
g.Expect(*frontendIP.Properties.PrivateIPAllocationMethod).To(Equal(armnetwork.IPAllocationMethodDynamic))
192+
g.Expect(*frontendIP.Properties.PrivateIPAddressVersion).To(Equal(armnetwork.IPVersionIPv6))
193+
g.Expect(frontendIP.Properties.PrivateIPAddress).To(BeNil())
194+
},
195+
expectedError: "",
196+
},
197+
{
198+
name: "public API load balancer with IPv6 frontend",
199+
spec: &fakePublicAPILBSpecIPv6,
200+
existing: nil,
201+
expect: func(g *WithT, result any) {
202+
g.Expect(result).To(BeAssignableToTypeOf(armnetwork.LoadBalancer{}))
203+
lb := result.(armnetwork.LoadBalancer)
204+
g.Expect(lb.Properties.FrontendIPConfigurations).To(HaveLen(1))
205+
frontendIP := lb.Properties.FrontendIPConfigurations[0]
206+
g.Expect(*frontendIP.Name).To(Equal("my-publiclb-frontEnd-ipv6"))
207+
g.Expect(*frontendIP.Properties.PublicIPAddress.ID).To(Equal("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/publicIPAddresses/my-publicip-ipv6"))
208+
},
209+
expectedError: "",
210+
},
181211
}
182212
for _, tc := range testcases {
183213
t.Run(tc.name, func(t *testing.T) {

docs/book/src/self-managed/api-server-endpoint.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,50 @@ Note that `dns` is the FQDN associated to your public IP address (look for "DNS
101101

102102
When you BYO api server IP, CAPZ does not manage its lifecycle, ie. the IP will not get deleted as part of cluster deletion.
103103

104+
### Frontend IP Version
105+
106+
By default, frontend IPs are IPv4. You can configure individual frontend IPs to use IPv6 by setting the `ipVersion` field to `"IPv6"`.
107+
108+
For a public load balancer with an IPv6 frontend:
109+
110+
```yaml
111+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
112+
kind: AzureCluster
113+
metadata:
114+
name: my-cluster
115+
namespace: default
116+
spec:
117+
location: eastus
118+
networkSpec:
119+
apiServerLB:
120+
type: Public
121+
frontendIPs:
122+
- name: lb-public-ip-frontend-ipv6
123+
ipVersion: IPv6
124+
publicIP:
125+
name: my-public-ipv6
126+
```
127+
128+
For an internal load balancer with an IPv6 frontend:
129+
130+
```yaml
131+
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
132+
kind: AzureCluster
133+
metadata:
134+
name: my-private-cluster
135+
namespace: default
136+
spec:
137+
location: eastus
138+
networkSpec:
139+
apiServerLB:
140+
type: Internal
141+
frontendIPs:
142+
- name: lb-private-ip-frontend-ipv6
143+
ipVersion: IPv6
144+
```
145+
146+
Note that IPv6 internal frontend IPs use dynamic allocation and do not support specifying a `privateIP` address.
147+
104148
### Load Balancer SKU
105149

106150
At this time, CAPZ only supports Azure Standard Load Balancers. See [SKU comparison](https://learn.microsoft.com/azure/load-balancer/skus#skus) for more information on Azure Load Balancers SKUs.

0 commit comments

Comments
 (0)