diff options
author | 2018-06-30 20:49:13 +0530 | |
---|---|---|
committer | 2018-06-30 16:19:13 +0100 | |
commit | 6fe27d99be622f69ac0b1d402a67a571c6f6166e (patch) | |
tree | 8ad19accd3f1a59137b3116396518c1031bb4701 /plugin | |
parent | f3afd700210ffee655672b90b8cb78698f1d9e42 (diff) | |
download | coredns-6fe27d99be622f69ac0b1d402a67a571c6f6166e.tar.gz coredns-6fe27d99be622f69ac0b1d402a67a571c6f6166e.tar.zst coredns-6fe27d99be622f69ac0b1d402a67a571c6f6166e.zip |
plugin/etcdv3: Add etcd v3 plugin (#1702)
* Update dependencies and add etcdv3 client
* Update etcd plugin to support etcd v3 clients
Fixes #341
Diffstat (limited to 'plugin')
-rw-r--r-- | plugin/etcd/README.md | 10 | ||||
-rw-r--r-- | plugin/etcd/etcd.go | 102 | ||||
-rw-r--r-- | plugin/etcd/handler.go | 3 | ||||
-rw-r--r-- | plugin/etcd/lookup_test.go | 5 | ||||
-rw-r--r-- | plugin/etcd/setup.go | 14 |
5 files changed, 64 insertions, 70 deletions
diff --git a/plugin/etcd/README.md b/plugin/etcd/README.md index 2306729b2..e60292a6d 100644 --- a/plugin/etcd/README.md +++ b/plugin/etcd/README.md @@ -2,11 +2,11 @@ ## Name -*etcd* - enables reading zone data from an etcd instance. +*etcd* - enables reading zone data from an etcd version 3 instance. ## Description -The data in etcd has to be encoded as +The data in etcd instance has to be encoded as a [message](https://github.com/skynetservices/skydns/blob/2fcff74cdc9f9a7dd64189a447ef27ac354b725f/msg/service.go#L26) like [SkyDNS](https://github.com/skynetservices/skydns). It should also work just like SkyDNS. @@ -21,7 +21,7 @@ etcd [ZONES...] * **ZONES** zones etcd should be authoritative for. -The path will default to `/skydns` the local etcd proxy (http://localhost:2379). If no zones are +The path will default to `/skydns` the local etcd3 proxy (http://localhost:2379). If no zones are specified the block's zone will be used as the zone. If you want to `round robin` A and AAAA responses look at the `loadbalance` plugin. @@ -169,7 +169,3 @@ dig +short skydns.local AAAA @localhost 2003::8:1 2003::8:2 ~~~ - -## Bugs - -Only the etcdv2 protocol is supported. diff --git a/plugin/etcd/etcd.go b/plugin/etcd/etcd.go index fc4542bfe..493764f0b 100644 --- a/plugin/etcd/etcd.go +++ b/plugin/etcd/etcd.go @@ -1,9 +1,10 @@ -// Package etcd provides the etcd backend plugin. +// Package etcd provides the etcd version 3 backend plugin. package etcd import ( "context" "encoding/json" + "errors" "fmt" "strings" "time" @@ -15,10 +16,19 @@ import ( "github.com/coredns/coredns/request" "github.com/coredns/coredns/plugin/pkg/upstream" - etcdc "github.com/coreos/etcd/client" + etcdcv3 "github.com/coreos/etcd/clientv3" + "github.com/coreos/etcd/mvcc/mvccpb" "github.com/miekg/dns" ) +const ( + priority = 10 // default priority when nothing is set + ttl = 300 // default ttl when nothing is set + etcdTimeout = 5 * time.Second +) + +var errKeyNotFound = errors.New("Key not found") + // Etcd is a plugin talks to an etcd cluster. type Etcd struct { Next plugin.Handler @@ -26,7 +36,7 @@ type Etcd struct { Zones []string PathPrefix string Upstream upstream.Upstream // Proxy for looking up names during the resolution process - Client etcdc.KeysAPI + Client *etcdcv3.Client Ctx context.Context Stubmap *map[string]proxy.Proxy // list of proxies for stub resolving. @@ -56,10 +66,7 @@ func (e *Etcd) Lookup(state request.Request, name string, typ uint16) (*dns.Msg, // IsNameError implements the ServiceBackend interface. func (e *Etcd) IsNameError(err error) bool { - if ee, ok := err.(etcdc.Error); ok && ee.Code == etcdc.ErrorCodeKeyNotFound { - return true - } - return false + return err == errKeyNotFound } // Records looks up records in etcd. If exact is true, it will lookup just this @@ -73,51 +80,50 @@ func (e *Etcd) Records(state request.Request, exact bool) ([]msg.Service, error) return nil, err } segments := strings.Split(msg.Path(name, e.PathPrefix), "/") - switch { - case exact && r.Node.Dir: - return nil, nil - case r.Node.Dir: - return e.loopNodes(r.Node.Nodes, segments, star, nil) - default: - return e.loopNodes([]*etcdc.Node{r.Node}, segments, false, nil) - } + return e.loopNodes(r.Kvs, segments, star) } -// get is a wrapper for client.Get -func (e *Etcd) get(path string, recursive bool) (*etcdc.Response, error) { +func (e *Etcd) get(path string, recursive bool) (*etcdcv3.GetResponse, error) { ctx, cancel := context.WithTimeout(e.Ctx, etcdTimeout) defer cancel() - r, err := e.Client.Get(ctx, path, &etcdc.GetOptions{Sort: false, Recursive: recursive}) + if recursive == true { + if !strings.HasSuffix(path, "/") { + path = path + "/" + } + r, err := e.Client.Get(ctx, path, etcdcv3.WithPrefix()) + if err != nil { + return nil, err + } + if r.Count == 0 { + path = strings.TrimSuffix(path, "/") + r, err = e.Client.Get(ctx, path) + if err != nil { + return nil, err + } + if r.Count == 0 { + return nil, errKeyNotFound + } + } + return r, nil + } + + r, err := e.Client.Get(ctx, path) if err != nil { return nil, err } + if r.Count == 0 { + return nil, errKeyNotFound + } return r, nil } -// 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 (e *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) - } +func (e *Etcd) loopNodes(kv []*mvccpb.KeyValue, nameParts []string, star bool) (sx []msg.Service, err error) { + bx := make(map[msg.Service]bool) Nodes: - for _, n := range ns { - if n.Dir { - nodes, err := e.loopNodes(n.Nodes, nameParts, star, bx) - if err != nil { - return nil, err - } - sx = append(sx, nodes...) - continue - } + for _, n := range kv { if star { - keyParts := strings.Split(n.Key, "/") + s := string(n.Key) + keyParts := strings.Split(s, "/") for i, n := range nameParts { if i > len(keyParts)-1 { // name is longer than key @@ -132,16 +138,16 @@ Nodes: } } serv := new(msg.Service) - if err := json.Unmarshal([]byte(n.Value), serv); err != nil { + if err := json.Unmarshal(n.Value, serv); err != nil { return nil, fmt.Errorf("%s: %s", n.Key, err.Error()) } - b := msg.Service{Host: serv.Host, Port: serv.Port, Priority: serv.Priority, Weight: serv.Weight, Text: serv.Text, Key: n.Key} + b := msg.Service{Host: serv.Host, Port: serv.Port, Priority: serv.Priority, Weight: serv.Weight, Text: serv.Text, Key: string(n.Key)} if _, ok := bx[b]; ok { continue } bx[b] = true - serv.Key = n.Key + serv.Key = string(n.Key) serv.TTL = e.TTL(n, serv) if serv.Priority == 0 { serv.Priority = priority @@ -153,8 +159,8 @@ Nodes: // 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 (e *Etcd) TTL(node *etcdc.Node, serv *msg.Service) uint32 { - etcdTTL := uint32(node.TTL) +func (e *Etcd) TTL(kv *mvccpb.KeyValue, serv *msg.Service) uint32 { + etcdTTL := uint32(kv.Lease) if etcdTTL == 0 && serv.TTL == 0 { return ttl @@ -170,9 +176,3 @@ func (e *Etcd) TTL(node *etcdc.Node, serv *msg.Service) uint32 { } return serv.TTL } - -const ( - priority = 10 // default priority when nothing is set - ttl = 300 // default ttl when nothing is set - etcdTimeout = 5 * time.Second -) diff --git a/plugin/etcd/handler.go b/plugin/etcd/handler.go index 8520680d8..b70684165 100644 --- a/plugin/etcd/handler.go +++ b/plugin/etcd/handler.go @@ -65,8 +65,7 @@ func (e *Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ( // Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN _, err = plugin.A(e, zone, state, nil, opt) } - - if e.IsNameError(err) { + if err != nil && e.IsNameError(err) { if e.Fall.Through(state.Name()) { return plugin.NextOrFailure(e.Name(), e.Next, ctx, w, r) } diff --git a/plugin/etcd/lookup_test.go b/plugin/etcd/lookup_test.go index 51e4b954b..68f654d34 100644 --- a/plugin/etcd/lookup_test.go +++ b/plugin/etcd/lookup_test.go @@ -15,7 +15,6 @@ import ( "github.com/coredns/coredns/plugin/proxy" "github.com/coredns/coredns/plugin/test" - etcdc "github.com/coreos/etcd/client" "github.com/miekg/dns" ) @@ -300,12 +299,12 @@ func set(t *testing.T, e *Etcd, k string, ttl time.Duration, m *msg.Service) { t.Fatal(err) } path, _ := msg.PathWithWildcard(k, e.PathPrefix) - e.Client.Set(ctxt, path, string(b), &etcdc.SetOptions{TTL: ttl}) + e.Client.KV.Put(ctxt, path, string(b)) } func delete(t *testing.T, e *Etcd, k string) { path, _ := msg.PathWithWildcard(k, e.PathPrefix) - e.Client.Delete(ctxt, path, &etcdc.DeleteOptions{Recursive: false}) + e.Client.Delete(ctxt, path) } func TestLookup(t *testing.T) { diff --git a/plugin/etcd/setup.go b/plugin/etcd/setup.go index 0cc8b0553..7e03acf0d 100644 --- a/plugin/etcd/setup.go +++ b/plugin/etcd/setup.go @@ -11,7 +11,7 @@ import ( "github.com/coredns/coredns/plugin/pkg/upstream" "github.com/coredns/coredns/plugin/proxy" - etcdc "github.com/coreos/etcd/client" + etcdcv3 "github.com/coreos/etcd/clientv3" "github.com/mholt/caddy" ) @@ -124,22 +124,22 @@ func etcdParse(c *caddy.Controller) (*Etcd, bool, error) { } etc.Client = client etc.endpoints = endpoints - + return &etc, stubzones, nil } return &Etcd{}, false, nil } -func newEtcdClient(endpoints []string, cc *tls.Config) (etcdc.KeysAPI, error) { - etcdCfg := etcdc.Config{ +func newEtcdClient(endpoints []string, cc *tls.Config) (*etcdcv3.Client, error) { + etcdCfg := etcdcv3.Config{ Endpoints: endpoints, - Transport: mwtls.NewHTTPSTransport(cc), + TLS: cc, } - cli, err := etcdc.New(etcdCfg) + cli, err := etcdcv3.New(etcdCfg) if err != nil { return nil, err } - return etcdc.NewKeysAPI(cli), nil + return cli, nil } const defaultEndpoint = "http://localhost:2379" |