Fix protocol changes on existing load balancer rules - #104
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #104 +/- ##
==========================================
+ Coverage 50.05% 56.99% +6.94%
==========================================
Files 4 4
Lines 975 1058 +83
==========================================
+ Hits 488 603 +115
+ Misses 473 425 -48
- Partials 14 30 +16 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Fixes reconciliation getting wedged when toggling service.beta.kubernetes.io/cloudstack-load-balancer-proxy-protocol on an existing LoadBalancer Service by resolving existing CloudStack LB rules by their uniqueness tuple (public IP, IP protocol, public port) and updating rules in-place (including renaming) rather than attempting conflicting creates. Also includes related robustness fixes around CIDR list comparison, VPC ACL protocol handling, and management-server version parsing.
Changes:
- Reworks LB rule reconciliation into resolve/prune/apply phases, with rule lookup falling back from name to (public IP, IP protocol, public port) to support protocol toggles.
- Normalizes CIDR list parsing for rule comparisons and gates CIDR updates on CloudStack version support.
- Hardens management server version parsing (avoids panics on short versions) and expands unit test coverage, including regression tests for issue #2.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Documents that proxy-protocol toggling updates the rule in-place without port interruption. |
| cloudstack.go | Makes management-server version parsing resilient to short/4-part version strings. |
| cloudstack_test.go | Adds coverage for short/empty version strings to prevent panics and validate parsing behavior. |
| cloudstack_loadbalancer.go | Implements tuple-based LB rule resolution, phased prune/apply reconciliation, CIDR list normalization, and VPC ACL protocol fix. |
| cloudstack_loadbalancer_test.go | Adds tests for CIDR splitting, tuple-based rule matching, in-place protocol toggles, phased pruning behavior, and VPC ACL behavior. |
Suppressed comments (1)
cloudstack_loadbalancer.go:890
- When a rule must be recreated, checkLoadBalancerRule deletes it in CloudStack but leaves it in lb.rules. That leftover entry is then treated as obsolete and pruned, which can trigger a second DeleteLoadBalancerRule call (and potentially fail reconciliation with a "not found"/API error) even though the rule was already deleted in the resolve phase.
Remove the rule from lb.rules immediately after a successful delete so it can’t be pruned again later in the same reconciliation.
// Delete the load balancer rule so we can create a new one using the new values.
if err := lb.deleteLoadBalancerRule(lbRule); err != nil {
return nil, false, err
}
return nil, false, nil
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
345a563 to
bc832c4
Compare
Fixes #2.
Toggling
service.beta.kubernetes.io/cloudstack-load-balancer-proxy-protocolon anexisting LoadBalancer service wedges reconciliation permanently:
Cause: rule names embed the protocol (
<lb>-<protocol>-<port>) and rules are lookedup by name, so a protocol change misses the lookup and tries to create a rule on a port
the old rule still holds. The in-place update was already written but unreachable — a
name-keyed lookup can never return a rule whose protocol differs from the one requested.
Changes
findLoadBalancerRulematches on an exact name, then falls back to(public IP, IP protocol, public port) — the tuple CloudStack enforces uniqueness on.
A proxy-protocol toggle now resolves to the existing rule and updates it in place.
Rules on a stale IP are pruned rather than matched, which used to strand their
firewall rule.
SetNameso the name stops contradicting its protocol.deleted before the creates, everything else after, so a cleanup failure can't take a
service down. Blocking is keyed on port alone —
detectRulesConflictnever exemptsLoadBalancing pairs with differing protocols.
Also fixed:
updateNetworkACLcreated ACLs with
CSProtocol()(tcp-proxy, which CloudStack rejects) whilefiltering with
IPProtocol(). Both now useIPProtocol().Cidrlistwas split on" "while CloudStack returns it comma-separated.three parts.