NET-7637 / NET-7659/NET-7636/NET-7647/NET-7648/NET-7646/NET-7649/NET-7645 - Multiple DNS v2 fixes (#20556)

This commit is contained in:
John Murret 2024-02-08 19:56:04 -07:00 committed by GitHub
parent 738dc8c89d
commit 7cac918811
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 453 additions and 125 deletions

View File

@ -11,10 +11,11 @@ import (
)
var (
ErrECSNotGlobal = fmt.Errorf("ECS response is not global")
ErrNoData = fmt.Errorf("no data")
ErrNotFound = fmt.Errorf("not found")
ErrNotSupported = fmt.Errorf("not supported")
ErrECSNotGlobal = fmt.Errorf("ECS response is not global")
ErrNoData = fmt.Errorf("no data")
ErrNotFound = fmt.Errorf("not found")
ErrNotSupported = fmt.Errorf("not supported")
ErrNoPathToDatacenter = fmt.Errorf("no path to datacenter")
)
// ECSNotGlobalError may be used to wrap an error or nil, to indicate that the

View File

@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"net"
"strings"
"sync/atomic"
"time"
@ -90,6 +91,8 @@ func (f *V1DataFetcher) LoadConfig(config *config.RuntimeConfig) {
cacheMaxAge: config.DNSCacheMaxAge,
onlyPassing: config.DNSOnlyPassing,
datacenter: config.Datacenter,
segmentName: config.SegmentName,
nodeName: config.NodeName,
}
f.dynamicConfig.Store(dynamicConfig)
}
@ -142,11 +145,7 @@ func (f *V1DataFetcher) FetchNodes(ctx Context, req *QueryPayload) ([]*Result, e
func (f *V1DataFetcher) FetchEndpoints(ctx Context, req *QueryPayload, lookupType LookupType) ([]*Result, error) {
f.logger.Debug(fmt.Sprintf("FetchEndpoints - req: %+v / lookupType: %+v", req, lookupType))
cfg := f.dynamicConfig.Load().(*v1DataFetcherDynamicConfig)
if lookupType == LookupTypeService {
return f.fetchService(ctx, req, cfg)
}
return nil, errors.New(fmt.Sprintf("unsupported lookup type: %s", lookupType))
return f.fetchService(ctx, req, cfg, lookupType)
}
// FetchVirtualIP fetches A/AAAA records for virtual IPs
@ -341,7 +340,7 @@ func (f *V1DataFetcher) FetchPreparedQuery(ctx Context, req *QueryPayload) ([]*R
// If we have no nodes, return not found!
if len(out.Nodes) == 0 {
return nil, ECSNotGlobalError{ErrNoData}
return nil, ECSNotGlobalError{ErrNotFound}
}
// Perform a random shuffle
@ -475,17 +474,19 @@ RPC:
return &out, nil
}
func (f *V1DataFetcher) fetchService(ctx Context, req *QueryPayload, cfg *v1DataFetcherDynamicConfig) ([]*Result, error) {
func (f *V1DataFetcher) fetchService(ctx Context, req *QueryPayload,
cfg *v1DataFetcherDynamicConfig, lookupType LookupType) ([]*Result, error) {
f.logger.Debug("fetchService", "req", req)
if req.Tenancy.SamenessGroup == "" {
return f.fetchServiceBasedOnTenancy(ctx, req, cfg)
return f.fetchServiceBasedOnTenancy(ctx, req, cfg, lookupType)
}
return f.fetchServiceFromSamenessGroup(ctx, req, cfg)
return f.fetchServiceFromSamenessGroup(ctx, req, cfg, lookupType)
}
// fetchServiceBasedOnTenancy is used to look up a service in the Consul catalog based on its tenancy or default tenancy.
func (f *V1DataFetcher) fetchServiceBasedOnTenancy(ctx Context, req *QueryPayload, cfg *v1DataFetcherDynamicConfig) ([]*Result, error) {
func (f *V1DataFetcher) fetchServiceBasedOnTenancy(ctx Context, req *QueryPayload,
cfg *v1DataFetcherDynamicConfig, lookupType LookupType) ([]*Result, error) {
f.logger.Debug(fmt.Sprintf("fetchServiceBasedOnTenancy - req: %+v", req))
if req.Tenancy.SamenessGroup != "" {
return nil, errors.New("sameness groups are not allowed for service lookups based on tenancy")
@ -502,8 +503,8 @@ func (f *V1DataFetcher) fetchServiceBasedOnTenancy(ctx Context, req *QueryPayloa
}
args := structs.ServiceSpecificRequest{
PeerName: req.Tenancy.Peer,
Connect: false,
Ingress: false,
Connect: lookupType == LookupTypeConnect,
Ingress: lookupType == LookupTypeIngress,
Datacenter: datacenter,
ServiceName: req.Name,
ServiceTags: serviceTags,
@ -520,12 +521,15 @@ func (f *V1DataFetcher) fetchServiceBasedOnTenancy(ctx Context, req *QueryPayloa
out, _, err := f.rpcFuncForServiceNodes(context.TODO(), args)
if err != nil {
if strings.Contains(err.Error(), structs.ErrNoDCPath.Error()) {
return nil, ErrNoPathToDatacenter
}
return nil, fmt.Errorf("rpc request failed: %w", err)
}
// If we have no nodes, return not found!
if len(out.Nodes) == 0 {
return nil, ErrNoData
return nil, ErrNotFound
}
// Filter out any service nodes due to health checks
@ -539,7 +543,7 @@ func (f *V1DataFetcher) fetchServiceBasedOnTenancy(ctx Context, req *QueryPayloa
// If we have no nodes, return not found!
if len(out.Nodes) == 0 {
return nil, ErrNoData
return nil, ErrNotFound
}
// Perform a random shuffle

View File

@ -29,10 +29,10 @@ func queryTenancyToEntMeta(_ QueryTenancy) acl.EnterpriseMeta {
}
// fetchServiceFromSamenessGroup fetches a service from a sameness group.
func (f *V1DataFetcher) fetchServiceFromSamenessGroup(ctx Context, req *QueryPayload, cfg *v1DataFetcherDynamicConfig) ([]*Result, error) {
func (f *V1DataFetcher) fetchServiceFromSamenessGroup(ctx Context, req *QueryPayload, cfg *v1DataFetcherDynamicConfig, lookupType LookupType) ([]*Result, error) {
f.logger.Debug(fmt.Sprintf("fetchServiceFromSamenessGroup - req: %+v", req))
if req.Tenancy.SamenessGroup == "" {
return nil, errors.New("sameness groups must be provided for service lookups")
}
return f.fetchServiceBasedOnTenancy(ctx, req, cfg)
return f.fetchServiceBasedOnTenancy(ctx, req, cfg, lookupType)
}

View File

@ -7,13 +7,14 @@ import (
"encoding/hex"
"errors"
"fmt"
"github.com/hashicorp/consul/acl"
"net"
"regexp"
"strings"
"sync/atomic"
"time"
"github.com/hashicorp/consul/acl"
"github.com/armon/go-radix"
"github.com/miekg/dns"
@ -51,8 +52,9 @@ var (
// Context is used augment a DNS message with Consul-specific metadata.
type Context struct {
Token string
DefaultPartition string
Token string
DefaultPartition string
DefaultDatacenter string
}
// RouterDynamicConfig is the dynamic configuration that can be hot-reloaded
@ -198,46 +200,12 @@ func (r *Router) handleRequestRecursively(req *dns.Msg, reqCtx Context,
reqType := parseRequestType(req)
results, query, err := r.getQueryResults(req, reqCtx, reqType, qName, remoteAddress)
// incase of the wrapped ECSNotGlobalError, extract the error from it.
// in case of the wrapped ECSNotGlobalError, extract the error from it.
isECSGlobal := !errors.Is(err, discovery.ErrECSNotGlobal)
err = getErrorFromECSNotGlobalError(err)
if err != nil {
switch {
case errors.Is(err, errInvalidQuestion):
r.logger.Error("invalid question", "name", qName)
ecsGlobal := !errors.Is(err, discovery.ErrECSNotGlobal)
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeNameError, ecsGlobal)
case errors.Is(err, errNameNotFound):
r.logger.Error("name not found", "name", qName)
ecsGlobal := !errors.Is(err, discovery.ErrECSNotGlobal)
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeNameError, ecsGlobal)
case errors.Is(err, errNotImplemented):
r.logger.Error("query not implemented", "name", qName, "type", dns.Type(req.Question[0].Qtype).String())
ecsGlobal := !errors.Is(err, discovery.ErrECSNotGlobal)
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeNotImplemented, ecsGlobal)
case errors.Is(err, discovery.ErrNotSupported):
r.logger.Debug("query name syntax not supported", "name", req.Question[0].Name)
ecsGlobal := !errors.Is(err, discovery.ErrECSNotGlobal)
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeNameError, ecsGlobal)
case errors.Is(err, discovery.ErrNotFound):
r.logger.Debug("query name not found", "name", req.Question[0].Name)
ecsGlobal := !errors.Is(err, discovery.ErrECSNotGlobal)
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeNameError, ecsGlobal)
case errors.Is(err, discovery.ErrNoData):
r.logger.Debug("no data available", "name", qName)
ecsGlobal := !errors.Is(err, discovery.ErrECSNotGlobal)
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeSuccess, ecsGlobal)
default:
r.logger.Error("error processing discovery query", "error", err)
return createServerFailureResponse(req, configCtx, canRecurse(configCtx))
}
return r.generateResponseFromError(req, err, qName, configCtx, responseDomain,
isECSGlobal, query, canRecurse(configCtx))
}
// This needs the question information because it affects the serialization format.
@ -245,7 +213,8 @@ func (r *Router) handleRequestRecursively(req *dns.Msg, reqCtx Context,
resp, err := r.serializeQueryResults(req, reqCtx, query, results, configCtx, responseDomain, remoteAddress, maxRecursionLevel)
if err != nil {
r.logger.Error("error serializing DNS results", "error", err)
return createServerFailureResponse(req, configCtx, false)
return r.generateResponseFromError(req, err, qName, configCtx, responseDomain,
false, query, false)
}
// Switch to TCP if the client is
@ -260,6 +229,47 @@ func (r *Router) handleRequestRecursively(req *dns.Msg, reqCtx Context,
return resp
}
// generateResponseFromError generates a response from an error.
func (r *Router) generateResponseFromError(req *dns.Msg, err error, qName string,
configCtx *RouterDynamicConfig, responseDomain string, isECSGlobal bool,
query *discovery.Query, canRecurse bool) *dns.Msg {
switch {
case errors.Is(err, errInvalidQuestion):
r.logger.Error("invalid question", "name", qName)
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeNameError, isECSGlobal)
case errors.Is(err, errNameNotFound):
r.logger.Error("name not found", "name", qName)
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeNameError, isECSGlobal)
case errors.Is(err, errNotImplemented):
r.logger.Error("query not implemented", "name", qName, "type", dns.Type(req.Question[0].Qtype).String())
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeNotImplemented, isECSGlobal)
case errors.Is(err, discovery.ErrNotSupported):
r.logger.Debug("query name syntax not supported", "name", req.Question[0].Name)
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeNameError, isECSGlobal)
case errors.Is(err, discovery.ErrNotFound):
r.logger.Debug("query name not found", "name", req.Question[0].Name)
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeNameError, isECSGlobal)
case errors.Is(err, discovery.ErrNoData):
r.logger.Debug("no data available", "name", qName)
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeSuccess, isECSGlobal)
case errors.Is(err, discovery.ErrNoPathToDatacenter):
dc := ""
if query != nil {
dc = query.QueryPayload.Tenancy.Datacenter
}
r.logger.Debug("no path to datacenter", "datacenter", dc)
return createAuthoritativeResponse(req, configCtx, responseDomain, dns.RcodeNameError, isECSGlobal)
}
r.logger.Error("error processing discovery query", "error", err)
return createServerFailureResponse(req, configCtx, canRecurse)
}
// trimDomain trims the domain from the question name.
func (r *Router) trimDomain(questionName string) string {
longer := r.domain
@ -511,6 +521,10 @@ func (r *Router) serializeQueryResults(req *dns.Msg, reqCtx Context,
r.appendResultsToDNSResponse(req, reqCtx, query, resp, results, cfg, responseDomain, remoteAddress, maxRecursionLevel)
}
if len(resp.Answer) == 0 && len(resp.Extra) == 0 {
return nil, discovery.ErrNoData
}
return resp, nil
}
@ -575,7 +589,8 @@ func (r *Router) appendResultsToDNSResponse(req *dns.Msg, reqCtx Context,
// defaultAgentDNSRequestContext returns a default request context based on the agent's config.
func (r *Router) defaultAgentDNSRequestContext() Context {
return Context{
Token: r.tokenFunc(),
Token: r.tokenFunc(),
DefaultDatacenter: r.datacenter,
// We don't need to specify the agent's partition here because that will be handled further down the stack
// in the query processor.
}
@ -1064,8 +1079,19 @@ func shouldAppendTXTRecord(query *discovery.Query, cfg *RouterDynamicConfig, req
// getAnswerExtrasForIP creates the dns answer and extra from IP dnsAddress pairs.
func getAnswerExtrasForIP(name string, addr *dnsAddress, question dns.Question,
reqType requestType, result *discovery.Result, ttl uint32, _ string) (answer []dns.RR, extra []dns.RR) {
reqType requestType, result *discovery.Result, ttl uint32, domain string) (answer []dns.RR, extra []dns.RR) {
qType := question.Qtype
canReturnARecord := qType == dns.TypeSRV || qType == dns.TypeA || qType == dns.TypeANY || qType == dns.TypeNS || qType == dns.TypeTXT
canReturnAAAARecord := qType == dns.TypeSRV || qType == dns.TypeAAAA || qType == dns.TypeANY || qType == dns.TypeNS || qType == dns.TypeTXT
if reqType != requestTypeAddress {
switch {
// check IPV4
case addr.IsIP() && addr.IsIPV4() && !canReturnARecord,
// check IPV6
addr.IsIP() && !addr.IsIPV4() && !canReturnAAAARecord:
return
}
}
// Have to pass original question name here even if the system has recursed
// and stripped off the domain suffix.
@ -1080,6 +1106,17 @@ func getAnswerExtrasForIP(name string, addr *dnsAddress, question dns.Question,
name = question.Name
}
if reqType != requestTypeAddress && qType == dns.TypeSRV {
if result.Type == discovery.ResultTypeService && addr.IsIP() && result.Service.
Address == addr.String() {
// encode the ip to be used in the header of the A/AAAA record
// as well as the target of the SRV record.
recHdrName = encodeIPAsFqdn(result, addr.IP(), domain)
}
srv := makeSRVRecord(name, recHdrName, result, ttl)
answer = append(answer, srv)
}
record := makeIPBasedRecord(recHdrName, addr, ttl)
isARecordWhenNotExplicitlyQueried := record.Header().Rrtype == dns.TypeA && qType != dns.TypeA && qType != dns.TypeANY
@ -1093,10 +1130,6 @@ func getAnswerExtrasForIP(name string, addr *dnsAddress, question dns.Question,
answer = append(answer, record)
}
if reqType != requestTypeAddress && qType == dns.TypeSRV {
srv := makeSRVRecord(name, recHdrName, result, ttl)
answer = append(answer, srv)
}
return
}

View File

@ -94,6 +94,7 @@ func getQueryTenancy(reqCtx Context, queryType discovery.QueryType, querySuffixe
Namespace: labels.Namespace,
Partition: labels.Partition,
SamenessGroup: labels.SamenessGroup,
Datacenter: reqCtx.DefaultDatacenter,
}, nil
}
@ -108,10 +109,21 @@ func getQueryTenancy(reqCtx Context, queryType discovery.QueryType, querySuffixe
Namespace: labels.Namespace,
Partition: labels.Partition,
Peer: labels.Peer,
Datacenter: labels.Datacenter,
Datacenter: getEffectiveDatacenter(labels, reqCtx.DefaultDatacenter),
}, nil
}
// getEffectiveDatacenter returns the effective datacenter from the parsed labels.
func getEffectiveDatacenter(labels *parsedLabels, defaultDC string) string {
switch {
case labels.Datacenter != "":
return labels.Datacenter
case labels.PeerOrDatacenter != "" && labels.Peer != labels.PeerOrDatacenter:
return labels.PeerOrDatacenter
}
return defaultDC
}
// getQueryTypePartsAndSuffixesFromDNSMessage returns the query type, the parts, and suffixes of the query name.
func getQueryTypePartsAndSuffixesFromDNSMessage(req *dns.Msg, domain, altDomain string) (queryType discovery.QueryType, parts []string, suffixes []string) {
// Get the QName without the domain suffix

View File

@ -159,15 +159,20 @@ func Test_buildQueryFromDNSMessage(t *testing.T) {
},
},
},
requestContext: &Context{
DefaultDatacenter: "default-dc",
DefaultPartition: "default-partition",
},
expectedQuery: &discovery.Query{
QueryType: discovery.QueryTypeWorkload,
QueryPayload: discovery.QueryPayload{
Name: "foo",
PortName: "api",
Tenancy: discovery.QueryTenancy{
Namespace: "banana",
Partition: "orange",
Peer: "apple",
Namespace: "banana",
Partition: "orange",
Peer: "apple",
Datacenter: "default-dc",
},
},
},
@ -186,6 +191,10 @@ func Test_buildQueryFromDNSMessage(t *testing.T) {
},
},
},
requestContext: &Context{
DefaultDatacenter: "default-dc",
DefaultPartition: "default-partition",
},
expectedQuery: &discovery.Query{
QueryType: discovery.QueryTypeService,
QueryPayload: discovery.QueryPayload{
@ -194,6 +203,7 @@ func Test_buildQueryFromDNSMessage(t *testing.T) {
Namespace: "banana",
Partition: "orange",
SamenessGroup: "apple",
Datacenter: "default-dc",
},
},
},

View File

@ -5,7 +5,9 @@ package dns
import (
"errors"
"fmt"
"net"
"reflect"
"testing"
"time"
@ -41,6 +43,21 @@ type HandleTestCase struct {
}
func Test_HandleRequest(t *testing.T) {
soa := &dns.SOA{
Hdr: dns.RR_Header{
Name: "consul.",
Rrtype: dns.TypeSOA,
Class: dns.ClassINET,
Ttl: 4,
},
Ns: "ns.consul.",
Mbox: "hostmaster.consul.",
Serial: uint32(time.Now().Unix()),
Refresh: 1,
Retry: 2,
Expire: 3,
Minttl: 4,
}
testCases := []HandleTestCase{
// recursor queries
@ -788,17 +805,7 @@ func Test_HandleRequest(t *testing.T) {
Qclass: dns.ClassINET,
},
},
Extra: []dns.RR{
&dns.AAAA{
Hdr: dns.RR_Header{
Name: "20010db800010002cafe000000001337.virtual.dc1.consul.",
Rrtype: dns.TypeAAAA,
Class: dns.ClassINET,
Ttl: 123,
},
AAAA: net.ParseIP("2001:db8:1:2:cafe::1337"),
},
},
Ns: []dns.RR{soa},
},
},
// SOA Queries
@ -1491,6 +1498,7 @@ func Test_HandleRequest(t *testing.T) {
Opcode: dns.OpcodeQuery,
Response: true,
Authoritative: true,
Rcode: dns.RcodeSuccess,
},
Compress: true,
Question: []dns.Question{
@ -1721,7 +1729,7 @@ func Test_HandleRequest(t *testing.T) {
},
},
{
name: "workload AAAA query with namespace, partition, and cluster id; returns A record",
name: "workload A query with namespace, partition, and cluster id; IPV4 address; returns A record",
request: &dns.Msg{
MsgHdr: dns.MsgHdr{
Opcode: dns.OpcodeQuery,
@ -1729,7 +1737,7 @@ func Test_HandleRequest(t *testing.T) {
Question: []dns.Question{
{
Name: "foo.workload.bar.ns.baz.ap.dc3.dc.consul.",
Qtype: dns.TypeAAAA,
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
},
},
@ -1767,11 +1775,11 @@ func Test_HandleRequest(t *testing.T) {
Question: []dns.Question{
{
Name: "foo.workload.bar.ns.baz.ap.dc3.dc.consul.",
Qtype: dns.TypeAAAA,
Qtype: dns.TypeA,
Qclass: dns.ClassINET,
},
},
Extra: []dns.RR{
Answer: []dns.RR{
&dns.A{
Hdr: dns.RR_Header{
Name: "foo.workload.bar.ns.baz.ap.dc3.dc.consul.",
@ -1901,3 +1909,268 @@ func buildDNSConfig(agentConfig *config.RuntimeConfig, cdf discovery.CatalogData
return cfg
}
// TestDNS_BinaryTruncate tests the dnsBinaryTruncate function.
func TestDNS_BinaryTruncate(t *testing.T) {
msgSrc := new(dns.Msg)
msgSrc.Compress = true
msgSrc.SetQuestion("redis.service.consul.", dns.TypeSRV)
for i := 0; i < 5000; i++ {
target := fmt.Sprintf("host-redis-%d-%d.test.acme.com.node.dc1.consul.", i/256, i%256)
msgSrc.Answer = append(msgSrc.Answer, &dns.SRV{Hdr: dns.RR_Header{Name: "redis.service.consul.", Class: 1, Rrtype: dns.TypeSRV, Ttl: 0x3c}, Port: 0x4c57, Target: target})
msgSrc.Extra = append(msgSrc.Extra, &dns.CNAME{Hdr: dns.RR_Header{Name: target, Class: 1, Rrtype: dns.TypeCNAME, Ttl: 0x3c}, Target: fmt.Sprintf("fx.168.%d.%d.", i/256, i%256)})
}
for _, compress := range []bool{true, false} {
for idx, maxSize := range []int{12, 256, 512, 8192, 65535} {
t.Run(fmt.Sprintf("binarySearch %d", maxSize), func(t *testing.T) {
msg := new(dns.Msg)
msgSrc.Compress = compress
msgSrc.SetQuestion("redis.service.consul.", dns.TypeSRV)
msg.Answer = msgSrc.Answer
msg.Extra = msgSrc.Extra
msg.Ns = msgSrc.Ns
index := make(map[string]dns.RR, len(msg.Extra))
indexRRs(msg.Extra, index)
blen := dnsBinaryTruncate(msg, maxSize, index, true)
msg.Answer = msg.Answer[:blen]
syncExtra(index, msg)
predicted := msg.Len()
buf, err := msg.Pack()
if err != nil {
t.Error(err)
}
if predicted < len(buf) {
t.Fatalf("Bug in DNS library: %d != %d", predicted, len(buf))
}
if len(buf) > maxSize || (idx != 0 && len(buf) < 16) {
t.Fatalf("bad[%d]: %d > %d", idx, len(buf), maxSize)
}
})
}
}
}
// TestDNS_syncExtra tests the syncExtra function.
func TestDNS_syncExtra(t *testing.T) {
resp := &dns.Msg{
Answer: []dns.RR{
// These two are on the same host so the redundant extra
// records should get deduplicated.
&dns.SRV{
Hdr: dns.RR_Header{
Name: "redis-cache-redis.service.consul.",
Rrtype: dns.TypeSRV,
Class: dns.ClassINET,
},
Port: 1001,
Target: "ip-10-0-1-185.node.dc1.consul.",
},
&dns.SRV{
Hdr: dns.RR_Header{
Name: "redis-cache-redis.service.consul.",
Rrtype: dns.TypeSRV,
Class: dns.ClassINET,
},
Port: 1002,
Target: "ip-10-0-1-185.node.dc1.consul.",
},
// This one isn't in the Consul domain so it will get a
// CNAME and then an A record from the recursor.
&dns.SRV{
Hdr: dns.RR_Header{
Name: "redis-cache-redis.service.consul.",
Rrtype: dns.TypeSRV,
Class: dns.ClassINET,
},
Port: 1003,
Target: "demo.consul.io.",
},
// This one isn't in the Consul domain and it will get
// a CNAME and A record from a recursor that alters the
// case of the name. This proves we look up in the index
// in a case-insensitive way.
&dns.SRV{
Hdr: dns.RR_Header{
Name: "redis-cache-redis.service.consul.",
Rrtype: dns.TypeSRV,
Class: dns.ClassINET,
},
Port: 1001,
Target: "insensitive.consul.io.",
},
// This is also a CNAME, but it'll be set up to loop to
// make sure we don't crash.
&dns.SRV{
Hdr: dns.RR_Header{
Name: "redis-cache-redis.service.consul.",
Rrtype: dns.TypeSRV,
Class: dns.ClassINET,
},
Port: 1001,
Target: "deadly.consul.io.",
},
// This is also a CNAME, but it won't have another record.
&dns.SRV{
Hdr: dns.RR_Header{
Name: "redis-cache-redis.service.consul.",
Rrtype: dns.TypeSRV,
Class: dns.ClassINET,
},
Port: 1001,
Target: "nope.consul.io.",
},
},
Extra: []dns.RR{
// These should get deduplicated.
&dns.A{
Hdr: dns.RR_Header{
Name: "ip-10-0-1-185.node.dc1.consul.",
Rrtype: dns.TypeA,
Class: dns.ClassINET,
},
A: net.ParseIP("10.0.1.185"),
},
&dns.A{
Hdr: dns.RR_Header{
Name: "ip-10-0-1-185.node.dc1.consul.",
Rrtype: dns.TypeA,
Class: dns.ClassINET,
},
A: net.ParseIP("10.0.1.185"),
},
// This is a normal CNAME followed by an A record but we
// have flipped the order. The algorithm should emit them
// in the opposite order.
&dns.A{
Hdr: dns.RR_Header{
Name: "fakeserver.consul.io.",
Rrtype: dns.TypeA,
Class: dns.ClassINET,
},
A: net.ParseIP("127.0.0.1"),
},
&dns.CNAME{
Hdr: dns.RR_Header{
Name: "demo.consul.io.",
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
},
Target: "fakeserver.consul.io.",
},
// These differ in case to test case insensitivity.
&dns.CNAME{
Hdr: dns.RR_Header{
Name: "INSENSITIVE.CONSUL.IO.",
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
},
Target: "Another.Server.Com.",
},
&dns.A{
Hdr: dns.RR_Header{
Name: "another.server.com.",
Rrtype: dns.TypeA,
Class: dns.ClassINET,
},
A: net.ParseIP("127.0.0.1"),
},
// This doesn't appear in the answer, so should get
// dropped.
&dns.A{
Hdr: dns.RR_Header{
Name: "ip-10-0-1-186.node.dc1.consul.",
Rrtype: dns.TypeA,
Class: dns.ClassINET,
},
A: net.ParseIP("10.0.1.186"),
},
// These two test edge cases with CNAME handling.
&dns.CNAME{
Hdr: dns.RR_Header{
Name: "deadly.consul.io.",
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
},
Target: "deadly.consul.io.",
},
&dns.CNAME{
Hdr: dns.RR_Header{
Name: "nope.consul.io.",
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
},
Target: "notthere.consul.io.",
},
},
}
index := make(map[string]dns.RR)
indexRRs(resp.Extra, index)
syncExtra(index, resp)
expected := &dns.Msg{
Answer: resp.Answer,
Extra: []dns.RR{
&dns.A{
Hdr: dns.RR_Header{
Name: "ip-10-0-1-185.node.dc1.consul.",
Rrtype: dns.TypeA,
Class: dns.ClassINET,
},
A: net.ParseIP("10.0.1.185"),
},
&dns.CNAME{
Hdr: dns.RR_Header{
Name: "demo.consul.io.",
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
},
Target: "fakeserver.consul.io.",
},
&dns.A{
Hdr: dns.RR_Header{
Name: "fakeserver.consul.io.",
Rrtype: dns.TypeA,
Class: dns.ClassINET,
},
A: net.ParseIP("127.0.0.1"),
},
&dns.CNAME{
Hdr: dns.RR_Header{
Name: "INSENSITIVE.CONSUL.IO.",
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
},
Target: "Another.Server.Com.",
},
&dns.A{
Hdr: dns.RR_Header{
Name: "another.server.com.",
Rrtype: dns.TypeA,
Class: dns.ClassINET,
},
A: net.ParseIP("127.0.0.1"),
},
&dns.CNAME{
Hdr: dns.RR_Header{
Name: "deadly.consul.io.",
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
},
Target: "deadly.consul.io.",
},
&dns.CNAME{
Hdr: dns.RR_Header{
Name: "nope.consul.io.",
Rrtype: dns.TypeCNAME,
Class: dns.ClassINET,
},
Target: "notthere.consul.io.",
},
},
}
if !reflect.DeepEqual(resp, expected) {
t.Fatalf("Bad %#v vs. %#v", *resp, *expected)
}
}

View File

@ -652,13 +652,12 @@ func TestDNS_ServiceLookupWithInternalServiceAddress(t *testing.T) {
}
}
// TODO (v2-dns): NET-7659 - Fix Ingress and Connect Service Lookups
func TestDNS_ConnectServiceLookup(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, experimentsHCL)
defer a.Shutdown()
@ -704,13 +703,12 @@ func TestDNS_ConnectServiceLookup(t *testing.T) {
}
}
// TODO (v2-dns): NET-7659 - Fix Ingress and Connect Service Lookups
func TestDNS_IngressServiceLookup(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, experimentsHCL)
defer a.Shutdown()
@ -1118,13 +1116,12 @@ func TestDNS_ExternalServiceToConsulCNAMENestedLookup(t *testing.T) {
}
}
// TODO (v2-dns): NET-7636 - Implement target having encoded IP address
func TestDNS_ServiceLookup_ServiceAddress_A(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, experimentsHCL)
defer a.Shutdown()
@ -1219,13 +1216,12 @@ func TestDNS_ServiceLookup_ServiceAddress_A(t *testing.T) {
}
}
// TODO (v2-dns): NET-7636 - Implement target having encoded IP address
func TestDNS_AltDomain_ServiceLookup_ServiceAddress_A(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, `
alt_domain = "test-domain"
@ -1448,13 +1444,12 @@ func TestDNS_ServiceLookup_ServiceAddress_SRV(t *testing.T) {
}
}
// TODO (v2-dns): NET-7636 - Implement target having encoded IP address
func TestDNS_ServiceLookup_ServiceAddressIPV6(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, experimentsHCL)
defer a.Shutdown()
@ -1549,13 +1544,12 @@ func TestDNS_ServiceLookup_ServiceAddressIPV6(t *testing.T) {
}
}
// TODO (v2-dns): NET-7636 - Implement target having encoded IP address
func TestDNS_AltDomain_ServiceLookup_ServiceAddressIPV6(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, `
alt_domain = "test-domain"
@ -2389,13 +2383,12 @@ func TestDNS_ServiceLookup_Dedup_SRV(t *testing.T) {
}
}
// TODO (v2-dns): NET-7637 - implementing health filtering
func TestDNS_ServiceLookup_FilterCritical(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, experimentsHCL)
defer a.Shutdown()
@ -2553,13 +2546,12 @@ func TestDNS_ServiceLookup_FilterCritical(t *testing.T) {
}
}
// TODO (v2-dns): NET-7637 - implementing health filtering
func TestDNS_ServiceLookup_OnlyFailing(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, experimentsHCL)
defer a.Shutdown()
@ -2674,13 +2666,12 @@ func TestDNS_ServiceLookup_OnlyFailing(t *testing.T) {
}
}
// TODO (v2-dns): NET-7637 - implementing health filtering
func TestDNS_ServiceLookup_OnlyPassing(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, `
dns_config {
@ -2999,13 +2990,12 @@ func TestDNS_ServiceLookup_Truncate(t *testing.T) {
}
}
// TODO (v2-dns): NET-7638 - Implemented truncated response
func TestDNS_ServiceLookup_LargeResponses(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, `
dns_config {
@ -3211,7 +3201,7 @@ func checkDNSService(
expectedResultsCount int,
udpSize uint16,
) {
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, `
node_name = "test-node"

View File

@ -1599,7 +1599,6 @@ func TestDNS_RecursorTimeout(t *testing.T) {
}
}
// TODO(v2-dns): NET-7646 - account for this functionality in v1 since there is
// no way to run a v2 version of this test since it is calling a private function and not
// using a test agent.
func TestDNS_BinarySearch(t *testing.T) {
@ -1791,6 +1790,8 @@ func TestDNS_AddressLookup(t *testing.T) {
require.True(t, ok)
require.Equal(t, aRec.A.To4().String(), answer)
require.Zero(t, aRec.Hdr.Ttl)
require.Nil(t, in.Ns)
require.Nil(t, in.Extra)
}
})
}
@ -1857,6 +1858,11 @@ func TestDNS_AddressLookupInvalidType(t *testing.T) {
require.Nil(t, in.Answer)
require.NotNil(t, in.Extra)
require.Len(t, in.Extra, 1)
aRecord := in.Extra[0].(*dns.A)
require.Equal(t, "7f000001.addr.dc1.consul.", aRecord.Hdr.Name)
require.Equal(t, dns.TypeA, aRecord.Hdr.Rrtype)
require.Zero(t, aRecord.Hdr.Ttl)
require.Equal(t, "127.0.0.1", aRecord.A.String())
}
})
}
@ -1952,7 +1958,7 @@ func TestDNS_NonExistentDC_Server(t *testing.T) {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, experimentsHCL)
defer a.Shutdown()
@ -1967,14 +1973,18 @@ func TestDNS_NonExistentDC_Server(t *testing.T) {
t.Fatalf("err: %v", err)
}
if in.Rcode != dns.RcodeNameError {
t.Fatalf("Expected RCode: %#v, had: %#v", dns.RcodeNameError, in.Rcode)
}
require.Equal(t, dns.RcodeNameError, in.Rcode)
require.Equal(t, 0, len(in.Answer))
require.Equal(t, 0, len(in.Extra))
require.Equal(t, 1, len(in.Ns))
soa := in.Ns[0].(*dns.SOA)
require.Equal(t, "consul.", soa.Hdr.Name)
require.Equal(t, "ns.consul.", soa.Ns)
require.Equal(t, "hostmaster.consul.", soa.Mbox)
})
}
}
// TODO(v2-dns): NET-7647 - Fix non-existent dc tests
// TestDNS_NonExistentDC_RPC verifies NXDOMAIN is returned when
// Consul server agent is queried over RPC by a non-server agent
// for a service in a non-existent domain
@ -1983,7 +1993,7 @@ func TestDNS_NonExistentDC_RPC(t *testing.T) {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
s := NewTestAgent(t, `
node_name = "test-server"
@ -2019,13 +2029,12 @@ func TestDNS_NonExistentDC_RPC(t *testing.T) {
}
}
// TODO(v2-dns): NET-7647 - Fix non-existent dc tests
func TestDNS_NonExistingLookup(t *testing.T) {
func TestDNS_NonExistentLookup(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, experimentsHCL)
defer a.Shutdown()
@ -2056,13 +2065,12 @@ func TestDNS_NonExistingLookup(t *testing.T) {
}
}
// TODO(v2-dns): NET-7647 - Fix non-existent dc tests
func TestDNS_NonExistingLookupEmptyAorAAAA(t *testing.T) {
func TestDNS_NonExistentLookupEmptyAorAAAA(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, experimentsHCL)
defer a.Shutdown()
@ -2545,13 +2553,12 @@ func TestDNS_InvalidQueries(t *testing.T) {
}
}
// TODO(v2-dns): NET-7648 - Prepared Query - inject agent source and dc to RPC
func TestDNS_PreparedQuery_AgentSource(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, experimentsHCL)
defer a.Shutdown()
@ -2586,13 +2593,12 @@ func TestDNS_PreparedQuery_AgentSource(t *testing.T) {
}
}
// TODO(v2-dns): NET-7648 - Prepared Query - inject agent source and dc to RPC
func TestDNS_EDNS_Truncate_AgentSource(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
for name, experimentsHCL := range getVersionHCL(false) {
for name, experimentsHCL := range getVersionHCL(true) {
t.Run(name, func(t *testing.T) {
a := NewTestAgent(t, `
dns_config {
@ -3042,7 +3048,6 @@ func TestDNS_trimUDPResponse_TrimSizeMaxSize(t *testing.T) {
}
}
// TODO(v2-dns): NET-7649 - Implement sync extra
func TestDNS_syncExtra(t *testing.T) {
resp := &dns.Msg{
Answer: []dns.RR{