From fae1e26312764c4adcb96981da002459d09297af Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 29 Dec 2016 18:07:47 +0000 Subject: [PATCH 1/2] First pass at fixing stubzones --- backends/etcd/etcd.go | 31 +++++++++------- backends/etcd/etcd_test.go | 74 ++++++++++++++++++++++++++++++++++++++ server/backend.go | 6 ++-- server/server.go | 12 +++---- server/stub.go | 2 +- 5 files changed, 103 insertions(+), 22 deletions(-) create mode 100644 backends/etcd/etcd_test.go diff --git a/backends/etcd/etcd.go b/backends/etcd/etcd.go index 08e07e9b..32d1a3ab 100644 --- a/backends/etcd/etcd.go +++ b/backends/etcd/etcd.go @@ -42,7 +42,7 @@ func NewBackend(client etcd.KeysAPI, ctx context.Context, config *Config) *Backe } } -func (g *Backend) Records(name string, exact bool) ([]msg.Service, error) { +func (g *Backend) Records(name string, exact bool, stub bool) ([]msg.Service, error) { path, star := msg.PathWithWildcard(name) r, err := g.get(path, true) if err != nil { @@ -53,9 +53,9 @@ func (g *Backend) Records(name string, exact bool) ([]msg.Service, error) { case exact && r.Node.Dir: return nil, nil case r.Node.Dir: - return g.loopNodes(r.Node.Nodes, segments, star, nil) + return g.loopNodes(r.Node.Nodes, segments, star, stub, nil) default: - return g.loopNodes([]*etcd.Node{r.Node}, segments, false, nil) + return g.loopNodes([]*etcd.Node{r.Node}, segments, false, stub, nil) } } @@ -72,7 +72,7 @@ func (g *Backend) ReverseRecord(name string) (*msg.Service, error) { return nil, fmt.Errorf("reverse must not be a directory") } segments := strings.Split(msg.Path(name), "/") - records, err := g.loopNodes([]*etcd.Node{r.Node}, segments, false, nil) + records, err := g.loopNodes([]*etcd.Node{r.Node}, segments, false, false, nil) if err != nil { return nil, err } @@ -99,11 +99,12 @@ func (g *Backend) get(path string, recursive bool) (*etcd.Response, error) { } type bareService struct { - Host string - Port int - Priority int - Weight int - Text string + Host string + Port int + Priority int + Weight int + Text string + SubDomain string } // skydns/local/skydns/east/staging/web @@ -114,14 +115,14 @@ type bareService struct { // 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 *Backend) loopNodes(ns []*etcd.Node, nameParts []string, star bool, bx map[bareService]bool) (sx []msg.Service, err error) { +func (g *Backend) loopNodes(ns []*etcd.Node, nameParts []string, star bool, stub bool, bx map[bareService]bool) (sx []msg.Service, err error) { if bx == nil { bx = make(map[bareService]bool) } Nodes: for _, n := range ns { if n.Dir { - nodes, err := g.loopNodes(n.Nodes, nameParts, star, bx) + nodes, err := g.loopNodes(n.Nodes, nameParts, star, stub, bx) if err != nil { return nil, err } @@ -147,7 +148,13 @@ Nodes: if err := json.Unmarshal([]byte(n.Value), serv); err != nil { return nil, err } - b := bareService{serv.Host, serv.Port, serv.Priority, serv.Weight, serv.Text} + subdomain := "" + if stub { + index := strings.LastIndex(n.Key, "/") + subdomain = string(n.Key[0 : index]) + + } + b := bareService{serv.Host, serv.Port, serv.Priority, serv.Weight, serv.Text,subdomain} if _, ok := bx[b]; ok { continue } diff --git a/backends/etcd/etcd_test.go b/backends/etcd/etcd_test.go new file mode 100644 index 00000000..50092f75 --- /dev/null +++ b/backends/etcd/etcd_test.go @@ -0,0 +1,74 @@ +// Copyright (c) 2015 All Right Reserved, Improbable Worlds Ltd. + +package etcd + +import ( + "encoding/json" + "os" + "testing" + "time" + + etcd "github.com/coreos/etcd/client" + etcd_harness "github.com/mwitkow/go-etcd-harness" + "github.com/skynetservices/skydns/msg" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "golang.org/x/net/context" +) + +type etcdBackendTestSuite struct { + suite.Suite + etcdBackend *Backend +} + +func (s *etcdBackendTestSuite) SetupTest() { + s.etcdBackend.client.Delete(context.TODO(), "skydns", &etcd.DeleteOptions{Recursive: true, Dir: true}) +} + +func (s *etcdBackendTestSuite) TestGettingSingleRecordShouldReturnCorrectService() { + s.createEtcdRecord(msg.Service{Host: "1.1.1.1", Key: "/skydns/com/test"}) + records, err := s.etcdBackend.Records("test.com", true, false) + assert.NoError(s.T(), err, "no err") + assert.Equal(s.T(), 1, len(records)) + assert.Equal(s.T(), "1.1.1.1", records[0].Host) +} + +func (s *etcdBackendTestSuite) TestStubZone() { + s.createEtcdRecord(msg.Service{Host: "1.1.1.1", Key: "/skydns/com/test/dns/stub/com/othertest/a/stubzone1/a"}) + s.createEtcdRecord(msg.Service{Host: "1.1.1.2", Key: "/skydns/com/test/dns/stub/com/othertest/a/stubzone1/b"}) + s.createEtcdRecord(msg.Service{Host: "1.1.1.1", Key: "/skydns/com/test/dns/stub/com/othertest/stubzone2/b"}) + s.createEtcdRecord(msg.Service{Host: "1.1.1.2", Key: "/skydns/com/test/dns/stub/com/othertest/stubzone2/a"}) + records, err := s.etcdBackend.Records("stub.dns.test.com", false, true) + assert.NoError(s.T(), err) + assert.Equal(s.T(), 4, len(records)) +} + +func TestDeploymentServiceSuite(t *testing.T) { + if testing.Short() { + t.Skipf("DeploymentServiceSuite is a long integration test suite. Skipping due to test short.") + } + if !etcd_harness.LocalEtcdAvailable() { + t.Skipf("etcd is not available in $PATH, skipping suite") + } + + server, err := etcd_harness.New(os.Stderr) + if err != nil { + t.Fatalf("failed starting test server: %v", err) + } + t.Logf("will use etcd test endpoint: %v", server.Endpoint) + defer func() { + server.Stop() + t.Logf("cleaned up etcd test server") + }() + etcdClient := etcd.NewKeysAPI(server.Client) + suite.Run(t, &etcdBackendTestSuite{etcdBackend: NewBackend(etcdClient, context.TODO(), &Config{})}) +} + +func (s *etcdBackendTestSuite) createEtcdRecord(srv msg.Service) { + ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) + value, err := json.Marshal(srv) + require.NoError(s.T(), err, "marshaling etcd record should not fail") + _, err = s.etcdBackend.client.Create(ctx, srv.Key, string(value)) + require.NoError(s.T(), err, "creating etcd record should not fail") +} diff --git a/server/backend.go b/server/backend.go index bb3a8cdd..4beee239 100644 --- a/server/backend.go +++ b/server/backend.go @@ -7,7 +7,7 @@ package server import "github.com/skynetservices/skydns/msg" type Backend interface { - Records(name string, exact bool) ([]msg.Service, error) + Records(name string, exact bool, stub bool) ([]msg.Service, error) ReverseRecord(name string) (*msg.Service, error) } @@ -19,10 +19,10 @@ type FirstBackend []Backend // FirstBackend implements Backend var _ Backend = FirstBackend{} -func (g FirstBackend) Records(name string, exact bool) (records []msg.Service, err error) { +func (g FirstBackend) Records(name string, exact bool, stub bool) (records []msg.Service, err error) { var lastError error for _, backend := range g { - if records, err = backend.Records(name, exact); err == nil && len(records) > 0 { + if records, err = backend.Records(name, exact, stub); err == nil && len(records) > 0 { return records, nil } if err != nil { diff --git a/server/server.go b/server/server.go index b3c3843b..6f320591 100644 --- a/server/server.go +++ b/server/server.go @@ -419,7 +419,7 @@ func (s *server) ServeDNS(w dns.ResponseWriter, req *dns.Msg) { } func (s *server) AddressRecords(q dns.Question, name string, previousRecords []dns.RR, bufsize uint16, dnssec, both bool) (records []dns.RR, err error) { - services, err := s.backend.Records(name, false) + services, err := s.backend.Records(name, false, false) if err != nil { return nil, err } @@ -485,7 +485,7 @@ func (s *server) AddressRecords(q dns.Question, name string, previousRecords []d // NSRecords returns NS records from etcd. func (s *server) NSRecords(q dns.Question, name string) (records []dns.RR, extra []dns.RR, err error) { - services, err := s.backend.Records(name, false) + services, err := s.backend.Records(name, false, false) if err != nil { return nil, nil, err } @@ -513,7 +513,7 @@ func (s *server) NSRecords(q dns.Question, name string) (records []dns.RR, extra // SRVRecords returns SRV records from etcd. // If the Target is not a name but an IP address, a name is created. func (s *server) SRVRecords(q dns.Question, name string, bufsize uint16, dnssec bool) (records []dns.RR, extra []dns.RR, err error) { - services, err := s.backend.Records(name, false) + services, err := s.backend.Records(name, false, false) if err != nil { return nil, nil, err } @@ -598,7 +598,7 @@ func (s *server) SRVRecords(q dns.Question, name string, bufsize uint16, dnssec // MXRecords returns MX records from etcd. // If the Target is not a name but an IP address, a name is created. func (s *server) MXRecords(q dns.Question, name string, bufsize uint16, dnssec bool) (records []dns.RR, extra []dns.RR, err error) { - services, err := s.backend.Records(name, false) + services, err := s.backend.Records(name, false, false) if err != nil { return nil, nil, err } @@ -655,7 +655,7 @@ func (s *server) MXRecords(q dns.Question, name string, bufsize uint16, dnssec b } func (s *server) CNAMERecords(q dns.Question, name string) (records []dns.RR, err error) { - services, err := s.backend.Records(name, true) + services, err := s.backend.Records(name, true, false) if err != nil { return nil, err } @@ -672,7 +672,7 @@ func (s *server) CNAMERecords(q dns.Question, name string) (records []dns.RR, er } func (s *server) TXTRecords(q dns.Question, name string) (records []dns.RR, err error) { - services, err := s.backend.Records(name, false) + services, err := s.backend.Records(name, false, false) if err != nil { return nil, err } diff --git a/server/stub.go b/server/stub.go index f7223f4a..0abd008b 100644 --- a/server/stub.go +++ b/server/stub.go @@ -34,7 +34,7 @@ var ednsStub = func() *dns.OPT { func (s *server) UpdateStubZones() { stubmap := make(map[string][]string) - services, err := s.backend.Records("stub.dns."+s.config.Domain, false) + services, err := s.backend.Records("stub.dns."+s.config.Domain, false, true) if err != nil { logf("stub zone update failed: %s", err) return From 0a2eded530915e5c2a146a07a0539beae7632c75 Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 29 Dec 2016 18:30:56 +0000 Subject: [PATCH 2/2] build fixes --- backends/etcd/etcd.go | 10 +++++----- backends/etcd3/etcd3.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backends/etcd/etcd.go b/backends/etcd/etcd.go index 32d1a3ab..cd9adbcd 100644 --- a/backends/etcd/etcd.go +++ b/backends/etcd/etcd.go @@ -55,7 +55,7 @@ func (g *Backend) Records(name string, exact bool, stub bool) ([]msg.Service, er case r.Node.Dir: return g.loopNodes(r.Node.Nodes, segments, star, stub, nil) default: - return g.loopNodes([]*etcd.Node{r.Node}, segments, false, stub, nil) + return g.loopNodes([]*etcd.Node{r.Node}, segments, false, stub, nil) } } @@ -115,14 +115,14 @@ type bareService struct { // 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 *Backend) loopNodes(ns []*etcd.Node, nameParts []string, star bool, stub bool, bx map[bareService]bool) (sx []msg.Service, err error) { +func (g *Backend) loopNodes(ns []*etcd.Node, nameParts []string, star bool, stub bool, bx map[bareService]bool) (sx []msg.Service, err error) { if bx == nil { bx = make(map[bareService]bool) } Nodes: for _, n := range ns { if n.Dir { - nodes, err := g.loopNodes(n.Nodes, nameParts, star, stub, bx) + nodes, err := g.loopNodes(n.Nodes, nameParts, star, stub, bx) if err != nil { return nil, err } @@ -151,10 +151,10 @@ Nodes: subdomain := "" if stub { index := strings.LastIndex(n.Key, "/") - subdomain = string(n.Key[0 : index]) + subdomain = string(n.Key[0:index]) } - b := bareService{serv.Host, serv.Port, serv.Priority, serv.Weight, serv.Text,subdomain} + b := bareService{serv.Host, serv.Port, serv.Priority, serv.Weight, serv.Text, subdomain} if _, ok := bx[b]; ok { continue } diff --git a/backends/etcd3/etcd3.go b/backends/etcd3/etcd3.go index 5abff228..a653712a 100644 --- a/backends/etcd3/etcd3.go +++ b/backends/etcd3/etcd3.go @@ -40,7 +40,7 @@ func NewBackendv3 (client etcdv3.Client, ctx context.Context, config *Config) *B } } -func (g *Backendv3) Records(name string, exact bool) ([]msg.Service, error) { +func (g *Backendv3) Records(name string, exact bool, stub bool) ([]msg.Service, error) { path, star := msg.PathWithWildcard(name) r, err := g.get(path, true) if err != nil {