-
Notifications
You must be signed in to change notification settings - Fork 84
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
Vpc reconcile lbs #2023
Vpc reconcile lbs #2023
Conversation
Hi @cjschaef. Thanks for your PR. I'm waiting for a kubernetes-sigs member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
✅ Deploy Preview for kubernetes-sigs-cluster-api-ibmcloud ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
/ok-to-test |
/cc @dharaneeshvrd |
|
||
// Mark cluster as ready. | ||
clusterScope.IBMVPCCluster.Status.Ready = true | ||
clusterScope.Info("cluster infrastructure is now ready for cluster", "clusterName", clusterScope.IBMVPCCluster.Name) |
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.
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.
Okay, I will add the necessary logic to retrieve those values to set.
|
||
// ReconcileLoadBalancers reconciles Load Balancers. | ||
func (s *VPCClusterScope) ReconcileLoadBalancers() (bool, error) { | ||
// TODO(cjschaef): Determine if we want to use default LB configuration or require at least one is defined in Cluster spec. |
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.
Like other we can make a note of it to add it to webhook for good UX.
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.
Sure, I'll add another comment.
cloud/scope/vpc_cluster.go
Outdated
// If state is active, true is returned (no requeue required). | ||
// In all other cases, it returns false, indicating a requeue for reconciliation. |
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.
In general in function comments, we just add what the function does and avoid what caller will do, Since it may be called at different places, So I think we can avoid mentioning(no reque required) here.
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.
Okay, I'll only mention return values.
cloud/scope/vpc_cluster.go
Outdated
if lbStatus != nil { | ||
s.SetLoadBalancerStatus(lbStatus) | ||
// If the Load Balancer status isn't ready, flag for requeue and continue to next Load Balancer. | ||
if isReady := s.isLoadBalancerReady(string(lbStatus.State)); !isReady { |
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 its converted to string, Why can't we use VPCLoadBalancerState
instead?
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 can move things around.
cloud/scope/vpc_cluster.go
Outdated
return nil, nil | ||
} | ||
} else { | ||
loadBalancer, err = s.VPCClient.GetLoadBalancerByName(lb.Name) |
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.
Even both LB id and name can be nil right, Should we add a check and log proper message?
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, name cannot be nil
for a LB based on the spec.
So, my assumption based on that, was if ID is not set, we expect the Name to be set.
If there is a use case to have ID == nil && Name == ""
, I can another check to try to use a default LB name instead.
if loadBalancer.BackendPools != nil { | ||
for _, pool := range loadBalancer.BackendPools { | ||
backendPool := s.buildLoadBalancerBackendPool(pool) | ||
|
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.
empty line can be removed.
} | ||
options.SetListeners(listeners) | ||
|
||
// Create the load balancer. |
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.
May be if required we can log the options with higher V level for easy debugging.
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 can add a log call with v(5) with the options object prior to creation....unless you would rather have each field logged out separately in the log call?
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 think logging options should be sufficient.
cloud/scope/vpc_cluster.go
Outdated
if s.NetworkStatus() != nil { | ||
if s.NetworkStatus().ControlPlaneSubnets != nil { |
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.
if s.NetworkStatus() != nil { | |
if s.NetworkStatus().ControlPlaneSubnets != nil { | |
if s.NetworkStatus() != nil && if s.NetworkStatus().ControlPlaneSubnets != nil { |
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.
Will consolidate.
cloud/scope/vpc_cluster.go
Outdated
// SetLoadBalancerStatus sets the status for a Load Balancer. | ||
func (s *VPCClusterScope) SetLoadBalancerStatus(loadBalancer *infrav1beta2.VPCLoadBalancerStatus) { | ||
// Ignore attempts to set status without a load balancer. | ||
if loadBalancer == nil { |
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.
Not needed IMO, since it's an internal func and you are populating loadBalancer status or doing a nil check before calling the func on both the places.
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 is a public function, so it could be called wherever, I can make it private and remove the check. I'd like to avoid go panics whenever possible.
if loadBalancer.Subnets != nil { | ||
for _, subnet := range loadBalancer.Subnets { | ||
if subnet.ID != nil { | ||
subnetIDs = append(subnetIDs, *subnet.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.
Don't you need to verify that subnet with this id exists? Since it's coming from user spec.
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.
Sure, I could perform lookups to check if they exist.
if loadBalancer.SecurityGroups != nil { | ||
for _, securityGroup := range loadBalancer.SecurityGroups { | ||
if securityGroup.ID != nil { | ||
securityGroupIDs = append(securityGroupIDs, *securityGroup.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.
Same as previous comment on verifying user provided SG.
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.
Same, I could check if they exist.
} | ||
|
||
// getDefaultBalancerBackendPools returns a list of default Load Balancer Backend Pools for a Load Balancer. | ||
func (s *VPCClusterScope) getDefaultLoadBalancerBackendPools() []vpcv1.LoadBalancerPoolPrototype { |
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.
Any reason for creating a slice and appending one item and in caller again unpacking the slice?
Can't we return the item directly?
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 did this if any additional pools were necessary to add to the defaults.
} | ||
|
||
// getDefaultLoadBalancerListeners returns a list of default Load Balancer Listeners for a Load Balancer. | ||
func (s *VPCClusterScope) getDefaultLoadBalancerListeners(defaultBackendPool bool) []vpcv1.LoadBalancerListenerPrototypeLoadBalancerContext { |
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.
Same comment as getDefaultLoadBalancerBackendPools()
's comment.
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.
Same as the pools, regarding additional default listeners.
b24c899
to
ced6212
Compare
// NOTE(cjschaef): A webhook validation check could help ensure this. | ||
func (s *VPCClusterScope) GetLoadBalancerHostName() (*string, error) { | ||
// If no Status or Load Balancer Status is populated, assume the Load Balancer's are not ready (have not been reconciled), so no hostname will be available. | ||
if s.NetworkStatus() == nil || s.NetworkStatus().LoadBalancers == nil || len(s.NetworkStatus().LoadBalancers) == 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.
we are returning nil here for len(s.NetworkStatus().LoadBalancers)
and same codition returns error next step, May be we can simplify or remove if not required.
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.
So, the first set of checks looks at Status. Assuming if there is no Status or no LB status, the situation needs to wait for Status to exist or be updated.
The second check is against Spec. If Status was set, but Spec was not, that is an error. Checks are made immediately in the function to short circuit.
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.
apologies, thanks for the explanation, Its correct, I just overlooked spec and status.
cloud/scope/vpc_cluster.go
Outdated
ID: lb.ID, | ||
} | ||
loadBalancer, detailedResponse, err = s.VPCClient.GetLoadBalancer(getLBOptions) | ||
if detailedResponse != nil && detailedResponse.StatusCode == http.StatusNotFound { |
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 think we can log these cases with higher level V values for easy debugging in future.
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.
Okay, I bumped to v5.
cloud/scope/vpc_cluster.go
Outdated
} | ||
|
||
options.SetIsPublic(isPublic) | ||
options.SetName(loadBalancer.Name) |
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.
What if loadbalancer.Name is empty?
In getLoadBalancer() function we have this condition
name := lb.Name
if name == "" {
name = *s.GetServiceName(infrav1beta2.ResourceTypeLoadBalancer)
}
Should we do something similar here?
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'm hesitant to create LB's with a default service name, especially if multiple LB's get defined with empty names.
Given the structure of the VPCLoadBalancerStatus, we rely on ID as a priority field. We can let VPC API generate a Name, and rely on the ID from Status.
The default service name was added by request, but I don't expect to use it for most use cases. This is likely to be refactored, likely not relying on a default name, if possible.
} | ||
options.SetListeners(listeners) | ||
|
||
// Create the load balancer. |
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 think logging options should be sufficient.
cloud/scope/vpc_cluster.go
Outdated
|
||
// If only one Load Balancer was provided, attempt to use that Load Balancer's hostname. | ||
if len(s.NetworkSpec().LoadBalancers) == 1 { | ||
return getHostName(s.NetworkSpec().LoadBalancers[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.
Any reason for handling this special case, In case of 1 lb defined it can be private as well is this the reason?
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, if a Public LB was not found, and there was only one LB, we expect it must be a Private LB, and Public traffic isn't expected to be enabled. This isn't the normal path, but still need to cover this design.
cloud/scope/vpc_cluster.go
Outdated
|
||
// NOTE(cjschaef): As a default Load Balancer isn't supported (when none are provided in Spec), this should result in an error. | ||
// TODO(cjschaef): Likely would be beneficial to have webhook validation for this case. | ||
if loadBalancer.Name == "" { |
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.
Not sure this check is needed here? in getLoadBalancer()
we get the default name incase of name is empty.
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'll be refactoring, will see what makes sense.
cloud/scope/vpc_cluster.go
Outdated
} else { | ||
name := lb.Name | ||
if name == "" { | ||
name = *s.GetServiceName(infrav1beta2.ResourceTypeLoadBalancer) |
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.
In case of multiple loadbalancers configured without name in spec, This may not work as expected as we end up getting same name for all.
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.
That is why we do not create an LB with the default Name (let API generate one). But that will generate its own problems.
I will need to rethink how the default service name is being used, as I don't think it makes sense to use it as requested (perhaps only in the rare case that one LB was defined in Spec). Or perhaps I can try refactoring to append public
/private
and hope that doesn't cause overlap (although I think that case is just as susceptible to this same situation).
Perhaps I need to add webhook validation to limit the number of LB's defined in Spec to only be 2, in additional to the refactoring. But that can be very limiting to the design and logic as well.
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.
In Power VS we do something like this , append the index https://github.com/Karthik-K-N/cluster-api-provider-ibmcloud/blob/987191d98e364d4bbfc39423d433b791dbb6b102/cloud/scope/powervs_cluster.go#L1963-L1965
cloud/scope/vpc_cluster.go
Outdated
} | ||
loadBalancer, detailedResponse, err = s.VPCClient.GetLoadBalancer(getLBOptions) | ||
if detailedResponse != nil && detailedResponse.StatusCode == http.StatusNotFound { | ||
return nil, nil |
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.
Generally if user provides an id, we expect the resource to be exist with the id, if not we would throw an error and not proceed with the reconciliation. It's ok in case of name is provided and if resource not exist, we can create it.
Also If you return nil here and create a new load balancer, then the status and spec will have different ids.
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.
Okay, I'll return an error.
ced6212
to
267c18f
Compare
Add support to reconcile VPC Load Balancers as part of the extended VPC support. Related: kubernetes-sigs#1896
267c18f
to
48eebb2
Compare
@cjschaef Is this PR updated or tested? |
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.
/lgtm
/assign @dharaneeshvrd
/lgtm |
/assign @mkumatag @Prajyot-Parab |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: cjschaef, mkumatag 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 |
What this PR does / why we need it: Add support to reconcile extended VPC Load Balancers
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 #
Special notes for your reviewer:
/area provider/ibmcloud
Release note: