@ -2,6 +2,7 @@ package hcp
import (
"context"
"errors"
"fmt"
"net/url"
"regexp"
@ -11,6 +12,7 @@ import (
"time"
"github.com/armon/go-metrics"
"github.com/go-openapi/runtime"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
@ -36,64 +38,49 @@ type testConfig struct {
endpoint string
labels map [ string ] string
refreshInterval time . Duration
disabled bool
}
func TestNewTelemetryConfigProvider ( t * testing . T ) {
func TestNewTelemetryConfigProvider_DefaultConfig ( t * testing . T ) {
t . Parallel ( )
for name , tc := range map [ string ] struct {
testInputs * testConfig
wantErr string
} {
"success" : {
testInputs : & testConfig {
refreshInterval : 1 * time . Second ,
} ,
} ,
"failsWithInvalidRefreshInterval" : {
testInputs : & testConfig {
refreshInterval : 0 * time . Second ,
} ,
wantErr : "invalid refresh interval" ,
} ,
} {
tc := tc
t . Run ( name , func ( t * testing . T ) {
t . Parallel ( )
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer cancel ( )
// Initialize new provider, but fail all HCP fetches.
mc := client . NewMockClient ( t )
mc . EXPECT ( ) . FetchTelemetryConfig ( mock . Anything ) . Return ( nil , errors . New ( "failed to fetch config" ) )
testCfg , err := testTelemetryCfg ( tc . testInputs )
require . NoError ( t , err )
provider := NewHCPProvider ( ctx , mc )
provider . updateConfig ( ctx )
cfgProvider , err := NewHCPProvider ( ctx , client . NewMockClient ( t ) , testCfg )
if tc . wantErr != "" {
require . Error ( t , err )
require . Contains ( t , err . Error ( ) , tc . wantErr )
require . Nil ( t , cfgProvider )
return
}
require . NotNil ( t , cfgProvider )
} )
// Assert provider has default configuration and metrics processing is disabled.
defaultCfg := & dynamicConfig {
labels : map [ string ] string { } ,
filters : client . DefaultMetricFilters ,
refreshInterval : defaultTelemetryConfigRefreshInterval ,
endpoint : nil ,
disabled : true ,
}
require . Equal ( t , defaultCfg , provider . cfg )
}
func TestTelemetryConfigProviderGetUpdate ( t * testing . T ) {
func TestTelemetryConfigProvider_UpdateConfig ( t * testing . T ) {
for name , tc := range map [ string ] struct {
mockExpect func ( * client . MockClient )
metricKey string
optsInputs * testConfig
expected * testConfig
mockExpect func ( * client . MockClient )
metricKey string
initCfg * dynamicConfig
expected * dynamicConfig
expectedInterval time . Duration
} {
"noChanges" : {
optsInputs : & testConfig {
initCfg : testDynamicCfg ( & testConfig {
endpoint : "http://test.com/v1/metrics" ,
filters : "test" ,
labels : map [ string ] string {
"test_label" : "123" ,
} ,
refreshInterval : testRefreshInterval ,
} ,
} ) ,
mockExpect : func ( m * client . MockClient ) {
mockCfg , _ := testTelemetryCfg ( & testConfig {
endpoint : "http://test.com/v1/metrics" ,
@ -105,25 +92,26 @@ func TestTelemetryConfigProviderGetUpdate(t *testing.T) {
} )
m . EXPECT ( ) . FetchTelemetryConfig ( mock . Anything ) . Return ( mockCfg , nil )
} ,
expected : & testConfig {
expected : testDynamicCfg ( & testConfig {
endpoint : "http://test.com/v1/metrics" ,
labels : map [ string ] string {
"test_label" : "123" ,
} ,
filters : "test" ,
refreshInterval : testRefreshInterval ,
} ,
metricKey : testMetricKeySuccess ,
} ) ,
metricKey : testMetricKeySuccess ,
expectedInterval : testRefreshInterval ,
} ,
"newConfig" : {
optsInputs : & testConfig {
initCfg : testDynamicCfg ( & testConfig {
endpoint : "http://test.com/v1/metrics" ,
filters : "test" ,
labels : map [ string ] string {
"test_label" : "123" ,
} ,
refreshInterval : 2 * time . Second ,
} ,
} ) ,
mockExpect : func ( m * client . MockClient ) {
mockCfg , _ := testTelemetryCfg ( & testConfig {
endpoint : "http://newendpoint/v1/metrics" ,
@ -135,83 +123,136 @@ func TestTelemetryConfigProviderGetUpdate(t *testing.T) {
} )
m . EXPECT ( ) . FetchTelemetryConfig ( mock . Anything ) . Return ( mockCfg , nil )
} ,
expected : & testConfig {
expected : testDynamicCfg ( & testConfig {
endpoint : "http://newendpoint/v1/metrics" ,
filters : "consul" ,
labels : map [ string ] string {
"new_label" : "1234" ,
} ,
refreshInterval : 2 * time . Second ,
} ) ,
expectedInterval : 2 * time . Second ,
metricKey : testMetricKeySuccess ,
} ,
"newConfigMetricsDisabled" : {
initCfg : testDynamicCfg ( & testConfig {
endpoint : "http://test.com/v1/metrics" ,
filters : "test" ,
labels : map [ string ] string {
"test_label" : "123" ,
} ,
refreshInterval : 2 * time . Second ,
} ) ,
mockExpect : func ( m * client . MockClient ) {
mockCfg , _ := testTelemetryCfg ( & testConfig {
endpoint : "" ,
filters : "consul" ,
labels : map [ string ] string {
"new_label" : "1234" ,
} ,
refreshInterval : 2 * time . Second ,
disabled : true ,
} )
m . EXPECT ( ) . FetchTelemetryConfig ( mock . Anything ) . Return ( mockCfg , nil )
} ,
metricKey : testMetricKeySuccess ,
expected : testDynamicCfg ( & testConfig {
endpoint : "" ,
filters : "consul" ,
labels : map [ string ] string {
"new_label" : "1234" ,
} ,
refreshInterval : 2 * time . Second ,
disabled : true ,
} ) ,
metricKey : testMetricKeySuccess ,
expectedInterval : 2 * time . Second ,
} ,
"sameConfigInvalidRefreshInterval" : {
optsInputs : & testConfig {
initCfg : testDynamicCfg ( & testConfig {
endpoint : "http://test.com/v1/metrics" ,
filters : "test" ,
labels : map [ string ] string {
"test_label" : "123" ,
} ,
refreshInterval : testRefreshInterval ,
} ,
} ) ,
mockExpect : func ( m * client . MockClient ) {
mockCfg , _ := testTelemetryCfg ( & testConfig {
refreshInterval : 0 * time . Second ,
} )
m . EXPECT ( ) . FetchTelemetryConfig ( mock . Anything ) . Return ( mockCfg , nil )
} ,
expected : & testConfig {
expected : testDynamicCfg ( & testConfig {
endpoint : "http://test.com/v1/metrics" ,
labels : map [ string ] string {
"test_label" : "123" ,
} ,
filters : "test" ,
refreshInterval : testRefreshInterval ,
} ,
metricKey : testMetricKeyFailure ,
} ) ,
metricKey : testMetricKeyFailure ,
expectedInterval : 0 ,
} ,
"sameConfigHCPClientFailure" : {
optsInputs : & testConfig {
initCfg : testDynamicCfg ( & testConfig {
endpoint : "http://test.com/v1/metrics" ,
filters : "test" ,
labels : map [ string ] string {
"test_label" : "123" ,
} ,
refreshInterval : testRefreshInterval ,
} ,
} ) ,
mockExpect : func ( m * client . MockClient ) {
m . EXPECT ( ) . FetchTelemetryConfig ( mock . Anything ) . Return ( nil , fmt . Errorf ( "failure" ) )
} ,
expected : & testConfig {
expected : testDynamicCfg ( & testConfig {
endpoint : "http://test.com/v1/metrics" ,
filters : "test" ,
labels : map [ string ] string {
"test_label" : "123" ,
} ,
refreshInterval : testRefreshInterval ,
} ) ,
metricKey : testMetricKeyFailure ,
expectedInterval : 0 ,
} ,
"disableMetrics404" : {
initCfg : testDynamicCfg ( & testConfig {
endpoint : "http://test.com/v1/metrics" ,
filters : "test" ,
labels : map [ string ] string {
"test_label" : "123" ,
} ,
refreshInterval : testRefreshInterval ,
} ) ,
mockExpect : func ( m * client . MockClient ) {
err := runtime . NewAPIError ( "404 failure" , nil , 404 )
m . EXPECT ( ) . FetchTelemetryConfig ( mock . Anything ) . Return ( nil , err )
} ,
metricKey : testMetricKeyFailure ,
expected : defaultDisabledCfg ( ) ,
metricKey : testMetricKeySuccess ,
expectedInterval : defaultTelemetryConfigRefreshInterval ,
} ,
} {
tc := tc
t . Run ( name , func ( t * testing . T ) {
sink := initGlobalSink ( )
mockClient := client . NewMockClient ( t )
tc . mockExpect ( mockClient )
dynamicCfg , err := testDynamicCfg ( tc . optsInputs )
require . NoError ( t , err )
provider := & hcpProviderImpl {
hcpClient : mockClient ,
cfg : dynamic Cfg,
cfg : tc . initCfg ,
}
provider . getUpdate ( context . Background ( ) )
newInterval := provider . updateConfig ( context . Background ( ) )
require . Equal ( t , tc . expectedInterval , newInterval )
// Verify endpoint provider returns correct config values.
require . Equal ( t , tc . expected . endpoint , provider . GetEndpoint ( ) . String ( ) )
require . Equal ( t , tc . expected . filters , provider . GetFilters ( ) . String ( ) )
require . Equal ( t , tc . expected . endpoint , provider . GetEndpoint ( ) )
require . Equal ( t , tc . expected . filters , provider . GetFilters ( ) )
require . Equal ( t , tc . expected . labels , provider . GetLabels ( ) )
require . Equal ( t , tc . expected . disabled , provider . IsDisabled ( ) )
// Verify count for transform success metric.
interval := sink . Data ( ) [ 0 ]
@ -289,8 +330,7 @@ func TestTelemetryConfigProvider_Race(t *testing.T) {
}
// Start the provider goroutine, which fetches client TelemetryConfig every RefreshInterval.
provider , err := NewHCPProvider ( ctx , m , m . cfg )
require . NoError ( t , err )
provider := NewHCPProvider ( ctx , m )
for count := 0 ; count < testRaceWriteSampleCount ; count ++ {
// Force a TelemetryConfig value change in the mockRaceClient.
@ -298,7 +338,7 @@ func TestTelemetryConfigProvider_Race(t *testing.T) {
require . NoError ( t , err )
// Force provider to obtain new client TelemetryConfig immediately.
// This call is necessary to guarantee TelemetryConfig changes to assert on expected values below.
provider . getUpdate ( context . Background ( ) )
provider . updateConfig ( context . Background ( ) )
// Start goroutines to access label configuration.
wg := & sync . WaitGroup { }
@ -342,22 +382,20 @@ func initGlobalSink() *metrics.InmemSink {
}
// testDynamicCfg converts testConfig inputs to a dynamicConfig to be used in tests.
func testDynamicCfg ( testCfg * testConfig ) ( * dynamicConfig , error ) {
filters , err := regexp . Compile ( testCfg . filters )
if err != nil {
return nil , err
}
func testDynamicCfg ( testCfg * testConfig ) * dynamicConfig {
filters , _ := regexp . Compile ( testCfg . filters )
endpoint , err := url . Parse ( testCfg . endpoint )
if err != nil {
return nil , err
var endpoint * url . URL
if testCfg . endpoint != "" {
endpoint , _ = url . Parse ( testCfg . endpoint )
}
return & dynamicConfig {
Endpoint : endpoint ,
Filters : filters ,
Labels : testCfg . labels ,
RefreshInterval : testCfg . refreshInterval ,
} , nil
endpoint : endpoint ,
filters : filters ,
labels : testCfg . labels ,
refreshInterval : testCfg . refreshInterval ,
disabled : testCfg . disabled ,
}
}
// testTelemetryCfg converts testConfig inputs to a TelemetryConfig to be used in tests.
@ -367,15 +405,21 @@ func testTelemetryCfg(testCfg *testConfig) (*client.TelemetryConfig, error) {
return nil , err
}
endpoint , err := url . Parse ( testCfg . endpoint )
if err != nil {
return nil , err
var endpoint * url . URL
if testCfg . endpoint != "" {
u , err := url . Parse ( testCfg . endpoint )
if err != nil {
return nil , err
}
endpoint = u
}
return & client . TelemetryConfig {
MetricsConfig : & client . MetricsConfig {
Endpoint : endpoint ,
Filters : filters ,
Labels : testCfg . labels ,
Disabled : testCfg . disabled ,
} ,
RefreshConfig : & client . RefreshConfig {
RefreshInterval : testCfg . refreshInterval ,