diff --git a/agent/discovery/discovery.go b/agent/discovery/discovery.go index dc22daa462..921a9ef4c2 100644 --- a/agent/discovery/discovery.go +++ b/agent/discovery/discovery.go @@ -60,15 +60,26 @@ type QueryPayload struct { DisableFailover bool } +// ResultType indicates the Consul resource that a discovery record represents. +// This is useful for things like adding TTLs for different objects in the DNS. +type ResultType string + +const ( + ResultTypeService ResultType = "SERVICE" + ResultTypeNode ResultType = "NODE" + ResultTypeVirtual ResultType = "VIRTUAL" + ResultTypeWorkload ResultType = "WORKLOAD" +) + // Result is a generic format of targets that could be returned in a query. // It is the responsibility of the DNS encoder to know what to do with // each Result, based on the query type. type Result struct { - Address string // A/AAAA/CNAME records - could be used in the Extra section. CNAME is required to handle hostname addresses in workloads & nodes. - Weight uint32 // SRV queries - Port uint32 // SRV queries - TTL uint32 + Address string // A/AAAA/CNAME records - could be used in the Extra section. CNAME is required to handle hostname addresses in workloads & nodes. + Weight uint32 // SRV queries + Port uint32 // SRV queries Metadata []string // Used to collect metadata into TXT Records + Type ResultType // Used in SRV & PTR queries to point at an A/AAAA Record. // In V1, this could be a full-qualified Service or Node name. diff --git a/agent/dns.go b/agent/dns.go index 01ee317430..6fe3524692 100644 --- a/agent/dns.go +++ b/agent/dns.go @@ -1060,6 +1060,8 @@ func (d *DNSServer) dispatch(remoteAddr net.Addr, req, resp *dns.Msg, maxRecursi } else { resp.Answer = append(resp.Answer, aaaaRecord) } + default: + return invalid() } return nil default: diff --git a/agent/dns/router.go b/agent/dns/router.go index e518e2cc2c..08f5f8c814 100644 --- a/agent/dns/router.go +++ b/agent/dns/router.go @@ -4,6 +4,8 @@ package dns import ( + "encoding/hex" + "errors" "fmt" "net" "sync/atomic" @@ -16,6 +18,21 @@ import ( "github.com/hashicorp/consul/agent/config" "github.com/hashicorp/consul/agent/discovery" "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/logging" +) + +const ( + addrLabel = "addr" + + arpaDomain = "in-addr.arpa." + + suffixFailover = "failover." + suffixNoFailover = "no-failover." +) + +var ( + errInvalidQuestion = fmt.Errorf("invalid question") + errNameNotFound = fmt.Errorf("invalid question") ) // TODO (v2-dns): metrics @@ -77,8 +94,21 @@ type Router struct { var _ = dns.Handler(&Router{}) func NewRouter(cfg Config) (*Router, error) { + // Make sure domains are FQDN, make them case-insensitive for DNSRequestRouter + domain := dns.CanonicalName(cfg.AgentConfig.DNSDomain) + altDomain := dns.CanonicalName(cfg.AgentConfig.DNSAltDomain) + + // TODO (v2-dns): need to figure out tenancy information here in a way that work for V2 and V1 + router := &Router{ - // TODO (v2-dns): implement stub + processor: cfg.Processor, + domain: domain, + altDomain: altDomain, + logger: cfg.Logger.Named(logging.DNS), + tokenFunc: cfg.TokenFunc, + // TODO (v2-dns): see tenancy question above + //defaultPartition: ?, + //defaultNamespace: ?, } if err := router.ReloadConfig(cfg.AgentConfig); err != nil { @@ -88,42 +118,71 @@ func NewRouter(cfg Config) (*Router, error) { } // HandleRequest is used to process and individual DNS request. It returns a message in success or fail cases. -func (r Router) HandleRequest(req *dns.Msg, reqCtx discovery.Context, remoteAddress net.Addr) *dns.Msg { +func (r *Router) HandleRequest(req *dns.Msg, reqCtx discovery.Context, remoteAddress net.Addr) *dns.Msg { cfg := r.dynamicConfig.Load().(*RouterDynamicConfig) - // TODO (v2-dns): implement HandleRequest. This is just temporary - return createServerFailureResponse(req, cfg, false) + err := validateAndNormalizeRequest(req) + if err != nil { + r.logger.Error("error parsing DNS query", "error", err) + if errors.Is(err, errInvalidQuestion) { + return createRefusedResponse(req) + } + return createServerFailureResponse(req, cfg, false) + } - // Parse fields of the message + reqType, responseDomain, needRecurse := r.parseDomain(req) - // Route the request to the appropriate destination - // 1. r.processor.QueryByName - // 2. r.processor.QueryByIP - // 3. recurse + if needRecurse && canRecurse(cfg) { + // TODO (v2-dns): handle recursion + r.logger.Error("recursion not implemented") + return createServerFailureResponse(req, cfg, false) + } - // Serialize the output + var results []*discovery.Result + switch reqType { + case requestTypeName: + //query, err := r.buildQuery(req, reqCtx) + //results, err = r.processor.QueryByName(query, reqCtx) + // TODO (v2-dns): implement requestTypeName + // This will call discovery.QueryByName + r.logger.Error("requestTypeName not implemented") + case requestTypeIP: + // TODO (v2-dns): implement requestTypeIP + // This will call discovery.QueryByIP + r.logger.Error("requestTypeIP not implemented") + case requestTypeAddress: + results, err = buildAddressResults(req) + } + if err != nil && errors.Is(err, errNameNotFound) { + r.logger.Error("name not found", "name", req.Question[0].Name) + return createNameErrorResponse(req, cfg, responseDomain) + } + if err != nil { + r.logger.Error("error processing discovery query", "error", err) + return createServerFailureResponse(req, cfg, false) + } + // This needs the question information because it affects the serialization format. + // e.g., the Consul service has the same "results" for both NS and A/AAAA queries, but the serialization differs. + resp, err := r.serializeQueryResults(req, results, cfg, responseDomain) + if err != nil { + r.logger.Error("error serializing DNS results", "error", err) + return createServerFailureResponse(req, cfg, false) + } + return resp } -// ServeDNS implements the miekg/dns.Handler interface -func (r Router) ServeDNS(w dns.ResponseWriter, req *dns.Msg) { +// ServeDNS implements the miekg/dns.Handler interface. +// This is a standard DNS listener, so we inject a default request context based on the agent's config. +func (r *Router) ServeDNS(w dns.ResponseWriter, req *dns.Msg) { reqCtx := r.defaultAgentDNSRequestContext() out := r.HandleRequest(req, reqCtx, w.RemoteAddr()) w.WriteMsg(out) } -// GetDynamicRouterConfig takes global config and creates the config used by DNS server -func GetDynamicRouterConfig(conf *config.RuntimeConfig) (*RouterDynamicConfig, error) { - cfg := &RouterDynamicConfig{ - // TODO (v2-dns) - } - - return cfg, nil -} - // ReloadConfig hot-reloads the router config with new parameters -func (r Router) ReloadConfig(newCfg *config.RuntimeConfig) error { - cfg, err := GetDynamicRouterConfig(newCfg) +func (r *Router) ReloadConfig(newCfg *config.RuntimeConfig) error { + cfg, err := getDynamicRouterConfig(newCfg) if err != nil { return fmt.Errorf("error loading DNS config: %w", err) } @@ -131,10 +190,142 @@ func (r Router) ReloadConfig(newCfg *config.RuntimeConfig) error { return nil } -func (r Router) defaultAgentDNSRequestContext() discovery.Context { +func (r *Router) defaultAgentDNSRequestContext() discovery.Context { return discovery.Context{ - // TODO (v2-dns): implement stub + Token: r.tokenFunc(), + // TODO (v2-dns): tenancy information; maybe we choose not to specify and use the default + // attached to the Router (from the agent's config) + } +} + +func validateAndNormalizeRequest(req *dns.Msg) error { + // like upstream miekg/dns, we require at least one question, + // but we will only answer the first. + if len(req.Question) == 0 { + return errInvalidQuestion + } + + // We mutate the request name to respond with the canonical name. + // This is Consul convention. + req.Question[0].Name = dns.CanonicalName(req.Question[0].Name) + return nil +} + +// Request type is similar to miekg/dns.Type, but correlates to the different query processors we might need to invoke. +type requestType string + +const ( + requestTypeName requestType = "NAME" // A/AAAA/CNAME/SRV/SOA + requestTypeIP requestType = "IP" + requestTypeAddress requestType = "ADDR" +) + +// parseQuery converts a DNS message into a generic discovery request. +// If the request domain does not match "consul." or the alternative domain, +// it will return true for needRecurse. The logic is based on miekg/dns.ServeDNS matcher. +// The implementation assumes that the only valid domains are "consul." and the alternative domain, and +// that DS query types are not supported. +func (r *Router) parseDomain(req *dns.Msg) (requestType, string, bool) { + target := dns.CanonicalName(req.Question[0].Name) + target, _ = stripSuffix(target) + + for offset, overflow := 0, false; !overflow; offset, overflow = dns.NextLabel(target, offset) { + subdomain := target[offset:] + switch subdomain { + case r.domain: + if isAddrSubdomain(target) { + return requestTypeAddress, r.domain, false + } + return requestTypeName, r.domain, false + + case r.altDomain: + // TODO (v2-dns): the default, unspecified alt domain should be ".". Next label should never return this + // but write a test to verify that. + if isAddrSubdomain(target) { + return requestTypeAddress, r.altDomain, false + } + return requestTypeName, r.altDomain, false + case arpaDomain: + // PTR queries always respond with the primary domain. + return requestTypeIP, r.domain, false + // Default: fallthrough + } + } + // No match found; recurse if possible + return "", "", true +} + +func (r *Router) serializeQueryResults(req *dns.Msg, results []*discovery.Result, cfg *RouterDynamicConfig, responseDomain string) (*dns.Msg, error) { + resp := new(dns.Msg) + resp.SetReply(req) + resp.Compress = !cfg.DisableCompression + resp.Authoritative = true + resp.RecursionAvailable = canRecurse(cfg) + + // TODO (v2-dns): add SOA if that is the question type + + for _, result := range results { + appendResultToDNSResponse(result, req, resp, responseDomain, cfg) + } + + return resp, nil +} + +func stripSuffix(target string) (string, bool) { + enableFailover := false + + // Strip off any suffixes that may have been added. + offset, underflow := dns.PrevLabel(target, 1) + if !underflow { + maybeSuffix := target[offset:] + switch maybeSuffix { + case suffixFailover: + target = target[:offset] + enableFailover = true + case suffixNoFailover: + target = target[:offset] + } + } + return target, enableFailover +} + +func isAddrSubdomain(domain string) bool { + labels := dns.SplitDomainName(domain) + + // Looking for .addr..consul. + if len(labels) > 2 { + return labels[1] == addrLabel + } + return false +} + +// getDynamicRouterConfig takes agent config and creates/resets the config used by DNS Router +func getDynamicRouterConfig(conf *config.RuntimeConfig) (*RouterDynamicConfig, error) { + cfg := &RouterDynamicConfig{ + ARecordLimit: conf.DNSARecordLimit, + EnableTruncate: conf.DNSEnableTruncate, + NodeTTL: conf.DNSNodeTTL, + RecursorStrategy: conf.DNSRecursorStrategy, + RecursorTimeout: conf.DNSRecursorTimeout, + UDPAnswerLimit: conf.DNSUDPAnswerLimit, + NodeMetaTXT: conf.DNSNodeMetaTXT, + DisableCompression: conf.DNSDisableCompression, + SOAConfig: SOAConfig{ + Expire: conf.DNSSOA.Expire, + Minttl: conf.DNSSOA.Minttl, + Refresh: conf.DNSSOA.Refresh, + Retry: conf.DNSSOA.Retry, + }, } + + // TODO (v2-dns): add service TTL recalculation + + // TODO (v2-dns): add recursor address formatting + return cfg, nil +} + +func canRecurse(cfg *RouterDynamicConfig) bool { + return len(cfg.Recursors) > 0 } func createServerFailureResponse(req *dns.Msg, cfg *RouterDynamicConfig, recursionAvailable bool) *dns.Msg { @@ -143,6 +334,150 @@ func createServerFailureResponse(req *dns.Msg, cfg *RouterDynamicConfig, recursi m.SetReply(req) m.Compress = !cfg.DisableCompression m.SetRcode(req, dns.RcodeServerFailure) + // TODO (2-dns): set EDNS m.RecursionAvailable = recursionAvailable return m } + +func createRefusedResponse(req *dns.Msg) *dns.Msg { + // Return a REFUSED message + m := &dns.Msg{} + m.SetRcode(req, dns.RcodeRefused) + return m +} + +func createNameErrorResponse(req *dns.Msg, cfg *RouterDynamicConfig, domain string) *dns.Msg { + // Return a NXDOMAIN message + m := &dns.Msg{} + m.SetRcode(req, dns.RcodeNameError) + m.Compress = !cfg.DisableCompression + m.Authoritative = true + + // We add the SOA on NameErrors + // TODO (v2-dns): refactor into a common function + soa := &dns.SOA{ + Hdr: dns.RR_Header{ + Name: domain, + Rrtype: dns.TypeSOA, + Class: dns.ClassINET, + // Has to be consistent with MinTTL to avoid invalidation + Ttl: cfg.SOAConfig.Minttl, + }, + Ns: "ns." + domain, + Serial: uint32(time.Now().Unix()), + Mbox: "hostmaster." + domain, + Refresh: cfg.SOAConfig.Refresh, + Retry: cfg.SOAConfig.Retry, + Expire: cfg.SOAConfig.Expire, + Minttl: cfg.SOAConfig.Minttl, + } + m.Ns = append(m.Ns, soa) + + return m +} + +func buildAddressResults(req *dns.Msg) ([]*discovery.Result, error) { + domain := dns.CanonicalName(req.Question[0].Name) + labels := dns.SplitDomainName(domain) + hexadecimal := labels[0] + + if len(hexadecimal)/2 != 4 && len(hexadecimal)/2 != 16 { + return nil, errNameNotFound + } + + var ip net.IP + ip, err := hex.DecodeString(hexadecimal) + if err != nil { + return nil, errNameNotFound + } + + return []*discovery.Result{ + { + Address: ip.String(), + Type: discovery.ResultTypeNode, // We choose node by convention since we do not know the origin of the IP + }, + }, nil +} + +func appendResultToDNSResponse(result *discovery.Result, req *dns.Msg, resp *dns.Msg, _ string, cfg *RouterDynamicConfig) { + ip, ok := convertToIp(result) + + // if the result is not an IP, we can try to recurse on the hostname. + // TODO (v2-dns): hostnames are valid for workloads in V2, do we just want to return the CNAME? + if !ok { + // TODO (v2-dns): recurse on HandleRequest() + panic("not implemented") + } + + var ttl uint32 + switch result.Type { + case discovery.ResultTypeNode: + ttl = uint32(cfg.NodeTTL / time.Second) + case discovery.ResultTypeService: + // TODO (v2-dns): implement service TTL using the radix tree + } + + qName := dns.CanonicalName(req.Question[0].Name) + qType := req.Question[0].Qtype + + record, isIPV4 := makeRecord(qName, ip, ttl) + + if qType == dns.TypeSRV { + // We put A/AAAA/CNAME records in the additional section for SRV requests + resp.Extra = append(resp.Extra, record) + + // TODO (v2-dns): implement SRV records for the answer section + + return + } + + // For explicit A/AAAA queries, we must only return those records in the answer section. + if isIPV4 && qType != dns.TypeA && qType != dns.TypeANY { + resp.Extra = append(resp.Extra, record) + return + } + if !isIPV4 && qType != dns.TypeAAAA && qType != dns.TypeANY { + resp.Extra = append(resp.Extra, record) + return + } + + resp.Answer = append(resp.Answer, record) +} + +func convertToIp(result *discovery.Result) (net.IP, bool) { + ip := net.ParseIP(result.Address) + if ip == nil { + return nil, false + } + return ip, true +} + +// Note: we might want to pass in the Query Name here, which is used in addr. and virtual. queries +// since there is only ever one result. Right now choosing to leave it off for simplification. +func makeRecord(name string, ip net.IP, ttl uint32) (dns.RR, bool) { + + isIPV4 := ip.To4() != nil + + if isIPV4 { + // check if the query type is A for IPv4 or ANY + return &dns.A{ + Hdr: dns.RR_Header{ + Name: name, + Rrtype: dns.TypeA, + Class: dns.ClassINET, + Ttl: ttl, + }, + A: ip, + }, true + } + + return &dns.AAAA{ + Hdr: dns.RR_Header{ + Name: name, + Rrtype: dns.TypeAAAA, + Class: dns.ClassINET, + Ttl: ttl, + }, + AAAA: ip, + }, false +} diff --git a/agent/dns/router_test.go b/agent/dns/router_test.go index 824368b6bb..1735053d67 100644 --- a/agent/dns/router_test.go +++ b/agent/dns/router_test.go @@ -3,4 +3,470 @@ package dns +import ( + "net" + "testing" + "time" + + "github.com/hashicorp/go-hclog" + "github.com/miekg/dns" + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/agent/config" + "github.com/hashicorp/consul/agent/discovery" +) + // TODO (v2-dns) + +// Test Parameters +// 1. Domain vs AltDomain vs non-consul Main domain +// 2. Reload the configuration (e.g. SOA) +// 3. Something to check the token makes it through to the data fetcher +// 4. Something case insensitive + +func Test_HandleRequest(t *testing.T) { + + type testCase struct { + name string + agentConfig *config.RuntimeConfig // This will override the default test Router Config + mockProcessorResponseByName []*discovery.Result // These will be fed to the mock processor to be returned in order + mockProcessorResponseByIP []*discovery.Result + mockProcessorError error + request *dns.Msg + requestContext *discovery.Context + remoteAddress net.Addr + response *dns.Msg + } + + testCases := []testCase{ + { + name: "test A 'addr.' query, ipv4 response", + request: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + }, + Question: []dns.Question{ + { + Name: "c000020a.addr.dc1.consul", // "intentionally missing the trailing dot" + Qtype: dns.TypeA, + Qclass: dns.ClassINET, + }, + }, + }, + response: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + Response: true, + Authoritative: true, + }, + Compress: true, + Question: []dns.Question{ + { + Name: "c000020a.addr.dc1.consul.", + Qtype: dns.TypeA, + Qclass: dns.ClassINET, + }, + }, + Answer: []dns.RR{ + &dns.A{ + Hdr: dns.RR_Header{ + Name: "c000020a.addr.dc1.consul.", + Rrtype: dns.TypeA, + Class: dns.ClassINET, + Ttl: 123, + }, + A: net.ParseIP("192.0.2.10"), + }, + }, + }, + }, + { + name: "test AAAA 'addr.' query, ipv4 response", + // Since we asked for an AAAA record, the A record that resolves from the address is attached as an extra + request: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + }, + Question: []dns.Question{ + { + Name: "c000020a.addr.dc1.consul", + Qtype: dns.TypeAAAA, + Qclass: dns.ClassINET, + }, + }, + }, + response: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + Response: true, + Authoritative: true, + }, + Compress: true, + Question: []dns.Question{ + { + Name: "c000020a.addr.dc1.consul.", + Qtype: dns.TypeAAAA, + Qclass: dns.ClassINET, + }, + }, + Extra: []dns.RR{ + &dns.A{ + Hdr: dns.RR_Header{ + Name: "c000020a.addr.dc1.consul.", + Rrtype: dns.TypeA, + Class: dns.ClassINET, + Ttl: 123, + }, + A: net.ParseIP("192.0.2.10"), + }, + }, + }, + }, + { + name: "test SRV 'addr.' query, ipv4 response", + // Since we asked for a SRV record, the A record that resolves from the address is attached as an extra + request: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + }, + Question: []dns.Question{ + { + Name: "c000020a.addr.dc1.consul", + Qtype: dns.TypeSRV, + Qclass: dns.ClassINET, + }, + }, + }, + response: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + Response: true, + Authoritative: true, + }, + Compress: true, + Question: []dns.Question{ + { + Name: "c000020a.addr.dc1.consul.", + Qtype: dns.TypeSRV, + Qclass: dns.ClassINET, + }, + }, + Extra: []dns.RR{ + &dns.A{ + Hdr: dns.RR_Header{ + Name: "c000020a.addr.dc1.consul.", + Rrtype: dns.TypeA, + Class: dns.ClassINET, + Ttl: 123, + }, + A: net.ParseIP("192.0.2.10"), + }, + }, + }, + }, + { + name: "test ANY 'addr.' query, ipv4 response", + // The response to ANY should look the same as the A response + request: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + }, + Question: []dns.Question{ + { + Name: "c000020a.addr.dc1.consul", + Qtype: dns.TypeANY, + Qclass: dns.ClassINET, + }, + }, + }, + response: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + Response: true, + Authoritative: true, + }, + Compress: true, + Question: []dns.Question{ + { + Name: "c000020a.addr.dc1.consul.", + Qtype: dns.TypeANY, + Qclass: dns.ClassINET, + }, + }, + Answer: []dns.RR{ + &dns.A{ + Hdr: dns.RR_Header{ + Name: "c000020a.addr.dc1.consul.", + Rrtype: dns.TypeA, + Class: dns.ClassINET, + Ttl: 123, + }, + A: net.ParseIP("192.0.2.10"), + }, + }, + }, + }, + { + name: "test AAAA 'addr.' query, ipv6 response", + request: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + }, + Question: []dns.Question{ + { + Name: "20010db800010002cafe000000001337.addr.dc1.consul", + Qtype: dns.TypeAAAA, + Qclass: dns.ClassINET, + }, + }, + }, + response: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + Response: true, + Authoritative: true, + }, + Compress: true, + Question: []dns.Question{ + { + Name: "20010db800010002cafe000000001337.addr.dc1.consul.", + Qtype: dns.TypeAAAA, + Qclass: dns.ClassINET, + }, + }, + Answer: []dns.RR{ + &dns.AAAA{ + Hdr: dns.RR_Header{ + Name: "20010db800010002cafe000000001337.addr.dc1.consul.", + Rrtype: dns.TypeAAAA, + Class: dns.ClassINET, + Ttl: 123, + }, + AAAA: net.ParseIP("2001:db8:1:2:cafe::1337"), + }, + }, + }, + }, + { + name: "test A 'addr.' query, ipv6 response", + // Since we asked for an A record, the AAAA record that resolves from the address is attached as an extra + request: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + }, + Question: []dns.Question{ + { + Name: "20010db800010002cafe000000001337.addr.dc1.consul", + Qtype: dns.TypeA, + Qclass: dns.ClassINET, + }, + }, + }, + response: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + Response: true, + Authoritative: true, + }, + Compress: true, + Question: []dns.Question{ + { + Name: "20010db800010002cafe000000001337.addr.dc1.consul.", + Qtype: dns.TypeA, + Qclass: dns.ClassINET, + }, + }, + Extra: []dns.RR{ + &dns.AAAA{ + Hdr: dns.RR_Header{ + Name: "20010db800010002cafe000000001337.addr.dc1.consul.", + Rrtype: dns.TypeAAAA, + Class: dns.ClassINET, + Ttl: 123, + }, + AAAA: net.ParseIP("2001:db8:1:2:cafe::1337"), + }, + }, + }, + }, + { + name: "test SRV 'addr.' query, ipv6 response", + // Since we asked for an SRV record, the AAAA record that resolves from the address is attached as an extra + request: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + }, + Question: []dns.Question{ + { + Name: "20010db800010002cafe000000001337.addr.dc1.consul", + Qtype: dns.TypeSRV, + Qclass: dns.ClassINET, + }, + }, + }, + response: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + Response: true, + Authoritative: true, + }, + Compress: true, + Question: []dns.Question{ + { + Name: "20010db800010002cafe000000001337.addr.dc1.consul.", + Qtype: dns.TypeSRV, + Qclass: dns.ClassINET, + }, + }, + Extra: []dns.RR{ + &dns.AAAA{ + Hdr: dns.RR_Header{ + Name: "20010db800010002cafe000000001337.addr.dc1.consul.", + Rrtype: dns.TypeAAAA, + Class: dns.ClassINET, + Ttl: 123, + }, + AAAA: net.ParseIP("2001:db8:1:2:cafe::1337"), + }, + }, + }, + }, + { + name: "test ANY 'addr.' query, ipv6 response", + // The response to ANY should look the same as the AAAA response + request: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + }, + Question: []dns.Question{ + { + Name: "20010db800010002cafe000000001337.addr.dc1.consul", + Qtype: dns.TypeANY, + Qclass: dns.ClassINET, + }, + }, + }, + response: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + Response: true, + Authoritative: true, + }, + Compress: true, + Question: []dns.Question{ + { + Name: "20010db800010002cafe000000001337.addr.dc1.consul.", + Qtype: dns.TypeANY, + Qclass: dns.ClassINET, + }, + }, + Answer: []dns.RR{ + &dns.AAAA{ + Hdr: dns.RR_Header{ + Name: "20010db800010002cafe000000001337.addr.dc1.consul.", + Rrtype: dns.TypeAAAA, + Class: dns.ClassINET, + Ttl: 123, + }, + AAAA: net.ParseIP("2001:db8:1:2:cafe::1337"), + }, + }, + }, + }, + { + name: "test malformed 'addr.' query", + request: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + }, + Question: []dns.Question{ + { + Name: "c000.addr.dc1.consul", // too short + Qtype: dns.TypeA, + Qclass: dns.ClassINET, + }, + }, + }, + response: &dns.Msg{ + MsgHdr: dns.MsgHdr{ + Opcode: dns.OpcodeQuery, + Response: true, + Rcode: dns.RcodeNameError, // NXDOMAIN + Authoritative: true, + }, + Compress: true, + Question: []dns.Question{ + { + Name: "c000.addr.dc1.consul.", + Qtype: dns.TypeA, + Qclass: dns.ClassINET, + }, + }, + Ns: []dns.RR{ + &dns.SOA{ + Hdr: dns.RR_Header{ + Name: "consul.", + Rrtype: dns.TypeSOA, + Class: dns.ClassINET, + Ttl: 4, + }, + Ns: "ns.consul.", + Serial: uint32(time.Now().Unix()), + Mbox: "hostmaster.consul.", + Refresh: 1, + Expire: 3, + Retry: 2, + Minttl: 4, + }, + }, + }, + }, + } + + run := func(t *testing.T, tc testCase) { + cfg := buildDNSConfig(tc.agentConfig, tc.mockProcessorResponseByName, tc.mockProcessorResponseByIP, tc.mockProcessorError) + + router, err := NewRouter(cfg) + require.NoError(t, err) + + ctx := tc.requestContext + if ctx == nil { + ctx = &discovery.Context{} + } + + actual := router.HandleRequest(tc.request, *ctx, tc.remoteAddress) + require.Equal(t, tc.response, actual) + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + run(t, tc) + }) + } + +} + +func buildDNSConfig(agentConfig *config.RuntimeConfig, _ []*discovery.Result, _ []*discovery.Result, _ error) Config { + cfg := Config{ + AgentConfig: &config.RuntimeConfig{ + DNSDomain: "consul", + DNSNodeTTL: 123 * time.Second, + DNSSOA: config.RuntimeSOAConfig{ + Refresh: 1, + Retry: 2, + Expire: 3, + Minttl: 4, + }, + }, + EntMeta: nil, + Logger: hclog.NewNullLogger(), + Processor: nil, // TODO (v2-dns): build this from a mock with the reponses loaded + TokenFunc: func() string { return "" }, + } + + if agentConfig != nil { + cfg.AgentConfig = agentConfig + } + + return cfg +} diff --git a/agent/dns_node_lookup_test.go b/agent/dns_node_lookup_test.go index d09587a506..1e507187fc 100644 --- a/agent/dns_node_lookup_test.go +++ b/agent/dns_node_lookup_test.go @@ -5,11 +5,13 @@ package agent import ( "context" - "github.com/hashicorp/consul/agent/structs" - "github.com/hashicorp/consul/testrpc" + "testing" + "github.com/miekg/dns" "github.com/stretchr/testify/require" - "testing" + + "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/testrpc" ) func TestDNS_NodeLookup(t *testing.T) { @@ -18,7 +20,7 @@ func TestDNS_NodeLookup(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -121,7 +123,7 @@ func TestDNS_CaseInsensitiveNodeLookup(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -161,7 +163,7 @@ func TestDNS_NodeLookup_PeriodName(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -209,7 +211,7 @@ func TestDNS_NodeLookup_AAAA(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -269,7 +271,7 @@ func TestDNS_NodeLookup_CNAME(t *testing.T) { }) defer recursor.Shutdown() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` recursors = ["`+recursor.Addr+`"] @@ -327,7 +329,7 @@ func TestDNS_NodeLookup_TXT(t *testing.T) { t.Skip("too slow for testing.Short") } - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -381,7 +383,7 @@ func TestDNS_NodeLookup_TXT_DontSuppress(t *testing.T) { t.Skip("too slow for testing.Short") } - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, `dns_config = { enable_additional_node_meta_txt = false } `+experimentsHCL) defer a.Shutdown() @@ -435,7 +437,7 @@ func TestDNS_NodeLookup_ANY(t *testing.T) { t.Skip("too slow for testing.Short") } - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -484,7 +486,7 @@ func TestDNS_NodeLookup_ANY_DontSuppressTXT(t *testing.T) { t.Skip("too slow for testing.Short") } - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, `dns_config = { enable_additional_node_meta_txt = false } `+experimentsHCL) defer a.Shutdown() @@ -533,7 +535,7 @@ func TestDNS_NodeLookup_A_SuppressTXT(t *testing.T) { t.Skip("too slow for testing.Short") } - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, `dns_config = { enable_additional_node_meta_txt = false } `+experimentsHCL) defer a.Shutdown() @@ -586,7 +588,7 @@ func TestDNS_NodeLookup_TTL(t *testing.T) { }) defer recursor.Shutdown() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` recursors = ["`+recursor.Addr+`"] diff --git a/agent/dns_service_lookup_test.go b/agent/dns_service_lookup_test.go index 606923ab26..a40d00fa12 100644 --- a/agent/dns_service_lookup_test.go +++ b/agent/dns_service_lookup_test.go @@ -6,16 +6,18 @@ package agent import ( "context" "fmt" + "sort" + "strings" + "testing" + + "github.com/miekg/dns" + "github.com/stretchr/testify/require" + "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/sdk/testutil/retry" "github.com/hashicorp/consul/testrpc" - "github.com/miekg/dns" - "github.com/stretchr/testify/require" - "sort" - "strings" - "testing" ) func TestDNS_ServiceReverseLookup(t *testing.T) { @@ -24,7 +26,7 @@ func TestDNS_ServiceReverseLookup(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -80,7 +82,7 @@ func TestDNS_ServiceReverseLookup_IPV6(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -136,7 +138,7 @@ func TestDNS_ServiceReverseLookup_CustomDomain(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` domain = "custom" @@ -194,7 +196,7 @@ func TestDNS_ServiceReverseLookupNodeAddress(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -250,7 +252,7 @@ func TestDNS_ServiceLookupNoMultiCNAME(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -313,7 +315,7 @@ func TestDNS_ServiceLookupPreferNoCNAME(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -379,7 +381,7 @@ func TestDNS_ServiceLookupMultiAddrNoCNAME(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -461,7 +463,7 @@ func TestDNS_ServiceLookup(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -589,7 +591,7 @@ func TestDNS_ServiceLookupWithInternalServiceAddress(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` node_name = "my.test-node" @@ -655,7 +657,7 @@ func TestDNS_ConnectServiceLookup(t *testing.T) { t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -708,7 +710,7 @@ func TestDNS_IngressServiceLookup(t *testing.T) { t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -818,7 +820,7 @@ func TestDNS_ExternalServiceLookup(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -884,7 +886,7 @@ func TestDNS_ExternalServiceToConsulCNAMELookup(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` domain = "CONSUL." @@ -990,7 +992,7 @@ func TestDNS_ExternalServiceToConsulCNAMENestedLookup(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` node_name = "test-node" @@ -1125,7 +1127,7 @@ func TestDNS_ServiceLookup_ServiceAddress_A(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -1226,7 +1228,7 @@ func TestDNS_AltDomain_ServiceLookup_ServiceAddress_A(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` alt_domain = "test-domain" @@ -1342,7 +1344,7 @@ func TestDNS_ServiceLookup_ServiceAddress_SRV(t *testing.T) { }) defer recursor.Shutdown() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` recursors = ["`+recursor.Addr+`"] @@ -1455,7 +1457,7 @@ func TestDNS_ServiceLookup_ServiceAddressIPV6(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -1556,7 +1558,7 @@ func TestDNS_AltDomain_ServiceLookup_ServiceAddressIPV6(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` alt_domain = "test-domain" @@ -1664,7 +1666,7 @@ func TestDNS_ServiceLookup_WanTranslation(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a1 := NewTestAgent(t, ` datacenter = "dc1" @@ -1897,7 +1899,7 @@ func TestDNS_CaseInsensitiveServiceLookup(t *testing.T) { } for _, tst := range tests { t.Run(fmt.Sprintf("A lookup %v", tst.name), func(t *testing.T) { - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, fmt.Sprintf("%s %s", tst.config, experimentsHCL)) defer a.Shutdown() @@ -1982,7 +1984,7 @@ func TestDNS_ServiceLookup_TagPeriod(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -2062,7 +2064,7 @@ func TestDNS_ServiceLookup_PreparedQueryNamePeriod(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -2149,7 +2151,7 @@ func TestDNS_ServiceLookup_Dedup(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -2260,7 +2262,7 @@ func TestDNS_ServiceLookup_Dedup_SRV(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -2399,7 +2401,7 @@ func TestDNS_ServiceLookup_FilterCritical(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -2563,7 +2565,7 @@ func TestDNS_ServiceLookup_OnlyFailing(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -2684,7 +2686,7 @@ func TestDNS_ServiceLookup_OnlyPassing(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` dns_config { @@ -2835,7 +2837,7 @@ func TestDNS_ServiceLookup_Randomize(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -2934,7 +2936,7 @@ func TestDNS_ServiceLookup_Truncate(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` dns_config { @@ -3011,7 +3013,7 @@ func TestDNS_ServiceLookup_LargeResponses(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` dns_config { @@ -3217,7 +3219,7 @@ func checkDNSService( expectedResultsCount int, udpSize uint16, ) { - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` node_name = "test-node" @@ -3390,7 +3392,7 @@ func TestDNS_ServiceLookup_AnswerLimits(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { // Build a matrix of config parameters (udpAnswerLimit), and the @@ -3473,7 +3475,7 @@ func TestDNS_ServiceLookup_CNAME(t *testing.T) { }) defer recursor.Shutdown() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` recursors = ["`+recursor.Addr+`"] @@ -3578,7 +3580,7 @@ func TestDNS_ServiceLookup_ServiceAddress_CNAME(t *testing.T) { }) defer recursor.Shutdown() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` recursors = ["`+recursor.Addr+`"] @@ -3676,7 +3678,7 @@ func TestDNS_ServiceLookup_TTL(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` dns_config { @@ -3762,7 +3764,7 @@ func TestDNS_ServiceLookup_SRV_RFC(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() diff --git a/agent/dns_test.go b/agent/dns_test.go index b8b2771a3a..adc43acb8e 100644 --- a/agent/dns_test.go +++ b/agent/dns_test.go @@ -116,9 +116,16 @@ func dnsTXT(src string, txt []string) *dns.TXT { } } -var versionHCL = map[string]string{ - "DNS: v1 / Catalog: v1": "", - //"DNS: v2 / Catalog: v1": `experiments=["v2-dns"]`, +func getVersionHCL(enableV2 bool) map[string]string { + versions := map[string]string{ + "DNS: v1 / Catalog: v1": "", + //"DNS: v2 / Catalog: v1": `experiments=["v2dns"]`, + } + + if enableV2 { + versions["DNS: v2 / Catalog: v1"] = `experiments=["v2dns"]` + } + return versions } func TestRecursorAddr(t *testing.T) { @@ -182,7 +189,7 @@ func TestDNS_Over_TCP(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -223,7 +230,7 @@ func TestDNS_EmptyAltDomain(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -259,7 +266,7 @@ func TestDNSCycleRecursorCheck(t *testing.T) { }, }) defer server2.Shutdown() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { // Mock the agent startup with the necessary configs agent := NewTestAgent(t, @@ -301,7 +308,7 @@ func TestDNSCycleRecursorCheckAllFail(t *testing.T) { MsgHdr: dns.MsgHdr{Rcode: dns.RcodeRefused}, }) defer server3.Shutdown() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { // Mock the agent startup with the necessary configs agent := NewTestAgent(t, @@ -326,7 +333,7 @@ func TestDNS_EDNS0(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -374,7 +381,7 @@ func TestDNS_EDNS0_ECS(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -473,7 +480,7 @@ func TestDNS_ReverseLookup(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -521,7 +528,7 @@ func TestDNS_ReverseLookup_CustomDomain(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` domain = "custom" @@ -571,7 +578,7 @@ func TestDNS_ReverseLookup_IPV6(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -640,7 +647,7 @@ func TestDNS_SOA_Settings(t *testing.T) { require.Equal(t, uint32(retry), soaRec.Retry) require.Equal(t, uint32(ttl), soaRec.Hdr.Ttl) } - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { // Default configuration @@ -662,7 +669,7 @@ func TestDNS_VirtualIPLookup(t *testing.T) { t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := StartTestAgent(t, TestAgent{HCL: experimentsHCL, Overrides: `peering = { test_allow_peer_registrations = true }`}) defer a.Shutdown() @@ -760,7 +767,7 @@ func TestDNS_InifiniteRecursion(t *testing.T) { // This test should not create an infinite recursion t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` domain = "CONSUL." @@ -822,7 +829,7 @@ func TestDNS_NSRecords(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` domain = "CONSUL." @@ -865,7 +872,7 @@ func TestDNS_AltDomain_NSRecords(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` @@ -921,7 +928,7 @@ func TestDNS_NSRecords_IPV6(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` domain = "CONSUL." @@ -965,7 +972,7 @@ func TestDNS_AltDomain_NSRecords_IPV6(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` domain = "CONSUL." @@ -1021,7 +1028,7 @@ func TestDNS_Lookup_TaggedIPAddresses(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -1230,7 +1237,7 @@ func TestDNS_PreparedQueryNearIPEDNS(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -1365,7 +1372,7 @@ func TestDNS_PreparedQueryNearIP(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -1483,7 +1490,7 @@ func TestDNS_Recurse(t *testing.T) { }) defer recursor.Shutdown() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` recursors = ["`+recursor.Addr+`"] @@ -1523,7 +1530,7 @@ func TestDNS_Recurse_Truncation(t *testing.T) { }) defer recursor.Shutdown() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` recursors = ["`+recursor.Addr+`"] @@ -1572,7 +1579,7 @@ func TestDNS_RecursorTimeout(t *testing.T) { } defer resolver.Close() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` recursors = ["`+resolver.LocalAddr().String()+`"] // host must cause a connection|read|write timeout @@ -1659,7 +1666,7 @@ func TestDNS_TCP_and_UDP_Truncate(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` dns_config { @@ -1777,7 +1784,7 @@ func TestDNS_AddressLookup(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(true) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -1815,7 +1822,7 @@ func TestDNS_AddressLookupANY(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(true) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -1851,7 +1858,7 @@ func TestDNS_AddressLookupInvalidType(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(true) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -1883,7 +1890,7 @@ func TestDNS_AddressLookupIPV6(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(true) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -1932,7 +1939,7 @@ func TestDNS_AddressLookupIPV6InvalidType(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(true) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -1970,7 +1977,7 @@ func TestDNS_NonExistentDC_Server(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -2001,7 +2008,7 @@ func TestDNS_NonExistentDC_RPC(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { s := NewTestAgent(t, ` node_name = "test-server" @@ -2043,7 +2050,7 @@ func TestDNS_NonExistingLookup(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -2080,7 +2087,7 @@ func TestDNS_NonExistingLookupEmptyAorAAAA(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -2222,7 +2229,7 @@ func TestDNS_AltDomains_Service(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` alt_domain = "test-domain." @@ -2321,7 +2328,7 @@ func TestDNS_AltDomains_SOA(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` node_name = "test-node" @@ -2377,7 +2384,7 @@ func TestDNS_AltDomains_Overlap(t *testing.T) { // than one potential match (i.e. ambiguous match) // it should select the longer matching domain when dispatching t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` node_name = "test-node" @@ -2428,7 +2435,7 @@ func TestDNS_AltDomain_DCName_Overlap(t *testing.T) { // this tests the DC name overlap with the consul domain/alt-domain // we should get response when DC suffix is a prefix of consul alt-domain t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` datacenter = "dc-test" @@ -2469,7 +2476,7 @@ func TestDNS_PreparedQuery_AllowStale(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` dns_config { @@ -2526,7 +2533,7 @@ func TestDNS_InvalidQueries(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -2575,7 +2582,7 @@ func TestDNS_PreparedQuery_AgentSource(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown() @@ -2616,7 +2623,7 @@ func TestDNS_EDNS_Truncate_AgentSource(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` dns_config { @@ -2661,7 +2668,7 @@ func TestDNS_EDNS_Truncate_AgentSource(t *testing.T) { func TestDNS_trimUDPResponse_NoTrim(t *testing.T) { t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { req := &dns.Msg{} @@ -2724,7 +2731,7 @@ func TestDNS_trimUDPResponse_NoTrim(t *testing.T) { func TestDNS_trimUDPResponse_TrimLimit(t *testing.T) { t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { cfg := loadRuntimeConfig(t, `node_name = "test" data_dir = "a" bind_addr = "127.0.0.1" node_name = "dummy" `+experimentsHCL) @@ -2768,7 +2775,7 @@ func TestDNS_trimUDPResponse_TrimLimit(t *testing.T) { func TestDNS_trimUDPResponse_TrimLimitWithNS(t *testing.T) { t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { cfg := loadRuntimeConfig(t, `node_name = "test" data_dir = "a" bind_addr = "127.0.0.1" node_name = "dummy" `+experimentsHCL) @@ -2820,7 +2827,7 @@ func TestDNS_trimUDPResponse_TrimLimitWithNS(t *testing.T) { func TestDNS_trimTCPResponse_TrimLimitWithNS(t *testing.T) { t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { cfg := loadRuntimeConfig(t, `node_name = "test" data_dir = "a" bind_addr = "127.0.0.1" node_name = "dummy" `+experimentsHCL) @@ -2881,7 +2888,7 @@ func loadRuntimeConfig(t *testing.T, hcl string) *config.RuntimeConfig { func TestDNS_trimUDPResponse_TrimSize(t *testing.T) { t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { cfg := loadRuntimeConfig(t, `node_name = "test" data_dir = "a" bind_addr = "127.0.0.1" node_name = "dummy" `+experimentsHCL) @@ -2938,7 +2945,7 @@ func TestDNS_trimUDPResponse_TrimSize(t *testing.T) { func TestDNS_trimUDPResponse_TrimSizeEDNS(t *testing.T) { t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { cfg := loadRuntimeConfig(t, `node_name = "test" data_dir = "a" bind_addr = "127.0.0.1" node_name = "dummy" `+experimentsHCL) @@ -3022,7 +3029,7 @@ func TestDNS_trimUDPResponse_TrimSizeEDNS(t *testing.T) { func TestDNS_trimUDPResponse_TrimSizeMaxSize(t *testing.T) { t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { cfg := loadRuntimeConfig(t, `node_name = "test" data_dir = "a" bind_addr = "127.0.0.1" node_name = "dummy" `+experimentsHCL) @@ -3299,7 +3306,7 @@ func TestDNS_syncExtra(t *testing.T) { func TestDNS_Compression_trimUDPResponse(t *testing.T) { t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { cfg := loadRuntimeConfig(t, `data_dir = "a" bind_addr = "127.0.0.1" node_name = "dummy" `+experimentsHCL) @@ -3327,7 +3334,7 @@ func TestDNS_Compression_Query(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) @@ -3423,7 +3430,7 @@ func TestDNS_Compression_ReverseLookup(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) @@ -3489,7 +3496,7 @@ func TestDNS_Compression_Recurse(t *testing.T) { }) defer recursor.Shutdown() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, ` @@ -3683,7 +3690,7 @@ func TestDNS_ReloadConfig_DuringQuery(t *testing.T) { } t.Parallel() - for name, experimentsHCL := range versionHCL { + for name, experimentsHCL := range getVersionHCL(false) { t.Run(name, func(t *testing.T) { a := NewTestAgent(t, experimentsHCL) defer a.Shutdown()