1
0
Fork 0
telegraf/plugins/inputs/stackdriver/stackdriver_test.go
Daniel Baumann 4978089aab
Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-24 07:26:29 +02:00

1233 lines
32 KiB
Go

package stackdriver
import (
"context"
"sync"
"testing"
"time"
"cloud.google.com/go/monitoring/apiv3/v2/monitoringpb"
"github.com/stretchr/testify/require"
"google.golang.org/genproto/googleapis/api/distribution"
metricpb "google.golang.org/genproto/googleapis/api/metric"
"google.golang.org/genproto/googleapis/api/monitoredres"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
"github.com/influxdata/telegraf/testutil"
)
type call struct {
name string
args []interface{}
}
type mockStackdriverClient struct {
listMetricDescriptorsF func() (<-chan *metricpb.MetricDescriptor, error)
listTimeSeriesF func() (<-chan *monitoringpb.TimeSeries, error)
closeF func() error
calls []*call
sync.Mutex
}
func (m *mockStackdriverClient) listMetricDescriptors(
ctx context.Context,
req *monitoringpb.ListMetricDescriptorsRequest,
) (<-chan *metricpb.MetricDescriptor, error) {
call := &call{name: "listMetricDescriptors", args: []interface{}{ctx, req}}
m.Lock()
m.calls = append(m.calls, call)
m.Unlock()
return m.listMetricDescriptorsF()
}
func (m *mockStackdriverClient) listTimeSeries(
ctx context.Context,
req *monitoringpb.ListTimeSeriesRequest,
) (<-chan *monitoringpb.TimeSeries, error) {
call := &call{name: "listTimeSeries", args: []interface{}{ctx, req}}
m.Lock()
m.calls = append(m.calls, call)
m.Unlock()
return m.listTimeSeriesF()
}
func (m *mockStackdriverClient) close() error {
call := &call{name: "close", args: make([]interface{}, 0)}
m.Lock()
m.calls = append(m.calls, call)
m.Unlock()
return m.closeF()
}
func TestInitAndRegister(t *testing.T) {
expected := &Stackdriver{
CacheTTL: defaultCacheTTL,
RateLimit: defaultRateLimit,
Delay: defaultDelay,
GatherRawDistributionBuckets: true,
}
require.Equal(t, expected, inputs.Inputs["stackdriver"]())
}
func createTimeSeries(
point *monitoringpb.Point, valueType metricpb.MetricDescriptor_ValueType,
) *monitoringpb.TimeSeries {
return &monitoringpb.TimeSeries{
Metric: &metricpb.Metric{Labels: make(map[string]string)},
Resource: &monitoredres.MonitoredResource{
Type: "global",
Labels: map[string]string{
"project_id": "test",
},
},
Points: []*monitoringpb.Point{point},
ValueType: valueType,
}
}
func TestGather(t *testing.T) {
now := time.Now().Round(time.Second)
tests := []struct {
name string
descriptor *metricpb.MetricDescriptor
timeseries *monitoringpb.TimeSeries
expected []telegraf.Metric
wantAccErr bool
}{
{
name: "no_bucket",
descriptor: &metricpb.MetricDescriptor{
ValueType: metricpb.MetricDescriptor_DISTRIBUTION,
},
timeseries: createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_DistributionValue{
DistributionValue: &distribution.Distribution{
Count: 2,
},
},
},
},
metricpb.MetricDescriptor_DISTRIBUTION,
),
expected: []telegraf.Metric{
testutil.MustMetric("",
map[string]string{
"project_id": "test",
"resource_type": "global",
},
map[string]interface{}{
"value_count": 2,
"value_mean": float64(0),
"value_sum_of_squared_deviation": float64(0),
},
now),
},
wantAccErr: true,
},
{
name: "int64",
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_INT64,
},
timeseries: createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_Int64Value{
Int64Value: 42,
},
},
},
metricpb.MetricDescriptor_INT64,
),
expected: []telegraf.Metric{
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
},
map[string]interface{}{
"usage": 42,
},
now),
},
},
{
name: "double",
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DOUBLE,
},
timeseries: createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_DoubleValue{
DoubleValue: 42.0,
},
},
},
metricpb.MetricDescriptor_DOUBLE,
),
expected: []telegraf.Metric{
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
},
map[string]interface{}{
"usage": 42.0,
},
now),
},
},
{
name: "int64",
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_INT64,
},
timeseries: createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_Int64Value{
Int64Value: 42,
},
},
},
metricpb.MetricDescriptor_INT64,
),
expected: []telegraf.Metric{
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
},
map[string]interface{}{
"usage": 42,
},
now),
},
},
{
name: "bool",
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_BOOL,
},
timeseries: createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_BoolValue{
BoolValue: true,
},
},
},
metricpb.MetricDescriptor_BOOL,
),
expected: []telegraf.Metric{
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
},
map[string]interface{}{
"usage": true,
},
now),
},
},
{
name: "string",
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_STRING,
},
timeseries: createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_StringValue{
StringValue: "foo",
},
},
},
metricpb.MetricDescriptor_STRING,
),
expected: []telegraf.Metric{
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
},
map[string]interface{}{
"usage": "foo",
},
now),
},
},
{
name: "metric labels",
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DOUBLE,
},
timeseries: &monitoringpb.TimeSeries{
Metric: &metricpb.Metric{
Labels: map[string]string{
"resource_type": "instance",
},
},
Resource: &monitoredres.MonitoredResource{
Type: "global",
Labels: map[string]string{
"project_id": "test",
},
},
Points: []*monitoringpb.Point{
{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_DoubleValue{
DoubleValue: 42.0,
},
},
},
},
ValueType: metricpb.MetricDescriptor_DOUBLE,
},
expected: []telegraf.Metric{
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "instance",
"project_id": "test",
},
map[string]interface{}{
"usage": 42.0,
},
now),
},
},
{
name: "linear buckets",
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DISTRIBUTION,
},
timeseries: createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_DistributionValue{
DistributionValue: &distribution.Distribution{
Count: 2,
Mean: 2.0,
SumOfSquaredDeviation: 1.0,
Range: &distribution.Distribution_Range{
Min: 0.0,
Max: 3.0,
},
BucketCounts: []int64{0, 1, 3, 0},
BucketOptions: &distribution.Distribution_BucketOptions{
Options: &distribution.Distribution_BucketOptions_LinearBuckets{
LinearBuckets: &distribution.Distribution_BucketOptions_Linear{
NumFiniteBuckets: 2,
Width: 1,
Offset: 1,
},
},
},
},
},
},
},
metricpb.MetricDescriptor_DISTRIBUTION,
),
expected: []telegraf.Metric{
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
},
map[string]interface{}{
"usage_count": int64(2),
"usage_range_min": 0.0,
"usage_range_max": 3.0,
"usage_mean": 2.0,
"usage_sum_of_squared_deviation": 1.0,
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "1",
},
map[string]interface{}{
"usage_bucket": int64(0),
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "2",
},
map[string]interface{}{
"usage_bucket": int64(1),
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "3",
},
map[string]interface{}{
"usage_bucket": int64(4),
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "+Inf",
},
map[string]interface{}{
"usage_bucket": int64(4),
},
now),
},
},
{
name: "exponential buckets",
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DISTRIBUTION,
},
timeseries: createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_DistributionValue{
DistributionValue: &distribution.Distribution{
Count: 2,
Mean: 2.0,
SumOfSquaredDeviation: 1.0,
Range: &distribution.Distribution_Range{
Min: 0.0,
Max: 3.0,
},
BucketCounts: []int64{0, 1, 3, 0},
BucketOptions: &distribution.Distribution_BucketOptions{
Options: &distribution.Distribution_BucketOptions_ExponentialBuckets{
ExponentialBuckets: &distribution.Distribution_BucketOptions_Exponential{
NumFiniteBuckets: 2,
GrowthFactor: 2,
Scale: 1,
},
},
},
},
},
},
},
metricpb.MetricDescriptor_DISTRIBUTION,
),
expected: []telegraf.Metric{
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
},
map[string]interface{}{
"usage_count": int64(2),
"usage_range_min": 0.0,
"usage_range_max": 3.0,
"usage_mean": 2.0,
"usage_sum_of_squared_deviation": 1.0,
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "1",
},
map[string]interface{}{
"usage_bucket": int64(0),
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "2",
},
map[string]interface{}{
"usage_bucket": int64(1),
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "4",
},
map[string]interface{}{
"usage_bucket": int64(4),
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "+Inf",
},
map[string]interface{}{
"usage_bucket": int64(4),
},
now),
},
},
{
name: "explicit buckets",
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DISTRIBUTION,
},
timeseries: createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_DistributionValue{
DistributionValue: &distribution.Distribution{
Count: 4,
Mean: 2.0,
SumOfSquaredDeviation: 1.0,
Range: &distribution.Distribution_Range{
Min: 0.0,
Max: 3.0,
},
BucketCounts: []int64{0, 1, 3},
BucketOptions: &distribution.Distribution_BucketOptions{
Options: &distribution.Distribution_BucketOptions_ExplicitBuckets{
ExplicitBuckets: &distribution.Distribution_BucketOptions_Explicit{
Bounds: []float64{1.0, 2.0},
},
},
},
},
},
},
},
metricpb.MetricDescriptor_DISTRIBUTION,
),
expected: []telegraf.Metric{
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
},
map[string]interface{}{
"usage_count": int64(4),
"usage_range_min": 0.0,
"usage_range_max": 3.0,
"usage_mean": 2.0,
"usage_sum_of_squared_deviation": 1.0,
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "1",
},
map[string]interface{}{
"usage_bucket": int64(0),
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "2",
},
map[string]interface{}{
"usage_bucket": int64(1),
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "+Inf",
},
map[string]interface{}{
"usage_bucket": int64(4),
},
now),
},
},
{
name: "implicit buckets are zero",
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DISTRIBUTION,
},
timeseries: createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_DistributionValue{
DistributionValue: &distribution.Distribution{
Count: 2,
Mean: 2.0,
SumOfSquaredDeviation: 1.0,
Range: &distribution.Distribution_Range{
Min: 0.0,
Max: 3.0,
},
BucketCounts: []int64{0, 1},
BucketOptions: &distribution.Distribution_BucketOptions{
Options: &distribution.Distribution_BucketOptions_LinearBuckets{
LinearBuckets: &distribution.Distribution_BucketOptions_Linear{
NumFiniteBuckets: 2,
Width: 1,
Offset: 1,
},
},
},
},
},
},
},
metricpb.MetricDescriptor_DISTRIBUTION,
),
expected: []telegraf.Metric{
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
},
map[string]interface{}{
"usage_count": int64(2),
"usage_range_min": 0.0,
"usage_range_max": 3.0,
"usage_mean": 2.0,
"usage_sum_of_squared_deviation": 1.0,
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "1",
},
map[string]interface{}{
"usage_bucket": int64(0),
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "2",
},
map[string]interface{}{
"usage_bucket": int64(1),
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "3",
},
map[string]interface{}{
"usage_bucket": int64(1),
},
now),
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
"lt": "+Inf",
},
map[string]interface{}{
"usage_bucket": int64(1),
},
now),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var acc testutil.Accumulator
listMetricDescriptorsF := func() (<-chan *metricpb.MetricDescriptor, error) {
ch := make(chan *metricpb.MetricDescriptor, 1)
ch <- tt.descriptor
close(ch)
return ch, nil
}
listTimeSeriesF := func() (<-chan *monitoringpb.TimeSeries, error) {
ch := make(chan *monitoringpb.TimeSeries, 1)
ch <- tt.timeseries
close(ch)
return ch, nil
}
s := &Stackdriver{
Log: testutil.Logger{},
Project: "test",
RateLimit: 10,
GatherRawDistributionBuckets: true,
client: &mockStackdriverClient{
listMetricDescriptorsF: listMetricDescriptorsF,
listTimeSeriesF: listTimeSeriesF,
closeF: func() error {
return nil
},
},
}
err := s.Gather(&acc)
require.NoError(t, err)
require.Equalf(t, tt.wantAccErr, len(acc.Errors) > 0,
"Accumulator errors. got=%v, want=%t", acc.Errors, tt.wantAccErr)
actual := make([]telegraf.Metric, 0, len(acc.Metrics))
for _, m := range acc.Metrics {
actual = append(actual, testutil.FromTestMetric(m))
}
testutil.RequireMetricsEqual(t, tt.expected, actual)
})
}
}
func TestGatherAlign(t *testing.T) {
now := time.Now().Round(time.Second)
tests := []struct {
name string
descriptor *metricpb.MetricDescriptor
timeseries []*monitoringpb.TimeSeries
expected []telegraf.Metric
}{
{
name: "align",
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DISTRIBUTION,
},
timeseries: []*monitoringpb.TimeSeries{
createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_DoubleValue{
DoubleValue: 42.0,
},
},
},
metricpb.MetricDescriptor_DOUBLE,
),
createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_DoubleValue{
DoubleValue: 42.0,
},
},
},
metricpb.MetricDescriptor_DOUBLE,
),
createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_DoubleValue{
DoubleValue: 42.0,
},
},
},
metricpb.MetricDescriptor_DOUBLE,
),
},
expected: []telegraf.Metric{
testutil.MustMetric("telegraf/cpu",
map[string]string{
"resource_type": "global",
"project_id": "test",
},
map[string]interface{}{
"usage_align_percentile_99": 42.0,
"usage_align_percentile_95": 42.0,
"usage_align_percentile_50": 42.0,
},
now),
},
},
}
for listCall, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var acc testutil.Accumulator
client := &mockStackdriverClient{
listMetricDescriptorsF: func() (<-chan *metricpb.MetricDescriptor, error) {
ch := make(chan *metricpb.MetricDescriptor, 1)
ch <- tt.descriptor
close(ch)
return ch, nil
},
listTimeSeriesF: func() (<-chan *monitoringpb.TimeSeries, error) {
ch := make(chan *monitoringpb.TimeSeries, 1)
ch <- tt.timeseries[listCall]
close(ch)
return ch, nil
},
closeF: func() error {
return nil
},
}
s := &Stackdriver{
Log: testutil.Logger{},
Project: "test",
RateLimit: 10,
GatherRawDistributionBuckets: false,
DistributionAggregationAligners: []string{
"ALIGN_PERCENTILE_99",
"ALIGN_PERCENTILE_95",
"ALIGN_PERCENTILE_50",
},
client: client,
}
err := s.Gather(&acc)
require.NoError(t, err)
actual := make([]telegraf.Metric, 0, len(acc.Metrics))
for _, m := range acc.Metrics {
actual = append(actual, testutil.FromTestMetric(m))
}
testutil.RequireMetricsEqual(t, tt.expected, actual)
})
}
}
func TestListMetricDescriptorFilter(t *testing.T) {
type call struct {
name string
filter string
}
now := time.Now().Round(time.Second)
tests := []struct {
name string
stackdriver *Stackdriver
descriptor *metricpb.MetricDescriptor
calls []call
}{
{
name: "simple",
stackdriver: &Stackdriver{
Project: "test",
MetricTypePrefixInclude: []string{"telegraf/cpu/usage"},
RateLimit: 1,
},
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DOUBLE,
},
calls: []call{
{
name: "listMetricDescriptors",
filter: `metric.type = starts_with("telegraf/cpu/usage")`,
}, {
name: "listTimeSeries",
filter: `metric.type = "telegraf/cpu/usage"`,
},
},
},
{
name: "single resource labels string",
stackdriver: &Stackdriver{
Project: "test",
MetricTypePrefixInclude: []string{"telegraf/cpu/usage"},
Filter: &listTimeSeriesFilter{
ResourceLabels: []*label{
{
Key: "instance_name",
Value: `localhost`,
},
},
},
RateLimit: 1,
},
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DOUBLE,
},
calls: []call{
{
name: "listMetricDescriptors",
filter: `metric.type = starts_with("telegraf/cpu/usage")`,
}, {
name: "listTimeSeries",
filter: `metric.type = "telegraf/cpu/usage" AND resource.labels.instance_name = "localhost"`,
},
},
},
{
name: "single resource labels function",
stackdriver: &Stackdriver{
Project: "test",
MetricTypePrefixInclude: []string{"telegraf/cpu/usage"},
Filter: &listTimeSeriesFilter{
ResourceLabels: []*label{
{
Key: "instance_name",
Value: `starts_with("localhost")`,
},
},
},
RateLimit: 1,
},
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DOUBLE,
},
calls: []call{
{
name: "listMetricDescriptors",
filter: `metric.type = starts_with("telegraf/cpu/usage")`,
}, {
name: "listTimeSeries",
filter: `metric.type = "telegraf/cpu/usage" AND resource.labels.instance_name = starts_with("localhost")`,
},
},
},
{
name: "multiple resource labels",
stackdriver: &Stackdriver{
Project: "test",
MetricTypePrefixInclude: []string{"telegraf/cpu/usage"},
Filter: &listTimeSeriesFilter{
ResourceLabels: []*label{
{
Key: "instance_name",
Value: `localhost`,
},
{
Key: "zone",
Value: `starts_with("us-")`,
},
},
},
RateLimit: 1,
},
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DOUBLE,
},
calls: []call{
{
name: "listMetricDescriptors",
filter: `metric.type = starts_with("telegraf/cpu/usage")`,
}, {
name: "listTimeSeries",
filter: `metric.type = "telegraf/cpu/usage" AND (resource.labels.instance_name = "localhost" OR resource.labels.zone = starts_with("us-"))`,
},
},
},
{
name: "single metric label string",
stackdriver: &Stackdriver{
Project: "test",
MetricTypePrefixInclude: []string{"telegraf/cpu/usage"},
Filter: &listTimeSeriesFilter{
MetricLabels: []*label{
{
Key: "resource_type",
Value: `instance`,
},
},
},
RateLimit: 1,
},
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DOUBLE,
},
calls: []call{
{
name: "listMetricDescriptors",
filter: `metric.type = starts_with("telegraf/cpu/usage")`,
}, {
name: "listTimeSeries",
filter: `metric.type = "telegraf/cpu/usage" AND metric.labels.resource_type = "instance"`,
},
},
},
{
name: "single metric label function",
stackdriver: &Stackdriver{
Project: "test",
MetricTypePrefixInclude: []string{"telegraf/cpu/usage"},
Filter: &listTimeSeriesFilter{
MetricLabels: []*label{
{
Key: "resource_id",
Value: `starts_with("abc-")`,
},
},
},
RateLimit: 1,
},
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DOUBLE,
},
calls: []call{
{
name: "listMetricDescriptors",
filter: `metric.type = starts_with("telegraf/cpu/usage")`,
}, {
name: "listTimeSeries",
filter: `metric.type = "telegraf/cpu/usage" AND metric.labels.resource_id = starts_with("abc-")`,
},
},
},
{
name: "multiple metric labels",
stackdriver: &Stackdriver{
Project: "test",
MetricTypePrefixInclude: []string{"telegraf/cpu/usage"},
Filter: &listTimeSeriesFilter{
MetricLabels: []*label{
{
Key: "resource_type",
Value: "instance",
},
{
Key: "resource_id",
Value: `starts_with("abc-")`,
},
},
},
RateLimit: 1,
},
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DOUBLE,
},
calls: []call{
{
name: "listMetricDescriptors",
filter: `metric.type = starts_with("telegraf/cpu/usage")`,
}, {
name: "listTimeSeries",
filter: `metric.type = "telegraf/cpu/usage" AND ` +
`(metric.labels.resource_type = "instance" OR metric.labels.resource_id = starts_with("abc-"))`,
},
},
},
{
name: "all labels filters",
stackdriver: &Stackdriver{
Project: "test",
MetricTypePrefixInclude: []string{"telegraf/cpu/usage"},
Filter: &listTimeSeriesFilter{
ResourceLabels: []*label{
{
Key: "instance_name",
Value: `localhost`,
},
{
Key: "zone",
Value: `starts_with("us-")`,
},
},
MetricLabels: []*label{
{
Key: "resource_type",
Value: "instance",
},
{
Key: "resource_id",
Value: `starts_with("abc-")`,
},
},
UserLabels: []*label{
{
Key: "team",
Value: "badgers",
},
{
Key: "environment",
Value: `starts_with("prod-")`,
},
},
SystemLabels: []*label{
{
Key: "machine_type",
Value: "e2",
},
{
Key: "machine_type",
Value: `starts_with("n2")`,
},
},
},
RateLimit: 1,
},
descriptor: &metricpb.MetricDescriptor{
Type: "telegraf/cpu/usage",
ValueType: metricpb.MetricDescriptor_DOUBLE,
},
calls: []call{
{
name: "listMetricDescriptors",
filter: `metric.type = starts_with("telegraf/cpu/usage")`,
}, {
name: "listTimeSeries",
filter: `metric.type = "telegraf/cpu/usage" AND ` +
`(resource.labels.instance_name = "localhost" OR resource.labels.zone = starts_with("us-")) AND ` +
`(metric.labels.resource_type = "instance" OR metric.labels.resource_id = starts_with("abc-")) AND ` +
`(metadata.user_labels."team" = "badgers" OR metadata.user_labels."environment" = starts_with("prod-")) AND ` +
`(metadata.system_labels."machine_type" = "e2" OR metadata.system_labels."machine_type" = starts_with("n2"))`,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var acc testutil.Accumulator
client := &mockStackdriverClient{
listMetricDescriptorsF: func() (<-chan *metricpb.MetricDescriptor, error) {
ch := make(chan *metricpb.MetricDescriptor, 1)
ch <- tt.descriptor
close(ch)
return ch, nil
},
listTimeSeriesF: func() (<-chan *monitoringpb.TimeSeries, error) {
ch := make(chan *monitoringpb.TimeSeries, 1)
ch <- createTimeSeries(
&monitoringpb.Point{
Interval: &monitoringpb.TimeInterval{
EndTime: &timestamppb.Timestamp{
Seconds: now.Unix(),
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_DoubleValue{
DoubleValue: 42.0,
},
},
},
metricpb.MetricDescriptor_DOUBLE,
)
close(ch)
return ch, nil
},
closeF: func() error {
return nil
},
}
s := tt.stackdriver
s.client = client
err := s.Gather(&acc)
require.NoError(t, err)
require.Len(t, client.calls, len(tt.calls))
for i, expected := range tt.calls {
actual := client.calls[i]
require.Equal(t, expected.name, actual.name)
switch req := actual.args[1].(type) {
case *monitoringpb.ListMetricDescriptorsRequest:
require.Equal(t, expected.filter, req.Filter)
case *monitoringpb.ListTimeSeriesRequest:
require.Equal(t, expected.filter, req.Filter)
default:
panic("unknown request type")
}
}
})
}
}
func TestNewListTimeSeriesFilter(_ *testing.T) {
}
func TestTimeSeriesConfCacheIsValid(_ *testing.T) {
}