mirror of https://github.com/hashicorp/consul
337 lines
7.9 KiB
Go
337 lines
7.9 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package dns
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/miekg/dns"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/hashicorp/consul/agent/discovery"
|
|
)
|
|
|
|
// testCaseBuildQueryFromDNSMessage is a test case for the buildQueryFromDNSMessage function.
|
|
type testCaseBuildQueryFromDNSMessage struct {
|
|
name string
|
|
request *dns.Msg
|
|
requestContext *Context
|
|
expectedQuery *discovery.Query
|
|
expectedError string
|
|
}
|
|
|
|
// Test_buildQueryFromDNSMessage tests the buildQueryFromDNSMessage function.
|
|
func Test_buildQueryFromDNSMessage(t *testing.T) {
|
|
|
|
testCases := []testCaseBuildQueryFromDNSMessage{
|
|
// virtual ip queries
|
|
{
|
|
name: "test A 'virtual.' query",
|
|
request: &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Opcode: dns.OpcodeQuery,
|
|
},
|
|
Question: []dns.Question{
|
|
{
|
|
Name: "db.virtual.consul", // "intentionally missing the trailing dot"
|
|
Qtype: dns.TypeA,
|
|
Qclass: dns.ClassINET,
|
|
},
|
|
},
|
|
},
|
|
expectedQuery: &discovery.Query{
|
|
QueryType: discovery.QueryTypeVirtual,
|
|
QueryPayload: discovery.QueryPayload{
|
|
Name: "db",
|
|
Tenancy: discovery.QueryTenancy{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "test A 'virtual.' with kitchen sink labels",
|
|
request: &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Opcode: dns.OpcodeQuery,
|
|
},
|
|
Question: []dns.Question{
|
|
{
|
|
Name: "db.virtual.banana.ns.orange.ap.foo.peer.consul", // "intentionally missing the trailing dot"
|
|
Qtype: dns.TypeA,
|
|
Qclass: dns.ClassINET,
|
|
},
|
|
},
|
|
},
|
|
expectedQuery: &discovery.Query{
|
|
QueryType: discovery.QueryTypeVirtual,
|
|
QueryPayload: discovery.QueryPayload{
|
|
Name: "db",
|
|
Tenancy: discovery.QueryTenancy{
|
|
Peer: "foo",
|
|
Namespace: "banana",
|
|
Partition: "orange",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "test A 'virtual.' with implicit peer",
|
|
request: &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Opcode: dns.OpcodeQuery,
|
|
},
|
|
Question: []dns.Question{
|
|
{
|
|
Name: "db.virtual.foo.consul", // "intentionally missing the trailing dot"
|
|
Qtype: dns.TypeA,
|
|
Qclass: dns.ClassINET,
|
|
},
|
|
},
|
|
},
|
|
expectedQuery: &discovery.Query{
|
|
QueryType: discovery.QueryTypeVirtual,
|
|
QueryPayload: discovery.QueryPayload{
|
|
Name: "db",
|
|
Tenancy: discovery.QueryTenancy{
|
|
Peer: "foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "test A 'virtual.' with implicit peer and namespace query",
|
|
request: &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Opcode: dns.OpcodeQuery,
|
|
},
|
|
Question: []dns.Question{
|
|
{
|
|
Name: "db.virtual.frontend.foo.consul", // "intentionally missing the trailing dot"
|
|
Qtype: dns.TypeA,
|
|
Qclass: dns.ClassINET,
|
|
},
|
|
},
|
|
},
|
|
expectedQuery: &discovery.Query{
|
|
QueryType: discovery.QueryTypeVirtual,
|
|
QueryPayload: discovery.QueryPayload{
|
|
Name: "db",
|
|
Tenancy: discovery.QueryTenancy{
|
|
Namespace: "frontend",
|
|
Peer: "foo",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
// V1 Service Queries
|
|
{
|
|
name: "test A 'service.' standard query with tag",
|
|
request: &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Opcode: dns.OpcodeQuery,
|
|
},
|
|
Question: []dns.Question{
|
|
{
|
|
Name: "primary.db.service.dc1.consul", // "intentionally missing the trailing dot"
|
|
Qtype: dns.TypeA,
|
|
Qclass: dns.ClassINET,
|
|
},
|
|
},
|
|
},
|
|
expectedQuery: &discovery.Query{
|
|
QueryType: discovery.QueryTypeService,
|
|
QueryPayload: discovery.QueryPayload{
|
|
Name: "db",
|
|
Tag: "primary",
|
|
Tenancy: discovery.QueryTenancy{
|
|
Datacenter: "dc1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "test A 'service.' RFC 2782 query with tag",
|
|
request: &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Opcode: dns.OpcodeQuery,
|
|
},
|
|
Question: []dns.Question{
|
|
{
|
|
Name: "_db._primary.service.dc1.consul", // "intentionally missing the trailing dot"
|
|
Qtype: dns.TypeA,
|
|
Qclass: dns.ClassINET,
|
|
},
|
|
},
|
|
},
|
|
expectedQuery: &discovery.Query{
|
|
QueryType: discovery.QueryTypeService,
|
|
QueryPayload: discovery.QueryPayload{
|
|
Name: "db",
|
|
Tag: "primary",
|
|
Tenancy: discovery.QueryTenancy{
|
|
Datacenter: "dc1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "test A 'service.' RFC 2782 query",
|
|
request: &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Opcode: dns.OpcodeQuery,
|
|
},
|
|
Question: []dns.Question{
|
|
{
|
|
Name: "_db._tcp.service.dc1.consul", // the `tcp` tag should be ignored
|
|
Qtype: dns.TypeA,
|
|
Qclass: dns.ClassINET,
|
|
},
|
|
},
|
|
},
|
|
expectedQuery: &discovery.Query{
|
|
QueryType: discovery.QueryTypeService,
|
|
QueryPayload: discovery.QueryPayload{
|
|
Name: "db",
|
|
Tenancy: discovery.QueryTenancy{
|
|
Datacenter: "dc1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "test A 'service.' with too many query parts (RFC 2782 style)",
|
|
request: &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Opcode: dns.OpcodeQuery,
|
|
},
|
|
Question: []dns.Question{
|
|
{
|
|
Name: "nope._db._tcp.service.dc1.consul", // the `tcp` tag should be ignored
|
|
Qtype: dns.TypeA,
|
|
Qclass: dns.ClassINET,
|
|
},
|
|
},
|
|
},
|
|
expectedError: "invalid question",
|
|
},
|
|
{
|
|
name: "test A 'service.' with too many query parts (standard style)",
|
|
request: &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Opcode: dns.OpcodeQuery,
|
|
},
|
|
Question: []dns.Question{
|
|
{
|
|
Name: "too.many.parts.service.dc1.consul.",
|
|
Qtype: dns.TypeA,
|
|
Qclass: dns.ClassINET,
|
|
},
|
|
},
|
|
},
|
|
expectedError: "invalid question",
|
|
},
|
|
// V2 Catalog Queries
|
|
{
|
|
name: "test A 'workload.'",
|
|
request: &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Opcode: dns.OpcodeQuery,
|
|
},
|
|
Question: []dns.Question{
|
|
{
|
|
Name: "foo.workload.consul", // "intentionally missing the trailing dot"
|
|
Qtype: dns.TypeA,
|
|
Qclass: dns.ClassINET,
|
|
},
|
|
},
|
|
},
|
|
expectedQuery: &discovery.Query{
|
|
QueryType: discovery.QueryTypeWorkload,
|
|
QueryPayload: discovery.QueryPayload{
|
|
Name: "foo",
|
|
Tenancy: discovery.QueryTenancy{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "test A 'workload.' with all possible labels",
|
|
request: &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Opcode: dns.OpcodeQuery,
|
|
},
|
|
Question: []dns.Question{
|
|
{
|
|
Name: "api.port.foo.workload.banana.ns.orange.ap.apple.peer.consul", // "intentionally missing the trailing dot"
|
|
Qtype: dns.TypeA,
|
|
Qclass: dns.ClassINET,
|
|
},
|
|
},
|
|
},
|
|
requestContext: &Context{
|
|
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",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "test sameness group with all possible labels",
|
|
request: &dns.Msg{
|
|
MsgHdr: dns.MsgHdr{
|
|
Opcode: dns.OpcodeQuery,
|
|
},
|
|
Question: []dns.Question{
|
|
{
|
|
Name: "foo.service.apple.sg.banana.ns.orange.ap.consul", // "intentionally missing the trailing dot"
|
|
Qtype: dns.TypeA,
|
|
Qclass: dns.ClassINET,
|
|
},
|
|
},
|
|
},
|
|
requestContext: &Context{
|
|
DefaultPartition: "default-partition",
|
|
},
|
|
expectedQuery: &discovery.Query{
|
|
QueryType: discovery.QueryTypeService,
|
|
QueryPayload: discovery.QueryPayload{
|
|
Name: "foo",
|
|
Tenancy: discovery.QueryTenancy{
|
|
Namespace: "banana",
|
|
Partition: "orange",
|
|
SamenessGroup: "apple",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
context := tc.requestContext
|
|
if context == nil {
|
|
context = &Context{}
|
|
}
|
|
query, err := buildQueryFromDNSMessage(tc.request, *context, "consul.", ".", nil)
|
|
|
|
if tc.expectedError != "" {
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), tc.expectedError)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.expectedQuery, query)
|
|
})
|
|
}
|
|
}
|