2016-06-06 12:49:53 -07:00
// Package kubernetes provides the kubernetes backend.
package kubernetes
import (
2016-07-07 01:40:58 -07:00
"errors"
2016-10-30 15:54:16 +00:00
"fmt"
2016-07-18 10:47:36 -07:00
"log"
2017-02-01 12:56:10 -05:00
"net"
2016-09-23 09:48:11 -03:00
"strings"
2017-08-10 17:14:56 -07:00
"sync/atomic"
2016-06-06 12:49:53 -07:00
"time"
2017-02-21 22:51:47 -08:00
"github.com/coredns/coredns/middleware"
"github.com/coredns/coredns/middleware/etcd/msg"
2017-08-10 01:08:58 -07:00
"github.com/coredns/coredns/middleware/pkg/dnsutil"
2017-08-10 17:14:56 -07:00
"github.com/coredns/coredns/middleware/pkg/healthcheck"
2017-02-21 22:51:47 -08:00
dnsstrings "github.com/coredns/coredns/middleware/pkg/strings"
"github.com/coredns/coredns/middleware/proxy"
"github.com/coredns/coredns/request"
2016-06-06 12:49:53 -07:00
2016-07-07 01:40:58 -07:00
"github.com/miekg/dns"
2016-11-05 15:43:27 +00:00
"k8s.io/client-go/1.5/kubernetes"
2016-11-05 07:57:08 -04:00
"k8s.io/client-go/1.5/pkg/api"
unversionedapi "k8s.io/client-go/1.5/pkg/api/unversioned"
2016-11-05 15:43:27 +00:00
"k8s.io/client-go/1.5/pkg/labels"
2016-11-05 07:57:08 -04:00
"k8s.io/client-go/1.5/rest"
"k8s.io/client-go/1.5/tools/clientcmd"
clientcmdapi "k8s.io/client-go/1.5/tools/clientcmd/api"
2016-08-05 18:19:51 -07:00
)
2016-09-23 09:14:12 +01:00
// Kubernetes implements a middleware that connects to a Kubernetes cluster.
2016-06-06 12:49:53 -07:00
type Kubernetes struct {
2017-06-28 18:44:30 -04:00
Next middleware . Handler
Zones [ ] string
Proxy proxy . Proxy // Proxy for looking up names during the resolution process
2017-08-10 17:14:56 -07:00
APIServerList [ ] string
APIProxy * apiProxy
2017-06-28 18:44:30 -04:00
APICertAuth string
APIClientCert string
APIClientKey string
APIConn dnsController
ResyncPeriod time . Duration
Namespaces [ ] string
LabelSelector * unversionedapi . LabelSelector
Selector * labels . Selector
PodMode string
Fallthrough bool
2016-06-06 12:49:53 -07:00
2017-08-11 16:21:07 +01:00
primaryZoneIndex int
2017-08-07 07:09:32 -07:00
interfaceAddrsFunc func ( ) net . IP
2017-08-10 19:26:31 +01:00
autoPathSearch [ ] string // Local search path from /etc/resolv.conf. Needed for autopath.
2017-06-28 18:44:30 -04:00
}
2017-08-18 14:45:20 +01:00
// New returns a intialized Kubernetes. It default interfaceAddrFunc to return 127.0.0.1. All other
// values default to their zero value, primaryZoneIndex will thus point to the first zone.
func New ( zones [ ] string ) * Kubernetes {
k := new ( Kubernetes )
k . Zones = zones
k . interfaceAddrsFunc = func ( ) net . IP { return net . ParseIP ( "127.0.0.1" ) }
return k
}
2017-01-11 16:23:10 -05:00
const (
2017-01-29 12:06:26 -08:00
// PodModeDisabled is the default value where pod requests are ignored
PodModeDisabled = "disabled"
// PodModeVerified is where Pod requests are answered only if they exist
PodModeVerified = "verified"
// PodModeInsecure is where pod requests are answered without verfying they exist
PodModeInsecure = "insecure"
// DNSSchemaVersion is the schema version: https://github.com/kubernetes/dns/blob/master/docs/specification.md
2017-05-30 08:20:39 -04:00
DNSSchemaVersion = "1.0.1"
2017-01-11 16:23:10 -05:00
)
2017-01-05 10:09:59 -05:00
type endpoint struct {
addr api . EndpointAddress
port api . EndpointPort
}
2017-08-10 01:08:58 -07:00
// kService is a service as retrieved via the k8s API.
type kService struct {
2017-01-05 10:09:59 -05:00
name string
namespace string
addr string
ports [ ] api . ServicePort
endpoints [ ] endpoint
}
2017-08-10 01:08:58 -07:00
// kPod is a pod as retrieved via the k8s API.
type kPod struct {
2017-01-11 16:23:10 -05:00
name string
namespace string
addr string
}
2017-08-03 09:03:53 -07:00
var (
2017-08-06 05:54:24 -07:00
errNoItems = errors . New ( "no items found" )
errNsNotExposed = errors . New ( "namespace is not exposed" )
errInvalidRequest = errors . New ( "invalid query name" )
errAPIBadPodType = errors . New ( "expected type *api.Pod" )
errPodsDisabled = errors . New ( "pod records disabled" )
2017-08-03 09:03:53 -07:00
)
2016-11-10 16:24:06 -05:00
2016-10-30 15:54:16 +00:00
// Services implements the ServiceBackend interface.
2017-05-22 16:05:48 -04:00
func ( k * Kubernetes ) Services ( state request . Request , exact bool , opt middleware . Options ) ( svcs [ ] msg . Service , debug [ ] msg . Service , err error ) {
2017-08-10 01:08:58 -07:00
// We're looking again at types, which we've already done in ServeDNS, but there are some types k8s just can't answer.
switch state . QType ( ) {
2017-08-18 14:45:20 +01:00
2017-08-10 01:08:58 -07:00
case dns . TypeTXT :
2017-08-10 22:14:19 +01:00
// 1 label + zone, label must be "dns-version".
2017-08-18 14:45:20 +01:00
t , _ := dnsutil . TrimZone ( state . Name ( ) , state . Zone )
2017-08-10 01:08:58 -07:00
segs := dns . SplitDomainName ( t )
if len ( segs ) != 1 {
2017-08-18 14:45:20 +01:00
return nil , nil , fmt . Errorf ( "kubernetes: TXT query can only be for dns-version: %s" , state . QName ( ) )
2017-08-10 01:08:58 -07:00
}
if segs [ 0 ] != "dns-version" {
2017-08-18 14:45:20 +01:00
return nil , nil , nil
2017-08-10 01:08:58 -07:00
}
svc := msg . Service { Text : DNSSchemaVersion , TTL : 28800 , Key : msg . Path ( state . QName ( ) , "coredns" ) }
return [ ] msg . Service { svc } , nil , nil
2017-08-18 14:45:20 +01:00
2017-08-10 22:14:19 +01:00
case dns . TypeNS :
// We can only get here if the qname equal the zone, see ServeDNS in handler.go.
2017-08-10 23:13:08 +01:00
ns := k . nsAddr ( )
2017-08-10 22:14:19 +01:00
svc := msg . Service { Host : ns . A . String ( ) , Key : msg . Path ( state . QName ( ) , "coredns" ) }
return [ ] msg . Service { svc } , nil , nil
2017-08-10 01:08:58 -07:00
}
2017-08-18 14:45:20 +01:00
if state . QType ( ) == dns . TypeA && isDefaultNS ( state . Name ( ) , state . Zone ) {
// If this is an A request for "ns.dns", respond with a "fake" record for coredns.
// SOA records always use this hardcoded name
ns := k . nsAddr ( )
svc := msg . Service { Host : ns . A . String ( ) , Key : msg . Path ( state . QName ( ) , "coredns" ) }
return [ ] msg . Service { svc } , nil , nil
2017-01-05 10:09:59 -05:00
}
2017-08-10 01:08:58 -07:00
2017-08-19 14:03:03 +01:00
s , e := k . Records ( state , false )
2017-08-18 14:45:20 +01:00
// SRV for external services is not yet implemented, so remove those records.
if state . QType ( ) != dns . TypeSRV {
return s , nil , e
}
internal := [ ] msg . Service { }
for _ , svc := range s {
if t , _ := svc . HostType ( ) ; t != dns . TypeCNAME {
internal = append ( internal , svc )
2017-05-30 08:20:39 -04:00
}
2017-01-15 14:37:18 +00:00
}
2017-08-18 14:45:20 +01:00
return internal , nil , e
2017-01-15 14:37:18 +00:00
}
2017-08-11 16:21:07 +01:00
// primaryZone will return the first non-reverse zone being handled by this middleware
func ( k * Kubernetes ) primaryZone ( ) string {
return k . Zones [ k . primaryZoneIndex ]
2016-11-14 19:31:08 +00:00
}
2016-10-30 15:54:16 +00:00
// Lookup implements the ServiceBackend interface.
func ( k * Kubernetes ) Lookup ( state request . Request , name string , typ uint16 ) ( * dns . Msg , error ) {
return k . Proxy . Lookup ( state , name , typ )
}
// IsNameError implements the ServiceBackend interface.
func ( k * Kubernetes ) IsNameError ( err error ) bool {
2017-08-10 22:14:19 +01:00
return err == errNoItems || err == errNsNotExposed || err == errInvalidRequest
2016-10-30 15:54:16 +00:00
}
// Debug implements the ServiceBackend interface.
2017-03-06 11:42:59 +00:00
func ( k * Kubernetes ) Debug ( ) string { return "debug" }
2016-10-30 15:54:16 +00:00
2016-11-05 07:57:08 -04:00
func ( k * Kubernetes ) getClientConfig ( ) ( * rest . Config , error ) {
2017-06-23 18:02:45 -04:00
loadingRules := & clientcmd . ClientConfigLoadingRules { }
2016-08-05 18:19:51 -07:00
overrides := & clientcmd . ConfigOverrides { }
2016-09-23 18:07:06 -04:00
clusterinfo := clientcmdapi . Cluster { }
authinfo := clientcmdapi . AuthInfo { }
2017-08-10 17:14:56 -07:00
if len ( k . APIServerList ) > 0 {
endpoint := k . APIServerList [ 0 ]
if len ( k . APIServerList ) > 1 {
// Use a random port for api proxy, will get the value later through listener.Addr()
listener , err := net . Listen ( "tcp" , "127.0.0.1:0" )
if err != nil {
return nil , fmt . Errorf ( "failed to create kubernetes api proxy: %v" , err )
}
k . APIProxy = & apiProxy {
listener : listener ,
handler : proxyHandler {
HealthCheck : healthcheck . HealthCheck {
FailTimeout : 3 * time . Second ,
MaxFails : 1 ,
Future : 10 * time . Second ,
Path : "/" ,
Interval : 5 * time . Second ,
} ,
} ,
}
k . APIProxy . handler . Hosts = make ( [ ] * healthcheck . UpstreamHost , len ( k . APIServerList ) )
for i , entry := range k . APIServerList {
uh := & healthcheck . UpstreamHost {
Name : strings . TrimPrefix ( entry , "http://" ) ,
CheckDown : func ( upstream * proxyHandler ) healthcheck . UpstreamHostDownFunc {
return func ( uh * healthcheck . UpstreamHost ) bool {
down := false
uh . CheckMu . Lock ( )
until := uh . OkUntil
uh . CheckMu . Unlock ( )
if ! until . IsZero ( ) && time . Now ( ) . After ( until ) {
down = true
}
fails := atomic . LoadInt32 ( & uh . Fails )
if fails >= upstream . MaxFails && upstream . MaxFails != 0 {
down = true
}
return down
}
} ( & k . APIProxy . handler ) ,
}
k . APIProxy . handler . Hosts [ i ] = uh
}
k . APIProxy . Handler = & k . APIProxy . handler
// Find the random port used for api proxy
endpoint = fmt . Sprintf ( "http://%s" , listener . Addr ( ) )
}
clusterinfo . Server = endpoint
2016-10-19 17:04:35 -04:00
} else {
2016-11-05 07:57:08 -04:00
cc , err := rest . InClusterConfig ( )
2016-10-19 17:04:35 -04:00
if err != nil {
return nil , err
}
return cc , err
2016-09-23 18:07:06 -04:00
}
if len ( k . APICertAuth ) > 0 {
clusterinfo . CertificateAuthority = k . APICertAuth
2016-08-05 18:19:51 -07:00
}
2016-09-23 18:07:06 -04:00
if len ( k . APIClientCert ) > 0 {
authinfo . ClientCertificate = k . APIClientCert
}
if len ( k . APIClientKey ) > 0 {
authinfo . ClientKey = k . APIClientKey
}
2017-08-18 14:45:20 +01:00
2016-09-23 18:07:06 -04:00
overrides . ClusterInfo = clusterinfo
overrides . AuthInfo = authinfo
2016-08-05 18:19:51 -07:00
clientConfig := clientcmd . NewNonInteractiveDeferredLoadingClientConfig ( loadingRules , overrides )
2017-08-18 14:45:20 +01:00
2016-09-23 18:07:06 -04:00
return clientConfig . ClientConfig ( )
}
// InitKubeCache initializes a new Kubernetes cache.
2017-05-22 16:05:48 -04:00
func ( k * Kubernetes ) InitKubeCache ( ) ( err error ) {
2016-09-23 18:07:06 -04:00
config , err := k . getClientConfig ( )
2016-08-05 18:19:51 -07:00
if err != nil {
return err
}
2016-11-05 07:57:08 -04:00
kubeClient , err := kubernetes . NewForConfig ( config )
2016-08-05 18:19:51 -07:00
if err != nil {
2017-08-18 14:45:20 +01:00
return fmt . Errorf ( "failed to create kubernetes notification controller: %q" , err )
2016-08-05 18:19:51 -07:00
}
2016-10-30 15:54:16 +00:00
if k . LabelSelector != nil {
2016-08-19 17:14:17 -07:00
var selector labels . Selector
2016-08-22 23:15:21 -07:00
selector , err = unversionedapi . LabelSelectorAsSelector ( k . LabelSelector )
k . Selector = & selector
2016-08-19 17:14:17 -07:00
if err != nil {
2017-08-18 14:45:20 +01:00
return fmt . Errorf ( "unable to create Selector for LabelSelector '%s': %q" , k . LabelSelector , err )
2016-08-19 17:14:17 -07:00
}
2016-10-30 15:54:16 +00:00
}
2017-04-30 03:48:37 -04:00
if k . LabelSelector != nil {
2017-08-18 14:45:20 +01:00
log . Printf ( "[INFO] Kubernetes has label selector '%s'. Only objects matching this label selector will be exposed." , unversionedapi . FormatLabelSelector ( k . LabelSelector ) )
2016-08-12 20:44:08 -07:00
}
2016-10-30 15:54:16 +00:00
2017-06-14 09:38:00 -04:00
opts := dnsControlOpts {
2017-08-09 03:13:38 -07:00
initPodCache : k . PodMode == PodModeVerified ,
2017-06-14 09:38:00 -04:00
}
k . APIConn = newdnsController ( kubeClient , k . ResyncPeriod , k . Selector , opts )
2016-08-22 23:15:21 -07:00
2016-08-05 18:19:51 -07:00
return err
}
2017-08-19 14:03:03 +01:00
// Records looks up services in kubernetes.
func ( k * Kubernetes ) Records ( state request . Request , exact bool ) ( [ ] msg . Service , error ) {
2017-08-18 14:45:20 +01:00
r , e := k . parseRequest ( state )
if e != nil {
return nil , e
}
if ! k . namespaceExposed ( r . namespace ) {
2016-11-11 16:56:15 +00:00
return nil , errNsNotExposed
2016-07-07 01:40:58 -07:00
}
2017-08-18 14:45:20 +01:00
2017-01-29 12:06:26 -08:00
services , pods , err := k . get ( r )
2016-07-07 01:40:58 -07:00
if err != nil {
return nil , err
}
2017-01-11 16:23:10 -05:00
if len ( services ) == 0 && len ( pods ) == 0 {
2016-11-11 16:56:15 +00:00
return nil , errNoItems
2016-07-07 01:40:58 -07:00
}
2016-06-06 12:49:53 -07:00
2017-08-19 07:18:35 +01:00
records := k . getRecordsForK8sItems ( services , pods , state . Zone )
2016-07-07 01:40:58 -07:00
return records , nil
}
2016-06-06 12:49:53 -07:00
2017-01-05 10:09:59 -05:00
func endpointHostname ( addr api . EndpointAddress ) string {
if addr . Hostname != "" {
return strings . ToLower ( addr . Hostname )
}
if strings . Contains ( addr . IP , "." ) {
return strings . Replace ( addr . IP , "." , "-" , - 1 )
}
if strings . Contains ( addr . IP , ":" ) {
return strings . ToLower ( strings . Replace ( addr . IP , ":" , "-" , - 1 ) )
}
return ""
}
2017-08-19 07:18:35 +01:00
func ( k * Kubernetes ) getRecordsForK8sItems ( services [ ] kService , pods [ ] kPod , zone string ) ( records [ ] msg . Service ) {
zonePath := msg . Path ( zone , "coredns" )
2016-06-06 12:49:53 -07:00
2017-01-11 16:23:10 -05:00
for _ , svc := range services {
2017-06-14 10:29:41 -04:00
if svc . addr == api . ClusterIPNone || len ( svc . endpoints ) > 0 {
// This is a headless service or endpoints are present, create records for each endpoint
2017-01-05 10:09:59 -05:00
for _ , ep := range svc . endpoints {
2017-08-14 08:49:26 +01:00
s := msg . Service { Host : ep . addr . IP , Port : int ( ep . port . Port ) }
s . Key = strings . Join ( [ ] string { zonePath , Svc , svc . namespace , svc . name , endpointHostname ( ep . addr ) } , "/" )
2017-01-05 10:09:59 -05:00
records = append ( records , s )
2016-12-02 17:50:01 -05:00
}
2017-08-05 12:29:43 -07:00
continue
}
2017-06-14 09:38:00 -04:00
2017-08-05 12:29:43 -07:00
// Create records for each exposed port...
for _ , p := range svc . ports {
2017-08-14 08:49:26 +01:00
s := msg . Service { Host : svc . addr , Port : int ( p . Port ) }
s . Key = strings . Join ( [ ] string { zonePath , Svc , svc . namespace , svc . name } , "/" )
2017-05-30 08:20:39 -04:00
2017-08-05 12:29:43 -07:00
records = append ( records , s )
}
// If the addr is not an IP (i.e. an external service), add the record ...
2017-08-14 08:49:26 +01:00
s := msg . Service { Key : strings . Join ( [ ] string { zonePath , Svc , svc . namespace , svc . name } , "/" ) , Host : svc . addr }
2017-08-05 12:29:43 -07:00
if t , _ := s . HostType ( ) ; t == dns . TypeCNAME {
2017-08-14 08:49:26 +01:00
s . Key = strings . Join ( [ ] string { zonePath , Svc , svc . namespace , svc . name } , "/" )
2017-08-05 12:29:43 -07:00
records = append ( records , s )
2016-07-07 01:40:58 -07:00
}
}
2016-06-06 12:49:53 -07:00
2017-01-11 16:23:10 -05:00
for _ , p := range pods {
2017-08-14 08:49:26 +01:00
s := msg . Service { Key : strings . Join ( [ ] string { zonePath , Pod , p . namespace , p . name } , "/" ) , Host : p . addr }
2017-01-11 16:23:10 -05:00
records = append ( records , s )
}
2016-07-07 01:40:58 -07:00
return records
2016-06-06 12:49:53 -07:00
}
2017-08-10 01:08:58 -07:00
func ( k * Kubernetes ) findPods ( namespace , podname string ) ( pods [ ] kPod , err error ) {
2017-01-11 16:23:10 -05:00
if k . PodMode == PodModeDisabled {
2017-05-22 16:05:48 -04:00
return pods , errPodsDisabled
2017-01-11 16:23:10 -05:00
}
var ip string
if strings . Count ( podname , "-" ) == 3 && ! strings . Contains ( podname , "--" ) {
ip = strings . Replace ( podname , "-" , "." , - 1 )
} else {
ip = strings . Replace ( podname , "-" , ":" , - 1 )
}
if k . PodMode == PodModeInsecure {
2017-08-10 01:08:58 -07:00
s := kPod { name : podname , namespace : namespace , addr : ip }
2017-01-11 16:23:10 -05:00
pods = append ( pods , s )
return pods , nil
}
2017-01-20 02:22:11 -05:00
// PodModeVerified
2017-05-22 16:05:48 -04:00
objList := k . APIConn . PodIndex ( ip )
2017-01-11 16:23:10 -05:00
2017-08-05 12:29:43 -07:00
nsWildcard := wildcard ( namespace )
2017-01-20 02:22:11 -05:00
for _ , o := range objList {
p , ok := o . ( * api . Pod )
if ! ok {
2017-05-25 12:08:34 -07:00
return nil , errAPIBadPodType
2017-01-20 02:22:11 -05:00
}
// If namespace has a wildcard, filter results against Corefile namespace list.
if nsWildcard && ( len ( k . Namespaces ) > 0 ) && ( ! dnsstrings . StringInSlice ( p . Namespace , k . Namespaces ) ) {
continue
}
// check for matching ip and namespace
2017-08-05 12:29:43 -07:00
if ip == p . Status . PodIP && match ( namespace , p . Namespace , nsWildcard ) {
2017-08-10 01:08:58 -07:00
s := kPod { name : podname , namespace : namespace , addr : ip }
2017-01-20 02:22:11 -05:00
pods = append ( pods , s )
return pods , nil
}
}
return pods , nil
2017-01-11 16:23:10 -05:00
}
2017-01-29 12:06:26 -08:00
// get retrieves matching data from the cache.
2017-08-10 01:08:58 -07:00
func ( k * Kubernetes ) get ( r recordRequest ) ( services [ ] kService , pods [ ] kPod , err error ) {
2017-01-15 03:12:28 -05:00
switch {
2017-08-10 01:08:58 -07:00
case r . podOrSvc == Pod :
2017-01-15 03:12:28 -05:00
pods , err = k . findPods ( r . namespace , r . service )
return nil , pods , err
default :
services , err = k . findServices ( r )
return services , nil , err
}
}
2017-08-10 01:08:58 -07:00
func ( k * Kubernetes ) findServices ( r recordRequest ) ( [ ] kService , error ) {
2016-10-12 12:46:35 +01:00
serviceList := k . APIConn . ServiceList ( )
2017-08-10 01:08:58 -07:00
var resultItems [ ] kService
2016-07-14 14:50:14 -07:00
2017-08-05 12:29:43 -07:00
nsWildcard := wildcard ( r . namespace )
serviceWildcard := wildcard ( r . service )
portWildcard := wildcard ( r . port ) || r . port == ""
protocolWildcard := wildcard ( r . protocol ) || r . protocol == ""
2017-01-05 10:09:59 -05:00
for _ , svc := range serviceList {
2017-08-05 12:29:43 -07:00
if ! ( match ( r . namespace , svc . Namespace , nsWildcard ) && match ( r . service , svc . Name , serviceWildcard ) ) {
2017-01-05 10:09:59 -05:00
continue
}
2017-08-18 14:45:20 +01:00
2017-01-05 10:09:59 -05:00
// If namespace has a wildcard, filter results against Corefile namespace list.
// (Namespaces without a wildcard were filtered before the call to this function.)
if nsWildcard && ( len ( k . Namespaces ) > 0 ) && ( ! dnsstrings . StringInSlice ( svc . Namespace , k . Namespaces ) ) {
continue
}
2017-08-10 01:08:58 -07:00
s := kService { name : svc . Name , namespace : svc . Namespace }
2017-06-14 10:29:41 -04:00
// Endpoint query or headless service
if svc . Spec . ClusterIP == api . ClusterIPNone || r . endpoint != "" {
2017-05-30 08:20:39 -04:00
s . addr = svc . Spec . ClusterIP
2017-06-14 10:29:41 -04:00
endpointsList := k . APIConn . EndpointsList ( )
for _ , ep := range endpointsList . Items {
if ep . ObjectMeta . Name != svc . Name || ep . ObjectMeta . Namespace != svc . Namespace {
2017-01-05 10:09:59 -05:00
continue
}
2017-06-14 10:29:41 -04:00
for _ , eps := range ep . Subsets {
for _ , addr := range eps . Addresses {
for _ , p := range eps . Ports {
ephostname := endpointHostname ( addr )
if r . endpoint != "" && r . endpoint != ephostname {
continue
}
2017-08-05 12:29:43 -07:00
if ! ( match ( r . port , p . Name , portWildcard ) && match ( r . protocol , string ( p . Protocol ) , protocolWildcard ) ) {
2017-06-14 10:29:41 -04:00
continue
}
s . endpoints = append ( s . endpoints , endpoint { addr : addr , port : p } )
}
}
}
}
if len ( s . endpoints ) > 0 {
resultItems = append ( resultItems , s )
2017-01-05 10:09:59 -05:00
}
2017-06-14 10:29:41 -04:00
continue
}
// External service
if svc . Spec . ExternalName != "" {
s . addr = svc . Spec . ExternalName
2017-01-05 10:09:59 -05:00
resultItems = append ( resultItems , s )
continue
}
2017-05-22 16:05:48 -04:00
2017-06-14 10:29:41 -04:00
// ClusterIP service
s . addr = svc . Spec . ClusterIP
for _ , p := range svc . Spec . Ports {
2017-08-05 12:29:43 -07:00
if ! ( match ( r . port , p . Name , portWildcard ) && match ( r . protocol , string ( p . Protocol ) , protocolWildcard ) ) {
2016-07-14 14:50:14 -07:00
continue
}
2017-06-14 10:29:41 -04:00
s . ports = append ( s . ports , p )
2016-07-14 14:50:14 -07:00
}
2017-06-14 10:29:41 -04:00
2017-01-05 10:09:59 -05:00
resultItems = append ( resultItems , s )
2016-07-14 14:50:14 -07:00
}
return resultItems , nil
2016-06-06 12:49:53 -07:00
}
2017-08-05 12:29:43 -07:00
func match ( a , b string , wildcard bool ) bool {
2017-01-15 03:12:28 -05:00
if wildcard {
return true
}
2017-08-05 12:29:43 -07:00
return strings . EqualFold ( a , b )
2016-07-14 14:50:14 -07:00
}
2017-01-05 10:09:59 -05:00
// getServiceRecordForIP: Gets a service record with a cluster ip matching the ip argument
// If a service cluster ip does not match, it checks all endpoints
2016-08-22 23:15:21 -07:00
func ( k * Kubernetes ) getServiceRecordForIP ( ip , name string ) [ ] msg . Service {
2017-01-05 10:09:59 -05:00
// First check services with cluster ips
2017-05-22 16:05:48 -04:00
svcList := k . APIConn . ServiceList ( )
2016-09-23 10:13:02 -03:00
for _ , service := range svcList {
2017-02-07 16:22:43 -05:00
if ( len ( k . Namespaces ) > 0 ) && ! dnsstrings . StringInSlice ( service . Namespace , k . Namespaces ) {
2017-01-05 10:09:59 -05:00
continue
}
2016-08-05 18:19:51 -07:00
if service . Spec . ClusterIP == ip {
2017-08-19 15:22:09 +01:00
domain := dnsutil . Join ( [ ] string { service . Name , service . Namespace , Svc , k . primaryZone ( ) } )
2017-01-29 12:06:26 -08:00
return [ ] msg . Service { { Host : domain } }
2017-01-05 10:09:59 -05:00
}
}
// If no cluster ips match, search endpoints
2017-05-22 16:05:48 -04:00
epList := k . APIConn . EndpointsList ( )
2017-01-05 10:09:59 -05:00
for _ , ep := range epList . Items {
2017-02-07 16:22:43 -05:00
if ( len ( k . Namespaces ) > 0 ) && ! dnsstrings . StringInSlice ( ep . ObjectMeta . Namespace , k . Namespaces ) {
2017-01-05 10:09:59 -05:00
continue
}
for _ , eps := range ep . Subsets {
for _ , addr := range eps . Addresses {
if addr . IP == ip {
2017-08-19 15:22:09 +01:00
domain := dnsutil . Join ( [ ] string { endpointHostname ( addr ) , ep . ObjectMeta . Name , ep . ObjectMeta . Namespace , Svc , k . primaryZone ( ) } )
2017-01-29 12:06:26 -08:00
return [ ] msg . Service { { Host : domain } }
2017-01-05 10:09:59 -05:00
}
}
2016-08-05 18:19:51 -07:00
}
2016-06-06 12:49:53 -07:00
}
2016-08-05 18:19:51 -07:00
return nil
2016-06-06 12:49:53 -07:00
}
2017-08-18 14:45:20 +01:00
// namespaceExposed returns true when the namespace is exposed.
func ( k * Kubernetes ) namespaceExposed ( namespace string ) bool {
// Abort if the namespace does not contain a wildcard, and namespace is
// not published per CoreFile Case where namespace contains a wildcard
// is handled in k.get(...) method.
if ( ! wildcard ( namespace ) ) && ( len ( k . Namespaces ) > 0 ) && ( ! dnsstrings . StringInSlice ( namespace , k . Namespaces ) ) {
return false
}
return true
}
2017-08-05 12:29:43 -07:00
// wildcard checks whether s contains a wildcard value
func wildcard ( s string ) bool {
return ( s == "*" || s == "any" )
2016-09-23 09:48:11 -03:00
}
2017-06-14 09:38:00 -04:00
2017-08-04 07:34:00 -07:00
const (
2017-08-04 09:46:40 -07:00
// Svc is the DNS schema for kubernetes services
2017-08-04 07:34:00 -07:00
Svc = "svc"
2017-08-04 09:46:40 -07:00
// Pod is the DNS schema for kubernetes pods
2017-08-04 07:34:00 -07:00
Pod = "pod"
)