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  }