Put the autopath stuff in a separate sub package. Tests are still included in the main kubernetes directory. Next steps (after this is merged), is pulling the autopath handling into the subpackage and fixing the tests.
248 lines
6.7 KiB
Go
248 lines
6.7 KiB
Go
package kubernetes
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/coredns/coredns/core/dnsserver"
|
|
"github.com/coredns/coredns/middleware"
|
|
"github.com/coredns/coredns/middleware/kubernetes/autopath"
|
|
"github.com/coredns/coredns/middleware/pkg/dnsutil"
|
|
"github.com/coredns/coredns/middleware/proxy"
|
|
"github.com/miekg/dns"
|
|
|
|
"github.com/mholt/caddy"
|
|
unversionedapi "k8s.io/client-go/1.5/pkg/api/unversioned"
|
|
)
|
|
|
|
func init() {
|
|
caddy.RegisterPlugin("kubernetes", caddy.Plugin{
|
|
ServerType: "dns",
|
|
Action: setup,
|
|
})
|
|
}
|
|
|
|
func setup(c *caddy.Controller) error {
|
|
kubernetes, err := kubernetesParse(c)
|
|
if err != nil {
|
|
return middleware.Error("kubernetes", err)
|
|
}
|
|
|
|
err = kubernetes.InitKubeCache()
|
|
if err != nil {
|
|
return middleware.Error("kubernetes", err)
|
|
}
|
|
|
|
// Register KubeCache start and stop functions with Caddy
|
|
c.OnStartup(func() error {
|
|
go kubernetes.APIConn.Run()
|
|
return nil
|
|
})
|
|
|
|
c.OnShutdown(func() error {
|
|
return kubernetes.APIConn.Stop()
|
|
})
|
|
|
|
dnsserver.GetConfig(c).AddMiddleware(func(next middleware.Handler) middleware.Handler {
|
|
kubernetes.Next = next
|
|
return kubernetes
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func kubernetesParse(c *caddy.Controller) (*Kubernetes, error) {
|
|
k8s := &Kubernetes{
|
|
ResyncPeriod: defaultResyncPeriod,
|
|
interfaceAddrsFunc: localPodIP,
|
|
PodMode: PodModeDisabled,
|
|
Proxy: proxy.Proxy{},
|
|
}
|
|
|
|
for c.Next() {
|
|
if c.Val() == "kubernetes" {
|
|
zones := c.RemainingArgs()
|
|
|
|
if len(zones) == 0 {
|
|
k8s.Zones = make([]string, len(c.ServerBlockKeys))
|
|
copy(k8s.Zones, c.ServerBlockKeys)
|
|
}
|
|
|
|
k8s.Zones = NormalizeZoneList(zones)
|
|
middleware.Zones(k8s.Zones).Normalize()
|
|
|
|
if k8s.Zones == nil || len(k8s.Zones) < 1 {
|
|
return nil, errors.New("zone name must be provided for kubernetes middleware")
|
|
}
|
|
|
|
k8s.primaryZone = -1
|
|
for i, z := range k8s.Zones {
|
|
if strings.HasSuffix(z, "in-addr.arpa.") || strings.HasSuffix(z, "ip6.arpa.") {
|
|
continue
|
|
}
|
|
k8s.primaryZone = i
|
|
break
|
|
}
|
|
|
|
if k8s.primaryZone == -1 {
|
|
return nil, errors.New("non-reverse zone name must be given for Kubernetes")
|
|
}
|
|
|
|
for c.NextBlock() {
|
|
switch c.Val() {
|
|
case "cidrs":
|
|
args := c.RemainingArgs()
|
|
if len(args) > 0 {
|
|
for _, cidrStr := range args {
|
|
_, cidr, err := net.ParseCIDR(cidrStr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid cidr: %s", cidrStr)
|
|
}
|
|
k8s.ReverseCidrs = append(k8s.ReverseCidrs, *cidr)
|
|
|
|
}
|
|
continue
|
|
}
|
|
return nil, c.ArgErr()
|
|
case "pods":
|
|
args := c.RemainingArgs()
|
|
if len(args) == 1 {
|
|
switch args[0] {
|
|
case PodModeDisabled, PodModeInsecure, PodModeVerified:
|
|
k8s.PodMode = args[0]
|
|
default:
|
|
return nil, fmt.Errorf("wrong value for pods: %s, must be one of: disabled, verified, insecure", args[0])
|
|
}
|
|
continue
|
|
}
|
|
return nil, c.ArgErr()
|
|
case "namespaces":
|
|
args := c.RemainingArgs()
|
|
if len(args) > 0 {
|
|
k8s.Namespaces = append(k8s.Namespaces, args...)
|
|
continue
|
|
}
|
|
return nil, c.ArgErr()
|
|
case "endpoint":
|
|
args := c.RemainingArgs()
|
|
if len(args) > 0 {
|
|
k8s.APIEndpoint = args[0]
|
|
continue
|
|
}
|
|
return nil, c.ArgErr()
|
|
case "tls": // cert key cacertfile
|
|
args := c.RemainingArgs()
|
|
if len(args) == 3 {
|
|
k8s.APIClientCert, k8s.APIClientKey, k8s.APICertAuth = args[0], args[1], args[2]
|
|
continue
|
|
}
|
|
return nil, c.ArgErr()
|
|
case "resyncperiod":
|
|
args := c.RemainingArgs()
|
|
if len(args) > 0 {
|
|
rp, err := time.ParseDuration(args[0])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to parse resync duration value: '%v': %v", args[0], err)
|
|
}
|
|
k8s.ResyncPeriod = rp
|
|
continue
|
|
}
|
|
return nil, c.ArgErr()
|
|
case "labels":
|
|
args := c.RemainingArgs()
|
|
if len(args) > 0 {
|
|
labelSelectorString := strings.Join(args, " ")
|
|
ls, err := unversionedapi.ParseToLabelSelector(labelSelectorString)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to parse label selector value: '%v': %v", labelSelectorString, err)
|
|
}
|
|
k8s.LabelSelector = ls
|
|
continue
|
|
}
|
|
return nil, c.ArgErr()
|
|
case "fallthrough":
|
|
args := c.RemainingArgs()
|
|
if len(args) == 0 {
|
|
k8s.Fallthrough = true
|
|
continue
|
|
}
|
|
return nil, c.ArgErr()
|
|
case "upstream":
|
|
args := c.RemainingArgs()
|
|
if len(args) == 0 {
|
|
return nil, c.ArgErr()
|
|
}
|
|
ups, err := dnsutil.ParseHostPortOrFile(args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
k8s.Proxy = proxy.NewLookup(ups)
|
|
case "federation": // name zone
|
|
args := c.RemainingArgs()
|
|
if len(args) == 2 {
|
|
k8s.Federations = append(k8s.Federations, Federation{
|
|
name: args[0],
|
|
zone: args[1],
|
|
})
|
|
continue
|
|
}
|
|
return nil, fmt.Errorf("incorrect number of arguments for federation, got %v, expected 2", len(args))
|
|
case "autopath": // name zone
|
|
args := c.RemainingArgs()
|
|
k8s.autoPath = &autopath.AutoPath{
|
|
NDots: defautNdots,
|
|
HostSearchPath: []string{},
|
|
ResolvConfFile: defaultResolvConfFile,
|
|
OnNXDOMAIN: defaultOnNXDOMAIN,
|
|
}
|
|
if len(args) > 3 {
|
|
return nil, fmt.Errorf("incorrect number of arguments for autopath, got %v, expected at most 3", len(args))
|
|
|
|
}
|
|
if len(args) > 0 {
|
|
ndots, err := strconv.Atoi(args[0])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid NDOTS argument for autopath, got '%v', expected an integer", ndots)
|
|
}
|
|
k8s.autoPath.NDots = ndots
|
|
}
|
|
if len(args) > 1 {
|
|
switch args[1] {
|
|
case dns.RcodeToString[dns.RcodeNameError]:
|
|
k8s.autoPath.OnNXDOMAIN = dns.RcodeNameError
|
|
case dns.RcodeToString[dns.RcodeSuccess]:
|
|
k8s.autoPath.OnNXDOMAIN = dns.RcodeSuccess
|
|
case dns.RcodeToString[dns.RcodeServerFailure]:
|
|
k8s.autoPath.OnNXDOMAIN = dns.RcodeServerFailure
|
|
default:
|
|
return nil, fmt.Errorf("invalid RESPONSE argument for autopath, got '%v', expected SERVFAIL, NOERROR, or NXDOMAIN", args[1])
|
|
}
|
|
}
|
|
if len(args) > 2 {
|
|
k8s.autoPath.ResolvConfFile = args[2]
|
|
}
|
|
rc, err := dns.ClientConfigFromFile(k8s.autoPath.ResolvConfFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error when parsing %v: %v", k8s.autoPath.ResolvConfFile, err)
|
|
}
|
|
k8s.autoPath.HostSearchPath = rc.Search
|
|
middleware.Zones(k8s.autoPath.HostSearchPath).Normalize()
|
|
continue
|
|
}
|
|
}
|
|
return k8s, nil
|
|
}
|
|
}
|
|
return nil, errors.New("kubernetes setup called without keyword 'kubernetes' in Corefile")
|
|
}
|
|
|
|
const (
|
|
defaultResyncPeriod = 5 * time.Minute
|
|
defautNdots = 0
|
|
defaultResolvConfFile = "/etc/resolv.conf"
|
|
defaultOnNXDOMAIN = dns.RcodeSuccess
|
|
)
|