Skip to content

feat(capabilities): detect servicelb and MetalLB as LoadBalancer providers#201

Merged
incognick merged 1 commit into
mainfrom
feat/detect-servicelb-metallb
Jul 3, 2026
Merged

feat(capabilities): detect servicelb and MetalLB as LoadBalancer providers#201
incognick merged 1 commit into
mainfrom
feat/detect-servicelb-metallb

Conversation

@incognick

Copy link
Copy Markdown
Contributor

Summary

LoadBalancer support was inferred only from a recognized cloud provider's node providerID, so k3s/servicelb and MetalLB clusters were wrongly reported as unsupported — even though a type: LoadBalancer Service gets a real external IP on them. This is the gap the LoadBalancer e2e surfaced in #193, where k3s servicelb handed a probe Service 172.18.0.2 while capability detection reported Supported=false.

Detection now reports LoadBalancer support from whatever actually services LoadBalancers (ADR-0043): a recognized cloud provider, k3s's built-in servicelb, or MetalLB. The capability also records which provider services LoadBalancers, so the agent can reason about cost (only a cloud load balancer is billable) and guide a MetalLB install when none is present.

Signals chosen

  • k3s servicelb — detected via the k3s providerID scheme. servicelb (klipper-lb) runs in-process in the k3s server, so there is no standalone controller Deployment to look for, and the per-Service svclb-* DaemonSets only exist after a LoadBalancer Service is created — neither is a reliable pre-exposure signal. The k3s cloud-controller that stamps k3s://<node> is the same one that runs servicelb, so the cluster-level k3s signal is the robust detector. Tradeoff (documented in the code): it can over-report only when servicelb was explicitly disabled (--disable=servicelb), a rare non-default — acceptable for a read-only survey where provisioning a real LoadBalancer is the only exact test.
  • MetalLB — detected via its controller Deployment, matching both the Helm-chart (app.kubernetes.io/name=metallb) and static-manifest (app=metallb) label schemes, in any namespace.

RBAC

No new grant needed: both signals reuse the nodes and apps/deployments get/list grants the burrowd-cluster-capabilities ClusterRole already holds (the deployments grant was added for ingress-controller detection). The ClusterRole stays strictly read-only and unchanged.

Capability shape

LoadBalancerCapability gains a Provider string field (json:"provider,omitempty") naming the mechanism — a cloud id, servicelb, or metallb; empty when none. Mirrored in the API client type; the burrow cluster view labels the source and whether it is free/billable.

Tests

  • controlplane/kube/capabilities_test.go: TestDetectCapabilitiesFullCluster (cloud → supported, provider digitalocean), new TestDetectLoadBalancerServiceLB (k3s → supported, provider servicelb), new TestDetectLoadBalancerMetalLB (both label schemes → supported, provider metallb), and TestDetectCapabilitiesBareMetal reworked to a truly bare cluster (no provider → unsupported).
  • controlplane/e2e/loadbalancer_e2e_test.go: the "DETECTION GAP" log becomes a real assertion that LoadBalancer.Supported == true on the k3d/servicelb cluster; the ground-truth external-address probe stays as-is.

Light gate green: build, vet, gofmt -l (empty), go test ./..., and the SPDX check all pass.

https://claude.ai/code/session_012euqxgVqHP5yQWi2HxDPKt

…iders

LoadBalancer support was inferred only from a recognized cloud provider's
node providerID, so k3s/servicelb and MetalLB clusters were wrongly reported
as unsupported even though a type=LoadBalancer Service gets a real external IP
on them (the gap the LoadBalancer e2e surfaced in #193, where servicelb handed
a probe Service 172.18.0.2 while detection reported Supported=false).

Detection now reports LoadBalancer support from whatever actually services
LoadBalancers (ADR-0043): a recognized cloud provider (billable), k3s's
built-in servicelb, or MetalLB. servicelb is detected via the k3s providerID
scheme — the k3s cloud-controller that stamps "k3s://<node>" is the same one
that runs servicelb, and servicelb runs in-process with no standalone
Deployment and only per-Service svclb DaemonSets, so the cluster-level k3s
signal is the robust pre-exposure detector. MetalLB is detected via its
controller Deployment (both the Helm-chart and static-manifest label schemes).
Both signals reuse the nodes and apps/deployments get/list grants the
capability ClusterRole already holds, so no new RBAC is required. The
capability records which provider services LoadBalancers so the agent can
reason about cost and guide MetalLB when none is present.

The LoadBalancer e2e now asserts LoadBalancer.Supported is true on the k3d
(servicelb) cluster instead of logging the detection gap, keeping the
ground-truth external-address probe as the load-bearing proof.

Signed-off-by: Nicholas Phillips <nsphilli@gmail.com>
@incognick incognick added the pr-ready A PR is open and ready for review label Jul 3, 2026
@incognick incognick enabled auto-merge July 3, 2026 20:46
@incognick incognick added this pull request to the merge queue Jul 3, 2026
Merged via the queue into main with commit 8e7ab20 Jul 3, 2026
5 checks passed
@incognick incognick deleted the feat/detect-servicelb-metallb branch July 3, 2026 20:57
incognick added a commit that referenced this pull request Jul 3, 2026
… k3s

Resolve the open install-mechanism question in favor of Option B, an installer
run on the VPS, over Option A, SSH from the laptop. The user SSHes in once and
runs curl | sh, which installs k3s (--tls-san the public IP), deploys burrowd,
and prints a burrow join <token>; running that token on the laptop lands both
the admin and the scoped agent credential, so after the one-time SSH bootstrap
every operation (governance and agent) runs from the laptop, matching the
managed-cluster experience. Burrow never SSHes (keeping it out of the
SSH-as-root, host-key, and key-handling surface, ADR-0005), and takes no
dependency on k3sup. Depends on the servicelb detection from ADR-0043 (#201).

Signed-off-by: Nicholas Phillips <nsphilli@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-ready A PR is open and ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant