2015-06-20 20:38:01 +08:00
package prometheus
import (
"fmt"
2020-02-11 06:18:30 +08:00
"math"
2015-06-20 20:38:01 +08:00
"net/http"
"net/http/httptest"
2017-09-19 06:06:11 +08:00
"net/url"
2021-03-09 00:00:56 +08:00
"os"
2015-06-20 20:38:01 +08:00
"testing"
2017-03-30 06:04:29 +08:00
"time"
2015-06-20 20:38:01 +08:00
2020-02-11 06:18:30 +08:00
"github.com/influxdata/telegraf"
2016-01-21 02:57:35 +08:00
"github.com/influxdata/telegraf/testutil"
2021-03-09 00:00:56 +08:00
"github.com/kubernetes/apimachinery/pkg/fields"
2015-06-20 20:38:01 +08:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const sampleTextFormat = ` # HELP go_gc_duration_seconds A summary of the GC invocation durations .
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds { quantile = "0" } 0.00010425500000000001
go_gc_duration_seconds { quantile = "0.25" } 0.000139108
go_gc_duration_seconds { quantile = "0.5" } 0.00015749400000000002
go_gc_duration_seconds { quantile = "0.75" } 0.000331463
go_gc_duration_seconds { quantile = "1" } 0.000667154
go_gc_duration_seconds_sum 0.0018183950000000002
go_gc_duration_seconds_count 7
# HELP go_goroutines Number of goroutines that currently exist .
# TYPE go_goroutines gauge
go_goroutines 15
2017-03-30 06:04:29 +08:00
# HELP test_metric An untyped metric with a timestamp
# TYPE test_metric untyped
test_metric { label = "value" } 1.0 1490802350000
2015-06-20 20:38:01 +08:00
`
2019-11-21 12:53:57 +08:00
const sampleSummaryTextFormat = ` # HELP go_gc_duration_seconds A summary of the GC invocation durations .
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds { quantile = "0" } 0.00010425500000000001
go_gc_duration_seconds { quantile = "0.25" } 0.000139108
go_gc_duration_seconds { quantile = "0.5" } 0.00015749400000000002
go_gc_duration_seconds { quantile = "0.75" } 0.000331463
go_gc_duration_seconds { quantile = "1" } 0.000667154
go_gc_duration_seconds_sum 0.0018183950000000002
go_gc_duration_seconds_count 7
`
const sampleGaugeTextFormat = `
# HELP go_goroutines Number of goroutines that currently exist .
# TYPE go_goroutines gauge
go_goroutines 15 1490802350000
`
2015-06-20 20:38:01 +08:00
func TestPrometheusGeneratesMetrics ( t * testing . T ) {
2015-08-05 06:09:59 +08:00
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
fmt . Fprintln ( w , sampleTextFormat )
} ) )
2015-06-20 20:38:01 +08:00
defer ts . Close ( )
p := & Prometheus {
2019-11-21 12:53:57 +08:00
Log : testutil . Logger { } ,
URLs : [ ] string { ts . URL } ,
URLTag : "url" ,
2015-06-20 20:38:01 +08:00
}
var acc testutil . Accumulator
2017-04-25 02:13:26 +08:00
err := acc . GatherError ( p . Gather )
2015-06-20 20:38:01 +08:00
require . NoError ( t , err )
2016-03-02 00:12:23 +08:00
assert . True ( t , acc . HasFloatField ( "go_gc_duration_seconds" , "count" ) )
assert . True ( t , acc . HasFloatField ( "go_goroutines" , "gauge" ) )
2017-03-30 06:04:29 +08:00
assert . True ( t , acc . HasFloatField ( "test_metric" , "value" ) )
assert . True ( t , acc . HasTimestamp ( "test_metric" , time . Unix ( 1490802350 , 0 ) ) )
2017-09-19 06:06:11 +08:00
assert . False ( t , acc . HasTag ( "test_metric" , "address" ) )
2018-11-03 08:51:40 +08:00
assert . True ( t , acc . TagValue ( "test_metric" , "url" ) == ts . URL + "/metrics" )
2017-09-19 06:06:11 +08:00
}
func TestPrometheusGeneratesMetricsWithHostNameTag ( t * testing . T ) {
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
fmt . Fprintln ( w , sampleTextFormat )
} ) )
defer ts . Close ( )
p := & Prometheus {
2019-09-24 06:39:50 +08:00
Log : testutil . Logger { } ,
2017-09-19 06:06:11 +08:00
KubernetesServices : [ ] string { ts . URL } ,
2019-11-21 12:53:57 +08:00
URLTag : "url" ,
2017-09-19 06:06:11 +08:00
}
u , _ := url . Parse ( ts . URL )
tsAddress := u . Hostname ( )
var acc testutil . Accumulator
err := acc . GatherError ( p . Gather )
require . NoError ( t , err )
assert . True ( t , acc . HasFloatField ( "go_gc_duration_seconds" , "count" ) )
assert . True ( t , acc . HasFloatField ( "go_goroutines" , "gauge" ) )
assert . True ( t , acc . HasFloatField ( "test_metric" , "value" ) )
assert . True ( t , acc . HasTimestamp ( "test_metric" , time . Unix ( 1490802350 , 0 ) ) )
assert . True ( t , acc . TagValue ( "test_metric" , "address" ) == tsAddress )
2017-09-23 08:26:19 +08:00
assert . True ( t , acc . TagValue ( "test_metric" , "url" ) == ts . URL )
2017-09-19 06:06:11 +08:00
}
2017-03-30 06:04:29 +08:00
2021-01-27 02:06:12 +08:00
func TestPrometheusGeneratesMetricsAlthoughFirstDNSFailsIntegration ( t * testing . T ) {
2017-09-19 06:06:11 +08:00
if testing . Short ( ) {
t . Skip ( "Skipping integration test in short mode" )
}
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
fmt . Fprintln ( w , sampleTextFormat )
} ) )
defer ts . Close ( )
p := & Prometheus {
2019-09-24 06:39:50 +08:00
Log : testutil . Logger { } ,
2018-02-06 03:16:00 +08:00
URLs : [ ] string { ts . URL } ,
2017-09-19 06:06:11 +08:00
KubernetesServices : [ ] string { "http://random.telegraf.local:88/metrics" } ,
}
var acc testutil . Accumulator
err := acc . GatherError ( p . Gather )
require . NoError ( t , err )
assert . True ( t , acc . HasFloatField ( "go_gc_duration_seconds" , "count" ) )
assert . True ( t , acc . HasFloatField ( "go_goroutines" , "gauge" ) )
assert . True ( t , acc . HasFloatField ( "test_metric" , "value" ) )
assert . True ( t , acc . HasTimestamp ( "test_metric" , time . Unix ( 1490802350 , 0 ) ) )
2015-06-20 20:38:01 +08:00
}
2019-11-21 12:53:57 +08:00
func TestPrometheusGeneratesSummaryMetricsV2 ( t * testing . T ) {
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
fmt . Fprintln ( w , sampleSummaryTextFormat )
} ) )
defer ts . Close ( )
p := & Prometheus {
URLs : [ ] string { ts . URL } ,
URLTag : "url" ,
MetricVersion : 2 ,
}
var acc testutil . Accumulator
err := acc . GatherError ( p . Gather )
require . NoError ( t , err )
assert . True ( t , acc . TagSetValue ( "prometheus" , "quantile" ) == "0" )
assert . True ( t , acc . HasFloatField ( "prometheus" , "go_gc_duration_seconds_sum" ) )
assert . True ( t , acc . HasFloatField ( "prometheus" , "go_gc_duration_seconds_count" ) )
assert . True ( t , acc . TagValue ( "prometheus" , "url" ) == ts . URL + "/metrics" )
}
2020-02-11 06:18:30 +08:00
func TestSummaryMayContainNaN ( t * testing . T ) {
const data = ` # HELP go_gc_duration_seconds A summary of the GC invocation durations .
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds { quantile = "0" } NaN
go_gc_duration_seconds { quantile = "1" } NaN
go_gc_duration_seconds_sum 42.0
go_gc_duration_seconds_count 42
`
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
fmt . Fprintln ( w , data )
} ) )
defer ts . Close ( )
p := & Prometheus {
URLs : [ ] string { ts . URL } ,
URLTag : "" ,
MetricVersion : 2 ,
}
var acc testutil . Accumulator
err := p . Gather ( & acc )
require . NoError ( t , err )
expected := [ ] telegraf . Metric {
testutil . MustMetric (
"prometheus" ,
map [ string ] string {
"quantile" : "0" ,
} ,
map [ string ] interface { } {
"go_gc_duration_seconds" : math . NaN ( ) ,
} ,
time . Unix ( 0 , 0 ) ,
telegraf . Summary ,
) ,
testutil . MustMetric (
"prometheus" ,
map [ string ] string {
"quantile" : "1" ,
} ,
map [ string ] interface { } {
"go_gc_duration_seconds" : math . NaN ( ) ,
} ,
time . Unix ( 0 , 0 ) ,
telegraf . Summary ,
) ,
testutil . MustMetric (
"prometheus" ,
map [ string ] string { } ,
map [ string ] interface { } {
"go_gc_duration_seconds_sum" : 42.0 ,
"go_gc_duration_seconds_count" : 42.0 ,
} ,
time . Unix ( 0 , 0 ) ,
telegraf . Summary ,
) ,
}
testutil . RequireMetricsEqual ( t , expected , acc . GetTelegrafMetrics ( ) ,
testutil . IgnoreTime ( ) , testutil . SortMetrics ( ) )
}
2019-11-21 12:53:57 +08:00
func TestPrometheusGeneratesGaugeMetricsV2 ( t * testing . T ) {
ts := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
fmt . Fprintln ( w , sampleGaugeTextFormat )
} ) )
defer ts . Close ( )
p := & Prometheus {
URLs : [ ] string { ts . URL } ,
URLTag : "url" ,
MetricVersion : 2 ,
}
var acc testutil . Accumulator
err := acc . GatherError ( p . Gather )
require . NoError ( t , err )
assert . True ( t , acc . HasFloatField ( "prometheus" , "go_goroutines" ) )
assert . True ( t , acc . TagValue ( "prometheus" , "url" ) == ts . URL + "/metrics" )
assert . True ( t , acc . HasTimestamp ( "prometheus" , time . Unix ( 1490802350 , 0 ) ) )
}
2021-03-09 00:00:56 +08:00
func TestUnsupportedFieldSelector ( t * testing . T ) {
fieldSelectorString := "spec.containerName=container"
prom := & Prometheus { Log : testutil . Logger { } , KubernetesFieldSelector : fieldSelectorString }
fieldSelector , _ := fields . ParseSelector ( prom . KubernetesFieldSelector )
isValid , invalidSelector := fieldSelectorIsSupported ( fieldSelector )
assert . Equal ( t , false , isValid )
assert . Equal ( t , "spec.containerName" , invalidSelector )
}
func TestInitConfigErrors ( t * testing . T ) {
p := & Prometheus {
MetricVersion : 2 ,
Log : testutil . Logger { } ,
URLs : nil ,
URLTag : "url" ,
MonitorPods : true ,
PodScrapeScope : "node" ,
PodScrapeInterval : 60 ,
}
// Both invalid IP addresses
p . NodeIP = "10.240.0.0.0"
os . Setenv ( "NODE_IP" , "10.000.0.0.0" )
err := p . Init ( )
expectedMessage := "The node_ip config and the environment variable NODE_IP are not set or invalid. Cannot get pod list for monitor_kubernetes_pods using node scrape scope"
assert . Equal ( t , expectedMessage , err . Error ( ) )
os . Setenv ( "NODE_IP" , "10.000.0.0" )
p . KubernetesLabelSelector = "label0==label0, label0 in (=)"
err = p . Init ( )
expectedMessage = "Error parsing the specified label selector(s): unable to parse requirement: found '=', expected: ',', ')' or identifier"
assert . Equal ( t , expectedMessage , err . Error ( ) )
p . KubernetesLabelSelector = "label0==label"
p . KubernetesFieldSelector = "field,"
err = p . Init ( )
expectedMessage = "Error parsing the specified field selector(s): invalid selector: 'field,'; can't understand 'field'"
assert . Equal ( t , expectedMessage , err . Error ( ) )
p . KubernetesFieldSelector = "spec.containerNames=containerNames"
err = p . Init ( )
expectedMessage = "The field selector spec.containerNames is not supported for pods"
assert . Equal ( t , expectedMessage , err . Error ( ) )
}