Skip to content

Commit cef7eb5

Browse files
committed
loadbalancer: resolve ControlPlaneEndpoint.Host when needed
`ControlPlaneEndpoint.Host` is not guaranteed to be an IP address, it can also be an hostname. Now we'll try to lookup the hostname if it's not an IP and set that for the LB VipAddress.
1 parent 64fd897 commit cef7eb5

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

pkg/cloud/services/loadbalancer/loadbalancer.go

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ limitations under the License.
1717
package loadbalancer
1818

1919
import (
20+
"context"
2021
"errors"
2122
"fmt"
23+
"net"
2224
"reflect"
2325
"time"
2426

@@ -27,7 +29,7 @@ import (
2729
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors"
2830
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools"
2931
"k8s.io/apimachinery/pkg/util/wait"
30-
"k8s.io/utils/net"
32+
utilsnet "k8s.io/utils/net"
3133
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
3234
"sigs.k8s.io/cluster-api/util"
3335

@@ -42,10 +44,23 @@ import (
4244
const (
4345
networkPrefix string = "k8s-clusterapi"
4446
kubeapiLBSuffix string = "kubeapi"
47+
resolvedMsg string = "ControlPlaneEndpoint.Host is not an IP address, using the first resolved IP address"
4548
)
4649

4750
const loadBalancerProvisioningStatusActive = "ACTIVE"
4851

52+
// We wrap the LookupHost function in a variable to allow overriding it in unit tests.
53+
//
54+
//nolint:gocritic
55+
var lookupHost = func(host string) ([]string, error) {
56+
ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
57+
defer cancel()
58+
return net.DefaultResolver.LookupHost(ctx, host)
59+
}
60+
61+
// ReconcileLoadBalancer reconciles the load balancer for the given cluster.
62+
//
63+
//nolint:gocyclo // Function requires a lot of conditions to be checked
4964
func (s *Service) ReconcileLoadBalancer(openStackCluster *infrav1.OpenStackCluster, clusterName string, apiServerPort int) (bool, error) {
5065
loadBalancerName := getLoadBalancerName(clusterName)
5166
s.scope.Logger().Info("Reconciling load balancer", "name", loadBalancerName)
@@ -57,13 +72,22 @@ func (s *Service) ReconcileLoadBalancer(openStackCluster *infrav1.OpenStackClust
5772
}
5873

5974
var fixedIPAddress string
75+
var err error
76+
6077
switch {
6178
case lbStatus.InternalIP != "":
6279
fixedIPAddress = lbStatus.InternalIP
6380
case openStackCluster.Spec.APIServerFixedIP != "":
6481
fixedIPAddress = openStackCluster.Spec.APIServerFixedIP
6582
case openStackCluster.Spec.DisableAPIServerFloatingIP && openStackCluster.Spec.ControlPlaneEndpoint.IsValid():
66-
fixedIPAddress = openStackCluster.Spec.ControlPlaneEndpoint.Host
83+
ips, err := lookupHost(openStackCluster.Spec.ControlPlaneEndpoint.Host)
84+
if err != nil {
85+
return false, fmt.Errorf("lookup host: %w", err)
86+
}
87+
fixedIPAddress = ips[0]
88+
if net.ParseIP(fixedIPAddress) == nil {
89+
s.scope.Logger().Info(resolvedMsg, "host", openStackCluster.Spec.ControlPlaneEndpoint.Host, "ip", fixedIPAddress)
90+
}
6791
}
6892

6993
providers, err := s.loadbalancerClient.ListLoadBalancerProviders()
@@ -108,7 +132,14 @@ func (s *Service) ReconcileLoadBalancer(openStackCluster *infrav1.OpenStackClust
108132
case openStackCluster.Spec.APIServerFloatingIP != "":
109133
floatingIPAddress = openStackCluster.Spec.APIServerFloatingIP
110134
case openStackCluster.Spec.ControlPlaneEndpoint.IsValid():
111-
floatingIPAddress = openStackCluster.Spec.ControlPlaneEndpoint.Host
135+
ips, err := lookupHost(openStackCluster.Spec.ControlPlaneEndpoint.Host)
136+
if err != nil {
137+
return false, fmt.Errorf("lookup host: %w", err)
138+
}
139+
floatingIPAddress = ips[0]
140+
if net.ParseIP(floatingIPAddress) == nil {
141+
s.scope.Logger().Info(resolvedMsg, "host", openStackCluster.Spec.ControlPlaneEndpoint.Host, "ip", floatingIPAddress)
142+
}
112143
}
113144
fp, err := s.networkingService.GetOrCreateFloatingIP(openStackCluster, openStackCluster, clusterName, floatingIPAddress)
114145
if err != nil {
@@ -307,9 +338,9 @@ func validateIPs(openStackCluster *infrav1.OpenStackCluster, definedCIDRs []stri
307338

308339
for _, v := range definedCIDRs {
309340
switch {
310-
case net.IsIPv4String(v):
341+
case utilsnet.IsIPv4String(v):
311342
marshaledCIDRs = append(marshaledCIDRs, v+"/32")
312-
case net.IsIPv4CIDRString(v):
343+
case utilsnet.IsIPv4CIDRString(v):
313344
marshaledCIDRs = append(marshaledCIDRs, v)
314345
default:
315346
record.Warnf(openStackCluster, "FailedIPAddressValidation", "%s is not a valid IPv4 nor CIDR address and will not get applied to allowed_cidrs", v)

pkg/cloud/services/loadbalancer/loadbalancer_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
package loadbalancer
1818

1919
import (
20+
"errors"
21+
"net"
2022
"testing"
2123

2224
"github.com/go-logr/logr"
@@ -28,6 +30,7 @@ import (
2830
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools"
2931
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/providers"
3032
. "github.com/onsi/gomega"
33+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
3134

3235
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha8"
3336
"sigs.k8s.io/cluster-api-provider-openstack/pkg/clients/mock"
@@ -38,9 +41,24 @@ func Test_ReconcileLoadBalancer(t *testing.T) {
3841
mockCtrl := gomock.NewController(t)
3942
defer mockCtrl.Finish()
4043

44+
// Stub the call to net.LookupHost
45+
lookupHost = func(host string) (addrs []string, err error) {
46+
if net.ParseIP(host) != nil {
47+
return []string{host}, nil
48+
} else if host == "api.test-cluster.test" {
49+
ips := []string{"192.168.100.10"}
50+
return ips, nil
51+
}
52+
return nil, errors.New("Unknown Host " + host)
53+
}
54+
4155
openStackCluster := &infrav1.OpenStackCluster{
4256
Spec: infrav1.OpenStackClusterSpec{
4357
DisableAPIServerFloatingIP: true,
58+
ControlPlaneEndpoint: clusterv1.APIEndpoint{
59+
Host: "api.test-cluster.test",
60+
Port: 6443,
61+
},
4462
},
4563
Status: infrav1.OpenStackClusterStatus{
4664
ExternalNetwork: &infrav1.NetworkStatus{

0 commit comments

Comments
 (0)