Plumbs the service name back and uses agent-specific TTL settings as a fallback.

This commit is contained in:
James Phillips 2015-11-13 10:38:44 -08:00
parent 81b43135f9
commit e9480ecb02
5 changed files with 107 additions and 21 deletions

View File

@ -587,13 +587,10 @@ RPC:
goto RPC goto RPC
} }
// TODO (slackpad) Do we want to apply the DNS server's per-service TTL
// configs if the query's TTL is not set? That seems like it adds a lot
// of complexity (we'd have to plumb the service name back with the query
// results to do this), but is it what people would expect?
// Determine the TTL. The parse should never fail since we vet it when // Determine the TTL. The parse should never fail since we vet it when
// the query is created, but we check anyway. // the query is created, but we check anyway. If the query didn't
// specify a TTL then we will try to use the agent's service-specific
// TTL configs.
var ttl time.Duration var ttl time.Duration
if out.DNS.TTL != "" { if out.DNS.TTL != "" {
var err error var err error
@ -601,6 +598,12 @@ RPC:
if err != nil { if err != nil {
d.logger.Printf("[WARN] dns: Failed to parse TTL '%s' for prepared query '%s', ignoring", out.DNS.TTL, query) d.logger.Printf("[WARN] dns: Failed to parse TTL '%s' for prepared query '%s', ignoring", out.DNS.TTL, query)
} }
} else if d.config.ServiceTTL != nil {
var ok bool
ttl, ok = d.config.ServiceTTL[out.Service]
if !ok {
ttl = d.config.ServiceTTL["*"]
}
} }
// If we have no nodes, return not found! // If we have no nodes, return not found!

View File

@ -2087,7 +2087,6 @@ func TestDNS_ServiceLookup_TTL(t *testing.T) {
} }
c.AllowStale = true c.AllowStale = true
c.MaxStale = time.Second c.MaxStale = time.Second
} }
dir, srv := makeDNSServerConfig(t, nil, confFn) dir, srv := makeDNSServerConfig(t, nil, confFn)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
@ -2184,7 +2183,15 @@ func TestDNS_ServiceLookup_TTL(t *testing.T) {
} }
func TestDNS_PreparedQuery_TTL(t *testing.T) { func TestDNS_PreparedQuery_TTL(t *testing.T) {
dir, srv := makeDNSServer(t) confFn := func(c *DNSConfig) {
c.ServiceTTL = map[string]time.Duration{
"db": 10 * time.Second,
"*": 5 * time.Second,
}
c.AllowStale = true
c.MaxStale = time.Second
}
dir, srv := makeDNSServerConfig(t, nil, confFn)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
defer srv.agent.Shutdown() defer srv.agent.Shutdown()
@ -2207,20 +2214,34 @@ func TestDNS_PreparedQuery_TTL(t *testing.T) {
if err := srv.agent.RPC("Catalog.Register", args, &out); err != nil { if err := srv.agent.RPC("Catalog.Register", args, &out); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
args = &structs.RegisterRequest{
Datacenter: "dc1",
Node: "foo",
Address: "127.0.0.1",
Service: &structs.NodeService{
Service: "api",
Port: 2222,
},
}
if err := srv.agent.RPC("Catalog.Register", args, &out); err != nil {
t.Fatalf("err: %v", err)
}
} }
// Register prepared queries with and without a TTL set. // Register prepared queries with and without a TTL set for "db", as
// well as one for "api".
{ {
args := &structs.PreparedQueryRequest{ args := &structs.PreparedQueryRequest{
Datacenter: "dc1", Datacenter: "dc1",
Op: structs.PreparedQueryCreate, Op: structs.PreparedQueryCreate,
Query: &structs.PreparedQuery{ Query: &structs.PreparedQuery{
Name: "ttl", Name: "db-ttl",
Service: structs.ServiceQuery{ Service: structs.ServiceQuery{
Service: "db", Service: "db",
}, },
DNS: structs.QueryDNSOptions{ DNS: structs.QueryDNSOptions{
TTL: "10s", TTL: "18s",
}, },
}, },
} }
@ -2234,7 +2255,7 @@ func TestDNS_PreparedQuery_TTL(t *testing.T) {
Datacenter: "dc1", Datacenter: "dc1",
Op: structs.PreparedQueryCreate, Op: structs.PreparedQueryCreate,
Query: &structs.PreparedQuery{ Query: &structs.PreparedQuery{
Name: "nottl", Name: "db-nottl",
Service: structs.ServiceQuery{ Service: structs.ServiceQuery{
Service: "db", Service: "db",
}, },
@ -2244,11 +2265,27 @@ func TestDNS_PreparedQuery_TTL(t *testing.T) {
if err := srv.agent.RPC("PreparedQuery.Apply", args, &id); err != nil { if err := srv.agent.RPC("PreparedQuery.Apply", args, &id); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
args = &structs.PreparedQueryRequest{
Datacenter: "dc1",
Op: structs.PreparedQueryCreate,
Query: &structs.PreparedQuery{
Name: "api-nottl",
Service: structs.ServiceQuery{
Service: "api",
},
},
} }
// Make sure the TTL is set when requested. if err := srv.agent.RPC("PreparedQuery.Apply", args, &id); err != nil {
t.Fatalf("err: %v", err)
}
}
// Make sure the TTL is set when requested, and overrides the agent-
// specific config since the query takes precedence.
m := new(dns.Msg) m := new(dns.Msg)
m.SetQuestion("ttl.query.consul.", dns.TypeSRV) m.SetQuestion("db-ttl.query.consul.", dns.TypeSRV)
c := new(dns.Client) c := new(dns.Client)
addr, _ := srv.agent.config.ClientListener("", srv.agent.config.Ports.DNS) addr, _ := srv.agent.config.ClientListener("", srv.agent.config.Ports.DNS)
@ -2265,7 +2302,7 @@ func TestDNS_PreparedQuery_TTL(t *testing.T) {
if !ok { if !ok {
t.Fatalf("Bad: %#v", in.Answer[0]) t.Fatalf("Bad: %#v", in.Answer[0])
} }
if srvRec.Hdr.Ttl != 10 { if srvRec.Hdr.Ttl != 18 {
t.Fatalf("Bad: %#v", in.Answer[0]) t.Fatalf("Bad: %#v", in.Answer[0])
} }
@ -2273,13 +2310,14 @@ func TestDNS_PreparedQuery_TTL(t *testing.T) {
if !ok { if !ok {
t.Fatalf("Bad: %#v", in.Extra[0]) t.Fatalf("Bad: %#v", in.Extra[0])
} }
if aRec.Hdr.Ttl != 10 { if aRec.Hdr.Ttl != 18 {
t.Fatalf("Bad: %#v", in.Extra[0]) t.Fatalf("Bad: %#v", in.Extra[0])
} }
// And the TTL should default to 0 otherwise. // And the TTL should take the service-specific value from the agent's
// config otherwise.
m = new(dns.Msg) m = new(dns.Msg)
m.SetQuestion("nottl.query.consul.", dns.TypeSRV) m.SetQuestion("db-nottl.query.consul.", dns.TypeSRV)
in, _, err = c.Exchange(m, addr.String()) in, _, err = c.Exchange(m, addr.String())
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
@ -2293,7 +2331,7 @@ func TestDNS_PreparedQuery_TTL(t *testing.T) {
if !ok { if !ok {
t.Fatalf("Bad: %#v", in.Answer[0]) t.Fatalf("Bad: %#v", in.Answer[0])
} }
if srvRec.Hdr.Ttl != 0 { if srvRec.Hdr.Ttl != 10 {
t.Fatalf("Bad: %#v", in.Answer[0]) t.Fatalf("Bad: %#v", in.Answer[0])
} }
@ -2301,7 +2339,36 @@ func TestDNS_PreparedQuery_TTL(t *testing.T) {
if !ok { if !ok {
t.Fatalf("Bad: %#v", in.Extra[0]) t.Fatalf("Bad: %#v", in.Extra[0])
} }
if aRec.Hdr.Ttl != 0 { if aRec.Hdr.Ttl != 10 {
t.Fatalf("Bad: %#v", in.Extra[0])
}
// If there's no query TTL and no service-specific value then the wild
// card value should be used.
m = new(dns.Msg)
m.SetQuestion("api-nottl.query.consul.", dns.TypeSRV)
in, _, err = c.Exchange(m, addr.String())
if err != nil {
t.Fatalf("err: %v", err)
}
if len(in.Answer) != 1 {
t.Fatalf("Bad: %#v", in)
}
srvRec, ok = in.Answer[0].(*dns.SRV)
if !ok {
t.Fatalf("Bad: %#v", in.Answer[0])
}
if srvRec.Hdr.Ttl != 5 {
t.Fatalf("Bad: %#v", in.Answer[0])
}
aRec, ok = in.Extra[0].(*dns.A)
if !ok {
t.Fatalf("Bad: %#v", in.Extra[0])
}
if aRec.Hdr.Ttl != 5 {
t.Fatalf("Bad: %#v", in.Extra[0]) t.Fatalf("Bad: %#v", in.Extra[0])
} }
} }

View File

@ -386,6 +386,7 @@ func (p *PreparedQuery) execute(query *structs.PreparedQuery,
} }
// Capture the nodes and pass the DNS information through to the reply. // Capture the nodes and pass the DNS information through to the reply.
reply.Service = query.Service.Service
reply.Nodes = nodes reply.Nodes = nodes
reply.DNS = query.DNS reply.DNS = query.DNS

View File

@ -1100,6 +1100,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
if len(reply.Nodes) != 10 || if len(reply.Nodes) != 10 ||
reply.Datacenter != "dc1" || reply.Failovers != 0 || reply.Datacenter != "dc1" || reply.Failovers != 0 ||
reply.Service != query.Query.Service.Service ||
!reflect.DeepEqual(reply.DNS, query.Query.DNS) || !reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
!reply.QueryMeta.KnownLeader { !reply.QueryMeta.KnownLeader {
t.Fatalf("bad: %v", reply) t.Fatalf("bad: %v", reply)
@ -1121,6 +1122,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
if len(reply.Nodes) != 3 || if len(reply.Nodes) != 3 ||
reply.Datacenter != "dc1" || reply.Failovers != 0 || reply.Datacenter != "dc1" || reply.Failovers != 0 ||
reply.Service != query.Query.Service.Service ||
!reflect.DeepEqual(reply.DNS, query.Query.DNS) || !reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
!reply.QueryMeta.KnownLeader { !reply.QueryMeta.KnownLeader {
t.Fatalf("bad: %v", reply) t.Fatalf("bad: %v", reply)
@ -1162,6 +1164,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
if len(reply.Nodes) != 10 || if len(reply.Nodes) != 10 ||
reply.Datacenter != "dc1" || reply.Failovers != 0 || reply.Datacenter != "dc1" || reply.Failovers != 0 ||
reply.Service != query.Query.Service.Service ||
!reflect.DeepEqual(reply.DNS, query.Query.DNS) || !reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
!reply.QueryMeta.KnownLeader { !reply.QueryMeta.KnownLeader {
t.Fatalf("bad: %v", reply) t.Fatalf("bad: %v", reply)
@ -1186,6 +1189,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
if len(reply.Nodes) != 10 || if len(reply.Nodes) != 10 ||
reply.Datacenter != "dc1" || reply.Failovers != 0 || reply.Datacenter != "dc1" || reply.Failovers != 0 ||
reply.Service != query.Query.Service.Service ||
!reflect.DeepEqual(reply.DNS, query.Query.DNS) || !reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
!reply.QueryMeta.KnownLeader { !reply.QueryMeta.KnownLeader {
t.Fatalf("bad: %v", reply) t.Fatalf("bad: %v", reply)
@ -1244,6 +1248,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
if len(reply.Nodes) != 9 || if len(reply.Nodes) != 9 ||
reply.Datacenter != "dc1" || reply.Failovers != 0 || reply.Datacenter != "dc1" || reply.Failovers != 0 ||
reply.Service != query.Query.Service.Service ||
!reflect.DeepEqual(reply.DNS, query.Query.DNS) || !reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
!reply.QueryMeta.KnownLeader { !reply.QueryMeta.KnownLeader {
t.Fatalf("bad: %v", reply) t.Fatalf("bad: %v", reply)
@ -1270,6 +1275,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
if len(reply.Nodes) != 10 || if len(reply.Nodes) != 10 ||
reply.Datacenter != "dc1" || reply.Failovers != 0 || reply.Datacenter != "dc1" || reply.Failovers != 0 ||
reply.Service != query.Query.Service.Service ||
!reflect.DeepEqual(reply.DNS, query.Query.DNS) || !reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
!reply.QueryMeta.KnownLeader { !reply.QueryMeta.KnownLeader {
t.Fatalf("bad: %v", reply) t.Fatalf("bad: %v", reply)
@ -1297,6 +1303,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
if len(reply.Nodes) != 9 || if len(reply.Nodes) != 9 ||
reply.Datacenter != "dc1" || reply.Failovers != 0 || reply.Datacenter != "dc1" || reply.Failovers != 0 ||
reply.Service != query.Query.Service.Service ||
!reflect.DeepEqual(reply.DNS, query.Query.DNS) || !reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
!reply.QueryMeta.KnownLeader { !reply.QueryMeta.KnownLeader {
t.Fatalf("bad: %v", reply) t.Fatalf("bad: %v", reply)
@ -1331,6 +1338,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
if len(reply.Nodes) != 8 || if len(reply.Nodes) != 8 ||
reply.Datacenter != "dc1" || reply.Failovers != 0 || reply.Datacenter != "dc1" || reply.Failovers != 0 ||
reply.Service != query.Query.Service.Service ||
!reflect.DeepEqual(reply.DNS, query.Query.DNS) || !reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
!reply.QueryMeta.KnownLeader { !reply.QueryMeta.KnownLeader {
t.Fatalf("bad: %v", reply) t.Fatalf("bad: %v", reply)
@ -1359,6 +1367,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
if len(reply.Nodes) != 0 || if len(reply.Nodes) != 0 ||
reply.Datacenter != "dc1" || reply.Failovers != 0 || reply.Datacenter != "dc1" || reply.Failovers != 0 ||
reply.Service != query.Query.Service.Service ||
!reflect.DeepEqual(reply.DNS, query.Query.DNS) || !reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
!reply.QueryMeta.KnownLeader { !reply.QueryMeta.KnownLeader {
t.Fatalf("bad: %v", reply) t.Fatalf("bad: %v", reply)
@ -1385,6 +1394,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
if len(reply.Nodes) != 9 || if len(reply.Nodes) != 9 ||
reply.Datacenter != "dc2" || reply.Failovers != 1 || reply.Datacenter != "dc2" || reply.Failovers != 1 ||
reply.Service != query.Query.Service.Service ||
!reflect.DeepEqual(reply.DNS, query.Query.DNS) || !reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
!reply.QueryMeta.KnownLeader { !reply.QueryMeta.KnownLeader {
t.Fatalf("bad: %v", reply) t.Fatalf("bad: %v", reply)
@ -1412,6 +1422,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
if len(reply.Nodes) != 3 || if len(reply.Nodes) != 3 ||
reply.Datacenter != "dc2" || reply.Failovers != 1 || reply.Datacenter != "dc2" || reply.Failovers != 1 ||
reply.Service != query.Query.Service.Service ||
!reflect.DeepEqual(reply.DNS, query.Query.DNS) || !reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
!reply.QueryMeta.KnownLeader { !reply.QueryMeta.KnownLeader {
t.Fatalf("bad: %v", reply) t.Fatalf("bad: %v", reply)
@ -1438,6 +1449,7 @@ func TestPreparedQuery_Execute(t *testing.T) {
if len(reply.Nodes) != 9 || if len(reply.Nodes) != 9 ||
reply.Datacenter != "dc2" || reply.Failovers != 1 || reply.Datacenter != "dc2" || reply.Failovers != 1 ||
reply.Service != query.Query.Service.Service ||
!reflect.DeepEqual(reply.DNS, query.Query.DNS) || !reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
!reply.QueryMeta.KnownLeader { !reply.QueryMeta.KnownLeader {
t.Fatalf("bad: %v", reply) t.Fatalf("bad: %v", reply)

View File

@ -186,6 +186,9 @@ func (q *PreparedQueryExecuteRemoteRequest) RequestDatacenter() string {
// PreparedQueryExecuteResponse has the results of executing a query. // PreparedQueryExecuteResponse has the results of executing a query.
type PreparedQueryExecuteResponse struct { type PreparedQueryExecuteResponse struct {
// Service is the service that was queried.
Service string
// Nodes has the nodes that were output by the query. // Nodes has the nodes that were output by the query.
Nodes CheckServiceNodes Nodes CheckServiceNodes