Repository navigation
feat(platform): package address-controller and its MetalLB driver - #3382
Timofei Larkin (lllamnyp) wants to merge 5 commits into
Conversation
|
This PR has had no activity for 60 days and was marked |
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
Timofei Larkin (@lllamnyp) NOT LGTM, mostly because of where the code and images come from.
- Both PackageSources pull from
github.com/lllamnyp/*onbranch: mainwithoutspec.verify(sources/address-controller.yaml:13-14,sources/metallb-iad.yaml:13-14). Any push to those branches reaches clusters within minutes, withupgradeCRDs: CreateReplace, and metallb-iad has cluster-wide write on Services. Please pin a tag or a commit, or vendor the charts. - The images are
ghcr.io/lllamnyp/{address-controller,metallb-iad}:mainwith no digest andIfNotPresent.docs/agents/image-refs.mdrequires tag plus digest, andhack/lib/image-refs.shdoes not see these refs, so promote, retag and mirror skip them. A release tag then does not say what it installs, and different nodes can run different builds. templates/sources.yamlrenders every file undersources/, so the GitRepository objects exist on every cluster whether the package is enabled or not, and air-gapped clusters keep polling github.com.- The commit carries
Assisted-By: Claude <[email protected]>and the body has the "Generated with Claude Code" line.contributing.mdaccepts onlyAssisted-by: LLM. The PR template is not filled in either: no release note and no downstream checklist, and a new platform component is a website trigger.
There is also a conflict with main in bundles/system.yaml, where main added talos-log-collector; both sides should stay. helm unittest packages/core/platform passes, 88/88.
aa66010 to
3ba44b1
Compare
|
Aleksei Sviridkin (@lexfrei) thanks, all four points held. The branch is now rebuilt around them.
The Two additions since the version you reviewed:
The driver is also no longer offered on |
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
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. Comment |
Two optional system packages bring in IP addresses as a first-class resource (cozystack/community#35): cozystack.address-controller, the class-agnostic core (IPAddressClass / IPAddress / IPAddressClaim, local.sdn.cozystack.io), and cozystack.metallb-iad, the driver implementing the contract for addresses announced by MetalLB. Both follow the umbrella shape: the upstream chart is vendored under charts/ at a pinned commit, and what belongs to Cozystack rather than to a generic Kubernetes controller lives in the wrapper. Upstream cuts no tags and publishes no chart artifact yet, so each `update` target clones at a SHA; that becomes a tag once the projects start releasing. The wrapper carries the tenant grant. The upstream chart is Cozystack- agnostic and grants only the controller, so nothing there lets a tenant use the API it serves; cozy:addresses, cozy:addresses:view and cozy:addresses:admin aggregate into the platform's base tenant roles, which resolve by label, so the grant is present exactly while the package is. Only IPAddressClaim is granted: the other two kinds are cluster-scoped and cozy:tenant* reaches a tenant through a per-namespace RoleBinding, so a rule for either could never be consulted. The wrapper also pins both controller images by digest, which the charts' own floating :main defaults would otherwise leave free to move underneath a cluster, and points the driver at cozy-metallb instead of the upstream default metallb-system. The driver's PackageSource declares dependsOn on the core, whose CRDs it needs, and on cozystack.metallb. The core installs with upgradeCRDs: CreateReplace since its CRDs are v1alpha1 and still evolving. Assisted-by: LLM Signed-off-by: Timofei Larkin <[email protected]>
cozystack.metallb-iad hard-depends on cozystack.metallb, and a driver Package rendered without MetalLB beside it never leaves DependenciesNotReady — the wedge the velero and bucket-application comments in system.yaml already record for default packages, reachable here through an opt-in one. MetalLB is absent in three ways: on isp-hosted, which never calls the common-packages helper; on a slim variant that did not opt into it; and wherever disabledPackages names it. Rather than restate those rules for the driver, the driver is gated on MetalLB's own include having rendered a Package, so the two cannot drift when the rules for MetalLB change. The class-agnostic core stays in the variant-agnostic body: it carries no MetalLB dependency and is usable with another driver. Assisted-by: LLM Signed-off-by: Timofei Larkin <[email protected]>
A reservation is only as good as the annotations that express it. The driver parks an address on a placeholder Service and hands it over by pinning the workload's Service with metallb.io/loadBalancerIPs — ordinary MetalLB annotations that any writer of a Service can set, so a tenant able to set them can steer MetalLB at an address reserved for someone else. The driver notices and marks the address Conflict, but only after the fact, and during the handoff gap the address is briefly unheld in MetalLB's books with admission the only thing in front of it. The driver's design leaves this policy to the installation rather than the controller, since who is allowed to pin is a property of the deployment; this package is that installation, so the policy ships beside the chart it protects and is withdrawn with it. Presence of an annotation is deliberately not what is forbidden: the driver writes the pin onto the tenant's own Service, and the tenant must stay able to edit the rest of it. The comparison is against oldObject, so an unchanged value always passes, while CREATE sees a null oldObject and reads as absent. spec.loadBalancerIP needs no clause — MetalLB stopped honouring it in v0.14 and this tree vendors v0.16.1. Assisted-by: LLM Signed-off-by: Timofei Larkin <[email protected]>
Both upstreams now cut releases, so the vendored charts move from bare commits to the v0.1.0 tags and the controller images from the moving :main builds to the 0.1.0 release builds. The image tag carries no leading `v`: upstream's release job tags images through the metadata-action semver pattern, which drops it, so a `:v0.1.0` image reference would name a tag that does not exist. Re-vendoring from the tags changes only the chart versions and drops metallb-iad's values-cozystack.yaml, which upstream removed because this wrapper already supplies the MetalLB namespace. Assisted-by: LLM Signed-off-by: Timofei Larkin <[email protected]>
3ba44b1 to
5279c46
Compare
Both projects moved from lllamnyp/* into the cozystack org, which publishes their images under ghcr.io/cozystack/. The org has so far built only main, so the charts are vendored from the main commits those builds came from and the images are pinned to the matching main-<sha> tags by digest, keeping chart and controller from one source. The tag in each ref names that commit, unlike the floating :main pointing at the same digest today. Re-vendoring changes only the repository links and the charts' own default image, which the wrappers override. Assisted-by: LLM Signed-off-by: Timofei Larkin <[email protected]>
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
Timofei Larkin (@lllamnyp) NOT LGTM, one thing left: a generated test fixture is stale, so Unit & controller tests is red and the required E2E Tests never ran.
The points from my last review are fixed at 1ae06175. The charts are vendored from cozystack/* at pinned commits and come from the cozystack-packages artifact, so no GitRepository is left and nothing polls github.com. The image digests match ghcr: a154074e... is address-controller:main-ee38d0a and 143bd582... is metallb-iad:main-7d0017a, the same commits the Makefiles pin, and hack/lib/image-refs.sh sees both refs. Trailers and the PR template are fine now, and a trial merge with current main is clean.
Blocker
The fixture packages/core/platform/tests/fixtures/selective-install-postgres-values.yaml was not regenerated after the two new PackageSources. hack/select-install_test.bats "generated Helm values fixture matches the current disable list" fails on this head (the Unit & controller tests run of 2026-10-07T11:28Z, attempt 1), and I get the same failure on a trial merge with main. hack/select-install.sh --disabled postgres now prints cozystack.address-controller and cozystack.metallb-iad, and the fixture has neither. Since that job is red, E2E (in-tree) was skipped. Regenerating the fixture from select-install.sh should fix it.
Non-blocking
- Please fold the last two commits into
2b9d011a. Each was true when written, but together they record three stops: a SHA inlllamnyp/*, thenv0.1.0inlllamnyp/*, thenmain-<sha>incozystack/*. The repo merges with merge commits, so all three land in main.5279c46bpinsghcr.io/lllamnyp/address-controller:0.1.0and the next commit replaces it, and the body of2b9d011astill says the update target "becomes a tag once the projects start releasing". - The PR body says the
cozystackorg "has not cut a release of either yet". Both repos have av0.1.0tag, the pinned commits are a few commits ahead of it, and ghcr has no0.1.0image underghcr.io/cozystack/. "Has not built a release image yet" would be accurate. Require API owner review for sizeable API changesis red because of the new API group. You are one of the two owners it lists, so it needs kvaps.
On the trial merge helm unittest passes for platform (268/268), address-controller (3/3) and metallb-iad (4/4), and hack/select-install.sh --validate passes.
What this PR does
Adds IP addresses as a first-class resource (cozystack/community#35) as two optional system packages, off by default and enabled through
bundles.enabledPackages:cozystack.address-controller— the class-agnostic core (cozystack/address-controller):IPAddressClass/IPAddress/IPAddressClaiminlocal.sdn.cozystack.io, the PVC/PV-binding analog. It owns claim–address binding, status and reclaim, and never allocates anything itself.cozystack.metallb-iad— the per-class driver for addresses announced by MetalLB (cozystack/metallb-iad). It renders oneIPAddressPool(autoAssign: false) andL2Advertisementper served class, holds reservations as placeholder Services in its own namespace, and attaches an address to a tenant Service through thelocal.sdn.cozystack.io/ip-address-claimannotation.Packaging
Both are ordinary wrapper packages under
packages/system/, sourced from thecozystack-packagesartifact like every other package. The upstream chart is vendored verbatim undercharts/bymake updateat a pinned commit of itsmain, since neither project publishes a chart artifact and thecozystackorg has not cut a release of either yet. Each controller image is pinned by digest to themain-<sha>build of that same commit, in the wrapper'svalues.yaml, wherehack/lib/image-refs.shenumerates it. The images live underghcr.io/cozystack/<name>, outside theghcr.io/cozystack/cozystack/repositories the release tooling owns, so promotion and the nightly mirror pass over them. The core installs its v1alpha1 CRDs withupgradeCRDs: CreateReplace.What the wrappers add
cozy:addresses,cozy:addresses:viewandcozy:addresses:adminaggregate intocozy:tenant,cozy:tenant:viewandcozy:tenant:adminrespectively, so a tenant can manageIPAddressClaims in its own namespace, and the grant exists only while the package is installed. The cluster-scopedIPAddressandIPAddressClassare not granted:cozy:tenant*reaches a tenant through a per-namespace RoleBinding, so a rule for either would never be consulted. A tenant reads its address from the claim'sstatus.addresses[].cozystack-metallb-address-policystops anyone except the driver's ServiceAccount from introducing or changingmetallb.io/loadBalancerIPsormetallb.io/address-poolon a Service in atenant-*namespace. Without it a tenant could steer MetalLB at an address reserved for someone else; the driver would flag the addressConflict, but only after the fact. The policy compares againstoldObjectrather than forbidding the annotations outright, because the driver writes the pin onto the tenant's own Service and the tenant must still be able to edit the rest of that Service.cozy-metallbinstead of the upstream defaultmetallb-system.Variant scoping
cozystack.metallb-iaddepends oncozystack.metallb, which is absent onisp-hosted, on a slim variant that did not opt into it, and whereverdisabledPackagesnames it. The driver is emitted from thecommon-packageshelper only when MetalLB's own include rendered a Package, so it cannot be enabled where it would sit inDependenciesNotReadyforever, and it follows MetalLB automatically if MetalLB's rules change. The core carries no MetalLB dependency and remains available everywhere.Validation
helm unittestcovers the aggregation labels and rules on the tenant roles, the policy's annotation comparison, driver exemption, tenant scoping and capability gate, and the driver's per-variant emission.hack/select-install.sh --validatepasses. Both packages run on a live Cozystack integration cluster, where the claim → reservation → Service attachment → theft refusal → Retain/re-adoption → teardown path has been exercised end to end.Screenshots
Not a UI change.
Downstream repositories
The website is the one repository this change reaches: two new platform components need cards in
guides/platform-stack/_index.mdandoperations/configuration/licenses.md. The follow-up is not open yet.Release note