From d7673d597ff29142168a084d18132c64706d49e7 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Tue, 19 May 2026 00:24:38 -0700 Subject: [PATCH 1/2] source/pod: skip internal-hostname pod when PodIP is empty addInternalHostnameAnnotationEndpoints called endpoint.SuitableType(pod.Status.PodIP) and addToEndpointMap without first checking whether PodIP was empty. Pending or Scheduled pods that carry external-dns.alpha.kubernetes.io/internal-hostname ended up producing a CNAME record whose target was the empty string, because SuitableType on an empty string falls through to CNAME. Once the pod got a PodIP and external-dns tried to write an A record at the same name, the provider rejected the update (A and CNAME cannot coexist), leaving the zone permanently stuck on the empty-target CNAME. Skip the pod entirely when PodIP is empty. The pod will be reconciled again once the kubelet assigns an address. Ref: https://github.com/kubernetes-sigs/external-dns/issues/6375 Signed-off-by: SAY-5 --- source/pod.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/source/pod.go b/source/pod.go index 84e9551072..b2a1ae015b 100644 --- a/source/pod.go +++ b/source/pod.go @@ -188,6 +188,16 @@ func (ps *podSource) addInternalHostnameAnnotationEndpoints(endpointMap map[endp domainList := annotations.SplitHostnameAnnotation(domainAnnotation) for _, domain := range domainList { if len(targets) == 0 { + // Pods in Pending / Scheduled state without a PodIP must + // not emit an endpoint: endpoint.SuitableType("") returns + // CNAME, which would create a provider record whose target + // is the empty string. Once the pod eventually gets an IP + // external-dns then tries to create an A record at the + // same name and the provider rejects it because A and + // CNAME cannot coexist (#6375, #6199 on Azure / RFC2136). + if pod.Status.PodIP == "" { + continue + } addToEndpointMap(endpointMap, pod, domain, endpoint.SuitableType(pod.Status.PodIP), pod.Status.PodIP) } else { addTargetsToEndpointMap(endpointMap, pod, targets, domain) From 3115d27aa370baacba2d2d5187140bea4f8a44f4 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Tue, 19 May 2026 00:24:45 -0700 Subject: [PATCH 2/2] source/pod: skip kops and pod-source-domain endpoints when PodIP is empty Apply the same empty-PodIP guard to addKopsDNSControllerEndpoints and addPodSourceDomainEndpoints so kops-dns-controller and pod-source-domain pods without an assigned address do not emit empty-target CNAME records. Signed-off-by: SAY-5 --- source/pod.go | 13 ++++++------ source/pod_test.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/source/pod.go b/source/pod.go index b2a1ae015b..08c86f14b0 100644 --- a/source/pod.go +++ b/source/pod.go @@ -188,13 +188,6 @@ func (ps *podSource) addInternalHostnameAnnotationEndpoints(endpointMap map[endp domainList := annotations.SplitHostnameAnnotation(domainAnnotation) for _, domain := range domainList { if len(targets) == 0 { - // Pods in Pending / Scheduled state without a PodIP must - // not emit an endpoint: endpoint.SuitableType("") returns - // CNAME, which would create a provider record whose target - // is the empty string. Once the pod eventually gets an IP - // external-dns then tries to create an A record at the - // same name and the provider rejects it because A and - // CNAME cannot coexist (#6375, #6199 on Azure / RFC2136). if pod.Status.PodIP == "" { continue } @@ -222,6 +215,9 @@ func (ps *podSource) addKopsDNSControllerEndpoints(endpointMap map[endpoint.Endp if domainAnnotation, ok := pod.Annotations[kopsDNSControllerInternalHostnameAnnotationKey]; ok { domainList := annotations.SplitHostnameAnnotation(domainAnnotation) for _, domain := range domainList { + if pod.Status.PodIP == "" { + continue + } addToEndpointMap(endpointMap, pod, domain, endpoint.SuitableType(pod.Status.PodIP), pod.Status.PodIP) } } @@ -237,6 +233,9 @@ func (ps *podSource) addPodSourceDomainEndpoints(endpointMap map[endpoint.Endpoi if ps.podSourceDomain != "" { domain := pod.Name + "." + ps.podSourceDomain if len(targets) == 0 { + if pod.Status.PodIP == "" { + return + } addToEndpointMap(endpointMap, pod, domain, endpoint.SuitableType(pod.Status.PodIP), pod.Status.PodIP) } addTargetsToEndpointMap(endpointMap, pod, targets, domain) diff --git a/source/pod_test.go b/source/pod_test.go index 6d9464139c..04f529ec7c 100644 --- a/source/pod_test.go +++ b/source/pod_test.go @@ -797,6 +797,59 @@ func TestPodSource(t *testing.T) { }, }, }, + { + "kops-dns-controller internal-hostname pod without a PodIP is skipped", + "", + "kops-dns-controller", + true, + "", + []*endpoint.Endpoint{}, + false, + nodesFixturesIPv4(), + []*corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-pod1", + Namespace: "kube-system", + Annotations: map[string]string{ + kopsDNSControllerInternalHostnameAnnotationKey: "internal.a.foo.example.org", + }, + }, + Spec: corev1.PodSpec{ + HostNetwork: true, + NodeName: "my-node1", + }, + Status: corev1.PodStatus{ + PodIP: "", + }, + }, + }, + }, + { + "pod-source-domain pod without a PodIP is skipped", + "", + "", + true, + "pod.example.org", + []*endpoint.Endpoint{}, + false, + nodesFixturesIPv4(), + []*corev1.Pod{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-pod1", + Namespace: "kube-system", + }, + Spec: corev1.PodSpec{ + HostNetwork: true, + NodeName: "my-node1", + }, + Status: corev1.PodStatus{ + PodIP: "", + }, + }, + }, + }, } { t.Run(tc.title, func(t *testing.T) { kubernetes := fake.NewClientset()