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")