2024-01-05 20:37:27 +00:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package agent
import (
"context"
"fmt"
2024-02-03 03:23:52 +00:00
"net"
2024-01-17 03:36:02 +00:00
"sort"
"strings"
"testing"
"github.com/miekg/dns"
"github.com/stretchr/testify/require"
2024-01-05 20:37:27 +00:00
"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"
)
func TestDNS_ServiceLookupNoMultiCNAME ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-01-29 22:33:45 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with a service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "198.18.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Port : 12345 ,
Address : "foo.node.consul" ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
require . NoError ( t , a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a second node node with the same service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "bar" ,
Address : "198.18.0.2" ,
Service : & structs . NodeService {
Service : "db" ,
Port : 12345 ,
Address : "bar.node.consul" ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
m := new ( dns . Msg )
m . SetQuestion ( "db.service.consul." , dns . TypeANY )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
require . NoError ( t , err )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// expect a CNAME and an A RR
require . Len ( t , in . Answer , 2 )
require . IsType ( t , & dns . CNAME { } , in . Answer [ 0 ] )
require . IsType ( t , & dns . A { } , in . Answer [ 1 ] )
} )
}
2024-01-05 20:37:27 +00:00
}
func TestDNS_ServiceLookupPreferNoCNAME ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-01-29 22:33:45 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with a service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "198.18.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Port : 12345 ,
Address : "198.18.0.1" ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
require . NoError ( t , a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a second node node with the same service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "bar" ,
Address : "198.18.0.2" ,
Service : & structs . NodeService {
Service : "db" ,
Port : 12345 ,
Address : "bar.node.consul" ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
m := new ( dns . Msg )
m . SetQuestion ( "db.service.consul." , dns . TypeANY )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
require . NoError ( t , err )
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
// expect an A RR
2024-01-08 20:14:26 +00:00
require . Len ( t , in . Answer , 1 )
aRec , ok := in . Answer [ 0 ] . ( * dns . A )
require . Truef ( t , ok , "Not an A RR" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
require . Equal ( t , "db.service.consul." , aRec . Hdr . Name )
require . Equal ( t , "198.18.0.1" , aRec . A . String ( ) )
} )
}
2024-01-05 20:37:27 +00:00
}
func TestDNS_ServiceLookupMultiAddrNoCNAME ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-01-29 22:33:45 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with a service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "198.18.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Port : 12345 ,
Address : "198.18.0.1" ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
require . NoError ( t , a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a second node node with the same service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "bar" ,
Address : "198.18.0.2" ,
Service : & structs . NodeService {
Service : "db" ,
Port : 12345 ,
Address : "bar.node.consul" ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a second node node with the same service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "baz" ,
Address : "198.18.0.3" ,
Service : & structs . NodeService {
Service : "db" ,
Port : 12345 ,
Address : "198.18.0.3" ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
m := new ( dns . Msg )
m . SetQuestion ( "db.service.consul." , dns . TypeANY )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
require . NoError ( t , err )
2024-01-05 20:37:27 +00:00
2024-02-03 03:23:52 +00:00
// expect two A RRs
2024-01-08 20:14:26 +00:00
require . Len ( t , in . Answer , 2 )
require . IsType ( t , & dns . A { } , in . Answer [ 0 ] )
2024-02-03 03:23:52 +00:00
require . Equal ( t , "db.service.consul." , in . Answer [ 0 ] . Header ( ) . Name )
isOneOfTheseIPs := func ( ip net . IP ) bool {
if ip . Equal ( net . ParseIP ( "198.18.0.1" ) ) || ip . Equal ( net . ParseIP ( "198.18.0.3" ) ) {
return true
}
return false
}
require . True ( t , isOneOfTheseIPs ( in . Answer [ 0 ] . ( * dns . A ) . A ) )
2024-01-08 20:14:26 +00:00
require . IsType ( t , & dns . A { } , in . Answer [ 1 ] )
2024-02-03 03:23:52 +00:00
require . Equal ( t , "db.service.consul." , in . Answer [ 1 ] . Header ( ) . Name )
require . True ( t , isOneOfTheseIPs ( in . Answer [ 1 ] . ( * dns . A ) . A ) )
2024-01-08 20:14:26 +00:00
} )
}
2024-01-05 20:37:27 +00:00
}
func TestDNS_ServiceLookup ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-08 04:24:00 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with a service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "db" ,
} ,
2024-02-09 16:26:02 +00:00
DNS : structs . QueryDNSOptions {
TTL : "3s" ,
} ,
2024-01-08 20:14:26 +00:00
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] string {
"db.service.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
aRec , ok := in . Extra [ 0 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Name != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . A . String ( ) != "127.0.0.1" {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
2024-02-09 16:26:02 +00:00
if strings . Contains ( question , "query" ) {
// The query should have the TTL associated with the query registration.
if srvRec . Hdr . Ttl != 3 {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if aRec . Hdr . Ttl != 3 {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
} else {
if srvRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if aRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
2024-01-08 20:14:26 +00:00
}
2024-02-09 16:26:02 +00:00
2024-01-08 20:14:26 +00:00
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Lookup a non-existing service/query, we should receive an SOA.
questions = [ ] string {
"nodb.service.consul." ,
"nope.query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Ns ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
soaRec , ok := in . Ns [ 0 ] . ( * dns . SOA )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Ns [ 0 ] )
}
if soaRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Ns [ 0 ] )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
2024-06-25 17:42:25 +00:00
// TestDNS_ServiceAddressWithTagLookup tests some specific cases that Nomad would exercise,
// Like registering a service w/o a Node. https://github.com/hashicorp/nomad/blob/1174019676ff3d65b39323eb0c7234fb1e09b80c/command/agent/consul/service_client.go#L1366-L1381
// Errors with this were reported in https://github.com/hashicorp/consul/issues/21325#issuecomment-2166845574
// Also we test that only one tag is valid in the URL.
func TestDNS_ServiceAddressWithTagLookup ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
for name , experimentsHCL := range getVersionHCL ( true ) {
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
{
// This emulates a Nomad service registration.
// Using an internal RPC for Catalog.Register will not trigger the same condition.
err := a . Client ( ) . Agent ( ) . ServiceRegister ( & api . AgentServiceRegistration {
Kind : api . ServiceKindTypical ,
ID : "db-1" ,
Name : "db" ,
Tags : [ ] string { "primary" } ,
Address : "127.0.0.1" ,
Port : 12345 ,
Checks : make ( [ ] * api . AgentServiceCheck , 0 ) ,
} )
require . NoError ( t , err )
}
{
err := a . Client ( ) . Agent ( ) . ServiceRegister ( & api . AgentServiceRegistration {
Kind : api . ServiceKindTypical ,
ID : "db-2" ,
Name : "db" ,
Tags : [ ] string { "secondary" } ,
Address : "127.0.0.2" , // The address here has to be different, or the DNS server will dedupe it.
Port : 12345 ,
Checks : make ( [ ] * api . AgentServiceCheck , 0 ) ,
} )
require . NoError ( t , err )
}
// Query the service using a tag - this also checks that we're filtering correctly
questions := [ ] string {
"_db._primary.service.dc1.consul." , // w/ RFC 2782 style syntax
"primary.db.service.dc1.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
require . NoError ( t , err )
require . Len ( t , in . Answer , 1 , "Expected only one result in the Answer section" )
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
require . True ( t , ok , "Expected an SRV record in the Answer section" )
require . Equal ( t , uint16 ( 12345 ) , srvRec . Port )
require . Equal ( t , "7f000001.addr.dc1.consul." , srvRec . Target )
aRec , ok := in . Extra [ 0 ] . ( * dns . A )
require . True ( t , ok , "Expected an A record in the Extra section" )
require . Equal ( t , "7f000001.addr.dc1.consul." , aRec . Hdr . Name )
require . Equal ( t , "127.0.0.1" , aRec . A . String ( ) )
if strings . Contains ( question , "query" ) {
// The query should have the TTL associated with the query registration.
require . Equal ( t , uint32 ( 3 ) , srvRec . Hdr . Ttl )
require . Equal ( t , uint32 ( 3 ) , aRec . Hdr . Ttl )
} else {
require . Equal ( t , uint32 ( 0 ) , srvRec . Hdr . Ttl )
require . Equal ( t , uint32 ( 0 ) , aRec . Hdr . Ttl )
}
}
// Multiple tags are not supported in the legacy DNS server
questions = [ ] string {
"banana._db._primary.service.dc1.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
require . NoError ( t , err )
require . Len ( t , in . Answer , 0 , "Expected no results in the Answer section" )
// For v1dns, we combine the tags with a period, which results in NXDOMAIN
// For v2dns, we are also return NXDomain
// The reported issue says that v2dns this is returning valid results.
require . Equal ( t , dns . RcodeNameError , in . Rcode )
}
} )
}
}
2024-01-05 20:37:27 +00:00
func TestDNS_ServiceLookupWithInternalServiceAddress ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-03 03:23:52 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
node_name = "my.test-node"
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with a service.
// The service is using the consul DNS name as service address
// which triggers a lookup loop and a subsequent stack overflow
// crash.
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Address : "db.service.consul" ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Looking up the service should not trigger a loop
m := new ( dns . Msg )
m . SetQuestion ( "db.service.consul." , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
wantAnswer := [ ] dns . RR {
& dns . SRV {
Hdr : dns . RR_Header { Name : "db.service.consul." , Rrtype : 0x21 , Class : 0x1 , Rdlength : 0x1b } ,
Priority : 0x1 ,
Weight : 0x1 ,
Port : 12345 ,
Target : "foo.node.dc1.consul." ,
} ,
}
require . Equal ( t , wantAnswer , in . Answer , "answer" )
wantExtra := [ ] dns . RR {
& dns . A {
Hdr : dns . RR_Header { Name : "foo.node.dc1.consul." , Rrtype : 0x1 , Class : 0x1 , Rdlength : 0x4 } ,
A : [ ] byte { 0x7f , 0x0 , 0x0 , 0x1 } , // 127.0.0.1
} ,
}
require . Equal ( t , wantExtra , in . Extra , "extra" )
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ConnectServiceLookup ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-09 02:56:04 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register
{
args := structs . TestRegisterRequestProxy ( t )
args . Address = "127.0.0.55"
args . Service . Proxy . DestinationServiceName = "db"
args . Service . Address = ""
args . Service . Port = 12345
var out struct { }
require . Nil ( t , a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service
questions := [ ] string {
"db.connect.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
require . Nil ( t , err )
require . Len ( t , in . Answer , 1 )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
require . True ( t , ok )
require . Equal ( t , uint16 ( 12345 ) , srvRec . Port )
require . Equal ( t , "foo.node.dc1.consul." , srvRec . Target )
require . Equal ( t , uint32 ( 0 ) , srvRec . Hdr . Ttl )
cnameRec , ok := in . Extra [ 0 ] . ( * dns . A )
require . True ( t , ok )
require . Equal ( t , "foo.node.dc1.consul." , cnameRec . Hdr . Name )
require . Equal ( t , uint32 ( 0 ) , srvRec . Hdr . Ttl )
require . Equal ( t , "127.0.0.55" , cnameRec . A . String ( ) )
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_IngressServiceLookup ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-09 02:56:04 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register ingress-gateway service
{
args := structs . TestRegisterIngressGateway ( t )
var out struct { }
require . Nil ( t , a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register db service
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Address : "" ,
Port : 80 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
require . Nil ( t , a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register proxy-defaults with 'http' protocol
{
req := structs . ConfigEntryRequest {
Op : structs . ConfigEntryUpsert ,
Datacenter : "dc1" ,
Entry : & structs . ProxyConfigEntry {
Kind : structs . ProxyDefaults ,
Name : structs . ProxyConfigGlobal ,
Config : map [ string ] interface { } {
"protocol" : "http" ,
} ,
2024-01-05 20:37:27 +00:00
} ,
2024-01-08 20:14:26 +00:00
WriteRequest : structs . WriteRequest { Token : "root" } ,
}
var out bool
require . Nil ( t , a . RPC ( context . Background ( ) , "ConfigEntry.Apply" , req , & out ) )
require . True ( t , out )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register ingress-gateway config entry
{
args := & structs . IngressGatewayConfigEntry {
Name : "ingress-gateway" ,
Kind : structs . IngressGateway ,
Listeners : [ ] structs . IngressListener {
{
Port : 8888 ,
Protocol : "http" ,
Services : [ ] structs . IngressService {
{ Name : "db" } ,
{ Name : "api" } ,
} ,
} ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
req := structs . ConfigEntryRequest {
Op : structs . ConfigEntryUpsert ,
Datacenter : "dc1" ,
Entry : args ,
}
var out bool
require . Nil ( t , a . RPC ( context . Background ( ) , "ConfigEntry.Apply" , req , & out ) )
require . True ( t , out )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service
questions := [ ] string {
"api.ingress.consul." ,
"api.ingress.dc1.consul." ,
"db.ingress.consul." ,
"db.ingress.dc1.consul." ,
}
for _ , question := range questions {
t . Run ( question , func ( t * testing . T ) {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeA )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
require . Nil ( t , err )
require . Len ( t , in . Answer , 1 )
cnameRec , ok := in . Answer [ 0 ] . ( * dns . A )
require . True ( t , ok )
require . Equal ( t , question , cnameRec . Hdr . Name )
require . Equal ( t , uint32 ( 0 ) , cnameRec . Hdr . Ttl )
require . Equal ( t , "127.0.0.1" , cnameRec . A . String ( ) )
} )
}
2024-01-05 20:37:27 +00:00
} )
}
}
func TestDNS_ExternalServiceLookup ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-01-29 22:33:45 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with an external service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "www.google.com" ,
Service : & structs . NodeService {
Service : "db" ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service
questions := [ ] string {
"db.service.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
if len ( in . Answer ) != 1 || len ( in . Extra ) > 0 {
2024-01-08 20:14:26 +00:00
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "www.google.com." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ExternalServiceToConsulCNAMELookup ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-01-29 22:33:45 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
domain = "CONSUL."
node_name = "test node"
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register the initial node with a service
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "web" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "web" ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an external service pointing to the 'web' service
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "alias" ,
Address : "web.service.consul" ,
Service : & structs . NodeService {
Service : "alias" ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly
questions := [ ] string {
"alias.service.consul." ,
"alias.service.CoNsUl." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "web.service.consul." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Extra ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
aRec , ok := in . Extra [ 0 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Name != "web.service.consul." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . A . String ( ) != "127.0.0.1" {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ExternalServiceToConsulCNAMENestedLookup ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-03 03:23:52 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
node_name = "test-node"
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register the initial node with a service
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "web" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "web" ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an external service pointing to the 'web' service
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "alias" ,
Address : "web.service.consul" ,
Service : & structs . NodeService {
Service : "alias" ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an external service pointing to the 'alias' service
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "alias2" ,
Address : "alias.service.consul" ,
Service : & structs . NodeService {
Service : "alias2" ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly
questions := [ ] string {
"alias2.service.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "alias.service.consul." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if len ( in . Extra ) != 2 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
cnameRec , ok := in . Extra [ 0 ] . ( * dns . CNAME )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if cnameRec . Hdr . Name != "alias.service.consul." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if cnameRec . Target != "web.service.consul." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if cnameRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
aRec , ok := in . Extra [ 1 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 1 ] )
}
if aRec . Hdr . Name != "web.service.consul." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 1 ] )
}
if aRec . A . String ( ) != "127.0.0.1" {
t . Fatalf ( "Bad: %#v" , in . Extra [ 1 ] )
}
if aRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Extra [ 1 ] )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ServiceLookup_ServiceAddress_A ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-09 02:56:04 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with a service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Address : "127.0.0.2" ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "db" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] string {
"db.service.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "7f000002.addr.dc1.consul." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
aRec , ok := in . Extra [ 0 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Name != "7f000002.addr.dc1.consul." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . A . String ( ) != "127.0.0.2" {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_AltDomain_ServiceLookup_ServiceAddress_A ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-09 02:56:04 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
alt_domain = "test-domain"
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with a service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Address : "127.0.0.2" ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "db" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] struct {
ask string
wantDomain string
} {
{ "db.service.consul." , "consul." } ,
{ id + ".query.consul." , "consul." } ,
{ "db.service.test-domain." , "test-domain." } ,
{ id + ".query.test-domain." , "test-domain." } ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question . ask , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "7f000002.addr.dc1." + question . wantDomain {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
aRec , ok := in . Extra [ 0 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Name != "7f000002.addr.dc1." + question . wantDomain {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . A . String ( ) != "127.0.0.2" {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ServiceLookup_ServiceAddress_SRV ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
recursor := makeRecursor ( t , dns . Msg {
Answer : [ ] dns . RR {
dnsCNAME ( "www.google.com" , "google.com" ) ,
dnsA ( "google.com" , "1.2.3.4" ) ,
} ,
} )
defer recursor . Shutdown ( )
2024-02-12 19:27:25 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
recursors = [ "`+recursor.Addr+`" ]
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with a service whose address isn't an IP.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Address : "www.google.com" ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
// Specify prepared query name containing "." to test
// since that is technically supported (though atypical).
var id string
preparedQueryName := "query.name.with.dots"
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : preparedQueryName ,
Service : structs . ServiceQuery {
Service : "db" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] string {
"db.service.consul." ,
id + ".query.consul." ,
preparedQueryName + ".query.consul." ,
fmt . Sprintf ( "_%s._tcp.query.consul." , id ) ,
fmt . Sprintf ( "_%s._tcp.query.consul." , preparedQueryName ) ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "www.google.com." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Should have google CNAME
cnRec , ok := in . Extra [ 0 ] . ( * dns . CNAME )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if cnRec . Target != "google.com." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Check we recursively resolve
aRec , ok := in . Extra [ 1 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 1 ] )
}
if aRec . A . String ( ) != "1.2.3.4" {
t . Fatalf ( "Bad: %s" , aRec . A . String ( ) )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ServiceLookup_ServiceAddressIPV6 ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-09 02:56:04 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with a service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Address : "2607:20:4005:808::200e" ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "db" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] string {
"db.service.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "2607002040050808000000000000200e.addr.dc1.consul." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
aRec , ok := in . Extra [ 0 ] . ( * dns . AAAA )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Name != "2607002040050808000000000000200e.addr.dc1.consul." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . AAAA . String ( ) != "2607:20:4005:808::200e" {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_AltDomain_ServiceLookup_ServiceAddressIPV6 ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-09 02:56:04 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
alt_domain = "test-domain"
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with a service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Address : "2607:20:4005:808::200e" ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "db" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] struct {
ask string
want string
} {
{ "db.service.consul." , "2607002040050808000000000000200e.addr.dc1.consul." } ,
{ "db.service.test-domain." , "2607002040050808000000000000200e.addr.dc1.test-domain." } ,
{ id + ".query.consul." , "2607002040050808000000000000200e.addr.dc1.consul." } ,
{ id + ".query.test-domain." , "2607002040050808000000000000200e.addr.dc1.test-domain." } ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question . ask , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != question . want {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
aRec , ok := in . Extra [ 0 ] . ( * dns . AAAA )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Name != question . want {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . AAAA . String ( ) != "2607:20:4005:808::200e" {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ServiceLookup_WanTranslation ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-12 19:27:25 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a1 := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
datacenter = "dc1"
translate_wan_addrs = true
acl_datacenter = ""
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a1 . Shutdown ( )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
a2 := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
datacenter = "dc2"
translate_wan_addrs = true
acl_datacenter = ""
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a2 . Shutdown ( )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Join WAN cluster
addr := fmt . Sprintf ( "127.0.0.1:%d" , a1 . Config . SerfPortWAN )
_ , err := a2 . JoinWAN ( [ ] string { addr } )
require . NoError ( t , err )
2024-01-05 20:37:27 +00:00
retry . Run ( t , func ( r * retry . R ) {
2024-01-08 20:14:26 +00:00
require . Len ( r , a1 . WANMembers ( ) , 2 )
require . Len ( r , a2 . WANMembers ( ) , 2 )
2024-01-05 20:37:27 +00:00
} )
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc2" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "db" ,
} ,
} ,
}
require . NoError ( t , a2 . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) )
2024-01-05 20:37:27 +00:00
}
2024-01-08 20:14:26 +00:00
type testCase struct {
nodeTaggedAddresses map [ string ] string
serviceAddress string
serviceTaggedAddresses map [ string ] structs . ServiceAddress
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
dnsAddr string
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
expectedPort uint16
expectedAddress string
expectedARRName string
2024-01-05 20:37:27 +00:00
}
2024-01-08 20:14:26 +00:00
cases := map [ string ] testCase {
"node-addr-from-dc1" : {
dnsAddr : a1 . config . DNSAddrs [ 0 ] . String ( ) ,
expectedPort : 8080 ,
expectedAddress : "127.0.0.1" ,
expectedARRName : "foo.node.dc2.consul." ,
} ,
"node-wan-from-dc1" : {
dnsAddr : a1 . config . DNSAddrs [ 0 ] . String ( ) ,
nodeTaggedAddresses : map [ string ] string {
"wan" : "127.0.0.2" ,
} ,
expectedPort : 8080 ,
expectedAddress : "127.0.0.2" ,
expectedARRName : "7f000002.addr.dc2.consul." ,
} ,
"service-addr-from-dc1" : {
dnsAddr : a1 . config . DNSAddrs [ 0 ] . String ( ) ,
nodeTaggedAddresses : map [ string ] string {
"wan" : "127.0.0.2" ,
} ,
serviceAddress : "10.0.1.1" ,
expectedPort : 8080 ,
expectedAddress : "10.0.1.1" ,
expectedARRName : "0a000101.addr.dc2.consul." ,
} ,
"service-wan-from-dc1" : {
dnsAddr : a1 . config . DNSAddrs [ 0 ] . String ( ) ,
nodeTaggedAddresses : map [ string ] string {
"wan" : "127.0.0.2" ,
} ,
serviceAddress : "10.0.1.1" ,
serviceTaggedAddresses : map [ string ] structs . ServiceAddress {
"wan" : {
Address : "198.18.0.1" ,
Port : 80 ,
} ,
} ,
expectedPort : 80 ,
expectedAddress : "198.18.0.1" ,
expectedARRName : "c6120001.addr.dc2.consul." ,
} ,
"node-addr-from-dc2" : {
dnsAddr : a2 . config . DNSAddrs [ 0 ] . String ( ) ,
expectedPort : 8080 ,
expectedAddress : "127.0.0.1" ,
expectedARRName : "foo.node.dc2.consul." ,
} ,
"node-wan-from-dc2" : {
dnsAddr : a2 . config . DNSAddrs [ 0 ] . String ( ) ,
nodeTaggedAddresses : map [ string ] string {
"wan" : "127.0.0.2" ,
} ,
expectedPort : 8080 ,
expectedAddress : "127.0.0.1" ,
expectedARRName : "foo.node.dc2.consul." ,
} ,
"service-addr-from-dc2" : {
dnsAddr : a2 . config . DNSAddrs [ 0 ] . String ( ) ,
nodeTaggedAddresses : map [ string ] string {
"wan" : "127.0.0.2" ,
} ,
serviceAddress : "10.0.1.1" ,
expectedPort : 8080 ,
expectedAddress : "10.0.1.1" ,
expectedARRName : "0a000101.addr.dc2.consul." ,
} ,
"service-wan-from-dc2" : {
dnsAddr : a2 . config . DNSAddrs [ 0 ] . String ( ) ,
nodeTaggedAddresses : map [ string ] string {
"wan" : "127.0.0.2" ,
} ,
serviceAddress : "10.0.1.1" ,
serviceTaggedAddresses : map [ string ] structs . ServiceAddress {
"wan" : {
Address : "198.18.0.1" ,
Port : 80 ,
} ,
} ,
expectedPort : 8080 ,
expectedAddress : "10.0.1.1" ,
expectedARRName : "0a000101.addr.dc2.consul." ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
for name , tc := range cases {
name := name
tc := tc
t . Run ( name , func ( t * testing . T ) {
// Register a remote node with a service. This is in a retry since we
// need the datacenter to have a route which takes a little more time
// beyond the join, and we don't have direct access to the router here.
retry . Run ( t , func ( r * retry . R ) {
args := & structs . RegisterRequest {
Datacenter : "dc2" ,
Node : "foo" ,
Address : "127.0.0.1" ,
TaggedAddresses : tc . nodeTaggedAddresses ,
Service : & structs . NodeService {
Service : "db" ,
Address : tc . serviceAddress ,
Port : 8080 ,
TaggedAddresses : tc . serviceTaggedAddresses ,
} ,
}
var out struct { }
require . NoError ( r , a2 . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) )
} )
// Look up the SRV record via service and prepared query.
questions := [ ] string {
"db.service.dc2.consul." ,
id + ".query.dc2.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
c := new ( dns . Client )
addr := tc . dnsAddr
in , _ , err := c . Exchange ( m , addr )
require . NoError ( t , err )
require . Len ( t , in . Answer , 1 )
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
require . True ( t , ok , "Bad: %#v" , in . Answer [ 0 ] )
require . Equal ( t , tc . expectedPort , srvRec . Port )
aRec , ok := in . Extra [ 0 ] . ( * dns . A )
require . True ( t , ok , "Bad: %#v" , in . Extra [ 0 ] )
require . Equal ( t , tc . expectedARRName , aRec . Hdr . Name )
require . Equal ( t , tc . expectedAddress , aRec . A . String ( ) )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Also check the A record directly
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeA )
c := new ( dns . Client )
addr := tc . dnsAddr
in , _ , err := c . Exchange ( m , addr )
require . NoError ( t , err )
require . Len ( t , in . Answer , 1 )
aRec , ok := in . Answer [ 0 ] . ( * dns . A )
require . True ( t , ok , "Bad: %#v" , in . Answer [ 0 ] )
require . Equal ( t , question , aRec . Hdr . Name )
require . Equal ( t , tc . expectedAddress , aRec . A . String ( ) )
}
} )
2024-01-05 20:37:27 +00:00
}
} )
}
}
2024-02-06 14:53:39 +00:00
func TestDNS_ServiceLookup_CaseInsensitive ( t * testing . T ) {
2024-01-05 20:37:27 +00:00
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
tests := [ ] struct {
name string
config string
} {
// UDP + EDNS
{ "normal" , "" } ,
{ "cache" , ` dns_config { allow_stale=true, max_stale="3h", use_cache=true, "cache_max_age"="3h"} ` } ,
{ "cache-with-streaming" , `
rpc {
enable_streaming = true
}
use_streaming_backend = true
dns_config { allow_stale = true , max_stale = "3h" , use_cache = true , "cache_max_age" = "3h" }
` } ,
}
for _ , tst := range tests {
t . Run ( fmt . Sprintf ( "A lookup %v" , tst . name ) , func ( t * testing . T ) {
2024-02-03 03:23:52 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , fmt . Sprintf ( "%s %s" , tst . config , experimentsHCL ) )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
// Register a node with a service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "Db" ,
Tags : [ ] string { "Primary" } ,
Port : 12345 ,
} ,
}
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
// Register an equivalent prepared query, as well as a name.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "somequery" ,
Service : structs . ServiceQuery {
Service : "db" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
// Try some variations to make sure case doesn't matter.
questions := [ ] string {
"primary.Db.service.consul." ,
"primary.db.service.consul." ,
"pRIMARY.dB.service.consul." ,
"PRIMARY.dB.service.consul." ,
"db.service.consul." ,
"DB.service.consul." ,
"Db.service.consul." ,
"somequery.query.consul." ,
"SomeQuery.query.consul." ,
"SOMEQUERY.query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
c := new ( dns . Client )
retry . Run ( t , func ( r * retry . R ) {
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
r . Fatalf ( "err: %v" , err )
}
if len ( in . Answer ) != 1 {
r . Fatalf ( "question %v, empty lookup: %#v" , question , in )
}
} )
}
} )
}
} )
}
}
2024-02-14 22:38:11 +00:00
// V2 DNS: we have deprecated support for service tags w/ periods
2024-01-08 20:14:26 +00:00
func TestDNS_ServiceLookup_TagPeriod ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-01-17 03:36:02 +00:00
for name , experimentsHCL := range getVersionHCL ( false ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
// Register node
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "v1.primary" } ,
Port : 12345 ,
} ,
}
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
m1 := new ( dns . Msg )
m1 . SetQuestion ( "v1.primary2.db.service.consul." , dns . TypeSRV )
c1 := new ( dns . Client )
in , _ , err := c1 . Exchange ( m1 , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
if len ( in . Answer ) != 0 {
t . Fatalf ( "Bad: %#v" , in )
}
m := new ( dns . Msg )
m . SetQuestion ( "v1.primary.db.service.consul." , dns . TypeSRV )
c := new ( dns . Client )
in , _ , err = c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
aRec , ok := in . Extra [ 0 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Name != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . A . String ( ) != "127.0.0.1" {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
} )
}
}
2024-06-25 17:42:25 +00:00
// TestDNS_ServiceLookup_ExtraTags tests tag behavior.
// With v1dns, we still support period tags, but if a tag is not found it's an NXDOMAIN code.
// With v2dns, we do not support period tags, so it's an NXDOMAIN code because the name is not valid.
func TestDNS_ServiceLookup_ExtraTags ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
for name , experimentsHCL := range getVersionHCL ( true ) {
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
// Register node
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
}
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
m1 := new ( dns . Msg )
m1 . SetQuestion ( "dummy.primary.db.service.consul." , dns . TypeSRV )
c1 := new ( dns . Client )
in , _ , err := c1 . Exchange ( m1 , a . DNSAddr ( ) )
require . NoError ( t , err )
require . Len ( t , in . Answer , 0 , "Expected no answer" )
require . Equal ( t , dns . RcodeNameError , in . Rcode )
} )
}
}
2024-01-08 20:14:26 +00:00
func TestDNS_ServiceLookup_PreparedQueryNamePeriod ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-12 19:27:25 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
2024-01-05 20:37:27 +00:00
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
// Register a node with a service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
2024-01-08 20:14:26 +00:00
Service : "db" ,
2024-01-05 20:37:27 +00:00
Port : 12345 ,
} ,
}
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-08 20:14:26 +00:00
// Register a prepared query with a period in the name.
2024-01-05 20:37:27 +00:00
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
2024-01-08 20:14:26 +00:00
Name : "some.query.we.like" ,
2024-01-05 20:37:27 +00:00
Service : structs . ServiceQuery {
Service : "db" ,
} ,
} ,
}
2024-01-08 20:14:26 +00:00
var id string
2024-01-05 20:37:27 +00:00
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-08 20:14:26 +00:00
m := new ( dns . Msg )
m . SetQuestion ( "some.query.we.like.query.consul." , dns . TypeSRV )
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
2024-01-05 20:37:27 +00:00
}
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
aRec , ok := in . Extra [ 0 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Name != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . A . String ( ) != "127.0.0.1" {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
2024-01-05 20:37:27 +00:00
}
} )
}
}
2024-01-08 20:14:26 +00:00
func TestDNS_ServiceLookup_Dedup ( t * testing . T ) {
2024-01-05 20:37:27 +00:00
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-03 03:23:52 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a single node with multiple instances of a service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
args = & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
ID : "db2" ,
Service : "db" ,
Tags : [ ] string { "replica" } ,
Port : 12345 ,
} ,
}
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
args = & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
ID : "db3" ,
Service : "db" ,
Tags : [ ] string { "replica" } ,
Port : 12346 ,
} ,
}
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "db" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query, make sure only
// one IP is returned.
questions := [ ] string {
"db.service.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeANY )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
aRec , ok := in . Answer [ 0 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if aRec . A . String ( ) != "127.0.0.1" {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ServiceLookup_Dedup_SRV ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-08 04:24:00 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a single node with multiple instances of a service.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
args = & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
ID : "db2" ,
Service : "db" ,
Tags : [ ] string { "replica" } ,
Port : 12345 ,
} ,
}
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
args = & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
ID : "db3" ,
Service : "db" ,
Tags : [ ] string { "replica" } ,
Port : 12346 ,
} ,
}
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "db" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query, make sure only
// one IP is returned and two unique ports are returned.
questions := [ ] string {
"db.service.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 2 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 && srvRec . Port != 12346 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok = in . Answer [ 1 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 1 ] )
}
if srvRec . Port != 12346 && srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Port == in . Answer [ 0 ] . ( * dns . SRV ) . Port {
t . Fatalf ( "should be a different port" )
}
if srvRec . Target != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
aRec , ok := in . Extra [ 0 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Name != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . A . String ( ) != "127.0.0.1" {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ServiceLookup_FilterCritical ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-09 02:56:04 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register nodes with health checks in various states.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
Check : & structs . HealthCheck {
CheckID : "serf" ,
Name : "serf" ,
Status : api . HealthCritical ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
args2 := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "bar" ,
Address : "127.0.0.2" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
Check : & structs . HealthCheck {
CheckID : "serf" ,
Name : "serf" ,
Status : api . HealthCritical ,
} ,
}
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args2 , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
args3 := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "bar" ,
Address : "127.0.0.2" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
Check : & structs . HealthCheck {
CheckID : "db" ,
Name : "db" ,
ServiceID : "db" ,
Status : api . HealthCritical ,
} ,
}
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args3 , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
args4 := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "baz" ,
Address : "127.0.0.3" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
}
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args4 , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
args5 := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "quux" ,
Address : "127.0.0.4" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
Check : & structs . HealthCheck {
CheckID : "db" ,
Name : "db" ,
ServiceID : "db" ,
Status : api . HealthWarning ,
} ,
}
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args5 , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "db" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] string {
"db.service.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeANY )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Only 4 and 5 are not failing, so we should get 2 answers
if len ( in . Answer ) != 2 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
ips := make ( map [ string ] bool )
for _ , resp := range in . Answer {
aRec := resp . ( * dns . A )
ips [ aRec . A . String ( ) ] = true
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if ! ips [ "127.0.0.3" ] {
t . Fatalf ( "Bad: %#v should contain 127.0.0.3 (state healthy)" , in )
}
if ! ips [ "127.0.0.4" ] {
t . Fatalf ( "Bad: %#v should contain 127.0.0.4 (state warning)" , in )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ServiceLookup_OnlyFailing ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-09 02:56:04 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register nodes with all health checks in a critical state.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
Check : & structs . HealthCheck {
CheckID : "serf" ,
Name : "serf" ,
Status : api . HealthCritical ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
args2 := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "bar" ,
Address : "127.0.0.2" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
Check : & structs . HealthCheck {
CheckID : "serf" ,
Name : "serf" ,
Status : api . HealthCritical ,
} ,
}
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args2 , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
args3 := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "bar" ,
Address : "127.0.0.2" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
Check : & structs . HealthCheck {
CheckID : "db" ,
Name : "db" ,
ServiceID : "db" ,
Status : api . HealthCritical ,
} ,
}
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args3 , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "db" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] string {
"db.service.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeANY )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// All 3 are failing, so we should get 0 answers and an NXDOMAIN response
if len ( in . Answer ) != 0 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if in . Rcode != dns . RcodeNameError {
t . Fatalf ( "Bad: %#v" , in )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ServiceLookup_OnlyPassing ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-09 02:56:04 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
dns_config {
only_passing = true
}
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register nodes with health checks in various states.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
Check : & structs . HealthCheck {
CheckID : "db" ,
Name : "db" ,
ServiceID : "db" ,
Status : api . HealthPassing ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
args2 := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "bar" ,
Address : "127.0.0.2" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
Check : & structs . HealthCheck {
CheckID : "db" ,
Name : "db" ,
ServiceID : "db" ,
Status : api . HealthWarning ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args2 , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
args3 := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "baz" ,
Address : "127.0.0.3" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
Check : & structs . HealthCheck {
CheckID : "db" ,
Name : "db" ,
ServiceID : "db" ,
Status : api . HealthCritical ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args3 , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "db" ,
OnlyPassing : true ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] string {
"db.service.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeANY )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Only 1 is passing, so we should only get 1 answer
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
resp := in . Answer [ 0 ]
aRec := resp . ( * dns . A )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if aRec . A . String ( ) != "127.0.0.1" {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
newCfg := * a . Config
newCfg . DNSOnlyPassing = false
err := a . reloadConfigInternal ( & newCfg )
require . NoError ( t , err )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// only_passing is now false. we should now get two nodes
m := new ( dns . Msg )
m . SetQuestion ( "db.service.consul." , dns . TypeANY )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
require . NoError ( t , err )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
require . Equal ( t , 2 , len ( in . Answer ) )
ips := [ ] string { in . Answer [ 0 ] . ( * dns . A ) . A . String ( ) , in . Answer [ 1 ] . ( * dns . A ) . A . String ( ) }
sort . Strings ( ips )
require . Equal ( t , [ ] string { "127.0.0.1" , "127.0.0.2" } , ips )
} )
}
2024-01-05 20:37:27 +00:00
}
func TestDNS_ServiceLookup_Randomize ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-08 04:24:00 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a large number of nodes.
for i := 0 ; i < generateNumNodes ; i ++ {
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : fmt . Sprintf ( "foo%d" , i ) ,
Address : fmt . Sprintf ( "127.0.0.%d" , i + 1 ) ,
Service : & structs . NodeService {
Service : "web" ,
Port : 8000 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
}
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "web" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
2024-01-05 20:37:27 +00:00
}
}
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query. Ensure the
// response is randomized each time.
questions := [ ] string {
"web.service.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
uniques := map [ string ] struct { } { }
for i := 0 ; i < 10 ; i ++ {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeANY )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := & dns . Client { Net : "udp" }
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Response length should be truncated and we should get
// an A record for each response.
if len ( in . Answer ) != defaultNumUDPResponses {
t . Fatalf ( "Bad: %#v" , len ( in . Answer ) )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Collect all the names.
var names [ ] string
for _ , rec := range in . Answer {
switch v := rec . ( type ) {
case * dns . SRV :
names = append ( names , v . Target )
case * dns . A :
names = append ( names , v . A . String ( ) )
}
}
nameS := strings . Join ( names , "|" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Tally the results.
uniques [ nameS ] = struct { } { }
}
// Give some wiggle room. Since the responses are randomized and
// there is a finite number of combinations, requiring 0
// duplicates every test run eventually gives us failures.
if len ( uniques ) < 2 {
t . Fatalf ( "unique response ratio too low: %d/10\n%v" , len ( uniques ) , uniques )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
2024-01-08 20:14:26 +00:00
func TestDNS_ServiceLookup_Truncate ( t * testing . T ) {
2024-01-05 20:37:27 +00:00
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-08 04:24:00 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
dns_config {
enable_truncate = true
}
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a large number of nodes.
for i := 0 ; i < generateNumNodes ; i ++ {
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : fmt . Sprintf ( "foo%d" , i ) ,
Address : fmt . Sprintf ( "127.0.0.%d" , i + 1 ) ,
Service : & structs . NodeService {
Service : "web" ,
Port : 8000 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "web" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query. Ensure the
// response is truncated each time.
questions := [ ] string {
"web.service.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeANY )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
// Check for the truncate bit
if ! in . Truncated {
t . Fatalf ( "should have truncate bit" )
}
}
} )
2024-01-05 20:37:27 +00:00
}
2024-01-08 20:14:26 +00:00
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
func TestDNS_ServiceLookup_LargeResponses ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-01-05 20:37:27 +00:00
2024-02-09 02:56:04 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
dns_config {
enable_truncate = true
2024-01-05 20:37:27 +00:00
}
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
longServiceName := "this-is-a-very-very-very-very-very-long-name-for-a-service"
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a lot of nodes.
for i := 0 ; i < 4 ; i ++ {
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : fmt . Sprintf ( "foo%d" , i ) ,
Address : fmt . Sprintf ( "127.0.0.%d" , i + 1 ) ,
Service : & structs . NodeService {
Service : longServiceName ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
}
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : longServiceName ,
Service : structs . ServiceQuery {
Service : longServiceName ,
Tags : [ ] string { "primary" } ,
} ,
} ,
}
var id string
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
}
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] string {
"_" + longServiceName + "._primary.service.consul." ,
longServiceName + ".query.consul." ,
2024-01-05 20:37:27 +00:00
}
2024-01-08 20:14:26 +00:00
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
if ! in . Truncated {
t . Fatalf ( "should have truncate bit" )
}
// Make sure the response size is RFC 1035-compliant for UDP messages
if in . Len ( ) > 512 {
t . Fatalf ( "Bad: %d" , in . Len ( ) )
}
// We should only have two answers now
if len ( in . Answer ) != 2 {
t . Fatalf ( "Bad: %d" , len ( in . Answer ) )
}
// Make sure the ADDITIONAL section matches the ANSWER section.
if len ( in . Answer ) != len ( in . Extra ) {
t . Fatalf ( "Bad: %d vs. %d" , len ( in . Answer ) , len ( in . Extra ) )
}
for i := 0 ; i < len ( in . Answer ) ; i ++ {
srv , ok := in . Answer [ i ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ i ] )
}
a , ok := in . Extra [ i ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ i ] )
}
if srv . Target != a . Hdr . Name {
t . Fatalf ( "Bad: %#v %#v" , srv , a )
}
}
// Check for the truncate bit
if ! in . Truncated {
t . Fatalf ( "should have truncate bit" )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func testDNSServiceLookupResponseLimits ( t * testing . T , answerLimit int , qType uint16 ,
2024-01-08 20:14:26 +00:00
expectedService , expectedQuery , expectedQueryID int , additionalHCL string ) ( bool , error ) {
2024-01-05 20:37:27 +00:00
a := NewTestAgent ( t , `
node_name = "test-node"
dns_config {
udp_answer_limit = ` +fmt.Sprintf("%d", answerLimit)+ `
}
2024-01-08 20:14:26 +00:00
` + additionalHCL )
2024-01-05 20:37:27 +00:00
defer a . Shutdown ( )
testrpc . WaitForTestAgent ( t , a . RPC , "dc1" )
choices := perfectlyRandomChoices ( generateNumNodes , pctNodesWithIPv6 )
for i := 0 ; i < generateNumNodes ; i ++ {
nodeAddress := fmt . Sprintf ( "127.0.0.%d" , i + 1 )
if choices [ i ] {
nodeAddress = fmt . Sprintf ( "fe80::%d" , i + 1 )
}
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : fmt . Sprintf ( "foo%d" , i ) ,
Address : nodeAddress ,
Service : & structs . NodeService {
Service : "api-tier" ,
Port : 8080 ,
} ,
}
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
return false , fmt . Errorf ( "err: %v" , err )
}
}
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "api-tier" ,
Service : structs . ServiceQuery {
Service : "api-tier" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
return false , fmt . Errorf ( "err: %v" , err )
}
}
// Look up the service directly and via prepared query.
questions := [ ] string {
"api-tier.service.consul." ,
"api-tier.query.consul." ,
id + ".query.consul." ,
}
for idx , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , qType )
c := & dns . Client { Net : "udp" }
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
return false , fmt . Errorf ( "err: %v" , err )
}
switch idx {
case 0 :
if ( expectedService > 0 && len ( in . Answer ) != expectedService ) ||
( expectedService < - 1 && len ( in . Answer ) < lib . AbsInt ( expectedService ) ) {
return false , fmt . Errorf ( "%d/%d answers received for type %v for %s, sz:=%d" , len ( in . Answer ) , answerLimit , qType , question , in . Len ( ) )
}
case 1 :
if ( expectedQuery > 0 && len ( in . Answer ) != expectedQuery ) ||
( expectedQuery < - 1 && len ( in . Answer ) < lib . AbsInt ( expectedQuery ) ) {
return false , fmt . Errorf ( "%d/%d answers received for type %v for %s, sz:=%d" , len ( in . Answer ) , answerLimit , qType , question , in . Len ( ) )
}
case 2 :
if ( expectedQueryID > 0 && len ( in . Answer ) != expectedQueryID ) ||
( expectedQueryID < - 1 && len ( in . Answer ) < lib . AbsInt ( expectedQueryID ) ) {
return false , fmt . Errorf ( "%d/%d answers received for type %v for %s, sz:=%d" , len ( in . Answer ) , answerLimit , qType , question , in . Len ( ) )
}
default :
panic ( "abort" )
}
}
return true , nil
}
func checkDNSService (
t * testing . T ,
generateNumNodes int ,
aRecordLimit int ,
qType uint16 ,
expectedResultsCount int ,
udpSize uint16 ,
) {
2024-02-09 02:56:04 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
node_name = "test-node"
dns_config {
a_record_limit = ` +fmt.Sprintf("%d", aRecordLimit)+ `
udp_answer_limit = ` +fmt.Sprintf("%d", aRecordLimit)+ `
}
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
2024-01-18 23:30:04 +00:00
defer a . Shutdown ( )
2024-01-08 20:14:26 +00:00
testrpc . WaitForTestAgent ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
choices := perfectlyRandomChoices ( generateNumNodes , pctNodesWithIPv6 )
for i := 0 ; i < generateNumNodes ; i ++ {
nodeAddress := fmt . Sprintf ( "127.0.0.%d" , i + 1 )
if choices [ i ] {
nodeAddress = fmt . Sprintf ( "fe80::%d" , i + 1 )
}
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : fmt . Sprintf ( "foo%d" , i ) ,
Address : nodeAddress ,
Service : & structs . NodeService {
Service : "api-tier" ,
Port : 8080 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
require . NoError ( t , a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) )
}
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "api-tier" ,
Service : structs . ServiceQuery {
Service : "api-tier" ,
} ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
require . NoError ( t , a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] string {
"api-tier.service.consul." ,
"api-tier.query.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
question := question
t . Run ( "question: " + question , func ( t * testing . T ) {
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
m := new ( dns . Msg )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
m . SetQuestion ( question , qType )
protocol := "tcp"
if udpSize > 0 {
protocol = "udp"
}
if udpSize > 512 {
m . SetEdns0 ( udpSize , true )
}
c := & dns . Client { Net : protocol , UDPSize : 8192 }
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
require . NoError ( t , err )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
t . Logf ( "DNS Response for %+v - %+v" , m , in )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
require . Equal ( t , expectedResultsCount , len ( in . Answer ) ,
"%d/%d answers received for type %v for %s (%s)" , len ( in . Answer ) , expectedResultsCount , qType , question , protocol )
} )
}
2024-01-05 20:37:27 +00:00
} )
}
}
func TestDNS_ServiceLookup_ARecordLimits ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
tests := [ ] struct {
name string
aRecordLimit int
expectedAResults int
expectedAAAAResults int
expectedANYResults int
expectedSRVResults int
numNodesTotal int
udpSize uint16
_unused_udpAnswerLimit int // NOTE: this field is not used
} {
// UDP + EDNS
{ "udp-edns-1" , 1 , 1 , 1 , 1 , 30 , 30 , 8192 , 3 } ,
{ "udp-edns-2" , 2 , 2 , 2 , 2 , 30 , 30 , 8192 , 3 } ,
{ "udp-edns-3" , 3 , 3 , 3 , 3 , 30 , 30 , 8192 , 3 } ,
{ "udp-edns-4" , 4 , 4 , 4 , 4 , 30 , 30 , 8192 , 3 } ,
{ "udp-edns-5" , 5 , 5 , 5 , 5 , 30 , 30 , 8192 , 3 } ,
{ "udp-edns-6" , 6 , 6 , 6 , 6 , 30 , 30 , 8192 , 3 } ,
{ "udp-edns-max" , 6 , 2 , 1 , 3 , 3 , 3 , 8192 , 3 } ,
// All UDP without EDNS have a limit of 2 answers due to udpAnswerLimit
// Even SRV records are limit to 2 records
{ "udp-limit-1" , 1 , 1 , 0 , 1 , 1 , 1 , 512 , 2 } ,
{ "udp-limit-2" , 2 , 1 , 1 , 2 , 2 , 2 , 512 , 2 } ,
// AAAA results limited by size of payload
{ "udp-limit-3" , 3 , 1 , 1 , 2 , 2 , 2 , 512 , 2 } ,
{ "udp-limit-4" , 4 , 1 , 1 , 2 , 2 , 2 , 512 , 2 } ,
{ "udp-limit-5" , 5 , 1 , 1 , 2 , 2 , 2 , 512 , 2 } ,
{ "udp-limit-6" , 6 , 1 , 1 , 2 , 2 , 2 , 512 , 2 } ,
{ "udp-limit-max" , 6 , 1 , 1 , 2 , 2 , 2 , 512 , 2 } ,
// All UDP without EDNS and no udpAnswerLimit
// Size of records is limited by UDP payload
{ "udp-1" , 1 , 1 , 0 , 1 , 1 , 1 , 512 , 0 } ,
{ "udp-2" , 2 , 1 , 1 , 2 , 2 , 2 , 512 , 0 } ,
{ "udp-3" , 3 , 1 , 1 , 2 , 2 , 2 , 512 , 0 } ,
{ "udp-4" , 4 , 1 , 1 , 2 , 2 , 2 , 512 , 0 } ,
{ "udp-5" , 5 , 1 , 1 , 2 , 2 , 2 , 512 , 0 } ,
{ "udp-6" , 6 , 1 , 1 , 2 , 2 , 2 , 512 , 0 } ,
// Only 3 A and 3 SRV records on 512 bytes
{ "udp-max" , 6 , 1 , 1 , 2 , 2 , 2 , 512 , 0 } ,
{ "tcp-1" , 1 , 1 , 1 , 1 , 30 , 30 , 0 , 0 } ,
{ "tcp-2" , 2 , 2 , 2 , 2 , 30 , 30 , 0 , 0 } ,
{ "tcp-3" , 3 , 3 , 3 , 3 , 30 , 30 , 0 , 0 } ,
{ "tcp-4" , 4 , 4 , 4 , 4 , 30 , 30 , 0 , 0 } ,
{ "tcp-5" , 5 , 5 , 5 , 5 , 30 , 30 , 0 , 0 } ,
{ "tcp-6" , 6 , 6 , 6 , 6 , 30 , 30 , 0 , 0 } ,
{ "tcp-max" , 6 , 1 , 1 , 2 , 2 , 2 , 0 , 0 } ,
}
for _ , test := range tests {
test := test // capture loop var
t . Run ( test . name , func ( t * testing . T ) {
// All those queries should have at max queriesLimited elements
t . Run ( "A" , func ( t * testing . T ) {
checkDNSService ( t , test . numNodesTotal , test . aRecordLimit , dns . TypeA , test . expectedAResults , test . udpSize )
} )
t . Run ( "AAAA" , func ( t * testing . T ) {
checkDNSService ( t , test . numNodesTotal , test . aRecordLimit , dns . TypeAAAA , test . expectedAAAAResults , test . udpSize )
} )
t . Run ( "ANY" , func ( t * testing . T ) {
checkDNSService ( t , test . numNodesTotal , test . aRecordLimit , dns . TypeANY , test . expectedANYResults , test . udpSize )
} )
// No limits but the size of records for SRV records, since not subject to randomization issues
t . Run ( "SRV" , func ( t * testing . T ) {
checkDNSService ( t , test . expectedSRVResults , test . aRecordLimit , dns . TypeSRV , test . numNodesTotal , test . udpSize )
} )
} )
}
}
func TestDNS_ServiceLookup_AnswerLimits ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-02-08 04:24:00 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Build a matrix of config parameters (udpAnswerLimit), and the
// length of the response per query type and question. Negative
// values imply the test must return at least the abs(value) number
// of records in the answer section. This is required because, for
// example, on OS-X and Linux, the number of answers returned in a
// 512B response is different even though both platforms are x86_64
// and using the same version of Go.
//
// TODO(sean@): Why is it not identical everywhere when using the
// same compiler?
tests := [ ] struct {
name string
udpAnswerLimit int
expectedAService int
expectedAQuery int
expectedAQueryID int
expectedAAAAService int
expectedAAAAQuery int
expectedAAAAQueryID int
expectedANYService int
expectedANYQuery int
expectedANYQueryID int
} {
{ "0" , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ,
{ "1" , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 } ,
{ "2" , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 } ,
{ "3" , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 } ,
{ "4" , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 } ,
{ "5" , 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 , 5 } ,
{ "6" , 6 , 6 , 6 , 6 , 6 , 6 , 5 , 6 , 6 , - 5 } ,
{ "7" , 7 , 7 , 7 , 6 , 7 , 7 , 5 , 7 , 7 , - 5 } ,
{ "8" , 8 , 8 , 8 , 6 , 8 , 8 , 5 , 8 , 8 , - 5 } ,
{ "9" , 9 , 8 , 8 , 6 , 8 , 8 , 5 , 8 , 8 , - 5 } ,
{ "20" , 20 , 8 , 8 , 6 , 8 , 8 , 5 , 8 , - 5 , - 5 } ,
{ "30" , 30 , 8 , 8 , 6 , 8 , 8 , 5 , 8 , - 5 , - 5 } ,
2024-01-05 20:37:27 +00:00
}
2024-01-08 20:14:26 +00:00
for _ , test := range tests {
test := test // capture loop var
t . Run ( fmt . Sprintf ( "A lookup %v" , test ) , func ( t * testing . T ) {
ok , err := testDNSServiceLookupResponseLimits ( t , test . udpAnswerLimit , dns . TypeA , test . expectedAService , test . expectedAQuery , test . expectedAQueryID , experimentsHCL )
if ! ok {
t . Fatalf ( "Expected service A lookup %s to pass: %v" , test . name , err )
}
} )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
t . Run ( fmt . Sprintf ( "AAAA lookup %v" , test ) , func ( t * testing . T ) {
ok , err := testDNSServiceLookupResponseLimits ( t , test . udpAnswerLimit , dns . TypeAAAA , test . expectedAAAAService , test . expectedAAAAQuery , test . expectedAAAAQueryID , experimentsHCL )
if ! ok {
t . Fatalf ( "Expected service AAAA lookup %s to pass: %v" , test . name , err )
}
} )
t . Run ( fmt . Sprintf ( "ANY lookup %v" , test ) , func ( t * testing . T ) {
ok , err := testDNSServiceLookupResponseLimits ( t , test . udpAnswerLimit , dns . TypeANY , test . expectedANYService , test . expectedANYQuery , test . expectedANYQueryID , experimentsHCL )
if ! ok {
t . Fatalf ( "Expected service ANY lookup %s to pass: %v" , test . name , err )
}
} )
2024-01-05 20:37:27 +00:00
}
} )
}
}
func TestDNS_ServiceLookup_CNAME ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
recursor := makeRecursor ( t , dns . Msg {
Answer : [ ] dns . RR {
dnsCNAME ( "www.google.com" , "google.com" ) ,
dnsA ( "google.com" , "1.2.3.4" ) ,
} ,
} )
defer recursor . Shutdown ( )
2024-02-06 14:53:39 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
recursors = [ "`+recursor.Addr+`" ]
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with a name for an address.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "google" ,
Address : "www.google.com" ,
Service : & structs . NodeService {
Service : "search" ,
Port : 80 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "search" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] string {
"search.service.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeANY )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Service CNAME, google CNAME, google A record
if len ( in . Answer ) != 3 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Should have service CNAME
cnRec , ok := in . Answer [ 0 ] . ( * dns . CNAME )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if cnRec . Target != "www.google.com." {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Should have google CNAME
cnRec , ok = in . Answer [ 1 ] . ( * dns . CNAME )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 1 ] )
}
if cnRec . Target != "google.com." {
t . Fatalf ( "Bad: %#v" , in . Answer [ 1 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Check we recursively resolve
if _ , ok := in . Answer [ 2 ] . ( * dns . A ) ; ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 2 ] )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ServiceLookup_ServiceAddress_CNAME ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
recursor := makeRecursor ( t , dns . Msg {
Answer : [ ] dns . RR {
dnsCNAME ( "www.google.com" , "google.com" ) ,
dnsA ( "google.com" , "1.2.3.4" ) ,
} ,
} )
defer recursor . Shutdown ( )
2024-02-06 14:53:39 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
recursors = [ "`+recursor.Addr+`" ]
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register a node with a name for an address.
{
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "google" ,
Address : "1.2.3.4" ,
Service : & structs . NodeService {
Service : "search" ,
Port : 80 ,
Address : "www.google.com" ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register an equivalent prepared query.
var id string
{
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Name : "test" ,
Service : structs . ServiceQuery {
Service : "search" ,
} ,
} ,
}
if err := a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Look up the service directly and via prepared query.
questions := [ ] string {
"search.service.consul." ,
id + ".query.consul." ,
}
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeANY )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Service CNAME, google CNAME, google A record
if len ( in . Answer ) != 3 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Should have service CNAME
cnRec , ok := in . Answer [ 0 ] . ( * dns . CNAME )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if cnRec . Target != "www.google.com." {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Should have google CNAME
cnRec , ok = in . Answer [ 1 ] . ( * dns . CNAME )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 1 ] )
}
if cnRec . Target != "google.com." {
t . Fatalf ( "Bad: %#v" , in . Answer [ 1 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Check we recursively resolve
if _ , ok := in . Answer [ 2 ] . ( * dns . A ) ; ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 2 ] )
}
}
} )
2024-01-05 20:37:27 +00:00
}
}
func TestDNS_ServiceLookup_TTL ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-01-29 22:33:45 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
2024-01-05 20:37:27 +00:00
dns_config {
service_ttl = {
"d*" = "42s"
"db" = "10s"
"db*" = "66s"
"*" = "5s"
}
allow_stale = true
max_stale = "1s"
}
2024-01-08 20:14:26 +00:00
` + experimentsHCL )
defer a . Shutdown ( )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
for idx , service := range [ ] string { "db" , "dblb" , "dk" , "api" } {
nodeName := fmt . Sprintf ( "foo%d" , idx )
address := fmt . Sprintf ( "127.0.0.%d" , idx )
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : nodeName ,
Address : address ,
Service : & structs . NodeService {
Service : service ,
Tags : [ ] string { "primary" } ,
Port : 12345 + idx ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
expectResult := func ( dnsQuery string , expectedTTL uint32 ) {
t . Run ( dnsQuery , func ( t * testing . T ) {
m := new ( dns . Msg )
m . SetQuestion ( dnsQuery , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v, len is %d" , in , len ( in . Answer ) )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Hdr . Ttl != expectedTTL {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
aRec , ok := in . Extra [ 0 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Ttl != expectedTTL {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
} )
2024-01-05 20:37:27 +00:00
}
2024-01-08 20:14:26 +00:00
// Should have its exact TTL
expectResult ( "db.service.consul." , 10 )
// Should match db*
expectResult ( "dblb.service.consul." , 66 )
// Should match d*
expectResult ( "dk.service.consul." , 42 )
// Should match *
expectResult ( "api.service.consul." , 5 )
2024-01-05 20:37:27 +00:00
} )
}
}
func TestDNS_ServiceLookup_SRV_RFC ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-01-29 22:33:45 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-08 20:14:26 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
// Register node
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
questions := [ ] string {
"_db._primary.service.dc1.consul." ,
"_db._primary.service.consul." ,
"_db._primary.dc1.consul." ,
"_db._primary.consul." ,
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
for _ , question := range questions {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-08 20:14:26 +00:00
aRec , ok := in . Extra [ 0 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Name != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . A . String ( ) != "127.0.0.1" {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
}
} )
}
2024-01-05 20:37:27 +00:00
}
func TestDNS_ServiceLookup_SRV_RFC_TCP_Default ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-01-29 22:33:45 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
// Register node
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
questions := [ ] string {
"_db._tcp.service.dc1.consul." ,
"_db._tcp.service.consul." ,
"_db._tcp.dc1.consul." ,
"_db._tcp.consul." ,
}
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
for _ , question := range questions {
t . Run ( question , func ( t * testing . T ) {
m := new ( dns . Msg )
m . SetQuestion ( question , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
if len ( in . Answer ) != 1 {
t . Fatalf ( "Bad: %#v" , in )
}
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
srvRec , ok := in . Answer [ 0 ] . ( * dns . SRV )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
if srvRec . Port != 12345 {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Target != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , srvRec )
}
if srvRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Answer [ 0 ] )
}
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
aRec , ok := in . Extra [ 0 ] . ( * dns . A )
if ! ok {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Name != "foo.node.dc1.consul." {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . A . String ( ) != "127.0.0.1" {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
if aRec . Hdr . Ttl != 0 {
t . Fatalf ( "Bad: %#v" , in . Extra [ 0 ] )
}
} )
}
} )
2024-01-05 20:37:27 +00:00
}
}
func initDNSToken ( t * testing . T , rpc RPC ) {
t . Helper ( )
reqToken := structs . ACLTokenSetRequest {
Datacenter : "dc1" ,
ACLToken : structs . ACLToken {
SecretID : "279d4735-f8ca-4d48-b5cc-c00a9713bbf8" ,
Policies : nil ,
TemplatedPolicies : [ ] * structs . ACLTemplatedPolicy { { TemplateName : "builtin/dns" } } ,
} ,
WriteRequest : structs . WriteRequest { Token : "root" } ,
}
err := rpc . RPC ( context . Background ( ) , "ACL.TokenSet" , & reqToken , & structs . ACLToken { } )
require . NoError ( t , err )
}
func TestDNS_ServiceLookup_FilterACL ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
tests := [ ] struct {
token string
results int
} {
{ "root" , 1 } ,
{ "anonymous" , 0 } ,
{ "dns" , 1 } ,
}
for _ , tt := range tests {
t . Run ( "ACLToken == " + tt . token , func ( t * testing . T ) {
hcl := `
primary_datacenter = "dc1"
acl {
enabled = true
default_policy = "deny"
down_policy = "deny"
tokens {
initial_management = "root"
`
if tt . token == "dns" {
// Create a UUID for dns token since it doesn't have an alias
dnsToken := "279d4735-f8ca-4d48-b5cc-c00a9713bbf8"
hcl = hcl + `
default = "anonymous"
dns = "` + dnsToken + `"
`
} else {
hcl = hcl + `
default = "` + tt.token + `"
`
}
hcl = hcl + `
}
}
`
2024-02-06 14:53:39 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
2024-01-29 22:33:45 +00:00
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , hcl + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
if tt . token == "dns" {
initDNSToken ( t , a )
}
// Register a service
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "foo" ,
Address : "127.0.0.1" ,
Service : & structs . NodeService {
Service : "foo" ,
Port : 12345 ,
} ,
WriteRequest : structs . WriteRequest { Token : "root" } ,
}
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
// Set up the DNS query
c := new ( dns . Client )
m := new ( dns . Msg )
m . SetQuestion ( "foo.service.consul." , dns . TypeA )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
if len ( in . Answer ) != tt . results {
t . Fatalf ( "Bad: %#v" , in )
}
} )
2024-01-05 20:37:27 +00:00
}
2024-01-29 22:33:45 +00:00
} )
}
}
func TestDNS_ServiceLookup_MetaTXT ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
for name , experimentsHCL := range getVersionHCL ( true ) {
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , ` dns_config = { enable_additional_node_meta_txt = true } ` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
2024-01-29 22:33:45 +00:00
Node : "bar" ,
2024-01-05 20:37:27 +00:00
Address : "127.0.0.1" ,
2024-01-29 22:33:45 +00:00
NodeMeta : map [ string ] string {
"key" : "value" ,
} ,
2024-01-05 20:37:27 +00:00
Service : & structs . NodeService {
2024-01-29 22:33:45 +00:00
Service : "db" ,
Tags : [ ] string { "primary" } ,
2024-01-05 20:37:27 +00:00
Port : 12345 ,
} ,
}
2024-01-29 22:33:45 +00:00
2024-01-05 20:37:27 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
m := new ( dns . Msg )
2024-01-29 22:33:45 +00:00
m . SetQuestion ( "db.service.consul." , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
c := new ( dns . Client )
2024-01-05 20:37:27 +00:00
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-29 22:33:45 +00:00
wantAdditional := [ ] dns . RR {
& dns . A {
Hdr : dns . RR_Header { Name : "bar.node.dc1.consul." , Rrtype : dns . TypeA , Class : dns . ClassINET , Rdlength : 0x4 } ,
A : [ ] byte { 0x7f , 0x0 , 0x0 , 0x1 } , // 127.0.0.1
} ,
& dns . TXT {
Hdr : dns . RR_Header { Name : "bar.node.dc1.consul." , Rrtype : dns . TypeTXT , Class : dns . ClassINET , Rdlength : 0xa } ,
Txt : [ ] string { "key=value" } ,
} ,
2024-01-05 20:37:27 +00:00
}
2024-01-29 22:33:45 +00:00
require . Equal ( t , wantAdditional , in . Extra )
2024-01-05 20:37:27 +00:00
} )
}
}
func TestDNS_ServiceLookup_SuppressTXT ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
}
2024-01-29 22:33:45 +00:00
for name , experimentsHCL := range getVersionHCL ( true ) {
t . Run ( name , func ( t * testing . T ) {
a := NewTestAgent ( t , ` dns_config = { enable_additional_node_meta_txt = false } ` + experimentsHCL )
defer a . Shutdown ( )
testrpc . WaitForLeader ( t , a . RPC , "dc1" )
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
// Register a node with a service.
args := & structs . RegisterRequest {
Datacenter : "dc1" ,
Node : "bar" ,
Address : "127.0.0.1" ,
NodeMeta : map [ string ] string {
"key" : "value" ,
} ,
Service : & structs . NodeService {
Service : "db" ,
Tags : [ ] string { "primary" } ,
Port : 12345 ,
} ,
}
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
var out struct { }
if err := a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
m := new ( dns . Msg )
m . SetQuestion ( "db.service.consul." , dns . TypeSRV )
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
c := new ( dns . Client )
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
if err != nil {
t . Fatalf ( "err: %v" , err )
}
2024-01-05 20:37:27 +00:00
2024-01-29 22:33:45 +00:00
wantAdditional := [ ] dns . RR {
& dns . A {
Hdr : dns . RR_Header { Name : "bar.node.dc1.consul." , Rrtype : dns . TypeA , Class : dns . ClassINET , Rdlength : 0x4 } ,
A : [ ] byte { 0x7f , 0x0 , 0x0 , 0x1 } , // 127.0.0.1
} ,
}
require . Equal ( t , wantAdditional , in . Extra )
} )
2024-01-05 20:37:27 +00:00
}
}