Skip to content

feat: auto-set LB IP-pinning and sharing-key annotations from allocation IP#1

Merged
Exonical merged 2 commits into
mainfrom
devin/1780029629-lb-ip-annotation
May 30, 2026
Merged

feat: auto-set LB IP-pinning and sharing-key annotations from allocation IP#1
Exonical merged 2 commits into
mainfrom
devin/1780029629-lb-ip-annotation

Conversation

@devin-ai-integration

Copy link
Copy Markdown

Summary

When lb_ip_annotation and lb_sharing_key config keys are set, Wings automatically populates them on each game server's LoadBalancer Service using the server's allocation IP from the Panel. This eliminates the need to manually patch LB Services with IP-pinning and sharing-key annotations.

New config fields:

  • lb_ip_annotation — annotation key auto-set to the allocation IP (e.g. lbipam.cilium.io/ips for Cilium, metallb.universe.tf/loadBalancerIPs for MetalLB)
  • lb_sharing_key — annotation key for IP sharing, also set to the allocation IP (e.g. lbipam.cilium.io/sharing-key for Cilium)

Example Helm values for Cilium:

wings:
  kubernetes:
    networkMode: loadbalancer
    lbAnnotations:
      io.cilium/lb-ipam-pool: "game-servers"
    lbIPAnnotation: "lbipam.cilium.io/ips"
    lbSharingKey: "lbipam.cilium.io/sharing-key"

Non-routable IPs (0.0.0.0, 127.0.0.1) are filtered out — annotations are only set when the allocation has a real external IP.

Review & Testing Checklist for Human

  • Deploy with Cilium LB-IPAM: create two game servers with the same allocation IP but different ports, verify both LB Services get lbipam.cilium.io/ips and sharing-key annotations automatically
  • Verify that servers with 0.0.0.0 allocation don't get IP-pinning annotations
  • Confirm helm template renders lb_ip_annotation and lb_sharing_key correctly in the ConfigMap

Notes

  • Provider-agnostic: works with any LB provisioner (Cilium, MetalLB, cloud) by letting the user specify the annotation keys
  • Backwards compatible: empty config values (default) mean no change in behavior

Link to Devin session: https://app.devin.ai/sessions/44579189e7304c8c87ee19eb22ff18dc
Requested by: @Exonical

…ion IP

When lb_ip_annotation and lb_sharing_key config keys are set, Wings
automatically populates them on each game server's LoadBalancer Service
using the server's allocation IP from the Panel. This enables multiple
game servers to share a single external IP (on different ports) without
manual annotation patching.

Example config for Cilium LB-IPAM:
  kubernetes:
    lb_ip_annotation: "lbipam.cilium.io/ips"
    lb_sharing_key: "lbipam.cilium.io/sharing-key"
    lb_annotations:
      io.cilium/lb-ipam-pool: "game-servers"

Co-Authored-By: Bryce Anglin <brycemanglin@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 4 additional findings in Devin Review.

Open in Devin Review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Stale LB IP-pinning annotations persist on Service update when allocation IP becomes invalid

When updating an existing LoadBalancer Service, the annotation merge at lines 148-155 only adds/overwrites keys from the new annotations map — it never removes annotations that are no longer desired. If a server's allocation IP changes from a valid address (e.g. 23.227.184.222) to 0.0.0.0, 127.0.0.1, or empty, allocationIP() (network.go:362-371) returns "", so the LBIPAnnotation and LBSharingKey keys are not included in the annotations map. However, those annotation keys from the previous EnsureService call remain on the existing Service object, keeping the LoadBalancer pinned to the old/stale IP.

Example scenario
  1. Server allocation IP is 23.227.184.222; EnsureService creates the Service with lbipam.cilium.io/ips: "23.227.184.222"
  2. Admin changes allocation to 0.0.0.0 (wildcard)
  3. EnsureService runs again: allocationIP() returns "", so annotations map only has LBAnnotations entries
  4. Merge loop writes LBAnnotations but the stale lbipam.cilium.io/ips key is never deleted
  5. The LB controller keeps the Service pinned to 23.227.184.222

(Refers to lines 148-155)

Prompt for agents
In EnsureService in environment/kubernetes/network.go, the update path (lines 148-155) merges annotations into the existing service but never removes stale IP-pinning annotations when the allocation IP becomes invalid (0.0.0.0, 127.0.0.1, or empty).

To fix this, after the merge loop (or as part of the update block), explicitly delete the LBIPAnnotation and LBSharingKey annotation keys from existing.Annotations when allocationIP() returns empty but the config keys are set. For example:

if allocIP == "" {
    if cfg.Kubernetes.LBIPAnnotation != "" {
        delete(existing.Annotations, cfg.Kubernetes.LBIPAnnotation)
    }
    if cfg.Kubernetes.LBSharingKey != "" {
        delete(existing.Annotations, cfg.Kubernetes.LBSharingKey)
    }
}

Note that allocIP is currently scoped inside the if-block at line 102, so you will need to hoist the allocationIP() call to a broader scope (or call it again) so it is accessible in the update path. Also consider the case where the config key itself changes (e.g. LBIPAnnotation changes from one annotation name to another) — the old annotation key from the previous config would also remain stale.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed in 7457b9d — the update path now replaces annotations entirely (existing.Annotations = annotations) instead of merging, so stale IP-pinning and sharing-key annotations are cleaned up when the allocation IP becomes invalid. Added a test that verifies the stale annotation removal scenario.

When allocation IP becomes invalid (0.0.0.0, empty), the update path
now replaces annotations entirely instead of merging, ensuring stale
IP-pinning and sharing-key annotations are cleaned up.

Co-Authored-By: Bryce Anglin <brycemanglin@gmail.com>
@Exonical
Exonical merged commit c96a73b into main May 30, 2026
8 checks passed
@Exonical
Exonical deleted the devin/1780029629-lb-ip-annotation branch May 30, 2026 05:20
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.

1 participant