// 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 } // 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", }, }, }, }, { 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, }, }, }, 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, }, }, }, 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) require.NoError(t, err) assert.Equal(t, tc.expectedQuery, query) }) } }