Files
coredns/middleware/etcd/etcd.go

153 lines
3.6 KiB
Go
Raw Normal View History

2016-03-20 17:44:58 +00:00
// Package etcd provides the etcd backend.
package etcd
import (
2016-03-20 21:36:55 +00:00
"encoding/json"
"strings"
2016-03-20 17:54:21 +00:00
"github.com/miekg/coredns/middleware"
2016-03-20 21:36:55 +00:00
"github.com/miekg/coredns/middleware/etcd/msg"
"github.com/miekg/coredns/middleware/etcd/singleflight"
2016-03-20 17:44:58 +00:00
2016-03-20 18:17:07 +00:00
etcdc "github.com/coreos/etcd/client"
2016-03-20 17:44:58 +00:00
"golang.org/x/net/context"
)
type (
Etcd struct {
2016-03-21 07:20:22 +00:00
Next middleware.Handler
Zones []string
client etcdc.KeysAPI
ctx context.Context
inflight *singleflight.Group
PathPrefix string
2016-03-20 17:44:58 +00:00
}
)
2016-03-20 21:36:55 +00:00
func NewEtcd(client etcdc.KeysAPI, next middleware.Handler, zones []string) Etcd {
2016-03-20 17:54:21 +00:00
return Etcd{
2016-03-21 07:20:22 +00:00
Next: next,
Zones: zones,
client: client,
ctx: context.Background(),
inflight: &singleflight.Group{},
PathPrefix: "skydns", // TODO(miek): configurable
2016-03-20 17:44:58 +00:00
}
}
2016-03-20 18:17:07 +00:00
2016-03-20 21:36:55 +00:00
func (g Etcd) Records(name string, exact bool) ([]msg.Service, error) {
2016-03-21 07:20:22 +00:00
path, star := g.PathWithWildcard(name)
2016-03-20 21:36:55 +00:00
r, err := g.Get(path, true)
if err != nil {
return nil, err
}
2016-03-21 07:20:22 +00:00
segments := strings.Split(g.Path(name), "/")
2016-03-20 21:36:55 +00:00
switch {
case exact && r.Node.Dir:
return nil, nil
case r.Node.Dir:
return g.loopNodes(r.Node.Nodes, segments, star, nil)
default:
return g.loopNodes([]*etcdc.Node{r.Node}, segments, false, nil)
}
}
// Get is a wrapper for client.Get that uses SingleInflight to suppress multiple outstanding queries.
func (g Etcd) Get(path string, recursive bool) (*etcdc.Response, error) {
resp, err := g.inflight.Do(path, func() (interface{}, error) {
r, e := g.client.Get(g.ctx, path, &etcdc.GetOptions{Sort: false, Recursive: recursive})
if e != nil {
return nil, e
}
return r, e
})
if err != nil {
return nil, err
}
return resp.(*etcdc.Response), err
}
// skydns/local/skydns/east/staging/web
// skydns/local/skydns/west/production/web
//
// skydns/local/skydns/*/*/web
// skydns/local/skydns/*/web
// loopNodes recursively loops through the nodes and returns all the values. The nodes' keyname
// will be match against any wildcards when star is true.
func (g Etcd) loopNodes(ns []*etcdc.Node, nameParts []string, star bool, bx map[msg.Service]bool) (sx []msg.Service, err error) {
if bx == nil {
bx = make(map[msg.Service]bool)
}
Nodes:
for _, n := range ns {
if n.Dir {
nodes, err := g.loopNodes(n.Nodes, nameParts, star, bx)
if err != nil {
return nil, err
}
sx = append(sx, nodes...)
continue
}
if star {
keyParts := strings.Split(n.Key, "/")
for i, n := range nameParts {
if i > len(keyParts)-1 {
// name is longer than key
continue Nodes
}
if n == "*" || n == "any" {
continue
}
if keyParts[i] != n {
continue Nodes
}
}
}
serv := new(msg.Service)
if err := json.Unmarshal([]byte(n.Value), serv); err != nil {
return nil, err
}
b := msg.Service{Host: serv.Host, Port: serv.Port, Priority: serv.Priority, Weight: serv.Weight, Text: serv.Text}
if _, ok := bx[b]; ok {
continue
}
bx[b] = true
serv.Key = n.Key
serv.Ttl = g.Ttl(n, serv)
if serv.Priority == 0 {
serv.Priority = priority
}
sx = append(sx, *serv)
}
return sx, nil
}
// Ttl returns the smaller of the etcd TTL and the service's
// TTL. If neither of these are set (have a zero value), a default is used.
func (g Etcd) Ttl(node *etcdc.Node, serv *msg.Service) uint32 {
etcdTtl := uint32(node.TTL)
if etcdTtl == 0 && serv.Ttl == 0 {
return ttl
}
if etcdTtl == 0 {
return serv.Ttl
}
if serv.Ttl == 0 {
return etcdTtl
}
if etcdTtl < serv.Ttl {
return etcdTtl
}
return serv.Ttl
2016-03-20 18:17:07 +00:00
}
2016-03-20 21:36:55 +00:00
const (
priority = 10 // default priority when nothing is set
ttl = 300 // default ttl when nothing is set
minTtl = 60
hostmaster = "hostmaster"
)