| 
									
										
										
										
											2017-11-01 10:11:34 +00:00
										 |  |  | package kubernetes
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import (
 | 
					
						
							| 
									
										
										
										
											2018-04-22 08:34:35 +01:00
										 |  |  | 	"context"
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 	"math"
 | 
					
						
							|  |  |  | 	"net"
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 	"sort"
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 	"strings"
 | 
					
						
							| 
									
										
										
										
											2017-11-01 10:11:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 	"github.com/coredns/coredns/plugin"
 | 
					
						
							|  |  |  | 	"github.com/coredns/coredns/plugin/etcd/msg"
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 	"github.com/coredns/coredns/plugin/transfer"
 | 
					
						
							| 
									
										
										
										
											2017-11-01 10:11:34 +00:00
										 |  |  | 	"github.com/coredns/coredns/request"
 | 
					
						
							| 
									
										
										
										
											2018-04-19 07:41:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 	"github.com/miekg/dns"
 | 
					
						
							|  |  |  | 	api "k8s.io/api/core/v1"
 | 
					
						
							| 
									
										
										
										
											2017-11-01 10:11:34 +00:00
										 |  |  | )
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | // Transfer implements the transfer.Transfer interface.
 | 
					
						
							|  |  |  | func (k *Kubernetes) Transfer(zone string, serial uint32) (<-chan []dns.RR, error) {
 | 
					
						
							| 
									
										
										
										
											2021-08-13 11:02:00 -04:00
										 |  |  | 	match := plugin.Zones(k.Zones).Matches(zone)
 | 
					
						
							|  |  |  | 	if match == "" {
 | 
					
						
							|  |  |  | 		return nil, transfer.ErrNotAuthoritative
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 	// state is not used here, hence the empty request.Request{]
 | 
					
						
							|  |  |  | 	soa, err := plugin.SOA(context.TODO(), k, zone, request.Request{}, plugin.Options{})
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 	if err != nil {
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 		return nil, transfer.ErrNotAuthoritative
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 	ch := make(chan []dns.RR)
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 	zonePath := msg.Path(zone, "coredns")
 | 
					
						
							|  |  |  | 	serviceList := k.APIConn.ServiceList()
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 	go func() {
 | 
					
						
							|  |  |  | 		// ixfr fallback
 | 
					
						
							|  |  |  | 		if serial != 0 && soa[0].(*dns.SOA).Serial == serial {
 | 
					
						
							|  |  |  | 			ch <- soa
 | 
					
						
							|  |  |  | 			close(ch)
 | 
					
						
							|  |  |  | 			return
 | 
					
						
							| 
									
										
										
										
											2018-11-13 18:25:30 -05:00
										 |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 		ch <- soa
 | 
					
						
							| 
									
										
										
										
											2018-11-13 18:25:30 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-14 10:39:40 -04:00
										 |  |  | 		nsAddrs := k.nsAddrs(false, zone)
 | 
					
						
							|  |  |  | 		nsHosts := make(map[string]struct{})
 | 
					
						
							|  |  |  | 		for _, nsAddr := range nsAddrs {
 | 
					
						
							|  |  |  | 			nsHost := nsAddr.Header().Name
 | 
					
						
							|  |  |  | 			if _, ok := nsHosts[nsHost]; !ok {
 | 
					
						
							|  |  |  | 				nsHosts[nsHost] = struct{}{}
 | 
					
						
							|  |  |  | 				ch <- []dns.RR{&dns.NS{Hdr: dns.RR_Header{Name: zone, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: k.ttl}, Ns: nsHost}}
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			ch <- nsAddrs
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 		sort.Slice(serviceList, func(i, j int) bool {
 | 
					
						
							|  |  |  | 			return serviceList[i].Name < serviceList[j].Name
 | 
					
						
							|  |  |  | 		})
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 		for _, svc := range serviceList {
 | 
					
						
							|  |  |  | 			if !k.namespaceExposed(svc.Namespace) {
 | 
					
						
							|  |  |  | 				continue
 | 
					
						
							|  |  |  | 			}
 | 
					
						
							|  |  |  | 			svcBase := []string{zonePath, Svc, svc.Namespace, svc.Name}
 | 
					
						
							|  |  |  | 			switch svc.Type {
 | 
					
						
							|  |  |  | 			case api.ServiceTypeClusterIP, api.ServiceTypeNodePort, api.ServiceTypeLoadBalancer:
 | 
					
						
							| 
									
										
										
										
											2020-12-21 05:30:24 -05:00
										 |  |  | 				clusterIP := net.ParseIP(svc.ClusterIPs[0])
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 				if clusterIP != nil {
 | 
					
						
							| 
									
										
										
										
											2020-12-21 05:30:24 -05:00
										 |  |  | 					var host string
 | 
					
						
							|  |  |  | 					for _, ip := range svc.ClusterIPs {
 | 
					
						
							|  |  |  | 						s := msg.Service{Host: ip, TTL: k.ttl}
 | 
					
						
							|  |  |  | 						s.Key = strings.Join(svcBase, "/")
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-21 05:30:24 -05:00
										 |  |  | 						// Change host from IP to Name for SRV records
 | 
					
						
							|  |  |  | 						host = emitAddressRecord(ch, s)
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 					for _, p := range svc.Ports {
 | 
					
						
							|  |  |  | 						s := msg.Service{Host: host, Port: int(p.Port), TTL: k.ttl}
 | 
					
						
							|  |  |  | 						s.Key = strings.Join(svcBase, "/")
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 						// Need to generate this to handle use cases for peer-finder
 | 
					
						
							|  |  |  | 						// ref: https://github.com/coredns/coredns/pull/823
 | 
					
						
							|  |  |  | 						ch <- []dns.RR{s.NewSRV(msg.Domain(s.Key), 100)}
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 						// As per spec unnamed ports do not have a srv record
 | 
					
						
							|  |  |  | 						// https://github.com/kubernetes/dns/blob/master/docs/specification.md#232---srv-records
 | 
					
						
							|  |  |  | 						if p.Name == "" {
 | 
					
						
							|  |  |  | 							continue
 | 
					
						
							|  |  |  | 						}
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 						s.Key = strings.Join(append(svcBase, strings.ToLower("_"+string(p.Protocol)), strings.ToLower("_"+string(p.Name))), "/")
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 						ch <- []dns.RR{s.NewSRV(msg.Domain(s.Key), 100)}
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 					//  Skip endpoint discovery if clusterIP is defined
 | 
					
						
							| 
									
										
										
										
											2018-10-11 21:59:50 +01:00
										 |  |  | 					continue
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 				endpointsList := k.APIConn.EpIndex(svc.Name + "." + svc.Namespace)
 | 
					
						
							| 
									
										
										
										
											2018-10-11 21:59:50 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 				for _, ep := range endpointsList {
 | 
					
						
							|  |  |  | 					for _, eps := range ep.Subsets {
 | 
					
						
							|  |  |  | 						srvWeight := calcSRVWeight(len(eps.Addresses))
 | 
					
						
							|  |  |  | 						for _, addr := range eps.Addresses {
 | 
					
						
							|  |  |  | 							s := msg.Service{Host: addr.IP, TTL: k.ttl}
 | 
					
						
							|  |  |  | 							s.Key = strings.Join(svcBase, "/")
 | 
					
						
							|  |  |  | 							// We don't need to change the msg.Service host from IP to Name yet
 | 
					
						
							|  |  |  | 							// so disregard the return value here
 | 
					
						
							|  |  |  | 							emitAddressRecord(ch, s)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							s.Key = strings.Join(append(svcBase, endpointHostname(addr, k.endpointNameMode)), "/")
 | 
					
						
							|  |  |  | 							// Change host from IP to Name for SRV records
 | 
					
						
							|  |  |  | 							host := emitAddressRecord(ch, s)
 | 
					
						
							|  |  |  | 							s.Host = host
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							for _, p := range eps.Ports {
 | 
					
						
							|  |  |  | 								// As per spec unnamed ports do not have a srv record
 | 
					
						
							|  |  |  | 								// https://github.com/kubernetes/dns/blob/master/docs/specification.md#232---srv-records
 | 
					
						
							|  |  |  | 								if p.Name == "" {
 | 
					
						
							|  |  |  | 									continue
 | 
					
						
							|  |  |  | 								}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 								s.Port = int(p.Port)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 								s.Key = strings.Join(append(svcBase, strings.ToLower("_"+string(p.Protocol)), strings.ToLower("_"+string(p.Name))), "/")
 | 
					
						
							|  |  |  | 								ch <- []dns.RR{s.NewSRV(msg.Domain(s.Key), srvWeight)}
 | 
					
						
							|  |  |  | 							}
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 						}
 | 
					
						
							|  |  |  | 					}
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 			case api.ServiceTypeExternalName:
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 				s := msg.Service{Key: strings.Join(svcBase, "/"), Host: svc.ExternalName, TTL: k.ttl}
 | 
					
						
							|  |  |  | 				if t, _ := s.HostType(); t == dns.TypeCNAME {
 | 
					
						
							|  |  |  | 					ch <- []dns.RR{s.NewCNAME(msg.Domain(s.Key), s.Host)}
 | 
					
						
							|  |  |  | 				}
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 			}
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 		ch <- soa
 | 
					
						
							|  |  |  | 		close(ch)
 | 
					
						
							|  |  |  | 	}()
 | 
					
						
							|  |  |  | 	return ch, nil
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | // emitAddressRecord generates a new A or AAAA record based on the msg.Service and writes it to a channel.
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | // emitAddressRecord returns the host name from the generated record.
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | func emitAddressRecord(c chan<- []dns.RR, s msg.Service) string {
 | 
					
						
							|  |  |  | 	ip := net.ParseIP(s.Host)
 | 
					
						
							|  |  |  | 	dnsType, _ := s.HostType()
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 	switch dnsType {
 | 
					
						
							|  |  |  | 	case dns.TypeA:
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 		r := s.NewA(msg.Domain(s.Key), ip)
 | 
					
						
							|  |  |  | 		c <- []dns.RR{r}
 | 
					
						
							|  |  |  | 		return r.Hdr.Name
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 	case dns.TypeAAAA:
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 		r := s.NewAAAA(msg.Domain(s.Key), ip)
 | 
					
						
							|  |  |  | 		c <- []dns.RR{r}
 | 
					
						
							|  |  |  | 		return r.Hdr.Name
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 11:30:39 -07:00
										 |  |  | 	return ""
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // calcSrvWeight borrows the logic implemented in plugin.SRV for dynamically
 | 
					
						
							|  |  |  | // calculating the srv weight and priority
 | 
					
						
							|  |  |  | func calcSRVWeight(numservices int) uint16 {
 | 
					
						
							|  |  |  | 	var services []msg.Service
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i := 0; i < numservices; i++ {
 | 
					
						
							|  |  |  | 		services = append(services, msg.Service{})
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w := make(map[int]int)
 | 
					
						
							|  |  |  | 	for _, serv := range services {
 | 
					
						
							|  |  |  | 		weight := 100
 | 
					
						
							|  |  |  | 		if serv.Weight != 0 {
 | 
					
						
							|  |  |  | 			weight = serv.Weight
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		if _, ok := w[serv.Priority]; !ok {
 | 
					
						
							|  |  |  | 			w[serv.Priority] = weight
 | 
					
						
							|  |  |  | 			continue
 | 
					
						
							|  |  |  | 		}
 | 
					
						
							|  |  |  | 		w[serv.Priority] += weight
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2020-06-12 14:01:28 +08:00
										 |  |  | 	weight := uint16(math.Floor((100.0 / float64(w[0])) * 100))
 | 
					
						
							|  |  |  | 	// weight should be at least 1
 | 
					
						
							|  |  |  | 	if weight == 0 {
 | 
					
						
							|  |  |  | 		weight = 1
 | 
					
						
							|  |  |  | 	}
 | 
					
						
							| 
									
										
										
										
											2018-02-08 10:11:04 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-12 14:01:28 +08:00
										 |  |  | 	return weight
 | 
					
						
							| 
									
										
										
										
											2017-11-01 10:11:34 +00:00
										 |  |  | }
 |