// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package api
import (
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/consul/sdk/testutil/retry"
)
func TestAPI_CatalogDatacenters ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
catalog := c . Catalog ( )
retry . Run ( t , func ( r * retry . R ) {
datacenters , err := catalog . Datacenters ( )
if err != nil {
r . Fatal ( err )
}
if len ( datacenters ) < 1 {
r . Fatal ( "got 0 datacenters want at least one" )
}
} )
}
func TestAPI_CatalogNodes ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
s . WaitForSerfCheck ( t )
catalog := c . Catalog ( )
retry . Run ( t , func ( r * retry . R ) {
nodes , meta , err := catalog . Nodes ( nil )
require . NoError ( r , err )
require . Len ( r , nodes , 1 )
require . True ( r , meta . LastIndex >= 1 , "Last index must be greater than 1" )
// The raft indexes are not relevant for this test.
got := nodes [ 0 ]
got . CreateIndex = 0
got . ModifyIndex = 0
want := & Node {
ID : s . Config . NodeID ,
Node : s . Config . NodeName ,
Partition : defaultPartition ,
Address : "127.0.0.1" ,
Datacenter : "dc1" ,
TaggedAddresses : map [ string ] string {
"lan" : "127.0.0.1" ,
"lan_ipv4" : "127.0.0.1" ,
"wan" : "127.0.0.1" ,
"wan_ipv4" : "127.0.0.1" ,
} ,
Meta : map [ string ] string {
"consul-network-segment" : "" ,
"consul-version" : s . Config . Version ,
} ,
}
require . Equal ( r , want , got )
} )
}
func TestAPI_CatalogNodes_MetaFilter ( t * testing . T ) {
t . Parallel ( )
meta := map [ string ] string { "somekey" : "somevalue" }
c , s := makeClientWithConfig ( t , nil , func ( conf * testutil . TestServerConfig ) {
conf . NodeMeta = meta
} )
defer s . Stop ( )
catalog := c . Catalog ( )
// Make sure we get the node back when filtering by its metadata
retry . Run ( t , func ( r * retry . R ) {
nodes , meta , err := catalog . Nodes ( & QueryOptions { NodeMeta : meta } )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( nodes ) == 0 {
r . Fatalf ( "Bad: %v" , nodes )
}
if _ , ok := nodes [ 0 ] . TaggedAddresses [ "wan" ] ; ! ok {
r . Fatalf ( "Bad: %v" , nodes [ 0 ] )
}
if v , ok := nodes [ 0 ] . Meta [ "somekey" ] ; ! ok || v != "somevalue" {
r . Fatalf ( "Bad: %v" , nodes [ 0 ] . Meta )
}
if nodes [ 0 ] . Datacenter != "dc1" {
r . Fatalf ( "Bad datacenter: %v" , nodes [ 0 ] )
}
} )
retry . Run ( t , func ( r * retry . R ) {
// Get nothing back when we use an invalid filter
nodes , meta , err := catalog . Nodes ( & QueryOptions { NodeMeta : map [ string ] string { "nope" : "nope" } } )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( nodes ) != 0 {
r . Fatalf ( "Bad: %v" , nodes )
}
} )
}
func TestAPI_CatalogNodes_Filter ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
// this sets up the catalog entries with things we can filter on
testNodeServiceCheckRegistrations ( t , c , "dc1" )
catalog := c . Catalog ( )
nodes , _ , err := catalog . Nodes ( nil )
require . NoError ( t , err )
// 3 nodes inserted by the setup func above plus the agent itself
require . Len ( t , nodes , 4 )
// now filter down to just a couple nodes with a specific meta entry
nodes , _ , err = catalog . Nodes ( & QueryOptions { Filter : "Meta.env == production" } )
require . NoError ( t , err )
require . Len ( t , nodes , 2 )
// filter out everything that isn't bar or baz
nodes , _ , err = catalog . Nodes ( & QueryOptions { Filter : "Node == bar or Node == baz" } )
require . NoError ( t , err )
require . Len ( t , nodes , 2 )
// check for non-existent ip for the node addr
nodes , _ , err = catalog . Nodes ( & QueryOptions { Filter : "Address == `10.0.0.1`" } )
require . NoError ( t , err )
require . Empty ( t , nodes )
}
func TestAPI_CatalogServices ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
catalog := c . Catalog ( )
retry . Run ( t , func ( r * retry . R ) {
services , meta , err := catalog . Services ( nil )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) == 0 {
r . Fatalf ( "Bad: %v" , services )
}
} )
}
func TestAPI_CatalogServices_NodeMetaFilter ( t * testing . T ) {
t . Parallel ( )
meta := map [ string ] string { "somekey" : "somevalue" }
c , s := makeClientWithConfig ( t , nil , func ( conf * testutil . TestServerConfig ) {
conf . NodeMeta = meta
} )
defer s . Stop ( )
catalog := c . Catalog ( )
// Make sure we get the service back when filtering by the node's metadata
retry . Run ( t , func ( r * retry . R ) {
services , meta , err := catalog . Services ( & QueryOptions { NodeMeta : meta } )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) == 0 {
r . Fatalf ( "Bad: %v" , services )
}
} )
retry . Run ( t , func ( r * retry . R ) {
// Get nothing back when using an invalid filter
services , meta , err := catalog . Services ( & QueryOptions { NodeMeta : map [ string ] string { "nope" : "nope" } } )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) != 0 {
r . Fatalf ( "Bad: %v" , services )
}
} )
}
func TestAPI_CatalogServices_FilterExpr_NodeMeta ( t * testing . T ) {
t . Parallel ( )
meta := map [ string ] string { "somekey" : "somevalue" , "synthetic" : "true" }
c , s := makeClientWithConfig ( t , nil , func ( conf * testutil . TestServerConfig ) {
conf . NodeMeta = meta
} )
defer s . Stop ( )
catalog := c . Catalog ( )
// Make sure we get the service back when filtering by filter expression
retry . Run ( t , func ( r * retry . R ) {
services , meta , err := catalog . Services ( & QueryOptions { Filter : "NodeMeta[\"synthetic\"] == true and NodeMeta[\"somekey\"] == somevalue" } )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) == 0 {
r . Fatalf ( "Bad: %v" , services )
}
} )
retry . Run ( t , func ( r * retry . R ) {
services , meta , err := catalog . Services ( & QueryOptions { Filter : "NodeMeta.synthetic == true" } )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) == 0 {
r . Fatalf ( "Bad: %v" , services )
}
} )
retry . Run ( t , func ( r * retry . R ) {
services , meta , err := catalog . Services ( & QueryOptions { Filter : "NodeMeta.somekey == somevalue" } )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) == 0 {
r . Fatalf ( "Bad: %v" , services )
}
} )
retry . Run ( t , func ( r * retry . R ) {
services , meta , err := catalog . Services ( & QueryOptions { Filter : "NodeMeta.nope == nope" } )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) != 0 {
r . Fatalf ( "Bad: %v" , services )
}
} )
}
func TestAPI_CatalogService ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
catalog := c . Catalog ( )
retry . Run ( t , func ( r * retry . R ) {
services , meta , err := catalog . Service ( "consul" , "" , nil )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) == 0 {
r . Fatalf ( "Bad: %v" , services )
}
if services [ 0 ] . Datacenter != "dc1" {
r . Fatalf ( "Bad datacenter: %v" , services [ 0 ] )
}
} )
}
func TestAPI_CatalogServiceUnmanagedProxy ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
catalog := c . Catalog ( )
proxyReg := testUnmanagedProxyRegistration ( t )
retry . Run ( t , func ( r * retry . R ) {
_ , err := catalog . Register ( proxyReg , nil )
r . Check ( err )
services , meta , err := catalog . Service ( "web-proxy" , "" , nil )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) == 0 {
r . Fatalf ( "Bad: %v" , services )
}
if services [ 0 ] . Datacenter != "dc1" {
r . Fatalf ( "Bad datacenter: %v" , services [ 0 ] )
}
if ! reflect . DeepEqual ( services [ 0 ] . ServiceProxy , proxyReg . Service . Proxy ) {
r . Fatalf ( "bad proxy.\nwant: %v\n got: %v" , proxyReg . Service . Proxy ,
services [ 0 ] . ServiceProxy )
}
} )
}
func TestAPI_CatalogServiceCached ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
catalog := c . Catalog ( )
q := & QueryOptions {
UseCache : true ,
}
retry . Run ( t , func ( r * retry . R ) {
services , meta , err := catalog . Service ( "consul" , "" , q )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) == 0 {
r . Fatalf ( "Bad: %v" , services )
}
if services [ 0 ] . Datacenter != "dc1" {
r . Fatalf ( "Bad datacenter: %v" , services [ 0 ] )
}
} )
// Got success, next hit must be cache hit
_ , meta , err := catalog . Service ( "consul" , "" , q )
bulk rewrite using this script
set -euo pipefail
unset CDPATH
cd "$(dirname "$0")"
for f in $(git grep '\brequire := require\.New(' | cut -d':' -f1 | sort -u); do
echo "=== require: $f ==="
sed -i '/require := require.New(t)/d' $f
# require.XXX(blah) but not require.XXX(tblah) or require.XXX(rblah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\([^tr]\)/require.\1(t,\2/g' $f
# require.XXX(tblah) but not require.XXX(t, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/require.\1(t,\2/g' $f
# require.XXX(rblah) but not require.XXX(r, blah)
sed -i 's/\brequire\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/require.\1(t,\2/g' $f
gofmt -s -w $f
done
for f in $(git grep '\bassert := assert\.New(' | cut -d':' -f1 | sort -u); do
echo "=== assert: $f ==="
sed -i '/assert := assert.New(t)/d' $f
# assert.XXX(blah) but not assert.XXX(tblah) or assert.XXX(rblah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\([^tr]\)/assert.\1(t,\2/g' $f
# assert.XXX(tblah) but not assert.XXX(t, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(t[^,]\)/assert.\1(t,\2/g' $f
# assert.XXX(rblah) but not assert.XXX(r, blah)
sed -i 's/\bassert\.\([a-zA-Z0-9_]*\)(\(r[^,]\)/assert.\1(t,\2/g' $f
gofmt -s -w $f
done
3 years ago
require . NoError ( t , err )
require . True ( t , meta . CacheHit )
require . Equal ( t , time . Duration ( 0 ) , meta . CacheAge )
}
func TestAPI_CatalogService_SingleTag ( t * testing . T ) {
t . Parallel ( )
c , s := makeClientWithConfig ( t , nil , func ( conf * testutil . TestServerConfig ) {
conf . NodeName = "node123"
} )
defer s . Stop ( )
agent := c . Agent ( )
catalog := c . Catalog ( )
locality := & Locality { Region : "us-west-1" , Zone : "us-west-1a" }
reg := & AgentServiceRegistration {
Name : "foo" ,
ID : "foo1" ,
Tags : [ ] string { "bar" } ,
Locality : locality ,
}
require . NoError ( t , agent . ServiceRegister ( reg ) )
defer agent . ServiceDeregister ( "foo1" )
retry . Run ( t , func ( r * retry . R ) {
services , meta , err := catalog . Service ( "foo" , "bar" , nil )
require . NoError ( r , err )
require . NotEqual ( r , meta . LastIndex , 0 )
require . Len ( r , services , 1 )
require . Equal ( r , services [ 0 ] . ServiceID , "foo1" )
require . Equal ( r , locality , services [ 0 ] . ServiceLocality )
} )
}
func TestAPI_CatalogService_MultipleTags ( t * testing . T ) {
t . Parallel ( )
c , s := makeClientWithConfig ( t , nil , func ( conf * testutil . TestServerConfig ) {
conf . NodeName = "node123"
} )
defer s . Stop ( )
agent := c . Agent ( )
catalog := c . Catalog ( )
// Make two services with a check
reg := & AgentServiceRegistration {
Name : "foo" ,
ID : "foo1" ,
Tags : [ ] string { "bar" } ,
}
require . NoError ( t , agent . ServiceRegister ( reg ) )
defer agent . ServiceDeregister ( "foo1" )
reg2 := & AgentServiceRegistration {
Name : "foo" ,
ID : "foo2" ,
Tags : [ ] string { "bar" , "v2" } ,
}
require . NoError ( t , agent . ServiceRegister ( reg2 ) )
defer agent . ServiceDeregister ( "foo2" )
// Test searching with one tag (two results)
retry . Run ( t , func ( r * retry . R ) {
services , meta , err := catalog . ServiceMultipleTags ( "foo" , [ ] string { "bar" } , nil )
require . NoError ( r , err )
require . NotEqual ( r , meta . LastIndex , 0 )
// Should be 2 services with the `bar` tag
require . Len ( r , services , 2 )
} )
// Test searching with two tags (one result)
retry . Run ( t , func ( r * retry . R ) {
services , meta , err := catalog . ServiceMultipleTags ( "foo" , [ ] string { "bar" , "v2" } , nil )
require . NoError ( r , err )
require . NotEqual ( r , meta . LastIndex , 0 )
// Should be exactly 1 service, named "foo2"
require . Len ( r , services , 1 )
require . Equal ( r , services [ 0 ] . ServiceID , "foo2" )
} )
}
func TestAPI_CatalogService_NodeMetaFilter ( t * testing . T ) {
t . Parallel ( )
meta := map [ string ] string { "somekey" : "somevalue" }
c , s := makeClientWithConfig ( t , nil , func ( conf * testutil . TestServerConfig ) {
conf . NodeMeta = meta
} )
defer s . Stop ( )
catalog := c . Catalog ( )
retry . Run ( t , func ( r * retry . R ) {
services , meta , err := catalog . Service ( "consul" , "" , & QueryOptions { NodeMeta : meta } )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) == 0 {
r . Fatalf ( "Bad: %v" , services )
}
if services [ 0 ] . Datacenter != "dc1" {
r . Fatalf ( "Bad datacenter: %v" , services [ 0 ] )
}
} )
}
func TestAPI_CatalogService_Filter ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
// this sets up the catalog entries with things we can filter on
testNodeServiceCheckRegistrations ( t , c , "dc1" )
catalog := c . Catalog ( )
services , _ , err := catalog . Service ( "redis" , "" , & QueryOptions { Filter : "ServiceMeta.version == 1" } )
require . NoError ( t , err )
// finds it on both foo and bar nodes
require . Len ( t , services , 2 )
require . Condition ( t , func ( ) bool {
return ( services [ 0 ] . Node == "foo" && services [ 1 ] . Node == "bar" ) ||
( services [ 0 ] . Node == "bar" && services [ 1 ] . Node == "foo" )
} )
services , _ , err = catalog . Service ( "redis" , "" , & QueryOptions { Filter : "NodeMeta.os != windows" } )
require . NoError ( t , err )
// finds both service instances on foo
require . Len ( t , services , 2 )
require . Equal ( t , "foo" , services [ 0 ] . Node )
require . Equal ( t , "foo" , services [ 1 ] . Node )
services , _ , err = catalog . Service ( "redis" , "" , & QueryOptions { Filter : "Address == `10.0.0.1`" } )
require . NoError ( t , err )
require . Empty ( t , services )
}
func testUpstreams ( t * testing . T ) [ ] Upstream {
return [ ] Upstream {
{
DestinationName : "db" ,
LocalBindPort : 9191 ,
Config : map [ string ] interface { } {
"connect_timeout_ms" : float64 ( 1000 ) ,
} ,
} ,
{
DestinationType : UpstreamDestTypePreparedQuery ,
DestinationName : "geo-cache" ,
LocalBindPort : 8181 ,
} ,
}
}
func testExpectUpstreamsWithDefaults ( t * testing . T , upstreams [ ] Upstream ) [ ] Upstream {
ups := make ( [ ] Upstream , len ( upstreams ) )
for i := range upstreams {
ups [ i ] = upstreams [ i ]
// Fill in default fields we expect to have back explicitly in a response
if ups [ i ] . DestinationType == "" {
ups [ i ] . DestinationType = UpstreamDestTypeService
}
}
return ups
}
// testUnmanagedProxy returns a fully configured external proxy service suitable
// for checking that all the config fields make it back in a response intact.
func testUnmanagedProxy ( t * testing . T ) * AgentService {
return & AgentService {
Kind : ServiceKindConnectProxy ,
Proxy : & AgentServiceConnectProxyConfig {
DestinationServiceName : "web" ,
DestinationServiceID : "web1" ,
LocalServiceAddress : "127.0.0.2" ,
LocalServicePort : 8080 ,
Upstreams : testUpstreams ( t ) ,
Mode : ProxyModeTransparent ,
TransparentProxy : & TransparentProxyConfig {
OutboundListenerPort : 808 ,
} ,
} ,
ID : "web-proxy1" ,
Service : "web-proxy" ,
Port : 8001 ,
}
}
// testUnmanagedProxyRegistration returns a *CatalogRegistration for a fully
// configured external proxy.
func testUnmanagedProxyRegistration ( t * testing . T ) * CatalogRegistration {
return & CatalogRegistration {
Datacenter : "dc1" ,
Node : "foobar" ,
Address : "192.168.10.10" ,
Service : testUnmanagedProxy ( t ) ,
}
}
func TestAPI_CatalogConnect ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
catalog := c . Catalog ( )
// Register service and proxy instances to test against.
proxyReg := testUnmanagedProxyRegistration ( t )
proxy := proxyReg . Service
service := & AgentService {
ID : proxyReg . Service . Proxy . DestinationServiceID ,
Service : proxyReg . Service . Proxy . DestinationServiceName ,
Port : 8000 ,
}
check := & AgentCheck {
Node : "foobar" ,
CheckID : "service:" + service . ID ,
Name : "Redis health check" ,
Notes : "Script based health check" ,
Status : HealthPassing ,
ServiceID : service . ID ,
}
reg := & CatalogRegistration {
Datacenter : "dc1" ,
Node : "foobar" ,
Address : "192.168.10.10" ,
Service : service ,
Check : check ,
}
retry . Run ( t , func ( r * retry . R ) {
if _ , err := catalog . Register ( reg , nil ) ; err != nil {
r . Fatal ( err )
}
if _ , err := catalog . Register ( proxyReg , nil ) ; err != nil {
r . Fatal ( err )
}
services , meta , err := catalog . Connect ( proxyReg . Service . Proxy . DestinationServiceName , "" , nil )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) == 0 {
r . Fatalf ( "Bad: %v" , services )
}
if services [ 0 ] . Datacenter != "dc1" {
r . Fatalf ( "Bad datacenter: %v" , services [ 0 ] )
}
if services [ 0 ] . ServicePort != proxy . Port {
r . Fatalf ( "Returned port should be for proxy: %v" , services [ 0 ] )
}
if ! reflect . DeepEqual ( services [ 0 ] . ServiceProxy , proxy . Proxy ) {
r . Fatalf ( "Returned proxy config should match:\nWant: %v\n Got: %v" ,
proxy . Proxy , services [ 0 ] . ServiceProxy )
}
} )
}
func TestAPI_CatalogConnectNative ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
catalog := c . Catalog ( )
// Register service and proxy instances to test against.
service := & AgentService {
ID : "redis1" ,
Service : "redis" ,
Port : 8000 ,
Connect : & AgentServiceConnect { Native : true } ,
}
check := & AgentCheck {
Node : "foobar" ,
CheckID : "service:redis1" ,
Name : "Redis health check" ,
Notes : "Script based health check" ,
Status : HealthPassing ,
ServiceID : "redis1" ,
}
reg := & CatalogRegistration {
Datacenter : "dc1" ,
Node : "foobar" ,
Address : "192.168.10.10" ,
Service : service ,
Check : check ,
}
retry . Run ( t , func ( r * retry . R ) {
if _ , err := catalog . Register ( reg , nil ) ; err != nil {
r . Fatal ( err )
}
services , meta , err := catalog . Connect ( "redis" , "" , nil )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( services ) == 0 {
r . Fatalf ( "Bad: %v" , services )
}
if services [ 0 ] . Datacenter != "dc1" {
r . Fatalf ( "Bad datacenter: %v" , services [ 0 ] )
}
if services [ 0 ] . ServicePort != service . Port {
r . Fatalf ( "Returned port should be for proxy: %v" , services [ 0 ] )
}
} )
}
func TestAPI_CatalogConnect_Filter ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
// this sets up the catalog entries with things we can filter on
testNodeServiceCheckRegistrations ( t , c , "dc1" )
catalog := c . Catalog ( )
services , _ , err := catalog . Connect ( "web" , "" , & QueryOptions { Filter : "ServicePort == 443" } )
require . NoError ( t , err )
require . Len ( t , services , 2 )
require . Condition ( t , func ( ) bool {
return ( services [ 0 ] . Node == "bar" && services [ 1 ] . Node == "baz" ) ||
( services [ 0 ] . Node == "baz" && services [ 1 ] . Node == "bar" )
} )
// All the web-connect services are native
services , _ , err = catalog . Connect ( "web" , "" , & QueryOptions { Filter : "ServiceConnect.Native != true" } )
require . NoError ( t , err )
require . Empty ( t , services )
}
func TestAPI_CatalogNode ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
catalog := c . Catalog ( )
name , err := c . Agent ( ) . NodeName ( )
require . NoError ( t , err )
proxyReg := testUnmanagedProxyRegistration ( t )
proxyReg . Node = name
proxyReg . SkipNodeUpdate = true
retry . Run ( t , func ( r * retry . R ) {
// Register a connect proxy to ensure all it's config fields are returned
_ , err := catalog . Register ( proxyReg , nil )
r . Check ( err )
info , meta , err := catalog . Node ( name , nil )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( info . Services ) != 2 {
r . Fatalf ( "Bad: %v (len %d)" , info , len ( info . Services ) )
}
if _ , ok := info . Node . TaggedAddresses [ "wan" ] ; ! ok {
r . Fatalf ( "Bad: %v" , info . Node . TaggedAddresses )
}
if info . Node . Datacenter != "dc1" {
r . Fatalf ( "Bad datacenter: %v" , info )
}
if _ , ok := info . Services [ "web-proxy1" ] ; ! ok {
r . Fatalf ( "Missing proxy service: %v" , info . Services )
}
if ! reflect . DeepEqual ( proxyReg . Service . Proxy , info . Services [ "web-proxy1" ] . Proxy ) {
r . Fatalf ( "Bad proxy config:\nwant %v\n got: %v" , proxyReg . Service . Proxy ,
info . Services [ "web-proxy" ] . Proxy )
}
} )
}
func TestAPI_CatalogNodeServiceList ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
catalog := c . Catalog ( )
name , err := c . Agent ( ) . NodeName ( )
require . NoError ( t , err )
proxyReg := testUnmanagedProxyRegistration ( t )
proxyReg . Node = name
proxyReg . SkipNodeUpdate = true
retry . Run ( t , func ( r * retry . R ) {
// Register a connect proxy to ensure all it's config fields are returned
_ , err := catalog . Register ( proxyReg , nil )
r . Check ( err )
info , meta , err := catalog . NodeServiceList ( name , nil )
if err != nil {
r . Fatal ( err )
}
if meta . LastIndex == 0 {
r . Fatalf ( "Bad: %v" , meta )
}
if len ( info . Services ) != 2 {
r . Fatalf ( "Bad: %v (len %d)" , info , len ( info . Services ) )
}
if _ , ok := info . Node . TaggedAddresses [ "wan" ] ; ! ok {
r . Fatalf ( "Bad: %v" , info . Node . TaggedAddresses )
}
if info . Node . Datacenter != "dc1" {
r . Fatalf ( "Bad datacenter: %v" , info )
}
var proxySvc * AgentService
for _ , svc := range info . Services {
if svc . ID == "web-proxy1" {
proxySvc = svc
break
}
}
if proxySvc == nil {
r . Fatalf ( "Missing proxy service: %v" , info . Services )
return
}
if ! reflect . DeepEqual ( proxyReg . Service . Proxy , proxySvc . Proxy ) {
r . Fatalf ( "Bad proxy config:\nwant %v\n got: %v" , proxyReg . Service . Proxy ,
proxySvc . Proxy )
}
} )
}
func TestAPI_CatalogNode_Filter ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
// this sets up the catalog entries with things we can filter on
testNodeServiceCheckRegistrations ( t , c , "dc1" )
catalog := c . Catalog ( )
// should have only 1 matching service
info , _ , err := catalog . Node ( "bar" , & QueryOptions { Filter : "connect in Tags" } )
require . NoError ( t , err )
require . Len ( t , info . Services , 1 )
require . Contains ( t , info . Services , "webV1" )
require . Equal ( t , "web" , info . Services [ "webV1" ] . Service )
// should get two services for the node
info , _ , err = catalog . Node ( "baz" , & QueryOptions { Filter : "connect in Tags" } )
require . NoError ( t , err )
require . Len ( t , info . Services , 2 )
}
func TestAPI_CatalogRegistration ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
catalog := c . Catalog ( )
service := & AgentService {
ID : "redis1" ,
Service : "redis" ,
Tags : [ ] string { "primary" , "v1" } ,
Port : 8000 ,
}
check := & AgentCheck {
Node : "foobar" ,
CheckID : "service:redis1-a" ,
Name : "Redis health check" ,
Notes : "Script based health check" ,
Status : HealthPassing ,
ServiceID : "redis1" ,
}
checks := HealthChecks {
& HealthCheck {
Node : "foobar" ,
CheckID : "service:redis1-b" ,
Name : "Redis health check" ,
Notes : "Script based health check" ,
Status : HealthPassing ,
ServiceID : "redis1" ,
} ,
}
reg := & CatalogRegistration {
Datacenter : "dc1" ,
Node : "foobar" ,
Address : "192.168.10.10" ,
NodeMeta : map [ string ] string { "somekey" : "somevalue" } ,
Service : service ,
// Specifying both Check and Checks is accepted by Consul
Check : check ,
Checks : checks ,
}
// Register a connect proxy for that service too
proxy := & AgentService {
ID : "redis-proxy1" ,
Service : "redis-proxy" ,
Port : 8001 ,
Kind : ServiceKindConnectProxy ,
Proxy : & AgentServiceConnectProxyConfig {
DestinationServiceName : service . Service ,
} ,
}
proxyReg := & CatalogRegistration {
Datacenter : "dc1" ,
Node : "foobar" ,
Address : "192.168.10.10" ,
NodeMeta : map [ string ] string { "somekey" : "somevalue" } ,
Service : proxy ,
}
retry . Run ( t , func ( r * retry . R ) {
if _ , err := catalog . Register ( reg , nil ) ; err != nil {
r . Fatal ( err )
}
if _ , err := catalog . Register ( proxyReg , nil ) ; err != nil {
r . Fatal ( err )
}
node , _ , err := catalog . Node ( "foobar" , nil )
if err != nil {
r . Fatal ( err )
}
if _ , ok := node . Services [ "redis1" ] ; ! ok {
r . Fatal ( "missing service: redis1" )
}
if _ , ok := node . Services [ "redis-proxy1" ] ; ! ok {
r . Fatal ( "missing service: redis-proxy1" )
}
health , _ , err := c . Health ( ) . Node ( "foobar" , nil )
if err != nil {
r . Fatal ( err )
}
if health [ 0 ] . CheckID != "service:redis1-a" {
r . Fatal ( "missing checkid service:redis1-a" )
}
if health [ 1 ] . CheckID != "service:redis1-b" {
r . Fatal ( "missing checkid service:redis1-b" )
}
if v , ok := node . Node . Meta [ "somekey" ] ; ! ok || v != "somevalue" {
r . Fatal ( "missing node meta pair somekey:somevalue" )
}
} )
// Test catalog deregistration of the previously registered service
dereg := & CatalogDeregistration {
Datacenter : "dc1" ,
Node : "foobar" ,
Address : "192.168.10.10" ,
ServiceID : "redis1" ,
}
// ... and proxy
deregProxy := & CatalogDeregistration {
Datacenter : "dc1" ,
Node : "foobar" ,
Address : "192.168.10.10" ,
ServiceID : "redis-proxy1" ,
}
if _ , err := catalog . Deregister ( dereg , nil ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
if _ , err := catalog . Deregister ( deregProxy , nil ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
retry . Run ( t , func ( r * retry . R ) {
node , _ , err := catalog . Node ( "foobar" , nil )
if err != nil {
r . Fatal ( err )
}
if _ , ok := node . Services [ "redis1" ] ; ok {
r . Fatal ( "ServiceID:redis1 is not deregistered" )
}
if _ , ok := node . Services [ "redis-proxy1" ] ; ok {
r . Fatal ( "ServiceID:redis-proxy1 is not deregistered" )
}
} )
// Test deregistration of the previously registered check
dereg = & CatalogDeregistration {
Datacenter : "dc1" ,
Node : "foobar" ,
Address : "192.168.10.10" ,
CheckID : "service:redis1-a" ,
}
if _ , err := catalog . Deregister ( dereg , nil ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
dereg = & CatalogDeregistration {
Datacenter : "dc1" ,
Node : "foobar" ,
Address : "192.168.10.10" ,
CheckID : "service:redis1-b" ,
}
if _ , err := catalog . Deregister ( dereg , nil ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
retry . Run ( t , func ( r * retry . R ) {
health , _ , err := c . Health ( ) . Node ( "foobar" , nil )
if err != nil {
r . Fatal ( err )
}
if len ( health ) != 0 {
r . Fatal ( "CheckID:service:redis1-a or CheckID:service:redis1-a is not deregistered" )
}
} )
// Test node deregistration of the previously registered node
dereg = & CatalogDeregistration {
Datacenter : "dc1" ,
Node : "foobar" ,
Address : "192.168.10.10" ,
}
if _ , err := catalog . Deregister ( dereg , nil ) ; err != nil {
t . Fatalf ( "err: %v" , err )
}
retry . Run ( t , func ( r * retry . R ) {
node , _ , err := catalog . Node ( "foobar" , nil )
if err != nil {
r . Fatal ( err )
}
if node != nil {
r . Fatalf ( "node is not deregistered: %v" , node )
}
} )
}
func TestAPI_CatalogEnableTagOverride ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
s . WaitForSerfCheck ( t )
catalog := c . Catalog ( )
service := & AgentService {
ID : "redis1" ,
Service : "redis" ,
Tags : [ ] string { "primary" , "v1" } ,
Port : 8000 ,
}
reg := & CatalogRegistration {
Datacenter : "dc1" ,
Node : "foobar" ,
Address : "192.168.10.10" ,
Service : service ,
}
retry . Run ( t , func ( r * retry . R ) {
if _ , err := catalog . Register ( reg , nil ) ; err != nil {
r . Fatal ( err )
}
node , _ , err := catalog . Node ( "foobar" , nil )
if err != nil {
r . Fatal ( err )
}
if _ , ok := node . Services [ "redis1" ] ; ! ok {
r . Fatal ( "missing service: redis1" )
}
if node . Services [ "redis1" ] . EnableTagOverride != false {
r . Fatal ( "tag override set" )
}
services , _ , err := catalog . Service ( "redis" , "" , nil )
if err != nil {
r . Fatal ( err )
}
if len ( services ) < 1 || services [ 0 ] . ServiceName != "redis" {
r . Fatal ( "missing service: redis" )
}
if services [ 0 ] . ServiceEnableTagOverride != false {
r . Fatal ( "tag override set" )
}
} )
service . EnableTagOverride = true
retry . Run ( t , func ( r * retry . R ) {
if _ , err := catalog . Register ( reg , nil ) ; err != nil {
r . Fatal ( err )
}
node , _ , err := catalog . Node ( "foobar" , nil )
if err != nil {
r . Fatal ( err )
}
if _ , ok := node . Services [ "redis1" ] ; ! ok {
r . Fatal ( "missing service: redis1" )
}
if node . Services [ "redis1" ] . EnableTagOverride != true {
r . Fatal ( "tag override not set" )
}
services , _ , err := catalog . Service ( "redis" , "" , nil )
if err != nil {
r . Fatal ( err )
}
if len ( services ) < 1 || services [ 0 ] . ServiceName != "redis" {
r . Fatal ( "missing service: redis" )
}
if services [ 0 ] . ServiceEnableTagOverride != true {
r . Fatal ( "tag override not set" )
}
} )
}
func TestAPI_CatalogGatewayServices_Terminating ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
s . WaitForSerfCheck ( t )
catalog := c . Catalog ( )
// Register a service to be covered by a wildcard in the config entry
svc := & AgentService {
ID : "redis" ,
Service : "redis" ,
Port : 6379 ,
}
reg := & CatalogRegistration {
Datacenter : "dc1" ,
Node : "bar" ,
Address : "192.168.10.11" ,
Service : svc ,
}
retry . Run ( t , func ( r * retry . R ) {
if _ , err := catalog . Register ( reg , nil ) ; err != nil {
r . Fatal ( err )
}
} )
entries := c . ConfigEntries ( )
// Associate the gateway and api/redis services
gwEntry := TerminatingGatewayConfigEntry {
Kind : TerminatingGateway ,
Name : "terminating" ,
Services : [ ] LinkedService {
{
Name : "api" ,
CAFile : "api/ca.crt" ,
CertFile : "api/client.crt" ,
KeyFile : "api/client.key" ,
SNI : "my-domain" ,
} ,
{
Name : "*" ,
CAFile : "ca.crt" ,
CertFile : "client.crt" ,
KeyFile : "client.key" ,
SNI : "my-alt-domain" ,
} ,
} ,
}
retry . Run ( t , func ( r * retry . R ) {
if success , _ , err := entries . Set ( & gwEntry , nil ) ; err != nil || ! success {
r . Fatal ( err )
}
} )
expect := [ ] * GatewayService {
{
Service : CompoundServiceName { Name : "api" , Namespace : defaultNamespace , Partition : defaultPartition } ,
Gateway : CompoundServiceName { Name : "terminating" , Namespace : defaultNamespace , Partition : defaultPartition } ,
GatewayKind : ServiceKindTerminatingGateway ,
CAFile : "api/ca.crt" ,
CertFile : "api/client.crt" ,
KeyFile : "api/client.key" ,
SNI : "my-domain" ,
} ,
{
Service : CompoundServiceName { Name : "redis" , Namespace : defaultNamespace , Partition : defaultPartition } ,
Gateway : CompoundServiceName { Name : "terminating" , Namespace : defaultNamespace , Partition : defaultPartition } ,
GatewayKind : ServiceKindTerminatingGateway ,
CAFile : "ca.crt" ,
CertFile : "client.crt" ,
KeyFile : "client.key" ,
SNI : "my-alt-domain" ,
FromWildcard : true ,
} ,
}
retry . Run ( t , func ( r * retry . R ) {
resp , _ , err := catalog . GatewayServices ( "terminating" , nil )
assert . NoError ( r , err )
assert . Equal ( r , expect , resp )
} )
}
func TestAPI_CatalogGatewayServices_Ingress ( t * testing . T ) {
t . Parallel ( )
c , s := makeClient ( t )
defer s . Stop ( )
s . WaitForSerfCheck ( t )
entries := c . ConfigEntries ( )
// Associate the gateway and api/redis services
gwEntry := IngressGatewayConfigEntry {
Kind : "ingress-gateway" ,
Name : "ingress" ,
Listeners : [ ] IngressListener {
{
Port : 8888 ,
Services : [ ] IngressService {
{
Name : "api" ,
} ,
} ,
} ,
{
Port : 9999 ,
Services : [ ] IngressService {
{
Name : "redis" ,
} ,
} ,
} ,
} ,
}
retry . Run ( t , func ( r * retry . R ) {
if success , _ , err := entries . Set ( & gwEntry , nil ) ; err != nil || ! success {
r . Fatal ( err )
}
} )
catalog := c . Catalog ( )
expect := [ ] * GatewayService {
{
Service : CompoundServiceName { Name : "api" , Namespace : defaultNamespace , Partition : defaultPartition } ,
Gateway : CompoundServiceName { Name : "ingress" , Namespace : defaultNamespace , Partition : defaultPartition } ,
GatewayKind : ServiceKindIngressGateway ,
Protocol : "tcp" ,
Port : 8888 ,
} ,
{
Service : CompoundServiceName { Name : "redis" , Namespace : defaultNamespace , Partition : defaultPartition } ,
Gateway : CompoundServiceName { Name : "ingress" , Namespace : defaultNamespace , Partition : defaultPartition } ,
GatewayKind : ServiceKindIngressGateway ,
Protocol : "tcp" ,
Port : 9999 ,
} ,
}
retry . Run ( t , func ( r * retry . R ) {
resp , _ , err := catalog . GatewayServices ( "ingress" , nil )
assert . NoError ( r , err )
assert . Equal ( r , expect , resp )
} )
}