From f199f3473a07eb2361900704e93e15154d297236 Mon Sep 17 00:00:00 2001 From: Scot Wells Date: Tue, 15 Sep 2026 16:24:33 -0500 Subject: [PATCH] feat(cni): record the host device when the NAD is written The host device name is derived from the VPC and the attachment identifier, both of which this controller holds when it writes the attachment definition. Until now it was recorded only by the data plane, during the CNI ADD that creates the device, which is too late for a runtime that has to name the device in the same request that asks for the interface. Such a runtime finds nothing to read and falls back to a name of its own, which matches no device that exists. Writing the name when the definition is written makes it a prediction the ADD later fulfils rather than a fact only the ADD can report. The data plane writes the same key from the same inputs, so the two always agree and the later write is a no-op. Key changes: - Record the host interface name on the attachment definition from the controller that creates it - Pin the annotation key in a test: it is a contract with the data plane and with the runtime that reads it, and nothing else here would fail if it were renamed --- internal/controller/networkinterface_controller.go | 13 +++++++++++++ .../controller/networkinterface_controller_test.go | 12 ++++++++++++ 2 files changed, 25 insertions(+) diff --git a/internal/controller/networkinterface_controller.go b/internal/controller/networkinterface_controller.go index 2a08829..5b0a370 100644 --- a/internal/controller/networkinterface_controller.go +++ b/internal/controller/networkinterface_controller.go @@ -47,6 +47,15 @@ const ( // The NAD is the allocation record for that identifier. LabelVPCAttachment = "cloud.datumapis.com/vpc-attachment" + // AnnotationHostInterface records the host device this attachment will get, + // on the NAD, at the moment the NAD is written. The name is derived from the + // VPC and the attachment identifier, so it is known here — well before the + // CNI ADD that creates the device. A runtime that must name the device when + // it asks for an interface, rather than learn it from the ADD result, has + // nowhere else to read it in time. galactic writes the same key during ADD + // from the same inputs, so the two always agree. + AnnotationHostInterface = "k8s.v1.cni.cncf.io/host-interface" + // ConditionTypePrepared reports that the data plane's pre-Pod artifacts exist. // Unlike Programmed, which only becomes true at CNI ADD, it is safe to gate // Pod creation on. network-services-operator is adding the type in parallel. @@ -213,6 +222,10 @@ func (r *NetworkInterfaceReconciler) reconcileNAD( } nad.Labels[LabelVPC] = vpc.Status.VPC nad.Labels[LabelVPCAttachment] = attachmentID + if nad.Annotations == nil { + nad.Annotations = map[string]string{} + } + nad.Annotations[AnnotationHostInterface] = galactic.HostInterfaceName(vpc.Status.VPC, attachmentID) nad.Spec.Config = config return controllerutil.SetControllerReference(attachment, nad, r.Scheme) }); err != nil { diff --git a/internal/controller/networkinterface_controller_test.go b/internal/controller/networkinterface_controller_test.go index a5f57e1..6ce4090 100644 --- a/internal/controller/networkinterface_controller_test.go +++ b/internal/controller/networkinterface_controller_test.go @@ -119,6 +119,18 @@ func TestAttachmentModeFallsBackToTheCell(t *testing.T) { } // Only the declared mode asks the tap plugin to describe the device. +// TestHostInterfaceAnnotationKey pins the annotation key this controller writes +// on the NAD. The key is a contract with the data plane: galactic writes the +// same one during CNI ADD, and a runtime reads it to learn its host device. +// Renaming it here alone would leave that runtime with nothing to read, and +// nothing else in this repository would fail. +func TestHostInterfaceAnnotationKey(t *testing.T) { + const want = "k8s.v1.cni.cncf.io/host-interface" + if AnnotationHostInterface != want { + t.Errorf("AnnotationHostInterface = %q, want %q", AnnotationHostInterface, want) + } +} + func TestDeclaresDevice(t *testing.T) { if declaresDevice(cloudv1alpha1.VPCAttachmentInterfaceModeHypervisor) { t.Error("a discovered hypervisor attachment must not ask for a description")