From 8c98975387bb5a17f4726fb3e0a9db34a8614cf8 Mon Sep 17 00:00:00 2001 From: Scot Wells Date: Tue, 3 Nov 2026 11:22:16 -0600 Subject: [PATCH 1/3] feat(internal-dns): label resolver contexts with their network The DNSResolverContext for a network now carries the network name and UID labels, and existing contexts get them on the next pass. DNS copies these labels to the context's managed zone, so the zone names its VPC. --- internal/controller/internaldns_controller.go | 38 ++++++++++++++ .../controller/internaldns_controller_test.go | 49 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/internal/controller/internaldns_controller.go b/internal/controller/internaldns_controller.go index fbd0c28c..ba9e8cd8 100644 --- a/internal/controller/internaldns_controller.go +++ b/internal/controller/internaldns_controller.go @@ -19,6 +19,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/validation" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/cluster" @@ -296,10 +297,14 @@ func (r *InternalDNSProjectReconciler) ensureContext( if domain != internalDNSDefaultDomain || !enabled { return nil, fmt.Errorf("DNSResolverContext %s/%s does not request the network default domain", network.Namespace, name) } + if err := labelInternalDNSContext(ctx, cl, current, network); err != nil { + return nil, err + } return current, nil } desired := newInternalDNSObject(internalDNSContextGVK, network.Namespace, name) + desired.SetLabels(internalDNSNetworkLabels(network)) desired.SetAnnotations(map[string]string{ internalDNSOwnerNetworkUIDAnnotation: string(network.UID), internalDNSOwnerProjectUIDAnnotation: string(projectUID), @@ -318,6 +323,39 @@ func (r *InternalDNSProjectReconciler) ensureContext( return desired, nil } +// internalDNSNetworkLabels names the network a resolver context serves. DNS +// copies a context's labels to its managed zone, so the zone says which +// network it is for. +func internalDNSNetworkLabels(network *networkingv1alpha.Network) map[string]string { + labels := map[string]string{networkingv1alpha.NetworkUIDLabel: string(network.UID)} + if len(validation.IsValidLabelValue(network.Name)) == 0 { + labels[networkingv1alpha.NetworkLabel] = network.Name + } + return labels +} + +// labelInternalDNSContext adds the network labels to a context created before +// it carried them. +func labelInternalDNSContext(ctx context.Context, cl client.Client, current *unstructured.Unstructured, network *networkingv1alpha.Network) error { + base := current.DeepCopy() + labels := current.GetLabels() + if labels == nil { + labels = map[string]string{} + } + changed := false + for key, value := range internalDNSNetworkLabels(network) { + if labels[key] != value { + labels[key] = value + changed = true + } + } + if !changed { + return nil + } + current.SetLabels(labels) + return cl.Patch(ctx, current, client.MergeFrom(base)) +} + func resolverContextReady(obj *unstructured.Unstructured) bool { if !obj.GetDeletionTimestamp().IsZero() || obj.GetGeneration() < 1 { return false diff --git a/internal/controller/internaldns_controller_test.go b/internal/controller/internaldns_controller_test.go index 47cf1bef..4617d66f 100644 --- a/internal/controller/internaldns_controller_test.go +++ b/internal/controller/internaldns_controller_test.go @@ -4,7 +4,9 @@ package controller import ( "context" + "maps" "slices" + "strings" "testing" "time" @@ -810,3 +812,50 @@ func (f *dnsOffFixture) assertDNSOff(t *testing.T, ctx context.Context, key clie t.Errorf("the regional access the context used survived DNS turning off: %v", err) } } + +func TestInternalDNSContextNamesItsNetwork(t *testing.T) { + t.Parallel() + ctx := context.Background() + scheme := runtime.NewScheme() + scheme.AddKnownTypeWithName(internalDNSContextGVK, &unstructured.Unstructured{}) + network := &networkingv1alpha.Network{ObjectMeta: metav1.ObjectMeta{Namespace: "tenant", Name: "blue", UID: "network-uid"}} + want := map[string]string{networkingv1alpha.NetworkLabel: "blue", networkingv1alpha.NetworkUIDLabel: "network-uid"} + r := &InternalDNSProjectReconciler{} + + cl := fake.NewClientBuilder().WithScheme(scheme).Build() + created, err := r.ensureContext(ctx, cl, network, "project-uid", "project-uid/network-uid") + if err != nil { + t.Fatal(err) + } + if got := created.GetLabels(); !maps.Equal(got, want) { + t.Fatalf("new context labels = %v, want %v", got, want) + } + + // A context created before the labels existed gets them on the next pass. + existing := newInternalDNSObject(internalDNSContextGVK, "tenant", internalDNSResourceName("blue", network.UID)) + existing.SetLabels(map[string]string{"other": "kept"}) + existing.SetAnnotations(map[string]string{internalDNSOwnerNetworkUIDAnnotation: "network-uid"}) + existing.Object["spec"] = map[string]any{ + "consumerID": "project-uid/network-uid", + "managedNamespace": map[string]any{"enabled": true, "domainSuffix": internalDNSDefaultDomain}, + } + cl = fake.NewClientBuilder().WithScheme(scheme).WithObjects(existing).Build() + if _, err := r.ensureContext(ctx, cl, network, "project-uid", "project-uid/network-uid"); err != nil { + t.Fatal(err) + } + stored := newInternalDNSObject(internalDNSContextGVK, "tenant", existing.GetName()) + if err := cl.Get(ctx, client.ObjectKeyFromObject(stored), stored); err != nil { + t.Fatal(err) + } + want["other"] = "kept" + if got := stored.GetLabels(); !maps.Equal(got, want) { + t.Fatalf("existing context labels = %v, want %v", got, want) + } + + // A name too long for a label value still gets the UID label. + long := network.DeepCopy() + long.Name = strings.Repeat("a", 64) + if got := internalDNSNetworkLabels(long); got[networkingv1alpha.NetworkLabel] != "" || got[networkingv1alpha.NetworkUIDLabel] != "network-uid" { + t.Fatalf("long name labels = %v", got) + } +} From 5bdec35e3c73c0cb6465d7d3ce8b09f02e4b0deb Mon Sep 17 00:00:00 2001 From: Scot Wells Date: Tue, 3 Nov 2026 11:32:26 -0600 Subject: [PATCH 2/3] fix(internal-dns): ask DNS to copy the network labels, best effort Set the managed-object-labels annotation so DNS copies the network labels to the managed zone, and log a failed label write instead of holding up DNS access. --- internal/controller/internaldns_controller.go | 24 ++++++++++++++++--- .../controller/internaldns_controller_test.go | 6 +++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/internal/controller/internaldns_controller.go b/internal/controller/internaldns_controller.go index ba9e8cd8..54e5b3a5 100644 --- a/internal/controller/internaldns_controller.go +++ b/internal/controller/internaldns_controller.go @@ -53,6 +53,9 @@ const ( internalDNSOwnerNetworkUIDAnnotation = "networking.datumapis.com/internal-dns-network-uid" internalDNSOwnerProjectUIDAnnotation = "networking.datumapis.com/internal-dns-project-uid" + // internalDNSManagedLabelsAnnotation names the context labels DNS copies to + // the context's managed zone. + internalDNSManagedLabelsAnnotation = "dns.networking.miloapis.com/managed-object-labels" internalDNSRegionTopologyKey = "topology.datum.net/region" internalDNSRetryInterval = 10 * time.Second @@ -64,6 +67,8 @@ const ( ) var ( + internalDNSManagedLabels = networkingv1alpha.NetworkLabel + "," + networkingv1alpha.NetworkUIDLabel + internalDNSContextGVK = schema.GroupVersionKind{ Group: "dns.networking.miloapis.com", Version: "v1alpha1", Kind: "DNSResolverContext", } @@ -297,8 +302,10 @@ func (r *InternalDNSProjectReconciler) ensureContext( if domain != internalDNSDefaultDomain || !enabled { return nil, fmt.Errorf("DNSResolverContext %s/%s does not request the network default domain", network.Namespace, name) } + // The labels only tell people which network a zone is for, so a failed + // write never holds up DNS access. if err := labelInternalDNSContext(ctx, cl, current, network); err != nil { - return nil, err + log.FromContext(ctx).Error(err, "label DNSResolverContext with its network", "name", name) } return current, nil } @@ -308,6 +315,7 @@ func (r *InternalDNSProjectReconciler) ensureContext( desired.SetAnnotations(map[string]string{ internalDNSOwnerNetworkUIDAnnotation: string(network.UID), internalDNSOwnerProjectUIDAnnotation: string(projectUID), + internalDNSManagedLabelsAnnotation: internalDNSManagedLabels, }) desired.SetOwnerReferences([]metav1.OwnerReference{{ APIVersion: networkingv1alpha.GroupVersion.String(), Kind: "Network", Name: network.Name, @@ -334,8 +342,9 @@ func internalDNSNetworkLabels(network *networkingv1alpha.Network) map[string]str return labels } -// labelInternalDNSContext adds the network labels to a context created before -// it carried them. +// labelInternalDNSContext adds the network labels, and the annotation asking +// DNS to copy them to the managed zone, to a context created before it carried +// them. func labelInternalDNSContext(ctx context.Context, cl client.Client, current *unstructured.Unstructured, network *networkingv1alpha.Network) error { base := current.DeepCopy() labels := current.GetLabels() @@ -349,10 +358,19 @@ func labelInternalDNSContext(ctx context.Context, cl client.Client, current *uns changed = true } } + annotations := current.GetAnnotations() + if annotations == nil { + annotations = map[string]string{} + } + if annotations[internalDNSManagedLabelsAnnotation] != internalDNSManagedLabels { + annotations[internalDNSManagedLabelsAnnotation] = internalDNSManagedLabels + changed = true + } if !changed { return nil } current.SetLabels(labels) + current.SetAnnotations(annotations) return cl.Patch(ctx, current, client.MergeFrom(base)) } diff --git a/internal/controller/internaldns_controller_test.go b/internal/controller/internaldns_controller_test.go index 4617d66f..8051508c 100644 --- a/internal/controller/internaldns_controller_test.go +++ b/internal/controller/internaldns_controller_test.go @@ -830,6 +830,9 @@ func TestInternalDNSContextNamesItsNetwork(t *testing.T) { if got := created.GetLabels(); !maps.Equal(got, want) { t.Fatalf("new context labels = %v, want %v", got, want) } + if got := created.GetAnnotations()[internalDNSManagedLabelsAnnotation]; got != internalDNSManagedLabels { + t.Fatalf("new context managed labels annotation = %q", got) + } // A context created before the labels existed gets them on the next pass. existing := newInternalDNSObject(internalDNSContextGVK, "tenant", internalDNSResourceName("blue", network.UID)) @@ -851,6 +854,9 @@ func TestInternalDNSContextNamesItsNetwork(t *testing.T) { if got := stored.GetLabels(); !maps.Equal(got, want) { t.Fatalf("existing context labels = %v, want %v", got, want) } + if got := stored.GetAnnotations()[internalDNSManagedLabelsAnnotation]; got != internalDNSManagedLabels { + t.Fatalf("existing context managed labels annotation = %q", got) + } // A name too long for a label value still gets the UID label. long := network.DeepCopy() From 42e6a515beac70b736037423333812a1e56e2b8b Mon Sep 17 00:00:00 2001 From: Scot Wells Date: Tue, 3 Nov 2026 11:44:07 -0600 Subject: [PATCH 3/3] feat(internal-dns): describe the resolver context by its VPC Set kubernetes.io/description to "Private DNS for the VPC", using the network's display name when it has one. DNS copies it to the managed zone, which the portal shows as the zone description. --- internal/controller/internaldns_controller.go | 38 +++++++++++++------ .../controller/internaldns_controller_test.go | 7 ++++ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/internal/controller/internaldns_controller.go b/internal/controller/internaldns_controller.go index 54e5b3a5..72e4dff5 100644 --- a/internal/controller/internaldns_controller.go +++ b/internal/controller/internaldns_controller.go @@ -56,6 +56,8 @@ const ( // internalDNSManagedLabelsAnnotation names the context labels DNS copies to // the context's managed zone. internalDNSManagedLabelsAnnotation = "dns.networking.miloapis.com/managed-object-labels" + internalDNSDescriptionAnnotation = "kubernetes.io/description" + internalDNSDisplayNameAnnotation = "kubernetes.io/display-name" internalDNSRegionTopologyKey = "topology.datum.net/region" internalDNSRetryInterval = 10 * time.Second @@ -312,11 +314,10 @@ func (r *InternalDNSProjectReconciler) ensureContext( desired := newInternalDNSObject(internalDNSContextGVK, network.Namespace, name) desired.SetLabels(internalDNSNetworkLabels(network)) - desired.SetAnnotations(map[string]string{ - internalDNSOwnerNetworkUIDAnnotation: string(network.UID), - internalDNSOwnerProjectUIDAnnotation: string(projectUID), - internalDNSManagedLabelsAnnotation: internalDNSManagedLabels, - }) + annotations := internalDNSContextAnnotations(network) + annotations[internalDNSOwnerNetworkUIDAnnotation] = string(network.UID) + annotations[internalDNSOwnerProjectUIDAnnotation] = string(projectUID) + desired.SetAnnotations(annotations) desired.SetOwnerReferences([]metav1.OwnerReference{{ APIVersion: networkingv1alpha.GroupVersion.String(), Kind: "Network", Name: network.Name, UID: network.UID, Controller: boolPtr(true), BlockOwnerDeletion: boolPtr(true), @@ -342,9 +343,22 @@ func internalDNSNetworkLabels(network *networkingv1alpha.Network) map[string]str return labels } -// labelInternalDNSContext adds the network labels, and the annotation asking -// DNS to copy them to the managed zone, to a context created before it carried -// them. +// internalDNSContextAnnotations ask DNS to copy the network labels to the +// managed zone, and describe the zone by the network it serves, which DNS also +// copies to the zone. +func internalDNSContextAnnotations(network *networkingv1alpha.Network) map[string]string { + name := network.Name + if display := network.Annotations[internalDNSDisplayNameAnnotation]; display != "" { + name = display + } + return map[string]string{ + internalDNSManagedLabelsAnnotation: internalDNSManagedLabels, + internalDNSDescriptionAnnotation: "Private DNS for the " + name + " VPC", + } +} + +// labelInternalDNSContext adds the network labels and annotations to a context +// created before it carried them, and keeps the description current. func labelInternalDNSContext(ctx context.Context, cl client.Client, current *unstructured.Unstructured, network *networkingv1alpha.Network) error { base := current.DeepCopy() labels := current.GetLabels() @@ -362,9 +376,11 @@ func labelInternalDNSContext(ctx context.Context, cl client.Client, current *uns if annotations == nil { annotations = map[string]string{} } - if annotations[internalDNSManagedLabelsAnnotation] != internalDNSManagedLabels { - annotations[internalDNSManagedLabelsAnnotation] = internalDNSManagedLabels - changed = true + for key, value := range internalDNSContextAnnotations(network) { + if annotations[key] != value { + annotations[key] = value + changed = true + } } if !changed { return nil diff --git a/internal/controller/internaldns_controller_test.go b/internal/controller/internaldns_controller_test.go index 8051508c..aa29ca8a 100644 --- a/internal/controller/internaldns_controller_test.go +++ b/internal/controller/internaldns_controller_test.go @@ -857,6 +857,13 @@ func TestInternalDNSContextNamesItsNetwork(t *testing.T) { if got := stored.GetAnnotations()[internalDNSManagedLabelsAnnotation]; got != internalDNSManagedLabels { t.Fatalf("existing context managed labels annotation = %q", got) } + if got := stored.GetAnnotations()[internalDNSDescriptionAnnotation]; got != "Private DNS for the blue VPC" { + t.Fatalf("existing context description = %q", got) + } + network.Annotations = map[string]string{internalDNSDisplayNameAnnotation: "Blue network"} + if got := internalDNSContextAnnotations(network)[internalDNSDescriptionAnnotation]; got != "Private DNS for the Blue network VPC" { + t.Fatalf("display name description = %q", got) + } // A name too long for a label value still gets the UID label. long := network.DeepCopy()