-
Notifications
You must be signed in to change notification settings - Fork 261
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
🌱 Reduce cyclomatic complexity of ReconcileLoadBalancer #1904
🌱 Reduce cyclomatic complexity of ReconcileLoadBalancer #1904
Conversation
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: 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 |
✅ Deploy Preview for kubernetes-sigs-cluster-api-openstack ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
|
||
// getAPIServerVIPAddress gets the VIP address for the API server from wherever it is specified. | ||
// Returns an empty string if the VIP address is not specified and it should be allocated automatically. | ||
func getAPIServerVIPAddress(openStackCluster *infrav1.OpenStackCluster) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could unit test it but I'm not going to nitpick on that. Just make sure the existing tests cover the output of that function and I'm fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote it.
|
||
// getAPIServerFloatingIP gets the floating IP from wherever it is specified. | ||
// Returns an empty string if the floating IP is not specified and it should be allocated automatically. | ||
func getAPIServerFloatingIP(openStackCluster *infrav1.OpenStackCluster) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote it.
// getCanonicalAllowedCIDRs gets a filtered list of CIDRs which should be allowed to access the API server loadbalancer. | ||
// Invalid CIDRs are filtered from the list and emil a warning event. | ||
// It returns a canonical representation that can be directly compared with other canonicalized lists. | ||
func getCanonicalAllowedCIDRs(openStackCluster *infrav1.OpenStackCluster) []string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote it.
} | ||
|
||
func (s *Service) getOrCreateLoadBalancer(openStackCluster *infrav1.OpenStackCluster, loadBalancerName, subnetID, clusterName, vipAddress, provider string) (*loadbalancers.LoadBalancer, error) { | ||
// getOrCreateAPILoadBalancer returns an existing API loadbalancer if it already exists, or creates a new one if it does not. | ||
func (s *Service) getOrCreateAPILoadBalancer(openStackCluster *infrav1.OpenStackCluster, clusterName string) (*loadbalancers.LoadBalancer, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote it.
@@ -230,7 +307,41 @@ func (s *Service) getOrCreateLoadBalancer(openStackCluster *infrav1.OpenStackClu | |||
return lb, nil | |||
} | |||
|
|||
func (s *Service) getOrCreateListener(openStackCluster *infrav1.OpenStackCluster, listenerName, lbID string, port int) (*listeners.Listener, error) { | |||
// reconcileAPILoadBalancerListener ensures that the listener on the given port exists and is configured correctly. | |||
func (s *Service) reconcileAPILoadBalancerListener(lb *loadbalancers.LoadBalancer, openStackCluster *infrav1.OpenStackCluster, clusterName string, port int) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we have unit test coverage for this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no we don't but it's not an easy one so I've filled #1906 so we can get back to it at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome change, I just have questions on unit tests coverage.
/cc @MaysaMacedo as this touches some of your code. |
I 100% agree about unit test coverage. However, this is basically just code motion so I can separate these changes from the other changes I'm making to the same code without triggering the gocyclo linter. IOW this is just yak shaving for me right now. If you fancied taking the yak shears and writing some good unit tests while I get back to the API stuff I'd add to the beers I owe you. |
726d253
to
94b97cc
Compare
94b97cc
to
4a78a3f
Compare
4a78a3f
to
fb6e0ee
Compare
/lgtm |
/hold cancel |
return false, fmt.Errorf("load balancer %q with id %s is not active after timeout: %v", loadBalancerName, lb.ID, err) | ||
if lb.ProvisioningStatus != loadBalancerProvisioningStatusActive { | ||
var err error | ||
lb, err = s.waitForLoadBalancerActive(lb.ID) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need to return the lb again if it was already retrieved at line 79?
Looks like all the places that need the LB after this line 91 look for the provider, the ID or VipPortID of the LB, so I believe returning it again is unnecessarily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it's a different object, returned from the most recent query. It's probably not necessary, but I figured that as we already had the latest state we might as well use it.
e.g. If we don't do this and for whatever reason we happened to look at ProvisioningStatus again, we would see that it's still not active, because we're still referencing the original version of the object before it became active.
return fmt.Errorf("APIServerLoadBalancer is not yet available in OpenStackCluster.Status") | ||
} | ||
|
||
allowedCIDRs := openStackCluster.Status.APIServerLoadBalancer.AllowedCIDRs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
has the allowedCIDRs been updated in the openStackCluster previously?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It was set directly in ReconcileLoadBalancer before reconciling any listeners.
} | ||
|
||
// getCanonicalAllowedCIDRs gets a filtered list of CIDRs which should be allowed to access the API server loadbalancer. | ||
// Invalid CIDRs are filtered from the list and emil a warning event. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo emil
|
||
return false, nil | ||
// Filter invalid CIDRs and convert any IPs into CIDRs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
couldn't we keep using validateIPs
function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a toss-up, but I thought it was too small to be a separate function with only one caller. This function is small enough now, I think it's clearer to inline it here.
Also it doesn't just validate, it also filters, which you wouldn't know just from its name so you'd have to go read the function intentionally to notice that.
/hold @mdbooth holding just to give a chance for you to check my comments, feel free to unhold once you checked them |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, @MaysaMacedo. I fixed the typo and responded to the other comments. Let me know what you think.
return false, fmt.Errorf("load balancer %q with id %s is not active after timeout: %v", loadBalancerName, lb.ID, err) | ||
if lb.ProvisioningStatus != loadBalancerProvisioningStatusActive { | ||
var err error | ||
lb, err = s.waitForLoadBalancerActive(lb.ID) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it's a different object, returned from the most recent query. It's probably not necessary, but I figured that as we already had the latest state we might as well use it.
e.g. If we don't do this and for whatever reason we happened to look at ProvisioningStatus again, we would see that it's still not active, because we're still referencing the original version of the object before it became active.
return fmt.Errorf("APIServerLoadBalancer is not yet available in OpenStackCluster.Status") | ||
} | ||
|
||
allowedCIDRs := openStackCluster.Status.APIServerLoadBalancer.AllowedCIDRs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It was set directly in ReconcileLoadBalancer before reconciling any listeners.
|
||
return false, nil | ||
// Filter invalid CIDRs and convert any IPs into CIDRs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a toss-up, but I thought it was too small to be a separate function with only one caller. This function is small enough now, I think it's clearer to inline it here.
Also it doesn't just validate, it also filters, which you wouldn't know just from its name so you'd have to go read the function intentionally to notice that.
fb6e0ee
to
9ae35a3
Compare
/lgtm |
This function had become genuinely too complex over time, to the point that even the linter was starting to complain about it when making almost any change. This change refactors ReconcileLoadBalancer into several smaller logical functions which are much easier to read and reason about. It also revealed some trivial optimisations: * Only fetch Octavia providers if we need them to create a new loadbalancer * Only calculate allowed CIDRs once * Don't re-fetch a loadbalancer to check it's active if it's already active Co-Authored-By: Emilien Macchi <[email protected]>
9ae35a3
to
e8ccd21
Compare
Restoring Maysa's LGTM, fixing unit tests and removing /hold. |
allowedCIDRs = listener.AllowedCIDRs | ||
} | ||
|
||
if openStackCluster.Status.Router != nil && len(openStackCluster.Status.Router.IPs) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixes a bug we just hit when specifying both internalNetwork.networkFilter
and apiServerLoadBalancer.allowedCIDRs
. Thanks for adding this nil guard!
This function had become genuinely too complex over time, to the point that even the linter was starting to complain about it when making almost any change.
This change refactors ReconcileLoadBalancer into several smaller logical functions which are much easier to read and reason about. It also revealed some trivial optimisations:
Apart from just the code cleanup, this has the benefit of not requiring these changes to be mixed into other PRs when the complexity exceeds the linter's threshold.
/hold