Skip to content

Auto-claim addresses for LoadBalancer Services that name no claim - #7

Open
Timofei Larkin (lllamnyp) wants to merge 1 commit into
mainfrom
feat/service-auto-claim
Open

Timofei Larkin (lllamnyp) wants to merge 1 commit into
mainfrom
feat/service-auto-claim

Conversation

@lllamnyp

Copy link
Copy Markdown
Member

A LoadBalancer Service that names no IPAddressClaim currently gets one of two outcomes. It gets nothing, or it gets an address from some auto-assigning pool that no ledger records or attributes. Tenant Kubernetes clusters hit the first case. cloud-provider-kubevirt creates their host-side Services with no claim annotation, metallb-iad's pool is autoAssign: false, and the Services sit at <pending> (MetalLB: no available IPs). This PR has the core controller provision a claim for such Services, so they draw metered addresses from a class like any tenant-made claim.

Behaviour

  • Which Services qualify. A live type: LoadBalancer Service qualifies when it:
    • has no local.sdn.cozystack.io/ip-address-claim;
    • is not opted out with local.sdn.cozystack.io/auto-claim: "false";
    • sets neither spec.loadBalancerClass nor spec.loadBalancerIP;
    • has no status.loadBalancer.ingress yet. This check keeps Services that already have an address from being moved to a new one.
  • Which class. The class named by the Service's local.sdn.cozystack.io/ip-address-class wins. Otherwise it is the single class annotated ipaddressclass.local.sdn.cozystack.io/is-auto-claim-class: "true", which is independent of is-default-class. With no marked class, nothing happens. With several, a warning event goes on the Service and nothing is claimed.
  • What gets created.
    • An IPAddressClaim named after the Service, in its namespace, with the Service as its controller.
    • Its family comes from the Service: Dual only for RequireDualStack.
    • The Service gets the claim annotation, and from there the driver associates the address exactly as it does today.
    • A pre-existing claim of the same name that the Service does not control is never adopted; an event says so.
  • Lifecycle.
    • Deleting the Service garbage-collects the claim.
    • The claim finalizer now deletes the addresses of any claim whose controller is a Service, whatever their reclaimPolicy.
    • To keep the address, remove the claim's owner reference. It is then an ordinary claim under the class policy, and another Service can name it.
    • The controller sets the reference only at creation and never restores it.

Changes

  • internal/controller/service_controller.go: the new ServiceReconciler. It is re-triggered by IPAddressClass events so a newly marked class unblocks waiting Services. It is wired up in cmd/main.go.
  • internal/controller/ipaddressclaim_controller.go: the Service-owned exception in the reclaim flow.
  • api/v1alpha1/well_known.go: IsAutoClaimClassAnnotation, ServiceClassAnnotation, ServiceAutoClaimAnnotation, and the event reasons. Drivers reference ServiceAutoClaimAnnotation to exempt their own Services; metallb-iad needs this for its reservation placeholders, in a follow-up PR there.
  • ClusterRole, regenerated by make manifests: Services get/list/watch/patch, services/finalizers update (for the owner reference's blockOwnerDeletion), and create on claims.
  • docs/design.md gets a new §10, plus edits to §1–3, §6, §8 and §9 where they said the core never touches Services. The README gets an auto-claim section.

Rollout notes

  • Nothing changes until a class is marked is-auto-claim-class or a Service names a class.
  • Deploy metallb-iad with the placeholder opt-out before marking a class. Otherwise the core will auto-claim the driver's own placeholder Services.
  • Tenant clusters: cloud-provider-kubevirt copies the tenant Service's annotations onto the host Service only at creation. So ip-address-class, ip-address-claim and the opt-out work when set on the in-cluster Service before it is created, but not if added later.

go build ./..., go vet ./..., go test ./internal/controller/ and golangci-lint run pass. The new unit tests cover the auto-claim cases above and the Service-owned reclaim.

A LoadBalancer Service without a claim annotation got either nothing or
an address from an auto-assigning pool that nobody meters. Tenant
clusters hit the first case: their cloud provider creates host-side
Services with no claim, and the driver's pool is closed to automatic
assignment.

The core now watches Services. For a qualifying one it creates an
IPAddressClaim named after the Service, in its namespace and controlled
by it, from the class the Service names (ip-address-class) or else the
single class marked is-auto-claim-class, and points the Service at it
with the claim annotation. Services that name a claim, opt out
(auto-claim: "false"), set loadBalancerClass or loadBalancerIP, or
already have an address are skipped.

A claim controlled by a Service deletes its addresses on deletion
regardless of reclaimPolicy; removing the controller reference retains
it as an ordinary claim.

Assisted-By: LLM
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
@coderabbitai

coderabbitai Bot commented Oct 30, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 54138477-7a60-4b99-a0a5-4ccf90346cc2
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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