diff --git a/azure/scope/cluster.go b/azure/scope/cluster.go index d057c254d1e..3a1c49ea5fd 100644 --- a/azure/scope/cluster.go +++ b/azure/scope/cluster.go @@ -975,7 +975,12 @@ func (s *ClusterScope) AdditionalTags() infrav1.Tags { } // APIServerPort returns the APIServerPort to use when creating the load balancer. +// AzureCluster.Spec.ControlPlaneEndpoint.Port takes precedence so users can override the +// default port for the API server LB rule, health probe, and NSG rule. func (s *ClusterScope) APIServerPort() int32 { + if s.AzureCluster.Spec.ControlPlaneEndpoint.Port != 0 { + return s.AzureCluster.Spec.ControlPlaneEndpoint.Port + } if s.Cluster.Spec.ClusterNetwork.APIServerPort != 0 { return s.Cluster.Spec.ClusterNetwork.APIServerPort } diff --git a/azure/scope/cluster_test.go b/azure/scope/cluster_test.go index a22393a532c..338f9d3f6b6 100644 --- a/azure/scope/cluster_test.go +++ b/azure/scope/cluster_test.go @@ -2678,10 +2678,11 @@ func TestAdditionalTags(t *testing.T) { func TestAPIServerPort(t *testing.T) { tests := []struct { - name string - clusterName string - clusterNetowrk clusterv1.ClusterNetwork - expectAPIServerPort int32 + name string + clusterName string + clusterNetowrk clusterv1.ClusterNetwork + controlPlaneEndpoint clusterv1beta1.APIEndpoint + expectAPIServerPort int32 }{ { name: "Nil cluster network", @@ -2703,6 +2704,19 @@ func TestAPIServerPort(t *testing.T) { }, expectAPIServerPort: 7000, }, + { + name: "ControlPlaneEndpoint.Port set", + clusterName: "my-cluster", + controlPlaneEndpoint: clusterv1beta1.APIEndpoint{Port: 443}, + expectAPIServerPort: 443, + }, + { + name: "ControlPlaneEndpoint.Port takes precedence over ClusterNetwork.APIServerPort", + clusterName: "my-cluster", + clusterNetowrk: clusterv1.ClusterNetwork{APIServerPort: 7000}, + controlPlaneEndpoint: clusterv1beta1.APIEndpoint{Port: 443}, + expectAPIServerPort: 443, + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { @@ -2721,6 +2735,10 @@ func TestAPIServerPort(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: tc.clusterName, }, + Spec: infrav1.AzureClusterSpec{ + AzureClusterClassSpec: infrav1.AzureClusterClassSpec{}, + ControlPlaneEndpoint: tc.controlPlaneEndpoint, + }, } clusterScope := &ClusterScope{ diff --git a/azure/services/loadbalancers/spec_test.go b/azure/services/loadbalancers/spec_test.go index 691e1ce617b..7de4701d6e9 100644 --- a/azure/services/loadbalancers/spec_test.go +++ b/azure/services/loadbalancers/spec_test.go @@ -196,6 +196,38 @@ func TestParameters(t *testing.T) { } } +// TestAPIServerLBPortPropagation verifies that a non-default LBSpec.APIServerPort +// produces matching ports in the API server LB rule (frontend + backend) and the +// HTTPS health probe. +func TestAPIServerLBPortPropagation(t *testing.T) { + g := NewWithT(t) + + const customPort int32 = 443 + spec := LBSpec{ + Name: "my-lb", + SubscriptionID: "123", + ResourceGroup: "my-rg", + BackendPoolName: "my-lb-backendPool", + APIServerPort: customPort, + Role: infrav1.APIServerRole, + AdditionalPorts: []infrav1.LoadBalancerPort{ + {Name: "https-alt", Port: 8443}, + }, + } + frontendIDs := []*armnetwork.SubResource{{ID: ptr.To("/some/frontend/id")}} + + rules := getLoadBalancingRules(spec, frontendIDs) + g.Expect(rules).To(HaveLen(2)) + g.Expect(rules[0].Properties.FrontendPort).To(Equal(ptr.To(customPort))) + g.Expect(rules[0].Properties.BackendPort).To(Equal(ptr.To(customPort))) + g.Expect(rules[1].Properties.FrontendPort).To(Equal(ptr.To[int32](8443))) + g.Expect(rules[1].Properties.BackendPort).To(Equal(ptr.To[int32](8443))) + + probes := getProbes(spec) + g.Expect(probes).To(HaveLen(1)) + g.Expect(probes[0].Properties.Port).To(Equal(ptr.To(customPort))) +} + func newDefaultNodeOutboundLB() armnetwork.LoadBalancer { return armnetwork.LoadBalancer{ Tags: map[string]*string{