add a test to see if we copy the rcode correctly. Some minor cleanup in import ordering and renaming NewUpstream to New as we already are in the upstream package.
281 lines
6.4 KiB
Go
281 lines
6.4 KiB
Go
package kubernetes
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/coredns/coredns/core/dnsserver"
|
|
"github.com/coredns/coredns/plugin"
|
|
"github.com/coredns/coredns/plugin/pkg/dnsutil"
|
|
clog "github.com/coredns/coredns/plugin/pkg/log"
|
|
"github.com/coredns/coredns/plugin/pkg/parse"
|
|
"github.com/coredns/coredns/plugin/pkg/upstream"
|
|
|
|
"github.com/mholt/caddy"
|
|
"github.com/miekg/dns"
|
|
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
)
|
|
|
|
var log = clog.NewWithPlugin("kubernetes")
|
|
|
|
func init() {
|
|
// Kubernetes plugin uses the kubernetes library, which uses glog (ugh), we must set this *flag*,
|
|
// so we don't log to the filesystem, which can fill up and crash CoreDNS indirectly by calling os.Exit().
|
|
// We also set: os.Stderr = os.Stdout in the setup function below so we output to standard out; as we do for
|
|
// all CoreDNS logging. We can't do *that* in the init function, because we, when starting, also barf some
|
|
// things to stderr.
|
|
flag.Set("logtostderr", "true")
|
|
|
|
caddy.RegisterPlugin("kubernetes", caddy.Plugin{
|
|
ServerType: "dns",
|
|
Action: setup,
|
|
})
|
|
}
|
|
|
|
func setup(c *caddy.Controller) error {
|
|
// See comment in the init function.
|
|
os.Stderr = os.Stdout
|
|
|
|
k, err := kubernetesParse(c)
|
|
if err != nil {
|
|
return plugin.Error("kubernetes", err)
|
|
}
|
|
|
|
err = k.InitKubeCache()
|
|
if err != nil {
|
|
return plugin.Error("kubernetes", err)
|
|
}
|
|
|
|
k.RegisterKubeCache(c)
|
|
|
|
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
|
k.Next = next
|
|
return k
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// RegisterKubeCache registers KubeCache start and stop functions with Caddy
|
|
func (k *Kubernetes) RegisterKubeCache(c *caddy.Controller) {
|
|
c.OnStartup(func() error {
|
|
go k.APIConn.Run()
|
|
if k.APIProxy != nil {
|
|
k.APIProxy.Run()
|
|
}
|
|
synced := false
|
|
for synced == false {
|
|
synced = k.APIConn.HasSynced()
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
c.OnShutdown(func() error {
|
|
if k.APIProxy != nil {
|
|
k.APIProxy.Stop()
|
|
}
|
|
return k.APIConn.Stop()
|
|
})
|
|
}
|
|
|
|
func kubernetesParse(c *caddy.Controller) (*Kubernetes, error) {
|
|
var (
|
|
k8s *Kubernetes
|
|
err error
|
|
)
|
|
|
|
i := 0
|
|
for c.Next() {
|
|
if i > 0 {
|
|
return nil, plugin.ErrOnce
|
|
}
|
|
i++
|
|
|
|
k8s, err = ParseStanza(c)
|
|
if err != nil {
|
|
return k8s, err
|
|
}
|
|
}
|
|
return k8s, nil
|
|
}
|
|
|
|
// ParseStanza parses a kubernetes stanza
|
|
func ParseStanza(c *caddy.Controller) (*Kubernetes, error) {
|
|
|
|
k8s := New([]string{""})
|
|
k8s.interfaceAddrsFunc = localPodIP
|
|
k8s.autoPathSearch = searchFromResolvConf()
|
|
|
|
opts := dnsControlOpts{
|
|
initEndpointsCache: true,
|
|
ignoreEmptyService: false,
|
|
resyncPeriod: defaultResyncPeriod,
|
|
}
|
|
k8s.opts = opts
|
|
|
|
zones := c.RemainingArgs()
|
|
|
|
if len(zones) != 0 {
|
|
k8s.Zones = zones
|
|
for i := 0; i < len(k8s.Zones); i++ {
|
|
k8s.Zones[i] = plugin.Host(k8s.Zones[i]).Normalize()
|
|
}
|
|
} else {
|
|
k8s.Zones = make([]string, len(c.ServerBlockKeys))
|
|
for i := 0; i < len(c.ServerBlockKeys); i++ {
|
|
k8s.Zones[i] = plugin.Host(c.ServerBlockKeys[i]).Normalize()
|
|
}
|
|
}
|
|
|
|
k8s.primaryZoneIndex = -1
|
|
for i, z := range k8s.Zones {
|
|
if dnsutil.IsReverse(z) > 0 {
|
|
continue
|
|
}
|
|
k8s.primaryZoneIndex = i
|
|
break
|
|
}
|
|
|
|
if k8s.primaryZoneIndex == -1 {
|
|
return nil, errors.New("non-reverse zone name must be used")
|
|
}
|
|
|
|
for c.NextBlock() {
|
|
switch c.Val() {
|
|
case "endpoint_pod_names":
|
|
args := c.RemainingArgs()
|
|
if len(args) > 0 {
|
|
return nil, c.ArgErr()
|
|
}
|
|
k8s.endpointNameMode = true
|
|
continue
|
|
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 {
|
|
for _, a := range args {
|
|
k8s.Namespaces[a] = true
|
|
}
|
|
continue
|
|
}
|
|
return nil, c.ArgErr()
|
|
case "endpoint":
|
|
args := c.RemainingArgs()
|
|
if len(args) > 0 {
|
|
k8s.APIServerList = args
|
|
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.opts.resyncPeriod = rp
|
|
continue
|
|
}
|
|
return nil, c.ArgErr()
|
|
case "labels":
|
|
args := c.RemainingArgs()
|
|
if len(args) > 0 {
|
|
labelSelectorString := strings.Join(args, " ")
|
|
ls, err := meta.ParseToLabelSelector(labelSelectorString)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to parse label selector value: '%v': %v", labelSelectorString, err)
|
|
}
|
|
k8s.opts.labelSelector = ls
|
|
continue
|
|
}
|
|
return nil, c.ArgErr()
|
|
case "fallthrough":
|
|
k8s.Fall.SetZonesFromArgs(c.RemainingArgs())
|
|
case "upstream":
|
|
args := c.RemainingArgs()
|
|
u, err := upstream.New(args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
k8s.Upstream = u
|
|
case "ttl":
|
|
args := c.RemainingArgs()
|
|
if len(args) == 0 {
|
|
return nil, c.ArgErr()
|
|
}
|
|
t, err := strconv.Atoi(args[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if t < 5 || t > 3600 {
|
|
return nil, c.Errf("ttl must be in range [5, 3600]: %d", t)
|
|
}
|
|
k8s.ttl = uint32(t)
|
|
case "transfer":
|
|
tos, froms, err := parse.Transfer(c, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(froms) != 0 {
|
|
return nil, c.Errf("transfer from is not supported with this plugin")
|
|
}
|
|
k8s.TransferTo = tos
|
|
case "noendpoints":
|
|
if len(c.RemainingArgs()) != 0 {
|
|
return nil, c.ArgErr()
|
|
}
|
|
k8s.opts.initEndpointsCache = false
|
|
case "ignore":
|
|
args := c.RemainingArgs()
|
|
if len(args) > 0 {
|
|
ignore := args[0]
|
|
if ignore == "empty_service" {
|
|
k8s.opts.ignoreEmptyService = true
|
|
continue
|
|
} else {
|
|
return nil, fmt.Errorf("unable to parse ignore value: '%v'", ignore)
|
|
}
|
|
}
|
|
default:
|
|
return nil, c.Errf("unknown property '%s'", c.Val())
|
|
}
|
|
}
|
|
|
|
return k8s, nil
|
|
}
|
|
|
|
func searchFromResolvConf() []string {
|
|
rc, err := dns.ClientConfigFromFile("/etc/resolv.conf")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
plugin.Zones(rc.Search).Normalize()
|
|
return rc.Search
|
|
}
|
|
|
|
const defaultResyncPeriod = 5 * time.Minute
|