2017-03-15 01:33:53 -07:00
// Copyright 2015 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package consul
import (
2018-04-27 05:11:16 -07:00
"context"
2018-03-23 07:48:43 -07:00
"net/http"
"net/http/httptest"
"net/url"
2020-10-22 02:00:08 -07:00
"testing"
"time"
2018-03-23 07:48:43 -07:00
2021-06-11 09:17:59 -07:00
"github.com/go-kit/log"
2021-06-11 15:06:59 -07:00
"github.com/prometheus/common/config"
2018-03-23 07:48:43 -07:00
"github.com/prometheus/common/model"
2020-10-29 02:43:23 -07:00
"github.com/stretchr/testify/require"
2020-07-24 02:10:20 -07:00
"go.uber.org/goleak"
2021-06-11 15:06:59 -07:00
"gopkg.in/yaml.v2"
2020-10-22 02:00:08 -07:00
"github.com/prometheus/prometheus/discovery/targetgroup"
2017-03-15 01:33:53 -07:00
)
2020-07-24 02:10:20 -07:00
func TestMain ( m * testing . M ) {
goleak . VerifyTestMain ( m )
}
2017-03-15 01:33:53 -07:00
func TestConfiguredService ( t * testing . T ) {
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-29 12:01:34 -08:00
conf := & SDConfig {
2021-10-22 01:06:44 -07:00
Services : [ ] string { "configuredServiceName" } ,
}
2017-08-11 11:45:52 -07:00
consulDiscovery , err := NewDiscovery ( conf , nil )
2017-03-15 01:33:53 -07:00
if err != nil {
2018-11-27 08:44:29 -08:00
t . Errorf ( "Unexpected error when initializing discovery %v" , err )
2017-03-15 01:33:53 -07:00
}
2018-03-23 07:48:43 -07:00
if ! consulDiscovery . shouldWatch ( "configuredServiceName" , [ ] string { "" } ) {
2017-03-15 01:33:53 -07:00
t . Errorf ( "Expected service %s to be watched" , "configuredServiceName" )
}
2018-03-23 07:48:43 -07:00
if consulDiscovery . shouldWatch ( "nonConfiguredServiceName" , [ ] string { "" } ) {
2017-03-15 01:33:53 -07:00
t . Errorf ( "Expected service %s to not be watched" , "nonConfiguredServiceName" )
}
}
2018-03-23 07:48:43 -07:00
func TestConfiguredServiceWithTag ( t * testing . T ) {
conf := & SDConfig {
2019-03-12 03:31:27 -07:00
Services : [ ] string { "configuredServiceName" } ,
ServiceTags : [ ] string { "http" } ,
2018-03-23 07:48:43 -07:00
}
consulDiscovery , err := NewDiscovery ( conf , nil )
if err != nil {
2018-11-27 08:44:29 -08:00
t . Errorf ( "Unexpected error when initializing discovery %v" , err )
2018-03-23 07:48:43 -07:00
}
if consulDiscovery . shouldWatch ( "configuredServiceName" , [ ] string { "" } ) {
t . Errorf ( "Expected service %s to not be watched without tag" , "configuredServiceName" )
}
if ! consulDiscovery . shouldWatch ( "configuredServiceName" , [ ] string { "http" } ) {
t . Errorf ( "Expected service %s to be watched with tag %s" , "configuredServiceName" , "http" )
}
if consulDiscovery . shouldWatch ( "nonConfiguredServiceName" , [ ] string { "" } ) {
t . Errorf ( "Expected service %s to not be watched without tag" , "nonConfiguredServiceName" )
}
if consulDiscovery . shouldWatch ( "nonConfiguredServiceName" , [ ] string { "http" } ) {
t . Errorf ( "Expected service %s to not be watched with tag %s" , "nonConfiguredServiceName" , "http" )
}
}
2019-03-12 03:31:27 -07:00
func TestConfiguredServiceWithTags ( t * testing . T ) {
type testcase struct {
// What we've configured to watch.
conf * SDConfig
// The service we're checking if we should watch or not.
serviceName string
serviceTags [ ] string
shouldWatch bool
}
cases := [ ] testcase {
2019-08-13 01:34:14 -07:00
{
2019-03-12 03:31:27 -07:00
conf : & SDConfig {
Services : [ ] string { "configuredServiceName" } ,
ServiceTags : [ ] string { "http" , "v1" } ,
} ,
serviceName : "configuredServiceName" ,
serviceTags : [ ] string { "" } ,
shouldWatch : false ,
} ,
2019-08-13 01:34:14 -07:00
{
2019-03-12 03:31:27 -07:00
conf : & SDConfig {
Services : [ ] string { "configuredServiceName" } ,
ServiceTags : [ ] string { "http" , "v1" } ,
} ,
serviceName : "configuredServiceName" ,
serviceTags : [ ] string { "http" , "v1" } ,
shouldWatch : true ,
} ,
2019-08-13 01:34:14 -07:00
{
2019-03-12 03:31:27 -07:00
conf : & SDConfig {
Services : [ ] string { "configuredServiceName" } ,
ServiceTags : [ ] string { "http" , "v1" } ,
} ,
serviceName : "nonConfiguredServiceName" ,
serviceTags : [ ] string { "" } ,
shouldWatch : false ,
} ,
2019-08-13 01:34:14 -07:00
{
2019-03-12 03:31:27 -07:00
conf : & SDConfig {
Services : [ ] string { "configuredServiceName" } ,
ServiceTags : [ ] string { "http" , "v1" } ,
} ,
serviceName : "nonConfiguredServiceName" ,
serviceTags : [ ] string { "http, v1" } ,
shouldWatch : false ,
} ,
2019-08-13 01:34:14 -07:00
{
2019-03-12 03:31:27 -07:00
conf : & SDConfig {
Services : [ ] string { "configuredServiceName" } ,
ServiceTags : [ ] string { "http" , "v1" } ,
} ,
serviceName : "configuredServiceName" ,
serviceTags : [ ] string { "http" , "v1" , "foo" } ,
shouldWatch : true ,
} ,
2019-08-13 01:34:14 -07:00
{
2019-03-12 03:31:27 -07:00
conf : & SDConfig {
Services : [ ] string { "configuredServiceName" } ,
ServiceTags : [ ] string { "http" , "v1" , "foo" } ,
} ,
serviceName : "configuredServiceName" ,
serviceTags : [ ] string { "http" , "v1" , "foo" } ,
shouldWatch : true ,
} ,
2019-08-13 01:34:14 -07:00
{
2019-03-12 03:31:27 -07:00
conf : & SDConfig {
Services : [ ] string { "configuredServiceName" } ,
ServiceTags : [ ] string { "http" , "v1" } ,
} ,
serviceName : "configuredServiceName" ,
serviceTags : [ ] string { "http" , "v1" , "v1" } ,
shouldWatch : true ,
} ,
}
for _ , tc := range cases {
consulDiscovery , err := NewDiscovery ( tc . conf , nil )
if err != nil {
t . Errorf ( "Unexpected error when initializing discovery %v" , err )
}
ret := consulDiscovery . shouldWatch ( tc . serviceName , tc . serviceTags )
if ret != tc . shouldWatch {
2019-03-14 07:38:54 -07:00
t . Errorf ( "Expected should watch? %t, got %t. Watched service and tags: %s %+v, input was %s %+v" , tc . shouldWatch , ret , tc . conf . Services , tc . conf . ServiceTags , tc . serviceName , tc . serviceTags )
2019-03-12 03:31:27 -07:00
}
}
}
2017-03-15 01:33:53 -07:00
func TestNonConfiguredService ( t * testing . T ) {
Refactor SD configuration to remove `config` dependency (#3629)
* refactor: move targetGroup struct and CheckOverflow() to their own package
* refactor: move auth and security related structs to a utility package, fix import error in utility package
* refactor: Azure SD, remove SD struct from config
* refactor: DNS SD, remove SD struct from config into dns package
* refactor: ec2 SD, move SD struct from config into the ec2 package
* refactor: file SD, move SD struct from config to file discovery package
* refactor: gce, move SD struct from config to gce discovery package
* refactor: move HTTPClientConfig and URL into util/config, fix import error in httputil
* refactor: consul, move SD struct from config into consul discovery package
* refactor: marathon, move SD struct from config into marathon discovery package
* refactor: triton, move SD struct from config to triton discovery package, fix test
* refactor: zookeeper, move SD structs from config to zookeeper discovery package
* refactor: openstack, remove SD struct from config, move into openstack discovery package
* refactor: kubernetes, move SD struct from config into kubernetes discovery package
* refactor: notifier, use targetgroup package instead of config
* refactor: tests for file, marathon, triton SD - use targetgroup package instead of config.TargetGroup
* refactor: retrieval, use targetgroup package instead of config.TargetGroup
* refactor: storage, use config util package
* refactor: discovery manager, use targetgroup package instead of config.TargetGroup
* refactor: use HTTPClient and TLS config from configUtil instead of config
* refactor: tests, use targetgroup package instead of config.TargetGroup
* refactor: fix tagetgroup.Group pointers that were removed by mistake
* refactor: openstack, kubernetes: drop prefixes
* refactor: remove import aliases forced due to vscode bug
* refactor: move main SD struct out of config into discovery/config
* refactor: rename configUtil to config_util
* refactor: rename yamlUtil to yaml_config
* refactor: kubernetes, remove prefixes
* refactor: move the TargetGroup package to discovery/
* refactor: fix order of imports
2017-12-29 12:01:34 -08:00
conf := & SDConfig { }
2017-08-11 11:45:52 -07:00
consulDiscovery , err := NewDiscovery ( conf , nil )
2017-03-15 01:33:53 -07:00
if err != nil {
2018-11-27 08:44:29 -08:00
t . Errorf ( "Unexpected error when initializing discovery %v" , err )
2017-03-15 01:33:53 -07:00
}
2018-03-23 07:48:43 -07:00
if ! consulDiscovery . shouldWatch ( "nonConfiguredServiceName" , [ ] string { "" } ) {
2017-03-15 01:33:53 -07:00
t . Errorf ( "Expected service %s to be watched" , "nonConfiguredServiceName" )
}
}
2018-03-23 07:48:43 -07:00
const (
AgentAnswer = ` { "Config": { "Datacenter": "test-dc"}} `
2020-02-25 05:32:30 -08:00
ServiceTestAnswer = `
[ {
"Node" : {
"ID" : "b78c2e48-5ef3-1814-31b8-0d880f50471e" ,
"Node" : "node1" ,
"Address" : "1.1.1.1" ,
"Datacenter" : "test-dc" ,
"TaggedAddresses" : {
"lan" : "192.168.10.10" ,
"wan" : "10.0.10.10"
} ,
"Meta" : { "rack_name" : "2304" } ,
"CreateIndex" : 1 ,
"ModifyIndex" : 1
} ,
"Service" : {
"ID" : "test" ,
"Service" : "test" ,
"Tags" : [ "tag1" ] ,
"Address" : "" ,
2020-07-04 05:54:26 -07:00
"Meta" : { "version" : "1.0.0" , "environment" : "staging" } ,
2020-02-25 05:32:30 -08:00
"Port" : 3341 ,
"Weights" : {
"Passing" : 1 ,
"Warning" : 1
} ,
"EnableTagOverride" : false ,
"ProxyDestination" : "" ,
"Proxy" : { } ,
"Connect" : { } ,
"CreateIndex" : 1 ,
"ModifyIndex" : 1
} ,
"Checks" : [ {
"Node" : "node1" ,
"CheckID" : "serfHealth" ,
"Name" : "Serf Health Status" ,
"Status" : "passing"
} ]
2018-03-23 07:48:43 -07:00
} ] `
2020-02-25 05:32:30 -08:00
2018-03-23 07:48:43 -07:00
ServicesTestAnswer = ` { "test": ["tag1"], "other": ["tag2"]} `
)
func newServer ( t * testing . T ) ( * httptest . Server , * SDConfig ) {
// github.com/hashicorp/consul/testutil/ would be nice but it needs a local consul binary.
stub := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
response := ""
switch r . URL . String ( ) {
case "/v1/agent/self" :
response = AgentAnswer
2020-08-02 06:39:23 -07:00
case "/v1/health/service/test?node-meta=rack_name%3A2304&stale=&tag=tag1&wait=120000ms" :
2018-03-23 07:48:43 -07:00
response = ServiceTestAnswer
2020-08-02 06:39:23 -07:00
case "/v1/health/service/test?wait=120000ms" :
2018-03-23 07:48:43 -07:00
response = ServiceTestAnswer
2020-08-02 06:39:23 -07:00
case "/v1/health/service/other?wait=120000ms" :
2018-03-23 07:48:43 -07:00
response = ` [] `
2020-08-02 06:39:23 -07:00
case "/v1/catalog/services?node-meta=rack_name%3A2304&stale=&wait=120000ms" :
2018-03-23 07:48:43 -07:00
response = ServicesTestAnswer
2020-08-02 06:39:23 -07:00
case "/v1/catalog/services?wait=120000ms" :
2018-03-23 07:48:43 -07:00
response = ServicesTestAnswer
2020-08-02 06:39:23 -07:00
case "/v1/catalog/services?index=1&node-meta=rack_name%3A2304&stale=&wait=120000ms" :
2018-03-23 07:48:43 -07:00
time . Sleep ( 5 * time . Second )
response = ServicesTestAnswer
2020-08-02 06:39:23 -07:00
case "/v1/catalog/services?index=1&wait=120000ms" :
2018-03-23 07:48:43 -07:00
time . Sleep ( 5 * time . Second )
response = ServicesTestAnswer
default :
2019-08-05 22:11:41 -07:00
t . Errorf ( "Unhandled consul call: %s" , r . URL )
2018-03-23 07:48:43 -07:00
}
w . Header ( ) . Add ( "X-Consul-Index" , "1" )
w . Write ( [ ] byte ( response ) )
} ) )
2018-04-27 05:11:16 -07:00
stuburl , err := url . Parse ( stub . URL )
2020-10-29 02:43:23 -07:00
require . NoError ( t , err )
2018-03-23 07:48:43 -07:00
config := & SDConfig {
2018-04-27 05:11:16 -07:00
Server : stuburl . Host ,
2018-03-23 07:48:43 -07:00
Token : "fake-token" ,
RefreshInterval : model . Duration ( 1 * time . Second ) ,
}
return stub , config
}
func newDiscovery ( t * testing . T , config * SDConfig ) * Discovery {
logger := log . NewNopLogger ( )
d , err := NewDiscovery ( config , logger )
2020-10-29 02:43:23 -07:00
require . NoError ( t , err )
2018-03-23 07:48:43 -07:00
return d
}
func checkOneTarget ( t * testing . T , tg [ ] * targetgroup . Group ) {
2020-10-29 02:43:23 -07:00
require . Equal ( t , 1 , len ( tg ) )
2018-03-23 07:48:43 -07:00
target := tg [ 0 ]
2020-10-29 02:43:23 -07:00
require . Equal ( t , "test-dc" , string ( target . Labels [ "__meta_consul_dc" ] ) )
require . Equal ( t , target . Source , string ( target . Labels [ "__meta_consul_service" ] ) )
2018-03-23 07:48:43 -07:00
if target . Source == "test" {
// test service should have one node.
2020-10-29 02:43:23 -07:00
require . Greater ( t , len ( target . Targets ) , 0 , "Test service should have one node" )
2018-03-23 07:48:43 -07:00
}
}
// Watch all the services in the catalog.
func TestAllServices ( t * testing . T ) {
stub , config := newServer ( t )
defer stub . Close ( )
d := newDiscovery ( t , config )
ctx , cancel := context . WithCancel ( context . Background ( ) )
ch := make ( chan [ ] * targetgroup . Group )
2020-07-24 02:10:20 -07:00
go func ( ) {
d . Run ( ctx , ch )
close ( ch )
} ( )
2018-03-23 07:48:43 -07:00
checkOneTarget ( t , <- ch )
checkOneTarget ( t , <- ch )
cancel ( )
2020-07-24 02:10:20 -07:00
<- ch
2018-03-23 07:48:43 -07:00
}
2021-04-28 08:00:07 -07:00
// targetgroup with no targets is emitted if no services were discovered.
func TestNoTargets ( t * testing . T ) {
stub , config := newServer ( t )
defer stub . Close ( )
config . ServiceTags = [ ] string { "missing" }
d := newDiscovery ( t , config )
ctx , cancel := context . WithCancel ( context . Background ( ) )
ch := make ( chan [ ] * targetgroup . Group )
2021-10-21 01:13:09 -07:00
go func ( ) {
d . Run ( ctx , ch )
close ( ch )
} ( )
2021-04-28 08:00:07 -07:00
targets := ( <- ch ) [ 0 ] . Targets
require . Equal ( t , 0 , len ( targets ) )
cancel ( )
2021-10-21 01:13:09 -07:00
<- ch
2021-04-28 08:00:07 -07:00
}
2018-03-23 07:48:43 -07:00
// Watch only the test service.
func TestOneService ( t * testing . T ) {
stub , config := newServer ( t )
defer stub . Close ( )
config . Services = [ ] string { "test" }
d := newDiscovery ( t , config )
ctx , cancel := context . WithCancel ( context . Background ( ) )
ch := make ( chan [ ] * targetgroup . Group )
go d . Run ( ctx , ch )
checkOneTarget ( t , <- ch )
cancel ( )
}
// Watch the test service with a specific tag and node-meta.
func TestAllOptions ( t * testing . T ) {
stub , config := newServer ( t )
defer stub . Close ( )
config . Services = [ ] string { "test" }
config . NodeMeta = map [ string ] string { "rack_name" : "2304" }
2019-03-12 03:31:27 -07:00
config . ServiceTags = [ ] string { "tag1" }
2018-03-23 07:48:43 -07:00
config . AllowStale = true
config . Token = "fake-token"
d := newDiscovery ( t , config )
ctx , cancel := context . WithCancel ( context . Background ( ) )
ch := make ( chan [ ] * targetgroup . Group )
2020-07-24 02:10:20 -07:00
go func ( ) {
d . Run ( ctx , ch )
close ( ch )
} ( )
2018-03-23 07:48:43 -07:00
checkOneTarget ( t , <- ch )
cancel ( )
2020-07-24 02:10:20 -07:00
<- ch
2018-03-23 07:48:43 -07:00
}
2019-11-15 05:52:39 -08:00
func TestGetDatacenterShouldReturnError ( t * testing . T ) {
for _ , tc := range [ ] struct {
handler func ( http . ResponseWriter , * http . Request )
errMessage string
} {
{
// Define a handler that will return status 500.
handler : func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( 500 )
} ,
errMessage : "Unexpected response code: 500 ()" ,
} ,
{
// Define a handler that will return incorrect response.
handler : func ( w http . ResponseWriter , r * http . Request ) {
w . Write ( [ ] byte ( ` { "Config": { "Not-Datacenter": "test-dc"}} ` ) )
} ,
errMessage : "invalid value '<nil>' for Config.Datacenter" ,
} ,
} {
stub := httptest . NewServer ( http . HandlerFunc ( tc . handler ) )
stuburl , err := url . Parse ( stub . URL )
2020-10-29 02:43:23 -07:00
require . NoError ( t , err )
2019-11-15 05:52:39 -08:00
config := & SDConfig {
Server : stuburl . Host ,
Token : "fake-token" ,
RefreshInterval : model . Duration ( 1 * time . Second ) ,
}
defer stub . Close ( )
d := newDiscovery ( t , config )
// Should be empty if not initialized.
2020-10-29 02:43:23 -07:00
require . Equal ( t , "" , d . clientDatacenter )
2019-11-15 05:52:39 -08:00
err = d . getDatacenter ( )
// An error should be returned.
2020-10-29 02:43:23 -07:00
require . Equal ( t , tc . errMessage , err . Error ( ) )
2019-11-15 05:52:39 -08:00
// Should still be empty.
2020-10-29 02:43:23 -07:00
require . Equal ( t , "" , d . clientDatacenter )
2019-11-15 05:52:39 -08:00
}
}
2021-06-11 15:06:59 -07:00
func TestUnmarshalConfig ( t * testing . T ) {
unmarshal := func ( d [ ] byte ) func ( interface { } ) error {
return func ( o interface { } ) error {
return yaml . Unmarshal ( d , o )
}
}
goodConfig := DefaultSDConfig
goodConfig . Username = "123"
goodConfig . Password = "1234"
goodConfig . HTTPClientConfig = config . HTTPClientConfig {
BasicAuth : & config . BasicAuth {
Username : "123" ,
Password : "1234" ,
} ,
FollowRedirects : true ,
}
cases := [ ] struct {
name string
config string
expected SDConfig
errMessage string
} {
{
name : "good" ,
config : `
server : localhost : 8500
username : 123
password : 1234
` ,
expected : goodConfig ,
} ,
{
name : "username and password and basic auth configured" ,
config : `
server : localhost : 8500
username : 123
password : 1234
basic_auth :
username : 12345
password : 123456
` ,
errMessage : "at most one of consul SD configuration username and password and basic auth can be configured" ,
} ,
{
name : "token and authorization configured" ,
config : `
server : localhost : 8500
token : 1234567
authorization :
credentials : 12345678
` ,
errMessage : "at most one of consul SD token, authorization, or oauth2 can be configured" ,
} ,
{
name : "token and oauth2 configured" ,
config : `
server : localhost : 8500
token : 1234567
oauth2 :
client_id : 10
client_secret : 11
token_url : http : //example.com
` ,
errMessage : "at most one of consul SD token, authorization, or oauth2 can be configured" ,
} ,
}
for _ , test := range cases {
t . Run ( test . name , func ( t * testing . T ) {
var config SDConfig
err := config . UnmarshalYAML ( unmarshal ( [ ] byte ( test . config ) ) )
if err != nil {
require . Equalf ( t , err . Error ( ) , test . errMessage , "Expected error '%s', got '%v'" , test . errMessage , err )
return
}
if test . errMessage != "" {
t . Errorf ( "Expected error %s, got none" , test . errMessage )
return
}
require . Equal ( t , config , test . expected )
} )
}
}