Skip to content

Control plane - #16

Merged
mwindower merged 5 commits into
mainfrom
control-plane
Sep 25, 2026
Merged

mwindower merged 5 commits into
mainfrom
control-plane

Conversation

@mwindower

Copy link
Copy Markdown
Contributor

Description

Consider control plane.

Used AI-Tools ✨

  • Claude Opus 5.5

mwindower and others added 4 commits September 25, 2026 12:01
The metal-stack control plane is a Kubernetes cluster carrying metal-api,
masterdata-api, the IPAM and their databases, and it has to run somewhere. The
deployment guide leaves the location open ("it does not matter where your
control plane Kubernetes cluster is located, you can of course use a cluster
managed by a hyperscaler") and asks only that the partitions can reach it, so a
plan now says which of the two it is.

Plan.controlPlane is plan-level, since one control plane serves every
partition:

- 'kaas' orders no hardware and draws a capsule on each partition's routers, so
  the connection the partitions need to it stays visible.
- 'on-prem' are nodes this plan buys, either in the host partition's central
  rack attached to its exit switches, or in a control-plane rack of their own
  behind a leaf pair uplinked to the spines.

derive/controlPlane.ts is the single source of those placement rules; the BOM,
the rack elevations, the topology graph and validation all ask it instead of
re-reading the config. An own rack's leaves count in spinePortsPerSpine() and
the node ports in the exit switch budget, so the fabric checks stay honest. The
nodes run the Kubernetes cluster and are never metal-stack-managed machines:
they stay out of planNodes() and the hardware compatibility list does not apply
to them.

Validation stays thin on purpose: an error when an on-prem control plane has no
nodes, a warning below three for etcd quorum, and the exit port budget. Where
the cluster runs is free, so nothing warns about reachability; the info text
states the requirement instead.

The field is additive with a Zod default, so plan files written before it keep
importing and the format version stays at 1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…stack mark

The KaaS capsule was titled with the cluster's name and then repeated
"managed Kubernetes" underneath, while every other node in the diagram is
titled by what it is. It now reads "Control plane" with the cluster name as
the subtitle, the same shape as the on-prem box, which carries its hardware
there.

The control plane is metal-stack itself, so it takes the picture mark as its
pictogram everywhere it has one: the section header, the topology node in
either hosting mode, and the rack slot map. The mark is drawn as a stroke
glyph in views/icons/custom.ts at the proportions of the original (peaked top
layer, two zigzag layers, corners at 0.29 of the height) rather than embedding
the asset, so it takes the text color and sits on Lucide's 24 px grid like the
other custom glyphs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The control plane is plan-level: one cluster serves every partition, so it
belongs above the per-partition hardware rather than below it. The pane now
sits directly after the plan name block, as the first pane on the Plan tab,
and is titled "Control plane for metal-stack.io installation".

Naming the managed cluster bought nothing: the name only labelled the capsule
in the topology and the side panel line, where "managed Kubernetes" says it
better. The field is gone from the schema and the section, which leaves the
KaaS case with a single control: where it runs.

Dropping Plan.controlPlane.name is not a breaking change for plan files: an
unknown key is stripped by PlanSchema, so files written with a name still
import and the format version stays at 1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
At the top it read as the most important thing on the page, which it is not:
hosting is chosen once and then left alone. It sits with the external
networks at the end instead, the other plan-level pane.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rage attachment

Five files conflicted, all of them because both sides grew in the same place:

- templates.ts: the template is now Production and builds through the
  topology-less plan() helper, and still gets the on-prem control plane.
- planStore.ts: setTopology went away on main, the control plane actions stay.
- topology.ts: attachesAtStorageLeaves and addControlPlane are independent
  additions; both survive. In filterTopology the external networks are
  collected in main's later "still attached to something" pass, so only the
  control plane node joins the ids set, and the doc comment names both rules.
- Diagram.tsx: the top row is now main's centralNets (storage networks hang
  over the storage box instead) plus a managed control plane, placed with
  main's placeCapsules helper.
- topology.test.ts: imports of both sides.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mwindower
mwindower merged commit 7c7242b into main Sep 25, 2026
4 checks passed
@mwindower
mwindower deleted the control-plane branch September 25, 2026 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

1 participant