From 3b08db47dc05a212f35a6a0cacc9d48c2bec42b7 Mon Sep 17 00:00:00 2001 From: Max Timchenko Date: Wed, 27 Jul 2016 18:11:42 -0400 Subject: [PATCH 1/3] Corrects two issues with DNS prepared query failover When DNS prepared query fails over to another datacenter and the datacenter returns some nodes, the DNS result does not translate WAN addresses even when translation is enabled for SRV and A queries, and returns the wrong datacenter (the one that failed over) for SRV queries. --- command/agent/dns.go | 6 +- command/agent/dns_test.go | 123 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/command/agent/dns.go b/command/agent/dns.go index 59d0601c18..26ff492d3b 100644 --- a/command/agent/dns.go +++ b/command/agent/dns.go @@ -744,10 +744,10 @@ RPC: // Add various responses depending on the request. qType := req.Question[0].Qtype - if qType == dns.TypeSRV { - d.serviceSRVRecords(datacenter, out.Nodes, req, resp, ttl) +if qType == dns.TypeSRV { + d.serviceSRVRecords(out.Datacenter, out.Nodes, req, resp, ttl) } else { - d.serviceNodeRecords(datacenter, out.Nodes, req, resp, ttl) + d.serviceNodeRecords(out.Datacenter, out.Nodes, req, resp, ttl) } // If the network is not TCP, restrict the number of responses. diff --git a/command/agent/dns_test.go b/command/agent/dns_test.go index 8d0165d4c1..abc5050bf2 100644 --- a/command/agent/dns_test.go +++ b/command/agent/dns_test.go @@ -2737,6 +2737,129 @@ func TestDNS_PreparedQuery_TTL(t *testing.T) { } } +func TestDNS_PreparedQuery_Failover(t *testing.T) { + dir1, srv1 := makeDNSServerConfig(t, func(c *Config) { + c.Datacenter = "dc1" + c.TranslateWanAddrs = true + }, nil) + defer os.RemoveAll(dir1) + defer srv1.Shutdown() + + dir2, srv2 := makeDNSServerConfig(t, func(c *Config) { + c.Datacenter = "dc2" + c.TranslateWanAddrs = true + }, nil) + defer os.RemoveAll(dir2) + defer srv2.Shutdown() + + testutil.WaitForLeader(t, srv1.agent.RPC, "dc1") + testutil.WaitForLeader(t, srv2.agent.RPC, "dc2") + + // Join WAN cluster + addr := fmt.Sprintf("127.0.0.1:%d", + srv1.agent.config.Ports.SerfWan) + if _, err := srv2.agent.JoinWAN([]string{addr}); err != nil { + t.Fatalf("err: %v", err) + } + + testutil.WaitForResult( + func() (bool, error) { + return len(srv1.agent.WANMembers()) > 1, nil + }, + func(err error) { + t.Fatalf("Failed waiting for WAN join: %v", err) + }) + + // Register a remote node with a service. + { + args := &structs.RegisterRequest{ + Datacenter: "dc2", + Node: "foo", + Address: "127.0.0.1", + TaggedAddresses: map[string]string{ + "wan": "127.0.0.2", + }, + Service: &structs.NodeService{ + Service: "db", + }, + } + + var out struct{} + if err := srv2.agent.RPC("Catalog.Register", args, &out); err != nil { + t.Fatalf("err: %v", err) + } + } + + // Register an equivalent prepared query in both DCs. + var id_dc1 string + { + args := &structs.PreparedQueryRequest{ + Datacenter: "dc1", + Op: structs.PreparedQueryCreate, + Query: &structs.PreparedQuery{ + Name: "my-query", + Service: structs.ServiceQuery{ + Service: "db", + Failover: structs.QueryDatacenterOptions{ + NearestN: 1, + Datacenters: []string{"dc2"}, + }, + }, + }, + } + if err := srv1.agent.RPC("PreparedQuery.Apply", args, &id_dc1); err != nil { + t.Fatalf("err: %v", err) + } + } + + var id_dc2 string + { + args := &structs.PreparedQueryRequest{ + Datacenter: "dc2", + Op: structs.PreparedQueryCreate, + Query: &structs.PreparedQuery{ + Name: "my-query", + Service: structs.ServiceQuery{ + Service: "db", + Failover: structs.QueryDatacenterOptions{ + NearestN: 1, + Datacenters: []string{"dc1"}, + }, + }, + }, + } + if err := srv2.agent.RPC("PreparedQuery.Apply", args, &id_dc2); err != nil { + t.Fatalf("err: %v", err) + } + } + + // Look up the SRV record via the query + m := new(dns.Msg) + m.SetQuestion("my-query.query.consul.", dns.TypeSRV) + + c := new(dns.Client) + cl_addr, _ := srv1.agent.config.ClientListener("", srv1.agent.config.Ports.DNS) + in, _, err := c.Exchange(m, cl_addr.String()) + if err != nil { + t.Fatalf("err: %v", err) + } + + if len(in.Answer) != 1 { + t.Fatalf("Bad: %#v", in) + } + + aRec, ok := in.Extra[0].(*dns.A) + if !ok { + t.Fatalf("Bad: %#v", in.Extra[0]) + } + if aRec.Hdr.Name != "foo.node.dc2.consul." { + t.Fatalf("Bad: %#v", in.Extra[0]) + } + if aRec.A.String() != "127.0.0.2" { + t.Fatalf("Bad: %#v", in.Extra[0]) + } +} + func TestDNS_ServiceLookup_SRV_RFC(t *testing.T) { dir, srv := makeDNSServer(t) defer os.RemoveAll(dir) From a53c6a38e21dec99f2146b0bd494dc5bec20cddd Mon Sep 17 00:00:00 2001 From: James Phillips Date: Fri, 12 Aug 2016 17:16:08 -0700 Subject: [PATCH 2/3] Tweaks DNS prepared query failover unit test. --- command/agent/dns_test.go | 57 ++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/command/agent/dns_test.go b/command/agent/dns_test.go index abc5050bf2..60f0dac255 100644 --- a/command/agent/dns_test.go +++ b/command/agent/dns_test.go @@ -2739,9 +2739,9 @@ func TestDNS_PreparedQuery_TTL(t *testing.T) { func TestDNS_PreparedQuery_Failover(t *testing.T) { dir1, srv1 := makeDNSServerConfig(t, func(c *Config) { - c.Datacenter = "dc1" - c.TranslateWanAddrs = true - }, nil) + c.Datacenter = "dc1" + c.TranslateWanAddrs = true + }, nil) defer os.RemoveAll(dir1) defer srv1.Shutdown() @@ -2755,13 +2755,12 @@ func TestDNS_PreparedQuery_Failover(t *testing.T) { testutil.WaitForLeader(t, srv1.agent.RPC, "dc1") testutil.WaitForLeader(t, srv2.agent.RPC, "dc2") - // Join WAN cluster + // Join WAN cluster. addr := fmt.Sprintf("127.0.0.1:%d", srv1.agent.config.Ports.SerfWan) if _, err := srv2.agent.JoinWAN([]string{addr}); err != nil { t.Fatalf("err: %v", err) } - testutil.WaitForResult( func() (bool, error) { return len(srv1.agent.WANMembers()) > 1, nil @@ -2790,8 +2789,7 @@ func TestDNS_PreparedQuery_Failover(t *testing.T) { } } - // Register an equivalent prepared query in both DCs. - var id_dc1 string + // Register a local prepared query. { args := &structs.PreparedQueryRequest{ Datacenter: "dc1", @@ -2801,39 +2799,18 @@ func TestDNS_PreparedQuery_Failover(t *testing.T) { Service: structs.ServiceQuery{ Service: "db", Failover: structs.QueryDatacenterOptions{ - NearestN: 1, Datacenters: []string{"dc2"}, }, }, }, } - if err := srv1.agent.RPC("PreparedQuery.Apply", args, &id_dc1); err != nil { + var id string + if err := srv1.agent.RPC("PreparedQuery.Apply", args, &id); err != nil { t.Fatalf("err: %v", err) } } - var id_dc2 string - { - args := &structs.PreparedQueryRequest{ - Datacenter: "dc2", - Op: structs.PreparedQueryCreate, - Query: &structs.PreparedQuery{ - Name: "my-query", - Service: structs.ServiceQuery{ - Service: "db", - Failover: structs.QueryDatacenterOptions{ - NearestN: 1, - Datacenters: []string{"dc1"}, - }, - }, - }, - } - if err := srv2.agent.RPC("PreparedQuery.Apply", args, &id_dc2); err != nil { - t.Fatalf("err: %v", err) - } - } - - // Look up the SRV record via the query + // Look up the SRV record via the query. m := new(dns.Msg) m.SetQuestion("my-query.query.consul.", dns.TypeSRV) @@ -2844,18 +2821,30 @@ func TestDNS_PreparedQuery_Failover(t *testing.T) { t.Fatalf("err: %v", err) } + // Make sure we see the remote DC and that the address gets + // translated. if len(in.Answer) != 1 { t.Fatalf("Bad: %#v", in) } + if in.Answer[0].Header().Name != "my-query.query.consul." { + t.Fatalf("Bad: %#v", in.Answer[0]) + } + srv, ok := in.Answer[0].(*dns.SRV) + if !ok { + t.Fatalf("Bad: %#v", in.Answer[0]) + } + if srv.Target != "foo.node.dc2.consul." { + t.Fatalf("Bad: %#v", in.Answer[0]) + } - aRec, ok := in.Extra[0].(*dns.A) + a, ok := in.Extra[0].(*dns.A) if !ok { t.Fatalf("Bad: %#v", in.Extra[0]) } - if aRec.Hdr.Name != "foo.node.dc2.consul." { + if a.Hdr.Name != "foo.node.dc2.consul." { t.Fatalf("Bad: %#v", in.Extra[0]) } - if aRec.A.String() != "127.0.0.2" { + if a.A.String() != "127.0.0.2" { t.Fatalf("Bad: %#v", in.Extra[0]) } } From 456a4934f0dfe4463f61f532ba5e4a40f8310485 Mon Sep 17 00:00:00 2001 From: James Phillips Date: Fri, 12 Aug 2016 17:26:23 -0700 Subject: [PATCH 3/3] Runs `go fmt`. --- command/agent/dns.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/agent/dns.go b/command/agent/dns.go index 26ff492d3b..3755bc0502 100644 --- a/command/agent/dns.go +++ b/command/agent/dns.go @@ -744,7 +744,7 @@ RPC: // Add various responses depending on the request. qType := req.Question[0].Qtype -if qType == dns.TypeSRV { + if qType == dns.TypeSRV { d.serviceSRVRecords(out.Datacenter, out.Nodes, req, resp, ttl) } else { d.serviceNodeRecords(out.Datacenter, out.Nodes, req, resp, ttl)