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 }