feat(capabilities): detect servicelb and MetalLB as LoadBalancer providers#201
Merged
Conversation
…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
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 atype: LoadBalancerService gets a real external IP on them. This is the gap the LoadBalancer e2e surfaced in #193, where k3s servicelb handed a probe Service172.18.0.2while capability detection reportedSupported=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
k3sproviderIDscheme. servicelb (klipper-lb) runs in-process in the k3s server, so there is no standalone controller Deployment to look for, and the per-Servicesvclb-*DaemonSets only exist after a LoadBalancer Service is created — neither is a reliable pre-exposure signal. The k3s cloud-controller that stampsk3s://<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.app.kubernetes.io/name=metallb) and static-manifest (app=metallb) label schemes, in any namespace.RBAC
No new grant needed: both signals reuse the
nodesandapps/deploymentsget/list grants theburrowd-cluster-capabilitiesClusterRole already holds (the deployments grant was added for ingress-controller detection). The ClusterRole stays strictly read-only and unchanged.Capability shape
LoadBalancerCapabilitygains aProvider stringfield (json:"provider,omitempty") naming the mechanism — a cloud id,servicelb, ormetallb; empty when none. Mirrored in the API client type; theburrow clusterview labels the source and whether it is free/billable.Tests
controlplane/kube/capabilities_test.go:TestDetectCapabilitiesFullCluster(cloud → supported, providerdigitalocean), newTestDetectLoadBalancerServiceLB(k3s → supported, providerservicelb), newTestDetectLoadBalancerMetalLB(both label schemes → supported, providermetallb), andTestDetectCapabilitiesBareMetalreworked to a truly bare cluster (no provider → unsupported).controlplane/e2e/loadbalancer_e2e_test.go: the "DETECTION GAP" log becomes a real assertion thatLoadBalancer.Supported == trueon 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