Skip to content

Commit 95aadbe

Browse files
authored
Merge pull request #113 from leaseweb/lb_network_bug_fixes
Loadbalancer rule assignment fixes
2 parents 9028aa6 + ab7922b commit 95aadbe

5 files changed

Lines changed: 89 additions & 7 deletions

File tree

internal/controllers/cloudstackfailuredomain_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ func (r *CloudStackFailureDomainReconciler) reconcileNormal(ctx context.Context,
234234
// GenerateIsolatedNetwork creates a CloudStackIsolatedNetwork object in the cluster that is owned by the CloudStackFailureDomain.
235235
func (r *CloudStackFailureDomainReconciler) GenerateIsolatedNetwork(ctx context.Context, scope *scope.FailureDomainScope) error {
236236
lowerName := strings.ToLower(scope.NetworkName())
237-
metaName := fmt.Sprintf("%s-%s", scope.KubernetesClusterName(), lowerName)
237+
metaName := scope.IsolatedNetworkName()
238238
ownerGVK := scope.OwnerGVK()
239239
csIsoNet := &infrav1.CloudStackIsolatedNetwork{}
240240
csIsoNet.ObjectMeta = metav1.ObjectMeta{

internal/controllers/cloudstackmachine_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,7 @@ func (r *CloudStackMachineReconciler) SetupWithManager(ctx context.Context, mgr
898898
}
899899

900900
// Only trigger a CloudStackMachine reconcile if the loadbalancer rules changed.
901-
return len(oldCSIsoNet.Status.LoadBalancerRuleIDs) != len(newCSIsoNet.Status.LoadBalancerRuleIDs)
901+
return !cmp.Equal(oldCSIsoNet.Status.LoadBalancerRuleIDs, newCSIsoNet.Status.LoadBalancerRuleIDs)
902902
},
903903
},
904904
),

pkg/cloud/isolated_network.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,8 +778,22 @@ func (c *client) ReconcileLoadBalancer(
778778
return nil
779779
}
780780

781+
// isLBRuleNotFound checks if an error from the CloudStack API indicates that a load balancer rule does not exist.
782+
func isLBRuleNotFound(err error) bool {
783+
if err == nil {
784+
return false
785+
}
786+
msg := strings.ToLower(err.Error())
787+
788+
return strings.Contains(msg, "no match found") ||
789+
strings.Contains(msg, "unable to find") ||
790+
strings.Contains(msg, "does not exist") ||
791+
strings.Contains(msg, "entity does not exist")
792+
}
793+
781794
// AssignVMToLoadBalancerRules assigns a VM to the load balancing rules listed in isoNet.Status.LoadBalancerRuleIDs,
782795
// if not already assigned. It returns a bool indicating whether an instance was actually assigned, and an error in case an error occurred.
796+
// If a load balancer rule no longer exists in CloudStack, it is skipped.
783797
func (c *client) AssignVMToLoadBalancerRules(isoNet *infrav1.CloudStackIsolatedNetwork, instanceID string) (bool, error) {
784798
var assigned, found bool
785799
assigned = false
@@ -790,6 +804,14 @@ func (c *client) AssignVMToLoadBalancerRules(isoNet *infrav1.CloudStackIsolatedN
790804
lbRuleInstances, err := c.cs.LoadBalancer.ListLoadBalancerRuleInstances(
791805
c.cs.LoadBalancer.NewListLoadBalancerRuleInstancesParams(lbRuleID))
792806
if err != nil {
807+
if isLBRuleNotFound(err) {
808+
// The load balancer rule no longer exists in CloudStack. Skip it; the
809+
// isolated network controller will re-create it on its next reconciliation.
810+
record.Warnf(isoNet, "LoadBalancerRuleNotFound",
811+
"Load balancer rule %s no longer exists, skipping assignment for instance %s", lbRuleID, instanceID)
812+
813+
continue
814+
}
793815
c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err)
794816

795817
return false, err
@@ -822,13 +844,22 @@ func (c *client) AssignVMToLoadBalancerRules(isoNet *infrav1.CloudStackIsolatedN
822844

823845
// RemoveVMFromLoadBalancerRules removes a VM from the load balancing rules listed in isoNet.Status.LoadBalancerRuleIDs,
824846
// if not already removed. It returns a bool indicating whether an instance was actually removed, and an error in case an error occurred.
847+
// If a load balancer rule no longer exists in CloudStack, it is skipped (the VM is implicitly not assigned).
825848
func (c *client) RemoveVMFromLoadBalancerRules(isoNet *infrav1.CloudStackIsolatedNetwork, instanceID string) (bool, error) {
826849
for _, lbRuleID := range isoNet.Status.LoadBalancerRuleIDs {
827850
// Check that the instance isn't already in LB rotation.
828851
found := false
829852
lbRuleInstances, err := c.cs.LoadBalancer.ListLoadBalancerRuleInstances(
830853
c.cs.LoadBalancer.NewListLoadBalancerRuleInstancesParams(lbRuleID))
831854
if err != nil {
855+
if isLBRuleNotFound(err) {
856+
// The load balancer rule no longer exists in CloudStack, so the VM
857+
// is implicitly not assigned to it. Skip it.
858+
record.Warnf(isoNet, "LoadBalancerRuleNotFound",
859+
"Load balancer rule %s no longer exists, skipping removal for instance %s", lbRuleID, instanceID)
860+
861+
continue
862+
}
832863
c.customMetrics.EvaluateErrorAndIncrementAcsReconciliationErrorCounter(err)
833864

834865
return false, err

pkg/cloud/isolated_network_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,55 @@ var _ = Describe("Network", func() {
534534
Ω(assigned).Should(BeFalse())
535535
Ω(err).ShouldNot(HaveOccurred())
536536
})
537+
538+
It("Skips non-existent LB rule during assignment", func() {
539+
dummies.CSISONet1.Status.LoadBalancerRuleIDs = []string{"stale-rule-id"}
540+
lbip := &csapi.ListLoadBalancerRuleInstancesParams{}
541+
lbs.EXPECT().NewListLoadBalancerRuleInstancesParams(dummies.CSISONet1.Status.LoadBalancerRuleIDs[0]).
542+
Return(lbip)
543+
lbs.EXPECT().ListLoadBalancerRuleInstances(lbip).Return(nil, errors.New("No match found for stale-rule-id"))
544+
545+
assigned, err := client.AssignVMToLoadBalancerRules(dummies.CSISONet1, *dummies.CSMachine1.Spec.InstanceID)
546+
Ω(assigned).Should(BeFalse())
547+
Ω(err).ShouldNot(HaveOccurred())
548+
})
549+
550+
It("Skips non-existent LB rule and assigns to remaining rules", func() {
551+
dummies.CSISONet1.Status.LoadBalancerRuleIDs = []string{"stale-rule-id", "valid-rule-id"}
552+
lbip := &csapi.ListLoadBalancerRuleInstancesParams{}
553+
albp := &csapi.AssignToLoadBalancerRuleParams{}
554+
gomock.InOrder(
555+
lbs.EXPECT().NewListLoadBalancerRuleInstancesParams("stale-rule-id").
556+
Return(lbip),
557+
lbs.EXPECT().ListLoadBalancerRuleInstances(lbip).
558+
Return(nil, errors.New("entity does not exist")),
559+
560+
lbs.EXPECT().NewListLoadBalancerRuleInstancesParams("valid-rule-id").
561+
Return(lbip),
562+
lbs.EXPECT().ListLoadBalancerRuleInstances(lbip).
563+
Return(&csapi.ListLoadBalancerRuleInstancesResponse{}, nil),
564+
lbs.EXPECT().NewAssignToLoadBalancerRuleParams("valid-rule-id").Return(albp),
565+
lbs.EXPECT().AssignToLoadBalancerRule(albp).Return(&csapi.AssignToLoadBalancerRuleResponse{Success: true}, nil),
566+
)
567+
568+
assigned, err := client.AssignVMToLoadBalancerRules(dummies.CSISONet1, *dummies.CSMachine1.Spec.InstanceID)
569+
Ω(assigned).Should(BeTrue())
570+
Ω(err).ShouldNot(HaveOccurred())
571+
})
572+
})
573+
574+
Context("Remove VM from Load Balancer rule", func() {
575+
It("Skips non-existent LB rule during removal", func() {
576+
dummies.CSISONet1.Status.LoadBalancerRuleIDs = []string{"stale-rule-id"}
577+
lbip := &csapi.ListLoadBalancerRuleInstancesParams{}
578+
lbs.EXPECT().NewListLoadBalancerRuleInstancesParams("stale-rule-id").
579+
Return(lbip)
580+
lbs.EXPECT().ListLoadBalancerRuleInstances(lbip).Return(nil, errors.New("Unable to find uuid for id stale-rule-id"))
581+
582+
removed, err := client.RemoveVMFromLoadBalancerRules(dummies.CSISONet1, *dummies.CSMachine1.Spec.InstanceID)
583+
Ω(removed).Should(BeFalse())
584+
Ω(err).ShouldNot(HaveOccurred())
585+
})
537586
})
538587

539588
Context("load balancer rule does not exist", func() {

pkg/scope/machinescope.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,14 @@ func (s *MachineScope) NetworkType() string {
204204

205205
// IsolatedNetwork returns the isolated network of the machine.
206206
func (s *MachineScope) IsolatedNetwork(ctx context.Context) (*infrav1.CloudStackIsolatedNetwork, error) {
207+
if s.CloudStackIsolatedNetwork != nil && s.CloudStackIsolatedNetwork.Name != "" {
208+
return s.CloudStackIsolatedNetwork, nil
209+
}
210+
207211
isonet := &infrav1.CloudStackIsolatedNetwork{}
208-
if s.CloudStackIsolatedNetwork == nil || s.CloudStackIsolatedNetwork.Name == "" {
209-
err := s.client.Get(ctx, client.ObjectKey{Name: s.IsolatedNetworkName(), Namespace: s.Namespace()}, isonet)
210-
if err != nil {
211-
return nil, errors.Wrapf(err, "failed to get isolated network with name %s", s.IsolatedNetworkName())
212-
}
212+
err := s.client.Get(ctx, client.ObjectKey{Name: s.IsolatedNetworkName(), Namespace: s.Namespace()}, isonet)
213+
if err != nil {
214+
return nil, errors.Wrapf(err, "failed to get isolated network with name %s", s.IsolatedNetworkName())
213215
}
214216

215217
return isonet, nil

0 commit comments

Comments
 (0)