Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 loadbalancer: resolve ControlPlaneEndpoint.Host when needed #1738

Merged
merged 1 commit into from
Feb 20, 2024

Conversation

EmilienM
Copy link
Contributor

What this PR does / why we need it:

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.

Which issue(s) this PR fixes (optional, in fixes #<issue number>(, fixes #<issue_number>, ...) format, will close the issue(s) when PR gets merged):
Fixes #1714

Special notes for your reviewer:

Unit tests are tricky. We stub the call to net.LookupHost to avoid actual DNS lookups.

  • squashed commits
  • if necessary:
    • includes documentation
    • adds unit tests

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Oct 30, 2023
@netlify
Copy link

netlify bot commented Oct 30, 2023

Deploy Preview for kubernetes-sigs-cluster-api-openstack ready!

Name Link
🔨 Latest commit b319893
🔍 Latest deploy log https://app.netlify.com/sites/kubernetes-sigs-cluster-api-openstack/deploys/65ca4cf3308d84000868e84f
😎 Deploy Preview https://deploy-preview-1738--kubernetes-sigs-cluster-api-openstack.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@k8s-ci-robot k8s-ci-robot added the size/M Denotes a PR that changes 30-99 lines, ignoring generated files. label Oct 30, 2023
@EmilienM
Copy link
Contributor Author

/assign mdbooth

@EmilienM EmilienM force-pushed the issue_1714 branch 2 times, most recently from 134c6ce to f4aa951 Compare November 1, 2023 00:09
@jichenjc
Copy link
Contributor

jichenjc commented Nov 1, 2023

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Nov 1, 2023
@EmilienM
Copy link
Contributor Author

EmilienM commented Nov 1, 2023

Thanks for the review @jichenjc - I'll also tag @mdbooth since he reported the issue, I want to make sure he's fine too with the proposal.

@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Nov 2, 2023
@EmilienM EmilienM force-pushed the issue_1714 branch 2 times, most recently from c2428d4 to 1c44a80 Compare November 2, 2023 14:56
Comment on lines 68 to 90
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("ControlPlaneEndpoint.Host is not an IP address, using the first resolved IP address", "host", openStackCluster.Spec.ControlPlaneEndpoint.Host, "ip", fixedIPAddress)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code block feels slightly too long to be duplicated below. Could we DRY this somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've put it into a constant. Let me know if that works.

@@ -46,16 +47,32 @@ const (

const loadBalancerProvisioningStatusActive = "ACTIVE"

// We wrap the net.LookupHost function in a variable to allow overriding it in unit tests.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eww, but I don't personally know of a cleaner way to do this. I'll let it pass, but if anybody else knows better... @pierreprinetti maybe?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alternative that pops to mind is dependency injection: If ReconcileLoadBalancer can accept a resolver as an additional argument, I'd inject the resolver from the controller. This lookupHost woud then be defined inside the controller, rather than being a global var (which can be dangerous). However, my solution doesn't work if you want to unit-test the controller this way.

If that's what you're looking for:

type Resolver func(string) ([]string, error)

func (s *Service) ReconcileLoadBalancer(openStackCluster *infrav1.OpenStackCluster, clusterName string, apiServerPort int, lookupHost Resolver) (bool, error) {

and in openstackcluster_controller.go:

		resolver := func(host string) ([]string, error) {
			ctx, cancel := context.WithTimeout(context.TODO(), 30*time.Second)
			defer cancel()
			return net.DefaultResolver.LookupHost(ctx, host)
		}
		terminalFailure, err := loadBalancerService.ReconcileLoadBalancer(openStackCluster, clusterName, apiServerPort, resolver)

Not really a game changer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Pierre for your analysis. I think the PR is then ready.

@EmilienM
Copy link
Contributor Author

@mdbooth are we good now or do you expect me to change something else?
Thanks

//
//nolint:gocritic
var lookupHost = func(host string) ([]string, error) {
return net.LookupHost(host)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things here:

This should probably use Resolver.LookupHost() as documented in net.LookupHost(), but we can't because we don't currently pass the context into this ReconcileLoadBalancer. We should most likely also do that.

I seem to recall we have a forcing function coming soon which will require us to use Context everywhere. So we don't miss this one, which isn't obvious without reading the godocs, could we update it to use Resolver.LookupHost anyway and create a context explicitly to pass in?

Secondly, does this have the correct behaviour when given an IP address? Either way, I wonder if we should check if it's an IP address before calling the resolver and just return it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Using net.DefaultResolver.LookupHost: I agree, consider it done in the next patchset.

  2. Does this have the correct behaviour when given an IP address? yes:

// LookupHost looks up the given host using the local resolver.
// It returns a slice of that host's addresses.
func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) {
	// Make sure that no matter what we do later, host=="" is rejected.
	if host == "" {
		return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
	}
	if _, err := netip.ParseAddr(host); err == nil {             <----------- HERE
		return []string{host}, nil
	}
	return r.lookupHost(ctx, host)
}

@EmilienM EmilienM force-pushed the issue_1714 branch 2 times, most recently from e30cc4c to 7ccdd65 Compare November 22, 2023 18:04
@EmilienM
Copy link
Contributor Author

can someone review it again please?

@jichenjc
Copy link
Contributor

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Nov 27, 2023
Copy link
Contributor

@mdbooth mdbooth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/approve

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: EmilienM, mdbooth

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jan 30, 2024
@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jan 30, 2024
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 10, 2024
`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.
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 12, 2024
@mdbooth
Copy link
Contributor

mdbooth commented Feb 20, 2024

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Feb 20, 2024
@k8s-ci-robot k8s-ci-robot merged commit 82b3122 into kubernetes-sigs:main Feb 20, 2024
9 checks passed
@pierreprinetti pierreprinetti deleted the issue_1714 branch February 21, 2024 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/M Denotes a PR that changes 30-99 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

We assume ControlPlaneEndpoint.Host is IP address
6 participants