Skip to content

feat(vpc): add the VPC controller that realizes attachments on the data plane - #6

Merged
scotwells merged 11 commits into
mainfrom
feat/vpc-controller
Aug 21, 2026
Merged

feat(vpc): add the VPC controller that realizes attachments on the data plane#6
scotwells merged 11 commits into
mainfrom
feat/vpc-controller

Conversation

@scotwells

@scotwells scotwells commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Nothing in the org reconciles VPC or VPCAttachment. Galactic's CNI requires the NAD to have been "created by the external VPC operator before the CNI is invoked" — this is that operator, and this repo's first binary.

With it running in a POP cell, a compute Instance lands on a tenant VPC: NSO allocates the addresses, this controller turns them into galactic identifiers and a NetworkAttachmentDefinition, and the data plane's own report comes back as Programmed.

Contains #5's API changes, and targets main rather than that branch so CI runs at all.

Three reconcilers

Watches Produces
NetworkContext VPC, with a random 48-bit identifier
NetworkInterface VPCAttachment + NAD + attachment identifier, status.vpc, attachmentRef, Prepared
BGPAdvertisement Programmed, and the node-scoped facts

A fulfilled claim is the trigger, so no infrastructure provider creates an attachment or waits on one. Both objects are per-interface, so the identifier and the tap device name stay stable across instance replacement.

Decisions worth a reviewer's attention

A mutating Pod webhook injects k8s.v1.cni.cncf.io/networks. Whoever writes a NAD should be the only thing that knows NADs exist, so a provider stamps one opt-in label and nothing else. failurePolicy: Fail with an objectSelector narrowed to that label: an outage blocks exactly the Pods that need an interface, loudly, rather than blocking the cell or letting one boot unattached. The injection is recorded on the Pod.

--attachment-mode is required, with no default. Defaulting to Netns would hand a Unikraft microVM a veth it cannot use. It stands in for a capability class that does not exist yet; there is no AttachmentClass because a cell hosts one implementation today.

Prepared is the condition compute gates on. Programmed only becomes true at CNI ADD, after sandbox creation, so gating Pod creation on it deadlocks.

Identifiers are allocated by the leader, at random, so a freed one is not reissued while its BGPAdvertisement is still being collected. Object names never contain base62, which is not a valid lowercase RFC 1123 name.

Dependencies

go.datum.net/compute points at a merged commit on main; no branch pins remain.

Not covered

No envtest exercises a real admission request — the webhook's failure mode is a Pod coming up unattached, so that is the gap worth closing before this leaves draft. E2E not run.

Related

This is the "separate operator project" galactic handed VPC and VPCAttachment to when it removed them (its architecture doc names go.datum.net/cloud, and nadpatch.AnnotateNAD requires the NAD to have been "created by the external VPC operator before the CNI is invoked"). Network services integration under datum-cloud/galactic#17.

Realizes the data-plane end of datum-cloud/network-services-operator#164 and datum-cloud/compute#112: it writes the vpc, attachmentRef and Programmed fields those two leave for whoever programs the interface.

Allocating VPC identity here is a step toward datum-cloud/galactic#197 — one authority for a VPC's identity and address space rather than the CNI deciding locally.

Built on #5, and targets main rather than that branch so CI runs at all.

Deliberately out of scope: datum-cloud/galactic#332, #329 and #328 are data-plane concerns this controller does not touch. Note that the NAD is created per NetworkInterface rather than per VPC partly because two live workloads cannot currently share one attachment — the host interface name is derived from (vpc, vpcattachment) alone, so veth.Add deletes and recreates an existing one while tap.Add adopts it.

🤖 Generated with Claude Code

…ridge

Status becomes partially writable, the spec gains a NetworkInterface
back-reference and a master-plugin selector, and a guest that manages its own
addressing is now representable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…plugin

interface.mode (Netns | Hypervisor) replaces interface.type (veth | tap), so
galactic's plugin names stay out of a cloud-level API, and interfaceRef.uid
becomes load-bearing: a mismatch means the attachment is stale.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
scotwells and others added 8 commits August 20, 2026 19:56
The controller that writes interfaceRef also sets an owner reference carrying
the interface's UID, so the field restated a fact the object already held and
owner-reference garbage collection already acts on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ta plane

Reconciles NetworkContext into a VPC identity, allocates an attachment
identifier and renders a NetworkAttachmentDefinition per NetworkInterface, and
projects the galactic data plane's BGPAdvertisement onto VPCAttachment and
NetworkInterface status.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The attachment names the VPC, carries how the guest consumes the interface and
forward-references the NetworkInterface, so it owns the NAD and every render
input is reachable from it. Removes the reverse lookup that listed every
attachment in a namespace to render an interface's NAD, and with it the
NetworkInterface reconciler.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The controller now creates the VPCAttachment and the NAD from a bound,
allocated NetworkInterface, and publishes the annotations a workload must carry
so no infrastructure provider has to know what a NAD is. Attachment mode is
required configuration with no default, standing in for a capability class that
does not exist yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…owing it

An API server that does not know status.consumerAnnotations prunes it and
answers success, so the write is read back and a failure to persist is reported
on the attachment's Ready condition as ConsumerAnnotationsUnsupported.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Multus knowledge stays in the component that writes NetworkAttachmentDefinitions:
the controller injects the annotation itself instead of publishing it for a
provider to copy. Adds a Prepared condition on the interface and its claim, which
is safe to gate Pod creation on, and removes status.consumerAnnotations.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
datum-cloud/compute#245 has merged, so networkInterfaceRef is on main and the
branch pin this carried is no longer needed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The attachment already carries the interface's UID in its owner reference, so
the spec field and the staleness check reading it were a second copy of the
same fact with nothing to decide between them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@scotwells
scotwells force-pushed the feat/vpc-controller branch from 13a7a39 to a9f05f0 Compare August 21, 2026 00:57
@scotwells
scotwells requested a review from privateip August 21, 2026 01:48
@scotwells
scotwells marked this pull request as ready for review August 21, 2026 01:48
@scotwells
scotwells requested a review from a team as a code owner August 21, 2026 01:48
privateip
privateip previously approved these changes Aug 21, 2026
@scotwells
scotwells dismissed privateip’s stale review August 21, 2026 01:50

The merge-base changed after approval.

privateip
privateip previously approved these changes Aug 21, 2026
@scotwells
scotwells dismissed privateip’s stale review August 21, 2026 01:55

The merge-base changed after approval.

@scotwells
scotwells merged commit 43ec86a into main Aug 21, 2026
5 checks passed
@scotwells
scotwells deleted the feat/vpc-controller branch August 21, 2026 02:01
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.

2 participants