// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package proxycfgglue
import (
"context"
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/consul/state"
"github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs"
)
func TestServerInternalServiceDump ( t * testing . T ) {
t . Run ( "remote queries are delegated to the remote source" , func ( t * testing . T ) {
ctx , cancel := context . WithCancel ( context . Background ( ) )
t . Cleanup ( cancel )
var (
req = & structs . ServiceDumpRequest { Datacenter : "dc2" }
correlationID = "correlation-id"
ch = make ( chan <- proxycfg . UpdateEvent )
result = errors . New ( "KABOOM" )
)
remoteSource := newMockInternalServiceDump ( t )
remoteSource . On ( "Notify" , ctx , req , correlationID , ch ) . Return ( result )
dataSource := ServerInternalServiceDump ( ServerDataSourceDeps { Datacenter : "dc1" } , remoteSource )
err := dataSource . Notify ( ctx , req , correlationID , ch )
require . Equal ( t , result , err )
} )
t . Run ( "local queries are served from the state store" , func ( t * testing . T ) {
ctx , cancel := context . WithCancel ( context . Background ( ) )
t . Cleanup ( cancel )
nextIndex := indexGenerator ( )
store := state . NewStateStore ( nil )
services := [ ] * structs . NodeService {
{
Service : "mgw" ,
Kind : structs . ServiceKindMeshGateway ,
} ,
{
Service : "web" ,
Kind : structs . ServiceKindTypical ,
} ,
{
Service : "db" ,
Kind : structs . ServiceKindTypical ,
} ,
}
for idx , service := range services {
require . NoError ( t , store . EnsureRegistration ( nextIndex ( ) , & structs . RegisterRequest {
Node : fmt . Sprintf ( "node-%d" , idx ) ,
Service : service ,
} ) )
}
authz := newStaticResolver (
policyAuthorizer ( t , `
service "mgw" { policy = "read" }
service "web" { policy = "read" }
service "db" { policy = "read" }
node_prefix "node-" { policy = "read" }
` ) ,
)
dataSource := ServerInternalServiceDump ( ServerDataSourceDeps {
GetStore : func ( ) Store { return store } ,
ACLResolver : authz ,
} , nil )
t . Run ( "filter by kind" , func ( t * testing . T ) {
eventCh := make ( chan proxycfg . UpdateEvent )
require . NoError ( t , dataSource . Notify ( ctx , & structs . ServiceDumpRequest {
ServiceKind : structs . ServiceKindMeshGateway ,
UseServiceKind : true ,
} , "" , eventCh ) )
result := getEventResult [ * structs . IndexedCheckServiceNodes ] ( t , eventCh )
require . Len ( t , result . Nodes , 1 )
require . Equal ( t , "mgw" , result . Nodes [ 0 ] . Service . Service )
} )
t . Run ( "bexpr filtering" , func ( t * testing . T ) {
eventCh := make ( chan proxycfg . UpdateEvent )
require . NoError ( t , dataSource . Notify ( ctx , & structs . ServiceDumpRequest {
QueryOptions : structs . QueryOptions { Filter : ` Service.Service == "web" ` } ,
} , "" , eventCh ) )
result := getEventResult [ * structs . IndexedCheckServiceNodes ] ( t , eventCh )
require . Len ( t , result . Nodes , 1 )
require . Equal ( t , "web" , result . Nodes [ 0 ] . Service . Service )
} )
t . Run ( "all services" , func ( t * testing . T ) {
eventCh := make ( chan proxycfg . UpdateEvent )
require . NoError ( t , dataSource . Notify ( ctx , & structs . ServiceDumpRequest { } , "" , eventCh ) )
result := getEventResult [ * structs . IndexedCheckServiceNodes ] ( t , eventCh )
require . Len ( t , result . Nodes , 3 )
} )
t . Run ( "access denied" , func ( t * testing . T ) {
authz . SwapAuthorizer ( acl . DenyAll ( ) )
eventCh := make ( chan proxycfg . UpdateEvent )
require . NoError ( t , dataSource . Notify ( ctx , & structs . ServiceDumpRequest { } , "" , eventCh ) )
result := getEventResult [ * structs . IndexedCheckServiceNodes ] ( t , eventCh )
require . Empty ( t , result . Nodes )
} )
} )
}
func newMockInternalServiceDump ( t * testing . T ) * mockInternalServiceDump {
mock := & mockInternalServiceDump { }
mock . Mock . Test ( t )
t . Cleanup ( func ( ) { mock . AssertExpectations ( t ) } )
return mock
}
type mockInternalServiceDump struct {
mock . Mock
}
func ( m * mockInternalServiceDump ) Notify ( ctx context . Context , req * structs . ServiceDumpRequest , correlationID string , ch chan <- proxycfg . UpdateEvent ) error {
return m . Called ( ctx , req , correlationID , ch ) . Error ( 0 )
}