@ -2919,62 +2919,8 @@ func TestDNS_ServiceLookup_LargeResponses(t *testing.T) {
}
}
}
}
func testDNSServiceLookupResponseLimits ( t * testing . T , answerLimit int , qType uint16 ,
func testDNSServiceLookupResponseLimits ( questions [ ] string , answerLimit int , qType uint16 ,
expectedService , expectedQuery , expectedQueryID int , additionalHCL string ) ( bool , error ) {
expectedService , expectedQuery , expectedQueryID int , a * TestAgent ) ( bool , error ) {
a := NewTestAgent ( t , `
node_name = "test-node"
dns_config {
udp_answer_limit = ` +fmt.Sprintf("%d", answerLimit)+ `
}
` + additionalHCL )
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 {
for idx , question := range questions {
m := new ( dns . Msg )
m := new ( dns . Msg )
m . SetQuestion ( question , qType )
m . SetQuestion ( question , qType )
@ -3011,23 +2957,38 @@ func testDNSServiceLookupResponseLimits(t *testing.T, answerLimit int, qType uin
func checkDNSService (
func checkDNSService (
t * testing . T ,
t * testing . T ,
generateNumNodes int ,
protocol string ,
aRecordLimit int ,
questions [ ] string ,
a * TestAgent ,
qType uint16 ,
qType uint16 ,
expectedResultsCount int ,
expectedResultsCount int ,
udpSize uint16 ,
udpSize uint16 ,
setEDNS0 bool ,
) {
) {
a := NewTestAgent ( t , `
for _ , question := range questions {
node_name = "test-node"
t . Run ( "question: " + question , func ( t * testing . T ) {
dns_config {
a_record_limit = ` +fmt.Sprintf("%d", aRecordLimit)+ `
m := new ( dns . Msg )
udp_answer_limit = ` +fmt.Sprintf("%d", aRecordLimit)+ `
}
m . SetQuestion ( question , qType )
` )
if setEDNS0 {
defer a . Shutdown ( )
m . SetEdns0 ( udpSize , true )
testrpc . WaitForTestAgent ( t , a . RPC , "dc1" )
}
c := & dns . Client { Net : protocol , UDPSize : udpSize }
in , _ , err := c . Exchange ( m , a . DNSAddr ( ) )
require . NoError ( t , err )
t . Logf ( "DNS Response for %+v - %+v" , m , in )
require . Equal ( t , expectedResultsCount , len ( in . Answer ) ,
"%d/%d answers received for type %v for %s (%s)" , len ( in . Answer ) , expectedResultsCount , qType , question , protocol )
} )
}
}
func registerServicesAndPreparedQuery ( t * testing . T , generateNumNodes int , a * TestAgent , serviceUniquenessKey string ) [ ] string {
choices := perfectlyRandomChoices ( generateNumNodes , pctNodesWithIPv6 )
choices := perfectlyRandomChoices ( generateNumNodes , pctNodesWithIPv6 )
serviceName := fmt . Sprintf ( "api-tier-%s" , serviceUniquenessKey )
for i := 0 ; i < generateNumNodes ; i ++ {
for i := 0 ; i < generateNumNodes ; i ++ {
nodeAddress := fmt . Sprintf ( "127.0.0.%d" , i + 1 )
nodeAddress := fmt . Sprintf ( "127.0.0.%d" , i + 1 )
if choices [ i ] {
if choices [ i ] {
@ -3038,7 +2999,7 @@ func checkDNSService(
Node : fmt . Sprintf ( "foo%d" , i ) ,
Node : fmt . Sprintf ( "foo%d" , i ) ,
Address : nodeAddress ,
Address : nodeAddress ,
Service : & structs . NodeService {
Service : & structs . NodeService {
Service : "api-tier" ,
Service : serviceName ,
Port : 8080 ,
Port : 8080 ,
} ,
} ,
}
}
@ -3046,130 +3007,133 @@ func checkDNSService(
var out struct { }
var out struct { }
require . NoError ( t , a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) )
require . NoError ( t , a . RPC ( context . Background ( ) , "Catalog.Register" , args , & out ) )
}
}
var id string
var preparedQueryID string
{
{
args := & structs . PreparedQueryRequest {
args := & structs . PreparedQueryRequest {
Datacenter : "dc1" ,
Datacenter : "dc1" ,
Op : structs . PreparedQueryCreate ,
Op : structs . PreparedQueryCreate ,
Query : & structs . PreparedQuery {
Query : & structs . PreparedQuery {
Name : "api-tier" ,
Name : serviceName ,
Service : structs . ServiceQuery {
Service : structs . ServiceQuery {
Service : "api-tier" ,
Service : serviceName ,
} ,
} ,
} ,
} ,
}
}
require . NoError ( t , a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & id ) )
require . NoError ( t , a . RPC ( context . Background ( ) , "PreparedQuery.Apply" , args , & preparedQueryID ) )
}
}
// Look up the service directly and via prepared query.
// Look up the service directly and via prepared query.
questions := [ ] string {
questions := [ ] string {
"api-tier.service.consul." ,
fmt . Sprintf ( "%s.service.consul." , serviceName ) ,
"api-tier.query.consul." ,
fmt . Sprintf ( "%s.query.consul." , serviceName ) ,
id + ".query.consul." ,
preparedQueryID + ".query.consul." ,
}
for _ , question := range questions {
question := question
t . Run ( "question: " + question , func ( t * testing . T ) {
m := new ( dns . Msg )
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 )
t . Logf ( "DNS Response for %+v - %+v" , m , in )
require . Equal ( t , expectedResultsCount , len ( in . Answer ) ,
"%d/%d answers received for type %v for %s (%s)" , len ( in . Answer ) , expectedResultsCount , qType , question , protocol )
} )
}
}
return questions
}
}
func TestDNS_ServiceLookup_ARecordLimits ( t * testing . T ) {
func TestDNS_ServiceLookup_ARecordLimits ( t * testing . T ) {
if testing . Short ( ) {
if testing . Short ( ) {
t . Skip ( "too slow for testing.Short" )
t . Skip ( "too slow for testing.Short" )
}
}
const (
UDP = "udp"
TCP = "tcp"
)
tests := [ ] struct {
type testCase struct {
name string
protocol string
aRecordLimit int
aRecordLimit int
expectedAResults int
expectedAResults int
expectedAAAAResults int
expectedAAAAResults int
expectedANYResults int
expectedANYResults int
expectedSRVResults int
expectedSRVResults int
numNodesTotal int
numNodesTotal int
udpSize uint16
udpSize uint16
_unused_udpAnswerLimit int // NOTE: this field is not used
setEDNS0 bool
} {
}
type aRecordLimit struct {
name string
limit int
}
tests := map [ string ] testCase {
// UDP + EDNS
// UDP + EDNS
{ "udp-edns-1" , 1 , 1 , 1 , 1 , 30 , 30 , 8192 , 3 } ,
"udp-edns-1" : { UDP , 1 , 1 , 1 , 1 , 30 , 30 , 8192 , true } ,
{ "udp-edns-2" , 2 , 2 , 2 , 2 , 30 , 30 , 8192 , 3 } ,
"udp-edns-2" : { UDP , 2 , 2 , 2 , 2 , 30 , 30 , 8192 , true } ,
{ "udp-edns-3" , 3 , 3 , 3 , 3 , 30 , 30 , 8192 , 3 } ,
"udp-edns-3" : { UDP , 3 , 3 , 3 , 3 , 30 , 30 , 8192 , true } ,
{ "udp-edns-4" , 4 , 4 , 4 , 4 , 30 , 30 , 8192 , 3 } ,
"udp-edns-4" : { UDP , 4 , 4 , 4 , 4 , 30 , 30 , 8192 , true } ,
{ "udp-edns-5" , 5 , 5 , 5 , 5 , 30 , 30 , 8192 , 3 } ,
"udp-edns-5" : { UDP , 5 , 5 , 5 , 5 , 30 , 30 , 8192 , true } ,
{ "udp-edns-6" , 6 , 6 , 6 , 6 , 30 , 30 , 8192 , 3 } ,
"udp-edns-6" : { UDP , 6 , 6 , 6 , 6 , 30 , 30 , 8192 , true } ,
{ "udp-edns-max" , 6 , 2 , 1 , 3 , 3 , 3 , 8192 , 3 } ,
"udp-edns-max" : { UDP , 6 , 2 , 1 , 3 , 3 , 3 , 8192 , true } ,
// 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
// All UDP without EDNS and no udpAnswerLimit
// Size of records is limited by UDP payload
// Size of records is limited by UDP payload
{ "udp-1" , 1 , 1 , 0 , 1 , 1 , 1 , 512 , 0 } ,
"udp-1" : { UDP , 1 , 1 , 0 , 1 , 1 , 1 , 512 , false } ,
{ "udp-2" , 2 , 1 , 1 , 2 , 2 , 2 , 512 , 0 } ,
"udp-2" : { UDP , 2 , 1 , 1 , 2 , 2 , 2 , 512 , false } ,
{ "udp-3" , 3 , 1 , 1 , 2 , 2 , 2 , 512 , 0 } ,
"udp-3" : { UDP , 3 , 1 , 1 , 2 , 2 , 2 , 512 , false } ,
{ "udp-4" , 4 , 1 , 1 , 2 , 2 , 2 , 512 , 0 } ,
"udp-4" : { UDP , 4 , 1 , 1 , 2 , 2 , 2 , 512 , false } ,
{ "udp-5" , 5 , 1 , 1 , 2 , 2 , 2 , 512 , 0 } ,
"udp-5" : { UDP , 5 , 1 , 1 , 2 , 2 , 2 , 512 , false } ,
{ "udp-6" , 6 , 1 , 1 , 2 , 2 , 2 , 512 , 0 } ,
"udp-6" : { UDP , 6 , 1 , 1 , 2 , 2 , 2 , 512 , false } ,
// Only 3 A and 3 SRV records on 512 bytes
// Only 3 A and 3 SRV records on 512 bytes
{ "udp-max" , 6 , 1 , 1 , 2 , 2 , 2 , 512 , 0 } ,
"udp-max" : { UDP , 6 , 1 , 1 , 2 , 2 , 2 , 512 , false } ,
{ "tcp-1" , 1 , 1 , 1 , 1 , 30 , 30 , 0 , 0 } ,
"tcp-1" : { TCP , 1 , 1 , 1 , 1 , 30 , 30 , 0 , false } ,
{ "tcp-2" , 2 , 2 , 2 , 2 , 30 , 30 , 0 , 0 } ,
"tcp-2" : { TCP , 2 , 2 , 2 , 2 , 30 , 30 , 0 , false } ,
{ "tcp-3" , 3 , 3 , 3 , 3 , 30 , 30 , 0 , 0 } ,
"tcp-3" : { TCP , 3 , 3 , 3 , 3 , 30 , 30 , 0 , false } ,
{ "tcp-4" , 4 , 4 , 4 , 4 , 30 , 30 , 0 , 0 } ,
"tcp-4" : { TCP , 4 , 4 , 4 , 4 , 30 , 30 , 0 , false } ,
{ "tcp-5" , 5 , 5 , 5 , 5 , 30 , 30 , 0 , 0 } ,
"tcp-5" : { TCP , 5 , 5 , 5 , 5 , 30 , 30 , 0 , false } ,
{ "tcp-6" , 6 , 6 , 6 , 6 , 30 , 30 , 0 , 0 } ,
"tcp-6" : { TCP , 6 , 6 , 6 , 6 , 30 , 30 , 0 , false } ,
{ "tcp-max" , 6 , 1 , 1 , 2 , 2 , 2 , 0 , 0 } ,
"tcp-max" : { TCP , 6 , 1 , 1 , 2 , 2 , 2 , 0 , false } ,
}
}
for _ , test := range tests {
for _ , recordLimit := range [ ] aRecordLimit {
test := test // capture loop var
{ "1" , 1 } ,
{ "2" , 2 } ,
t . Run ( test . name , func ( t * testing . T ) {
{ "3" , 3 } ,
{ "4" , 4 } ,
// All those queries should have at max queriesLimited elements
{ "5" , 5 } ,
{ "6" , 6 } ,
t . Run ( "A" , func ( t * testing . T ) {
{ "max" , 6 } ,
checkDNSService ( t , test . numNodesTotal , test . aRecordLimit , dns . TypeA , test . expectedAResults , test . udpSize )
} {
} )
t . Run ( "record-limit-" + recordLimit . name , func ( t * testing . T ) {
a := NewTestAgent ( t , `
t . Run ( "AAAA" , func ( t * testing . T ) {
node_name = "test-node"
checkDNSService ( t , test . numNodesTotal , test . aRecordLimit , dns . TypeAAAA , test . expectedAAAAResults , test . udpSize )
dns_config {
} )
a_record_limit = ` +fmt.Sprintf("%d", recordLimit.limit)+ `
udp_answer_limit = ` +fmt.Sprintf("%d", recordLimit.limit)+ `
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
defer a . Shutdown ( )
t . Run ( "SRV" , func ( t * testing . T ) {
testrpc . WaitForTestAgent ( t , a . RPC , "dc1" )
checkDNSService ( t , test . expectedSRVResults , test . aRecordLimit , dns . TypeSRV , test . numNodesTotal , test . udpSize )
} )
for _ , testName := range [ ] string {
fmt . Sprintf ( "udp-edns-%s" , recordLimit . name ) ,
fmt . Sprintf ( "udp-%s" , recordLimit . name ) ,
fmt . Sprintf ( "tcp-%s" , recordLimit . name ) ,
} {
t . Run ( testName , func ( t * testing . T ) {
test := tests [ testName ]
questions := registerServicesAndPreparedQuery ( t , test . numNodesTotal , a , testName )
t . Run ( "A" , func ( t * testing . T ) {
checkDNSService ( t , test . protocol , questions , a , dns . TypeA , test . expectedAResults , test . udpSize , test . setEDNS0 )
} )
t . Run ( "AAAA" , func ( t * testing . T ) {
checkDNSService ( t , test . protocol , questions , a , dns . TypeAAAA , test . expectedAAAAResults , test . udpSize , test . setEDNS0 )
} )
t . Run ( "ANY" , func ( t * testing . T ) {
checkDNSService ( t , test . protocol , questions , a , dns . TypeANY , test . expectedANYResults , test . udpSize , test . setEDNS0 )
} )
// 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 . protocol , questions , a , dns . TypeSRV , test . numNodesTotal , test . udpSize , test . setEDNS0 )
} )
} )
}
} )
} )
}
}
}
}
@ -3217,25 +3181,36 @@ func TestDNS_ServiceLookup_AnswerLimits(t *testing.T) {
}
}
for _ , test := range tests {
for _ , test := range tests {
test := test // capture loop var
test := test // capture loop var
t . Run ( fmt . Sprintf ( "A lookup %v" , test ) , func ( t * testing . T ) {
t . Run ( fmt . Sprintf ( "answer-limit-%s" , test . name ) , func ( t * testing . T ) {
ok , err := testDNSServiceLookupResponseLimits ( t , test . udpAnswerLimit , dns . TypeA , test . expectedAService , test . expectedAQuery , test . expectedAQueryID , "" )
a := NewTestAgent ( t , fmt . Sprintf ( `
if ! ok {
node_name = "test-node"
t . Fatalf ( "Expected service A lookup %s to pass: %v" , test . name , err )
dns_config {
}
udp_answer_limit = % d
} )
} ` , test . udpAnswerLimit ) )
defer a . Shutdown ( )
testrpc . WaitForTestAgent ( t , a . RPC , "dc1" )
questions := registerServicesAndPreparedQuery ( t , generateNumNodes , a , test . name )
t . Run ( fmt . Sprintf ( "AAAA lookup %v" , test ) , func ( t * testing . T ) {
t . Run ( fmt . Sprintf ( " A lookup %v", test ) , func ( t * testing . T ) {
ok , err := testDNSServiceLookupResponseLimits ( t , test . udpAnswerLimit , dns . TypeAAAA , test . expectedAAAAService , test . expectedAAAAQuery , test . expectedAAAAQueryID , "" )
ok , err := testDNSServiceLookupResponseLimits ( ques tions , test . udpAnswerLimit , dns . TypeA , test . expectedA Service, test . expectedA Query, test . expectedA QueryID, a )
if ! ok {
if ! ok {
t . Fatalf ( "Expected service AAA A lookup %s to pass: %v", test . name , err )
t . Fatalf ( "Expected service A lookup %s to pass: %v", test . name , err )
}
}
} )
} )
t . Run ( fmt . Sprintf ( "ANY lookup %v" , test ) , func ( t * testing . T ) {
t . Run ( fmt . Sprintf ( "AAAA lookup %v" , test ) , func ( t * testing . T ) {
ok , err := testDNSServiceLookupResponseLimits ( t , test . udpAnswerLimit , dns . TypeANY , test . expectedANYService , test . expectedANYQuery , test . expectedANYQueryID , "" )
ok , err := testDNSServiceLookupResponseLimits ( questions , test . udpAnswerLimit , dns . TypeAAAA , test . expectedAAAAService , test . expectedAAAAQuery , test . expectedAAAAQueryID , a )
if ! ok {
if ! ok {
t . Fatalf ( "Expected service ANY lookup %s to pass: %v" , test . name , err )
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 ( questions , test . udpAnswerLimit , dns . TypeANY , test . expectedANYService , test . expectedANYQuery , test . expectedANYQueryID , a )
if ! ok {
t . Fatalf ( "Expected service ANY lookup %s to pass: %v" , test . name , err )
}
} )
} )
} )
}
}
}
}