aboutsummaryrefslogtreecommitdiff
path: root/plugin
diff options
context:
space:
mode:
authorGravatar Nitish Tiwari <nitish@minio.io> 2018-06-30 20:49:13 +0530
committerGravatar Miek Gieben <miek@miek.nl> 2018-06-30 16:19:13 +0100
commit6fe27d99be622f69ac0b1d402a67a571c6f6166e (patch)
tree8ad19accd3f1a59137b3116396518c1031bb4701 /plugin
parentf3afd700210ffee655672b90b8cb78698f1d9e42 (diff)
downloadcoredns-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.md10
-rw-r--r--plugin/etcd/etcd.go102
-rw-r--r--plugin/etcd/handler.go3
-rw-r--r--plugin/etcd/lookup_test.go5
-rw-r--r--plugin/etcd/setup.go14
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"