From 1ed9682a63f03ed9d605e5a36507a3b29ac4554a Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Mon, 12 Dec 2022 15:17:54 +0000 Subject: [PATCH] feat(inputs.stackdriver): allow filtering by resource metadata labels --- plugins/inputs/stackdriver/README.md | 16 +++++++- plugins/inputs/stackdriver/sample.conf | 16 +++++++- plugins/inputs/stackdriver/stackdriver.go | 38 +++++++++++++++++++ .../inputs/stackdriver/stackdriver_test.go | 24 +++++++++++- 4 files changed, 91 insertions(+), 3 deletions(-) diff --git a/plugins/inputs/stackdriver/README.md b/plugins/inputs/stackdriver/README.md index c88a6636333aa..22e4da835e0a9 100644 --- a/plugins/inputs/stackdriver/README.md +++ b/plugins/inputs/stackdriver/README.md @@ -79,9 +79,11 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. ## ## The logical operators when combining filters are defined statically using ## the following values: - ## filter ::= {AND } + ## filter ::= {AND AND AND } ## resource_labels ::= {OR } ## metric_labels ::= {OR } + ## user_labels ::= {OR } + ## system_labels ::= {OR } ## ## For more details, see https://cloud.google.com/monitoring/api/v3/filters # @@ -96,6 +98,18 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. # [[inputs.stackdriver.filter.metric_labels]] # key = "device_name" # value = 'one_of("sda", "sdb")' + # + ## User labels refine the time series selection with the following expression: + ## metadata.user_labels."" = + # [[inputs.stackdriver.filter.user_labels]] + # key = "environment" + # value = 'one_of("prod", "staging")' + # + ## System labels refine the time series selection with the following expression: + ## metadata.system_labels."" = + # [[inputs.stackdriver.filter.system_labels]] + # key = "machine_type" + # value = 'starts_with("e2-")' ``` ### Authentication diff --git a/plugins/inputs/stackdriver/sample.conf b/plugins/inputs/stackdriver/sample.conf index b571e9dca9545..04c47747b9f2f 100644 --- a/plugins/inputs/stackdriver/sample.conf +++ b/plugins/inputs/stackdriver/sample.conf @@ -59,9 +59,11 @@ ## ## The logical operators when combining filters are defined statically using ## the following values: - ## filter ::= {AND } + ## filter ::= {AND AND AND } ## resource_labels ::= {OR } ## metric_labels ::= {OR } + ## user_labels ::= {OR } + ## system_labels ::= {OR } ## ## For more details, see https://cloud.google.com/monitoring/api/v3/filters # @@ -76,3 +78,15 @@ # [[inputs.stackdriver.filter.metric_labels]] # key = "device_name" # value = 'one_of("sda", "sdb")' + # + ## User labels refine the time series selection with the following expression: + ## metadata.user_labels."" = + # [[inputs.stackdriver.filter.user_labels]] + # key = "environment" + # value = 'one_of("prod", "staging")' + # + ## System labels refine the time series selection with the following expression: + ## metadata.system_labels."" = + # [[inputs.stackdriver.filter.system_labels]] + # key = "machine_type" + # value = 'starts_with("e2-")' diff --git a/plugins/inputs/stackdriver/stackdriver.go b/plugins/inputs/stackdriver/stackdriver.go index 59d325cf9f57b..30b34e6b3de25 100644 --- a/plugins/inputs/stackdriver/stackdriver.go +++ b/plugins/inputs/stackdriver/stackdriver.go @@ -66,6 +66,8 @@ type ( ListTimeSeriesFilter struct { ResourceLabels []*Label `json:"resource_labels"` MetricLabels []*Label `json:"metric_labels"` + UserLabels []*Label `json:"user_labels"` + SystemLabels []*Label `json:"system_labels"` } // Label contains key and value @@ -305,6 +307,42 @@ func (s *Stackdriver) newListTimeSeriesFilter(metricType string) string { } } + if len(s.Filter.UserLabels) > 0 { + userLabelsFilter := make([]string, len(s.Filter.UserLabels)) + for i, metricLabel := range s.Filter.UserLabels { + // check if metric label value contains function + if includeExcludeHelper(metricLabel.Value, functions, nil) { + valueFmt = `metadata.user_labels."%s" = %s` + } else { + valueFmt = `metadata.user_labels."%s" = "%s"` + } + userLabelsFilter[i] = fmt.Sprintf(valueFmt, metricLabel.Key, metricLabel.Value) + } + if len(userLabelsFilter) == 1 { + filterString += fmt.Sprintf(" AND %s", userLabelsFilter[0]) + } else { + filterString += fmt.Sprintf(" AND (%s)", strings.Join(userLabelsFilter, " OR ")) + } + } + + if len(s.Filter.SystemLabels) > 0 { + systemLabelsFilter := make([]string, len(s.Filter.SystemLabels)) + for i, metricLabel := range s.Filter.SystemLabels { + // check if metric label value contains function + if includeExcludeHelper(metricLabel.Value, functions, nil) { + valueFmt = `metadata.system_labels."%s" = %s` + } else { + valueFmt = `metadata.system_labels."%s" = "%s"` + } + systemLabelsFilter[i] = fmt.Sprintf(valueFmt, metricLabel.Key, metricLabel.Value) + } + if len(systemLabelsFilter) == 1 { + filterString += fmt.Sprintf(" AND %s", systemLabelsFilter[0]) + } else { + filterString += fmt.Sprintf(" AND (%s)", strings.Join(systemLabelsFilter, " OR ")) + } + } + return filterString } diff --git a/plugins/inputs/stackdriver/stackdriver_test.go b/plugins/inputs/stackdriver/stackdriver_test.go index 9ec10e524d8cd..26e9b4b8ace2d 100644 --- a/plugins/inputs/stackdriver/stackdriver_test.go +++ b/plugins/inputs/stackdriver/stackdriver_test.go @@ -1126,6 +1126,26 @@ func TestListMetricDescriptorFilter(t *testing.T) { 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, }, @@ -1141,7 +1161,9 @@ func TestListMetricDescriptorFilter(t *testing.T) { 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-"))`, + `(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"))`, }, }, },