sigs.k8s.io/external-dns@v0.14.1/source/service.go (about)

     1  /*
     2  Copyright 2017 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  	"fmt"
    22  	"net"
    23  	"sort"
    24  	"strings"
    25  	"text/template"
    26  
    27  	log "github.com/sirupsen/logrus"
    28  	v1 "k8s.io/api/core/v1"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/labels"
    31  	kubeinformers "k8s.io/client-go/informers"
    32  	coreinformers "k8s.io/client-go/informers/core/v1"
    33  	"k8s.io/client-go/kubernetes"
    34  	"k8s.io/client-go/tools/cache"
    35  
    36  	"sigs.k8s.io/external-dns/endpoint"
    37  )
    38  
    39  // serviceSource is an implementation of Source for Kubernetes service objects.
    40  // It will find all services that are under our jurisdiction, i.e. annotated
    41  // desired hostname and matching or no controller annotation. For each of the
    42  // matched services' entrypoints it will return a corresponding
    43  // Endpoint object.
    44  type serviceSource struct {
    45  	client           kubernetes.Interface
    46  	namespace        string
    47  	annotationFilter string
    48  
    49  	// process Services with legacy annotations
    50  	compatibility                  string
    51  	fqdnTemplate                   *template.Template
    52  	combineFQDNAnnotation          bool
    53  	ignoreHostnameAnnotation       bool
    54  	publishInternal                bool
    55  	publishHostIP                  bool
    56  	alwaysPublishNotReadyAddresses bool
    57  	resolveLoadBalancerHostname    bool
    58  	serviceInformer                coreinformers.ServiceInformer
    59  	endpointsInformer              coreinformers.EndpointsInformer
    60  	podInformer                    coreinformers.PodInformer
    61  	nodeInformer                   coreinformers.NodeInformer
    62  	serviceTypeFilter              map[string]struct{}
    63  	labelSelector                  labels.Selector
    64  }
    65  
    66  // NewServiceSource creates a new serviceSource with the given config.
    67  func NewServiceSource(ctx context.Context, kubeClient kubernetes.Interface, namespace, annotationFilter string, fqdnTemplate string, combineFqdnAnnotation bool, compatibility string, publishInternal bool, publishHostIP bool, alwaysPublishNotReadyAddresses bool, serviceTypeFilter []string, ignoreHostnameAnnotation bool, labelSelector labels.Selector, resolveLoadBalancerHostname bool) (Source, error) {
    68  	tmpl, err := parseTemplate(fqdnTemplate)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	// Use shared informers to listen for add/update/delete of services/pods/nodes in the specified namespace.
    74  	// Set resync period to 0, to prevent processing when nothing has changed
    75  	informerFactory := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, 0, kubeinformers.WithNamespace(namespace))
    76  	serviceInformer := informerFactory.Core().V1().Services()
    77  	endpointsInformer := informerFactory.Core().V1().Endpoints()
    78  	podInformer := informerFactory.Core().V1().Pods()
    79  	nodeInformer := informerFactory.Core().V1().Nodes()
    80  
    81  	// Add default resource event handlers to properly initialize informer.
    82  	serviceInformer.Informer().AddEventHandler(
    83  		cache.ResourceEventHandlerFuncs{
    84  			AddFunc: func(obj interface{}) {
    85  			},
    86  		},
    87  	)
    88  	endpointsInformer.Informer().AddEventHandler(
    89  		cache.ResourceEventHandlerFuncs{
    90  			AddFunc: func(obj interface{}) {
    91  			},
    92  		},
    93  	)
    94  	podInformer.Informer().AddEventHandler(
    95  		cache.ResourceEventHandlerFuncs{
    96  			AddFunc: func(obj interface{}) {
    97  			},
    98  		},
    99  	)
   100  	nodeInformer.Informer().AddEventHandler(
   101  		cache.ResourceEventHandlerFuncs{
   102  			AddFunc: func(obj interface{}) {
   103  			},
   104  		},
   105  	)
   106  
   107  	informerFactory.Start(ctx.Done())
   108  
   109  	// wait for the local cache to be populated.
   110  	if err := waitForCacheSync(context.Background(), informerFactory); err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	// Transform the slice into a map so it will
   115  	// be way much easier and fast to filter later
   116  	serviceTypes := make(map[string]struct{})
   117  	for _, serviceType := range serviceTypeFilter {
   118  		serviceTypes[serviceType] = struct{}{}
   119  	}
   120  
   121  	return &serviceSource{
   122  		client:                         kubeClient,
   123  		namespace:                      namespace,
   124  		annotationFilter:               annotationFilter,
   125  		compatibility:                  compatibility,
   126  		fqdnTemplate:                   tmpl,
   127  		combineFQDNAnnotation:          combineFqdnAnnotation,
   128  		ignoreHostnameAnnotation:       ignoreHostnameAnnotation,
   129  		publishInternal:                publishInternal,
   130  		publishHostIP:                  publishHostIP,
   131  		alwaysPublishNotReadyAddresses: alwaysPublishNotReadyAddresses,
   132  		serviceInformer:                serviceInformer,
   133  		endpointsInformer:              endpointsInformer,
   134  		podInformer:                    podInformer,
   135  		nodeInformer:                   nodeInformer,
   136  		serviceTypeFilter:              serviceTypes,
   137  		labelSelector:                  labelSelector,
   138  		resolveLoadBalancerHostname:    resolveLoadBalancerHostname,
   139  	}, nil
   140  }
   141  
   142  // Endpoints returns endpoint objects for each service that should be processed.
   143  func (sc *serviceSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) {
   144  	services, err := sc.serviceInformer.Lister().Services(sc.namespace).List(sc.labelSelector)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	services, err = sc.filterByAnnotations(services)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	// filter on service types if at least one has been provided
   154  	if len(sc.serviceTypeFilter) > 0 {
   155  		services = sc.filterByServiceType(services)
   156  	}
   157  
   158  	endpoints := []*endpoint.Endpoint{}
   159  
   160  	for _, svc := range services {
   161  		// Check controller annotation to see if we are responsible.
   162  		controller, ok := svc.Annotations[controllerAnnotationKey]
   163  		if ok && controller != controllerAnnotationValue {
   164  			log.Debugf("Skipping service %s/%s because controller value does not match, found: %s, required: %s",
   165  				svc.Namespace, svc.Name, controller, controllerAnnotationValue)
   166  			continue
   167  		}
   168  
   169  		svcEndpoints := sc.endpoints(svc)
   170  
   171  		// process legacy annotations if no endpoints were returned and compatibility mode is enabled.
   172  		if len(svcEndpoints) == 0 && sc.compatibility != "" {
   173  			svcEndpoints, err = legacyEndpointsFromService(svc, sc)
   174  			if err != nil {
   175  				return nil, err
   176  			}
   177  		}
   178  
   179  		// apply template if none of the above is found
   180  		if (sc.combineFQDNAnnotation || len(svcEndpoints) == 0) && sc.fqdnTemplate != nil {
   181  			sEndpoints, err := sc.endpointsFromTemplate(svc)
   182  			if err != nil {
   183  				return nil, err
   184  			}
   185  
   186  			if sc.combineFQDNAnnotation {
   187  				svcEndpoints = append(svcEndpoints, sEndpoints...)
   188  			} else {
   189  				svcEndpoints = sEndpoints
   190  			}
   191  		}
   192  
   193  		if len(svcEndpoints) == 0 {
   194  			log.Debugf("No endpoints could be generated from service %s/%s", svc.Namespace, svc.Name)
   195  			continue
   196  		}
   197  
   198  		log.Debugf("Endpoints generated from service: %s/%s: %v", svc.Namespace, svc.Name, svcEndpoints)
   199  		sc.setResourceLabel(svc, svcEndpoints)
   200  		endpoints = append(endpoints, svcEndpoints...)
   201  	}
   202  
   203  	// this sorting is required to make merging work.
   204  	// after we merge endpoints that have same DNS, we want to ensure that we end up with the same service being an "owner"
   205  	// of all those records, as otherwise each time we update, we will end up with a different service that gets data merged in
   206  	// and that will cause external-dns to recreate dns record due to different service owner in TXT record.
   207  	// if new service is added or old one removed, that might cause existing record to get re-created due to potentially new
   208  	// owner being selected. Which is fine, since it shouldn't happen often and shouldn't cause any disruption.
   209  	if len(endpoints) > 1 {
   210  		sort.Slice(endpoints, func(i, j int) bool {
   211  			return endpoints[i].Labels[endpoint.ResourceLabelKey] < endpoints[j].Labels[endpoint.ResourceLabelKey]
   212  		})
   213  		// Use stable sort to not disrupt the order of services
   214  		sort.SliceStable(endpoints, func(i, j int) bool {
   215  			if endpoints[i].DNSName != endpoints[j].DNSName {
   216  				return endpoints[i].DNSName < endpoints[j].DNSName
   217  			}
   218  			return endpoints[i].RecordType < endpoints[j].RecordType
   219  		})
   220  		mergedEndpoints := []*endpoint.Endpoint{}
   221  		mergedEndpoints = append(mergedEndpoints, endpoints[0])
   222  		for i := 1; i < len(endpoints); i++ {
   223  			lastMergedEndpoint := len(mergedEndpoints) - 1
   224  			if mergedEndpoints[lastMergedEndpoint].DNSName == endpoints[i].DNSName &&
   225  				mergedEndpoints[lastMergedEndpoint].RecordType == endpoints[i].RecordType &&
   226  				mergedEndpoints[lastMergedEndpoint].SetIdentifier == endpoints[i].SetIdentifier &&
   227  				mergedEndpoints[lastMergedEndpoint].RecordTTL == endpoints[i].RecordTTL {
   228  				mergedEndpoints[lastMergedEndpoint].Targets = append(mergedEndpoints[lastMergedEndpoint].Targets, endpoints[i].Targets[0])
   229  			} else {
   230  				mergedEndpoints = append(mergedEndpoints, endpoints[i])
   231  			}
   232  		}
   233  		endpoints = mergedEndpoints
   234  	}
   235  
   236  	for _, ep := range endpoints {
   237  		sort.Sort(ep.Targets)
   238  	}
   239  
   240  	return endpoints, nil
   241  }
   242  
   243  // extractHeadlessEndpoints extracts endpoints from a headless service using the "Endpoints" Kubernetes API resource
   244  func (sc *serviceSource) extractHeadlessEndpoints(svc *v1.Service, hostname string, ttl endpoint.TTL) []*endpoint.Endpoint {
   245  	var endpoints []*endpoint.Endpoint
   246  
   247  	labelSelector, err := metav1.ParseToLabelSelector(labels.Set(svc.Spec.Selector).AsSelectorPreValidated().String())
   248  	if err != nil {
   249  		return nil
   250  	}
   251  	selector, err := metav1.LabelSelectorAsSelector(labelSelector)
   252  	if err != nil {
   253  		return nil
   254  	}
   255  
   256  	endpointsObject, err := sc.endpointsInformer.Lister().Endpoints(svc.Namespace).Get(svc.GetName())
   257  	if err != nil {
   258  		log.Errorf("Get endpoints of service[%s] error:%v", svc.GetName(), err)
   259  		return endpoints
   260  	}
   261  
   262  	pods, err := sc.podInformer.Lister().Pods(svc.Namespace).List(selector)
   263  	if err != nil {
   264  		log.Errorf("List pods of service[%s] error: %v", svc.GetName(), err)
   265  		return endpoints
   266  	}
   267  
   268  	endpointsType := getEndpointsTypeFromAnnotations(svc.Annotations)
   269  
   270  	targetsByHeadlessDomainAndType := make(map[endpoint.EndpointKey]endpoint.Targets)
   271  	for _, subset := range endpointsObject.Subsets {
   272  		addresses := subset.Addresses
   273  		if svc.Spec.PublishNotReadyAddresses || sc.alwaysPublishNotReadyAddresses {
   274  			addresses = append(addresses, subset.NotReadyAddresses...)
   275  		}
   276  
   277  		for _, address := range addresses {
   278  			// find pod for this address
   279  			if address.TargetRef == nil || address.TargetRef.APIVersion != "" || address.TargetRef.Kind != "Pod" {
   280  				log.Debugf("Skipping address because its target is not a pod: %v", address)
   281  				continue
   282  			}
   283  			var pod *v1.Pod
   284  			for _, v := range pods {
   285  				if v.Name == address.TargetRef.Name {
   286  					pod = v
   287  					break
   288  				}
   289  			}
   290  			if pod == nil {
   291  				log.Errorf("Pod %s not found for address %v", address.TargetRef.Name, address)
   292  				continue
   293  			}
   294  
   295  			headlessDomains := []string{hostname}
   296  			if pod.Spec.Hostname != "" {
   297  				headlessDomains = append(headlessDomains, fmt.Sprintf("%s.%s", pod.Spec.Hostname, hostname))
   298  			}
   299  
   300  			for _, headlessDomain := range headlessDomains {
   301  				targets := getTargetsFromTargetAnnotation(pod.Annotations)
   302  				if len(targets) == 0 {
   303  					if endpointsType == EndpointsTypeNodeExternalIP {
   304  						node, err := sc.nodeInformer.Lister().Get(pod.Spec.NodeName)
   305  						if err != nil {
   306  							log.Errorf("Get node[%s] of pod[%s] error: %v; not adding any NodeExternalIP endpoints", pod.Spec.NodeName, pod.GetName(), err)
   307  							return endpoints
   308  						}
   309  						for _, address := range node.Status.Addresses {
   310  							if address.Type == v1.NodeExternalIP || (address.Type == v1.NodeInternalIP && suitableType(address.Address) == endpoint.RecordTypeAAAA) {
   311  								targets = append(targets, address.Address)
   312  								log.Debugf("Generating matching endpoint %s with NodeExternalIP %s", headlessDomain, address.Address)
   313  							}
   314  						}
   315  					} else if endpointsType == EndpointsTypeHostIP || sc.publishHostIP {
   316  						targets = endpoint.Targets{pod.Status.HostIP}
   317  						log.Debugf("Generating matching endpoint %s with HostIP %s", headlessDomain, pod.Status.HostIP)
   318  					} else {
   319  						targets = endpoint.Targets{address.IP}
   320  						log.Debugf("Generating matching endpoint %s with EndpointAddress IP %s", headlessDomain, address.IP)
   321  					}
   322  				}
   323  				for _, target := range targets {
   324  					key := endpoint.EndpointKey{
   325  						DNSName:    headlessDomain,
   326  						RecordType: suitableType(target),
   327  					}
   328  					targetsByHeadlessDomainAndType[key] = append(targetsByHeadlessDomainAndType[key], target)
   329  				}
   330  			}
   331  		}
   332  	}
   333  
   334  	headlessKeys := []endpoint.EndpointKey{}
   335  	for headlessKey := range targetsByHeadlessDomainAndType {
   336  		headlessKeys = append(headlessKeys, headlessKey)
   337  	}
   338  	sort.Slice(headlessKeys, func(i, j int) bool {
   339  		if headlessKeys[i].DNSName != headlessKeys[j].DNSName {
   340  			return headlessKeys[i].DNSName < headlessKeys[j].DNSName
   341  		}
   342  		return headlessKeys[i].RecordType < headlessKeys[j].RecordType
   343  	})
   344  	for _, headlessKey := range headlessKeys {
   345  		allTargets := targetsByHeadlessDomainAndType[headlessKey]
   346  		targets := []string{}
   347  
   348  		deduppedTargets := map[string]struct{}{}
   349  		for _, target := range allTargets {
   350  			if _, ok := deduppedTargets[target]; ok {
   351  				log.Debugf("Removing duplicate target %s", target)
   352  				continue
   353  			}
   354  
   355  			deduppedTargets[target] = struct{}{}
   356  			targets = append(targets, target)
   357  		}
   358  
   359  		var ep *endpoint.Endpoint
   360  		if ttl.IsConfigured() {
   361  			ep = endpoint.NewEndpointWithTTL(headlessKey.DNSName, headlessKey.RecordType, ttl, targets...)
   362  		} else {
   363  			ep = endpoint.NewEndpoint(headlessKey.DNSName, headlessKey.RecordType, targets...)
   364  		}
   365  
   366  		if ep != nil {
   367  			endpoints = append(endpoints, ep)
   368  		}
   369  	}
   370  
   371  	return endpoints
   372  }
   373  
   374  func (sc *serviceSource) endpointsFromTemplate(svc *v1.Service) ([]*endpoint.Endpoint, error) {
   375  	hostnames, err := execTemplate(sc.fqdnTemplate, svc)
   376  	if err != nil {
   377  		return nil, err
   378  	}
   379  
   380  	providerSpecific, setIdentifier := getProviderSpecificAnnotations(svc.Annotations)
   381  
   382  	var endpoints []*endpoint.Endpoint
   383  	for _, hostname := range hostnames {
   384  		endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, providerSpecific, setIdentifier, false)...)
   385  	}
   386  
   387  	return endpoints, nil
   388  }
   389  
   390  // endpointsFromService extracts the endpoints from a service object
   391  func (sc *serviceSource) endpoints(svc *v1.Service) []*endpoint.Endpoint {
   392  	var endpoints []*endpoint.Endpoint
   393  	// Skip endpoints if we do not want entries from annotations
   394  	if !sc.ignoreHostnameAnnotation {
   395  		providerSpecific, setIdentifier := getProviderSpecificAnnotations(svc.Annotations)
   396  		var hostnameList []string
   397  		var internalHostnameList []string
   398  
   399  		hostnameList = getHostnamesFromAnnotations(svc.Annotations)
   400  		for _, hostname := range hostnameList {
   401  			endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, providerSpecific, setIdentifier, false)...)
   402  		}
   403  
   404  		internalHostnameList = getInternalHostnamesFromAnnotations(svc.Annotations)
   405  		for _, hostname := range internalHostnameList {
   406  			endpoints = append(endpoints, sc.generateEndpoints(svc, hostname, providerSpecific, setIdentifier, true)...)
   407  		}
   408  	}
   409  	return endpoints
   410  }
   411  
   412  // filterByAnnotations filters a list of services by a given annotation selector.
   413  func (sc *serviceSource) filterByAnnotations(services []*v1.Service) ([]*v1.Service, error) {
   414  	labelSelector, err := metav1.ParseToLabelSelector(sc.annotationFilter)
   415  	if err != nil {
   416  		return nil, err
   417  	}
   418  	selector, err := metav1.LabelSelectorAsSelector(labelSelector)
   419  	if err != nil {
   420  		return nil, err
   421  	}
   422  
   423  	// empty filter returns original list
   424  	if selector.Empty() {
   425  		return services, nil
   426  	}
   427  
   428  	filteredList := []*v1.Service{}
   429  
   430  	for _, service := range services {
   431  		// convert the service's annotations to an equivalent label selector
   432  		annotations := labels.Set(service.Annotations)
   433  
   434  		// include service if its annotations match the selector
   435  		if selector.Matches(annotations) {
   436  			filteredList = append(filteredList, service)
   437  		}
   438  	}
   439  
   440  	return filteredList, nil
   441  }
   442  
   443  // filterByServiceType filters services according their types
   444  func (sc *serviceSource) filterByServiceType(services []*v1.Service) []*v1.Service {
   445  	filteredList := []*v1.Service{}
   446  	for _, service := range services {
   447  		// Check if the service is of the given type or not
   448  		if _, ok := sc.serviceTypeFilter[string(service.Spec.Type)]; ok {
   449  			filteredList = append(filteredList, service)
   450  		}
   451  	}
   452  
   453  	return filteredList
   454  }
   455  
   456  func (sc *serviceSource) setResourceLabel(service *v1.Service, endpoints []*endpoint.Endpoint) {
   457  	for _, ep := range endpoints {
   458  		ep.Labels[endpoint.ResourceLabelKey] = fmt.Sprintf("service/%s/%s", service.Namespace, service.Name)
   459  	}
   460  }
   461  
   462  func (sc *serviceSource) generateEndpoints(svc *v1.Service, hostname string, providerSpecific endpoint.ProviderSpecific, setIdentifier string, useClusterIP bool) (endpoints []*endpoint.Endpoint) {
   463  	hostname = strings.TrimSuffix(hostname, ".")
   464  
   465  	resource := fmt.Sprintf("service/%s/%s", svc.Namespace, svc.Name)
   466  
   467  	ttl := getTTLFromAnnotations(svc.Annotations, resource)
   468  
   469  	targets := getTargetsFromTargetAnnotation(svc.Annotations)
   470  
   471  	if len(targets) == 0 {
   472  		switch svc.Spec.Type {
   473  		case v1.ServiceTypeLoadBalancer:
   474  			if useClusterIP {
   475  				targets = extractServiceIps(svc)
   476  			} else {
   477  				targets = extractLoadBalancerTargets(svc, sc.resolveLoadBalancerHostname)
   478  			}
   479  		case v1.ServiceTypeClusterIP:
   480  			if svc.Spec.ClusterIP == v1.ClusterIPNone {
   481  				endpoints = append(endpoints, sc.extractHeadlessEndpoints(svc, hostname, ttl)...)
   482  			} else if useClusterIP || sc.publishInternal {
   483  				targets = extractServiceIps(svc)
   484  			}
   485  		case v1.ServiceTypeNodePort:
   486  			// add the nodeTargets and extract an SRV endpoint
   487  			var err error
   488  			targets, err = sc.extractNodePortTargets(svc)
   489  			if err != nil {
   490  				log.Errorf("Unable to extract targets from service %s/%s error: %v", svc.Namespace, svc.Name, err)
   491  				return endpoints
   492  			}
   493  			endpoints = append(endpoints, sc.extractNodePortEndpoints(svc, hostname, ttl)...)
   494  		case v1.ServiceTypeExternalName:
   495  			targets = extractServiceExternalName(svc)
   496  		}
   497  
   498  		for _, endpoint := range endpoints {
   499  			endpoint.ProviderSpecific = providerSpecific
   500  			endpoint.SetIdentifier = setIdentifier
   501  		}
   502  	}
   503  
   504  	endpoints = append(endpoints, endpointsForHostname(hostname, targets, ttl, providerSpecific, setIdentifier, resource)...)
   505  
   506  	return endpoints
   507  }
   508  
   509  func extractServiceIps(svc *v1.Service) endpoint.Targets {
   510  	if svc.Spec.ClusterIP == v1.ClusterIPNone {
   511  		log.Debugf("Unable to associate %s headless service with a Cluster IP", svc.Name)
   512  		return endpoint.Targets{}
   513  	}
   514  	return endpoint.Targets{svc.Spec.ClusterIP}
   515  }
   516  
   517  func extractServiceExternalName(svc *v1.Service) endpoint.Targets {
   518  	if len(svc.Spec.ExternalIPs) > 0 {
   519  		return svc.Spec.ExternalIPs
   520  	}
   521  	return endpoint.Targets{svc.Spec.ExternalName}
   522  }
   523  
   524  func extractLoadBalancerTargets(svc *v1.Service, resolveLoadBalancerHostname bool) endpoint.Targets {
   525  	if len(svc.Spec.ExternalIPs) > 0 {
   526  		return svc.Spec.ExternalIPs
   527  	}
   528  
   529  	// Create a corresponding endpoint for each configured external entrypoint.
   530  	var targets endpoint.Targets
   531  	for _, lb := range svc.Status.LoadBalancer.Ingress {
   532  		if lb.IP != "" {
   533  			targets = append(targets, lb.IP)
   534  		}
   535  		if lb.Hostname != "" {
   536  			if resolveLoadBalancerHostname {
   537  				ips, err := net.LookupIP(lb.Hostname)
   538  				if err != nil {
   539  					log.Errorf("Unable to resolve %q: %v", lb.Hostname, err)
   540  					continue
   541  				}
   542  				for _, ip := range ips {
   543  					targets = append(targets, ip.String())
   544  				}
   545  			} else {
   546  				targets = append(targets, lb.Hostname)
   547  			}
   548  		}
   549  	}
   550  
   551  	return targets
   552  }
   553  
   554  func isPodStatusReady(status v1.PodStatus) bool {
   555  	_, condition := getPodCondition(&status, v1.PodReady)
   556  	return condition != nil && condition.Status == v1.ConditionTrue
   557  }
   558  
   559  func getPodCondition(status *v1.PodStatus, conditionType v1.PodConditionType) (int, *v1.PodCondition) {
   560  	if status == nil {
   561  		return -1, nil
   562  	}
   563  	return getPodConditionFromList(status.Conditions, conditionType)
   564  }
   565  
   566  func getPodConditionFromList(conditions []v1.PodCondition, conditionType v1.PodConditionType) (int, *v1.PodCondition) {
   567  	if conditions == nil {
   568  		return -1, nil
   569  	}
   570  	for i := range conditions {
   571  		if conditions[i].Type == conditionType {
   572  			return i, &conditions[i]
   573  		}
   574  	}
   575  	return -1, nil
   576  }
   577  
   578  func (sc *serviceSource) extractNodePortTargets(svc *v1.Service) (endpoint.Targets, error) {
   579  	var (
   580  		internalIPs endpoint.Targets
   581  		externalIPs endpoint.Targets
   582  		ipv6IPs     endpoint.Targets
   583  		nodes       []*v1.Node
   584  		err         error
   585  	)
   586  
   587  	switch svc.Spec.ExternalTrafficPolicy {
   588  	case v1.ServiceExternalTrafficPolicyTypeLocal:
   589  		nodesMap := map[*v1.Node]struct{}{}
   590  		labelSelector, err := metav1.ParseToLabelSelector(labels.Set(svc.Spec.Selector).AsSelectorPreValidated().String())
   591  		if err != nil {
   592  			return nil, err
   593  		}
   594  		selector, err := metav1.LabelSelectorAsSelector(labelSelector)
   595  		if err != nil {
   596  			return nil, err
   597  		}
   598  		pods, err := sc.podInformer.Lister().Pods(svc.Namespace).List(selector)
   599  		if err != nil {
   600  			return nil, err
   601  		}
   602  
   603  		var nodesReady []*v1.Node
   604  		var nodesRunning []*v1.Node
   605  		for _, v := range pods {
   606  			if v.Status.Phase == v1.PodRunning {
   607  				node, err := sc.nodeInformer.Lister().Get(v.Spec.NodeName)
   608  				if err != nil {
   609  					log.Debugf("Unable to find node where Pod %s is running", v.Spec.Hostname)
   610  					continue
   611  				}
   612  
   613  				if _, ok := nodesMap[node]; !ok {
   614  					nodesMap[node] = *new(struct{})
   615  					nodesRunning = append(nodesRunning, node)
   616  
   617  					if isPodStatusReady(v.Status) {
   618  						nodesReady = append(nodesReady, node)
   619  						// Check pod not terminating
   620  						if v.GetDeletionTimestamp() == nil {
   621  							nodes = append(nodes, node)
   622  						}
   623  					}
   624  				}
   625  			}
   626  		}
   627  
   628  		if len(nodes) > 0 {
   629  			// Works same as service endpoints
   630  		} else if len(nodesReady) > 0 {
   631  			// 2 level of panic modes as safe guard, because old wrong behavior can be used by someone
   632  			// Publish all endpoints not always a bad thing
   633  			log.Debugf("All pods in terminating state, use ready")
   634  			nodes = nodesReady
   635  		} else {
   636  			log.Debugf("All pods not ready, use all running")
   637  			nodes = nodesRunning
   638  		}
   639  	default:
   640  		nodes, err = sc.nodeInformer.Lister().List(labels.Everything())
   641  		if err != nil {
   642  			return nil, err
   643  		}
   644  	}
   645  
   646  	for _, node := range nodes {
   647  		for _, address := range node.Status.Addresses {
   648  			switch address.Type {
   649  			case v1.NodeExternalIP:
   650  				externalIPs = append(externalIPs, address.Address)
   651  			case v1.NodeInternalIP:
   652  				internalIPs = append(internalIPs, address.Address)
   653  				if suitableType(address.Address) == endpoint.RecordTypeAAAA {
   654  					ipv6IPs = append(ipv6IPs, address.Address)
   655  				}
   656  			}
   657  		}
   658  	}
   659  
   660  	access := getAccessFromAnnotations(svc.Annotations)
   661  	if access == "public" {
   662  		return append(externalIPs, ipv6IPs...), nil
   663  	}
   664  	if access == "private" {
   665  		return internalIPs, nil
   666  	}
   667  	if len(externalIPs) > 0 {
   668  		return append(externalIPs, ipv6IPs...), nil
   669  	}
   670  	return internalIPs, nil
   671  }
   672  
   673  func (sc *serviceSource) extractNodePortEndpoints(svc *v1.Service, hostname string, ttl endpoint.TTL) []*endpoint.Endpoint {
   674  	var endpoints []*endpoint.Endpoint
   675  
   676  	for _, port := range svc.Spec.Ports {
   677  		if port.NodePort > 0 {
   678  			// following the RFC 2782, SRV record must have a following format
   679  			// _service._proto.name. TTL class SRV priority weight port
   680  			// see https://en.wikipedia.org/wiki/SRV_record
   681  
   682  			// build a target with a priority of 0, weight of 50, and pointing the given port on the given host
   683  			target := fmt.Sprintf("0 50 %d %s", port.NodePort, hostname)
   684  
   685  			// take the service name from the K8s Service object
   686  			// it is safe to use since it is DNS compatible
   687  			// see https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names
   688  			serviceName := svc.ObjectMeta.Name
   689  
   690  			// figure out the protocol
   691  			protocol := strings.ToLower(string(port.Protocol))
   692  			if protocol == "" {
   693  				protocol = "tcp"
   694  			}
   695  
   696  			recordName := fmt.Sprintf("_%s._%s.%s", serviceName, protocol, hostname)
   697  
   698  			var ep *endpoint.Endpoint
   699  			if ttl.IsConfigured() {
   700  				ep = endpoint.NewEndpointWithTTL(recordName, endpoint.RecordTypeSRV, ttl, target)
   701  			} else {
   702  				ep = endpoint.NewEndpoint(recordName, endpoint.RecordTypeSRV, target)
   703  			}
   704  
   705  			if ep != nil {
   706  				endpoints = append(endpoints, ep)
   707  			}
   708  		}
   709  	}
   710  
   711  	return endpoints
   712  }
   713  
   714  func (sc *serviceSource) AddEventHandler(ctx context.Context, handler func()) {
   715  	log.Debug("Adding event handler for service")
   716  
   717  	// Right now there is no way to remove event handler from informer, see:
   718  	// https://github.com/kubernetes/kubernetes/issues/79610
   719  	sc.serviceInformer.Informer().AddEventHandler(eventHandlerFunc(handler))
   720  }