Skip to content

Load balancer reliability, annotation-based IP management, and docs overhaul#117

Merged
hrak merged 13 commits intomainfrom
develop
Mar 5, 2026
Merged

Load balancer reliability, annotation-based IP management, and docs overhaul#117
hrak merged 13 commits intomainfrom
develop

Conversation

@hrak
Copy link
Copy Markdown
Member

@hrak hrak commented Mar 5, 2026

Summary

This PR consolidates work from the develop branch: bug fixes, new annotation-based IP management, and documentation restructuring.

Bug fixes

  • Prevent public IP orphaning — Fix four scenarios where CloudStack public IPs could be permanently leaked: exact prefix matching in getLoadBalancerByName() to avoid CloudStack LIKE matching false positives, annotation-based IP cleanup/recovery in delete and ensure paths, and a new lookupPublicIPAddress() helper
  • Validate target IP before teardown — Pre-flight validatePublicIPAvailable() check prevents leaving a service broken when a user-specified IP is invalid
  • Multiple audit fixes — Nil guards for nodeAddresses and symmetricDifference, fix stale error tracking in updateFirewallRule, improved config error messages, warning logs for skipped rules
  • Enable gosec and wrapcheck linters — Fix all flagged issues

Features

  • Annotation-based IP management — Replace deprecated spec.LoadBalancerIP with cloudstack-load-balancer-address annotation as the primary way to request a specific IP. Add cloudstack-load-balancer-keep-ip annotation to control IP retention on deletion
  • ID-based load balancer lookup — Store CloudStack public IP UUID (cloudstack-load-balancer-id) and network UUID (cloudstack-load-balancer-network-id) as annotations for exact ID-based rule lookup instead of keyword LIKE matching
  • Clean up annotations on LB deletion — Remove all 6 CloudStack LB annotations when EnsureLoadBalancerDeleted succeeds (e.g., service type changed from LoadBalancer to ClusterIP), skip cleanup when service is being garbage collected

Refactoring

  • Remove live IP reassignment — Replace complex IP switch logic with a warning event telling users to delete and recreate the service (matches OpenStack, AWS, Azure, GCP behavior)

Documentation

  • Reorganize docs — Move from monolithic README to topic-based pages under docs/ (getting-started, configuration, load-balancer, development). Remove outdated references to pre-1.17 labels and the old in-tree provider

Test plan

  • All existing tests pass (go test ./cloudstack/...)
  • New tests for annotation cleanup on successful/failed LB deletion
  • New tests for orphaned IP recovery and release
  • New tests for ID-based load balancer lookup
  • Linter passes (golangci-lint run)

🤖 Generated with Claude Code

hrak and others added 13 commits February 20, 2026 22:44
- nodeAddresses: add nil guard for instance parameter to prevent panic
- updateFirewallRule: track deletion errors separately so a successful
  creation no longer returns a stale deletion error; return changed=false
  when nothing actually changed
- symmetricDifference: add nil guard for instance elements
- newCSCloud: improve error message to specify which config fields are required
- EnsureLoadBalancer: add warning logs when rules are skipped during IP
  switch cleanup due to invalid protocol or unparseable port

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a pre-flight validatePublicIPAvailable() check in EnsureLoadBalancer
when switching to a user-specified IP. This prevents leaving the service
in a broken state if the target IP is invalid or unavailable — the old
config is preserved and the error is returned immediately.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address four scenarios where CloudStack public IPs could be permanently
leaked:

1. Filter keyword search results in getLoadBalancerByName() with exact
   prefix matching to prevent CloudStack's LIKE %keyword% from returning
   rules belonging to other services with overlapping names (e.g., "foo"
   matching "foobar").

2. Add lookupPublicIPAddress() helper that checks if an IP is already
   allocated without associating unallocated IPs.

3. Add annotation-based IP cleanup in EnsureLoadBalancerDeleted() — when
   no LB rules exist but the service annotation still references an IP,
   attempt to release it.

4. Add annotation-based IP recovery in EnsureLoadBalancer() — on retry
   after partial failure, reuse the previously allocated IP found in the
   service annotation instead of allocating a new one. Also cleans up
   old IPs during failed IP switch retries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: Prevent public IP orphaning under partial failure conditions
…p support

Replace deprecated spec.LoadBalancerIP with the ServiceAnnotationLoadBalancerAddress
annotation as the primary way to request a specific IP. Add ServiceAnnotationLoadBalancerKeepIP
annotation to control IP retention on service deletion (breaking change: spec.LoadBalancerIP
no longer prevents IP release).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Live IP reassignment adds significant complexity for a feature most cloud
providers don't offer. OpenStack (architecturally closest to CloudStack)
explicitly declined to implement it. Replace the IP switch branch with a
warning event telling users to delete and recreate the service instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ookup

Store the CloudStack public IP UUID and network UUID as service annotations
so that subsequent reconciliation loops can look up load balancer rules by
exact ID instead of relying on keyword-based LIKE %keyword% matching. The
new getLoadBalancer orchestrator tries ID-based lookup first and falls back
to name-based search when annotations are absent or stale.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: Deprecate spec.LoadBalancerIP in favor of annotations
Reorganize documentation from a single monolithic README into topic-based
pages under docs/. Remove outdated references to pre-1.17 labels, the old
in-tree provider, and the migration guide. Slim README to an overview with
links to the new docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: Reorganize documentation into docs/ folder
When a service switches from type LoadBalancer to ClusterIP, Kubernetes
calls EnsureLoadBalancerDeleted. Previously this left stale CloudStack
annotations on the service. Now all 6 LB annotations are removed on
successful deletion via the service patcher, but only when the service
is not being deleted (DeletionTimestamp is zero) to avoid unnecessary
API calls on garbage-collected objects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@hrak hrak merged commit 2ee52c0 into main Mar 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants