sigs.k8s.io/external-dns@v0.14.1/source/pod.go (about) 1 /* 2 Copyright 2021 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package source 18 19 import ( 20 "context" 21 22 "sigs.k8s.io/external-dns/endpoint" 23 24 log "github.com/sirupsen/logrus" 25 corev1 "k8s.io/api/core/v1" 26 "k8s.io/apimachinery/pkg/labels" 27 kubeinformers "k8s.io/client-go/informers" 28 coreinformers "k8s.io/client-go/informers/core/v1" 29 "k8s.io/client-go/kubernetes" 30 "k8s.io/client-go/tools/cache" 31 ) 32 33 type podSource struct { 34 client kubernetes.Interface 35 namespace string 36 podInformer coreinformers.PodInformer 37 nodeInformer coreinformers.NodeInformer 38 compatibility string 39 } 40 41 // NewPodSource creates a new podSource with the given config. 42 func NewPodSource(ctx context.Context, kubeClient kubernetes.Interface, namespace string, compatibility string) (Source, error) { 43 informerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(namespace)) 44 podInformer := informerFactory.Core().V1().Pods() 45 nodeInformer := informerFactory.Core().V1().Nodes() 46 47 podInformer.Informer().AddEventHandler( 48 cache.ResourceEventHandlerFuncs{ 49 AddFunc: func(obj interface{}) { 50 }, 51 }, 52 ) 53 nodeInformer.Informer().AddEventHandler( 54 cache.ResourceEventHandlerFuncs{ 55 AddFunc: func(obj interface{}) { 56 }, 57 }, 58 ) 59 60 informerFactory.Start(ctx.Done()) 61 62 // wait for the local cache to be populated. 63 if err := waitForCacheSync(context.Background(), informerFactory); err != nil { 64 return nil, err 65 } 66 67 return &podSource{ 68 client: kubeClient, 69 podInformer: podInformer, 70 nodeInformer: nodeInformer, 71 namespace: namespace, 72 compatibility: compatibility, 73 }, nil 74 } 75 76 func (*podSource) AddEventHandler(ctx context.Context, handler func()) { 77 } 78 79 func (ps *podSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { 80 pods, err := ps.podInformer.Lister().Pods(ps.namespace).List(labels.Everything()) 81 if err != nil { 82 return nil, err 83 } 84 85 endpointMap := make(map[endpoint.EndpointKey][]string) 86 for _, pod := range pods { 87 if !pod.Spec.HostNetwork { 88 log.Debugf("skipping pod %s. hostNetwork=false", pod.Name) 89 continue 90 } 91 92 targets := getTargetsFromTargetAnnotation(pod.Annotations) 93 94 if domainAnnotation, ok := pod.Annotations[internalHostnameAnnotationKey]; ok { 95 domainList := splitHostnameAnnotation(domainAnnotation) 96 for _, domain := range domainList { 97 if len(targets) == 0 { 98 addToEndpointMap(endpointMap, domain, suitableType(pod.Status.PodIP), pod.Status.PodIP) 99 } else { 100 for _, target := range targets { 101 addToEndpointMap(endpointMap, domain, suitableType(target), target) 102 } 103 } 104 } 105 } 106 107 if domainAnnotation, ok := pod.Annotations[hostnameAnnotationKey]; ok { 108 domainList := splitHostnameAnnotation(domainAnnotation) 109 for _, domain := range domainList { 110 if len(targets) == 0 { 111 node, _ := ps.nodeInformer.Lister().Get(pod.Spec.NodeName) 112 for _, address := range node.Status.Addresses { 113 recordType := suitableType(address.Address) 114 // IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well. 115 if address.Type == corev1.NodeExternalIP || (address.Type == corev1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA) { 116 addToEndpointMap(endpointMap, domain, recordType, address.Address) 117 } 118 } 119 } else { 120 for _, target := range targets { 121 addToEndpointMap(endpointMap, domain, suitableType(target), target) 122 } 123 } 124 } 125 } 126 127 if ps.compatibility == "kops-dns-controller" { 128 if domainAnnotation, ok := pod.Annotations[kopsDNSControllerInternalHostnameAnnotationKey]; ok { 129 domainList := splitHostnameAnnotation(domainAnnotation) 130 for _, domain := range domainList { 131 addToEndpointMap(endpointMap, domain, suitableType(pod.Status.PodIP), pod.Status.PodIP) 132 } 133 } 134 135 if domainAnnotation, ok := pod.Annotations[kopsDNSControllerHostnameAnnotationKey]; ok { 136 domainList := splitHostnameAnnotation(domainAnnotation) 137 for _, domain := range domainList { 138 node, _ := ps.nodeInformer.Lister().Get(pod.Spec.NodeName) 139 for _, address := range node.Status.Addresses { 140 recordType := suitableType(address.Address) 141 // IPv6 addresses are labeled as NodeInternalIP despite being usable externally as well. 142 if address.Type == corev1.NodeExternalIP || (address.Type == corev1.NodeInternalIP && recordType == endpoint.RecordTypeAAAA) { 143 addToEndpointMap(endpointMap, domain, recordType, address.Address) 144 } 145 } 146 } 147 } 148 } 149 } 150 endpoints := []*endpoint.Endpoint{} 151 for key, targets := range endpointMap { 152 endpoints = append(endpoints, endpoint.NewEndpoint(key.DNSName, key.RecordType, targets...)) 153 } 154 return endpoints, nil 155 } 156 157 func addToEndpointMap(endpointMap map[endpoint.EndpointKey][]string, domain string, recordType string, address string) { 158 key := endpoint.EndpointKey{ 159 DNSName: domain, 160 RecordType: recordType, 161 } 162 if _, ok := endpointMap[key]; !ok { 163 endpointMap[key] = []string{} 164 } 165 endpointMap[key] = append(endpointMap[key], address) 166 }