Skip to content

Commit

Permalink
loadbalancer: resolve ControlPlaneEndpoint.Host when needed
Browse files Browse the repository at this point in the history
`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.
  • Loading branch information
EmilienM committed Nov 22, 2023
1 parent 821a1a2 commit 7ccdd65
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 5 deletions.
38 changes: 33 additions & 5 deletions pkg/cloud/services/loadbalancer/loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ limitations under the License.
package loadbalancer

import (
"context"
"errors"
"fmt"
"net"
"reflect"
"time"

Expand All @@ -27,7 +29,7 @@ import (
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/monitors"
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/pools"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/utils/net"
utilsnet "k8s.io/utils/net"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/util"

Expand All @@ -42,20 +44,39 @@ import (
const (
networkPrefix string = "k8s-clusterapi"
kubeapiLBSuffix string = "kubeapi"
resolvedMsg string = "ControlPlaneEndpoint.Host is not an IP address, using the first resolved IP address"
)

const loadBalancerProvisioningStatusActive = "ACTIVE"

// We wrap the LookupHost function in a variable to allow overriding it in unit tests.
//
//nolint:gocritic
var lookupHost = func(host string) ([]string, error) {
ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
defer cancel()
return net.DefaultResolver.LookupHost(ctx, host)
}

func (s *Service) ReconcileLoadBalancer(openStackCluster *infrav1.OpenStackCluster, clusterName string, apiServerPort int) (bool, error) {
loadBalancerName := getLoadBalancerName(clusterName)
s.scope.Logger().Info("Reconciling load balancer", "name", loadBalancerName)

var fixedIPAddress string
var err error

switch {
case openStackCluster.Spec.APIServerFixedIP != "":
fixedIPAddress = openStackCluster.Spec.APIServerFixedIP
case openStackCluster.Spec.DisableAPIServerFloatingIP && openStackCluster.Spec.ControlPlaneEndpoint.IsValid():
fixedIPAddress = openStackCluster.Spec.ControlPlaneEndpoint.Host
ips, err := lookupHost(openStackCluster.Spec.ControlPlaneEndpoint.Host)
if err != nil {
return false, fmt.Errorf("lookup host: %w", err)
}
fixedIPAddress = ips[0]
if net.ParseIP(fixedIPAddress) == nil {
s.scope.Logger().Info(resolvedMsg, "host", openStackCluster.Spec.ControlPlaneEndpoint.Host, "ip", fixedIPAddress)
}
}

providers, err := s.loadbalancerClient.ListLoadBalancerProviders()
Expand Down Expand Up @@ -93,7 +114,14 @@ func (s *Service) ReconcileLoadBalancer(openStackCluster *infrav1.OpenStackClust
case openStackCluster.Spec.APIServerFloatingIP != "":
floatingIPAddress = openStackCluster.Spec.APIServerFloatingIP
case openStackCluster.Spec.ControlPlaneEndpoint.IsValid():
floatingIPAddress = openStackCluster.Spec.ControlPlaneEndpoint.Host
ips, err := lookupHost(openStackCluster.Spec.ControlPlaneEndpoint.Host)
if err != nil {
return false, fmt.Errorf("lookup host: %w", err)
}
floatingIPAddress = ips[0]
if net.ParseIP(floatingIPAddress) == nil {
s.scope.Logger().Info(resolvedMsg, "host", openStackCluster.Spec.ControlPlaneEndpoint.Host, "ip", floatingIPAddress)
}
}
fp, err := s.networkingService.GetOrCreateFloatingIP(openStackCluster, openStackCluster, clusterName, floatingIPAddress)
if err != nil {
Expand Down Expand Up @@ -294,9 +322,9 @@ func validateIPs(openStackCluster *infrav1.OpenStackCluster, definedCIDRs []stri

for _, v := range definedCIDRs {
switch {
case net.IsIPv4String(v):
case utilsnet.IsIPv4String(v):
marshaledCIDRs = append(marshaledCIDRs, v+"/32")
case net.IsIPv4CIDRString(v):
case utilsnet.IsIPv4CIDRString(v):
marshaledCIDRs = append(marshaledCIDRs, v)
default:
record.Warnf(openStackCluster, "FailedIPAddressValidation", "%s is not a valid IPv4 nor CIDR address and will not get applied to allowed_cidrs", v)
Expand Down
18 changes: 18 additions & 0 deletions pkg/cloud/services/loadbalancer/loadbalancer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package loadbalancer

import (
"errors"
"net"
"testing"

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

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

// Stub the call to net.LookupHost
lookupHost = func(host string) (addrs []string, err error) {
if net.ParseIP(host) != nil {
return []string{host}, nil
} else if host == "api.test-cluster.test" {
ips := []string{"192.168.100.10"}
return ips, nil
}
return nil, errors.New("Unknown Host " + host)
}

openStackCluster := &infrav1.OpenStackCluster{
Spec: infrav1.OpenStackClusterSpec{
DisableAPIServerFloatingIP: true,
ControlPlaneEndpoint: clusterv1.APIEndpoint{
Host: "api.test-cluster.test",
Port: 6443,
},
},
Status: infrav1.OpenStackClusterStatus{
ExternalNetwork: &infrav1.NetworkStatus{
Expand Down

0 comments on commit 7ccdd65

Please sign in to comment.