From 4d8f594d31ea3fa6d82f6fb249abf16059f8decd Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 2 Oct 2018 17:07:46 +0200 Subject: [PATCH 01/11] stackdriver: interpolate stackdriver filter wildcards when asterix is used in filter --- pkg/tsdb/stackdriver/stackdriver.go | 56 ++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/pkg/tsdb/stackdriver/stackdriver.go b/pkg/tsdb/stackdriver/stackdriver.go index 586e154cd5d..e802a85fc24 100644 --- a/pkg/tsdb/stackdriver/stackdriver.go +++ b/pkg/tsdb/stackdriver/stackdriver.go @@ -15,6 +15,8 @@ import ( "strings" "time" + "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" + "golang.org/x/net/context/ctxhttp" "github.com/grafana/grafana/pkg/api/pluginproxy" @@ -159,6 +161,53 @@ func (e *StackdriverExecutor) buildQueries(tsdbQuery *tsdb.TsdbQuery) ([]*Stackd return stackdriverQueries, nil } +func reverse(s string) string { + chars := []rune(s) + for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 { + chars[i], chars[j] = chars[j], chars[i] + } + return string(chars) +} + +func escapeDoubleBackslash(target string) string { + var re = regexp.MustCompile(`\\`) + return re.ReplaceAllString(target, `\\\\`) + // return strings.Replace(target, `\`, "", -1) +} + +func escapeIllegalCharacters(target string) string { + var re = regexp.MustCompile(`[-\/^$+?.()|[\]{}]`) + return string(re.ReplaceAllFunc([]byte(target), func(in []byte) []byte { + return []byte(strings.Replace(string(in), string(in), `\\`+string(in), 1)) + })) +} + +func replaceSingleAsterixCharacters(target string) string { + return strings.Replace(target, "*", ".*", -1) +} + +func interpolateFilterWildcards(value string) string { + if strings.HasSuffix(value, "*") && strings.HasPrefix(value, "*") { + value = strings.Replace(value, "*", "", 1) + value = fmt.Sprintf(`has_substring("%s")`, value) + } else if strings.HasPrefix(value, "*") { + value = strings.Replace(value, "*", "", 1) + value = fmt.Sprintf(`ends_with("%s")`, value) + } else if strings.HasSuffix(value, "*") { + value = reverse(strings.Replace(reverse(value), "*", "", 1)) + value = fmt.Sprintf(`starts_with("%s")`, value) + } else if strings.Contains(value, "*") { + value = escapeIllegalCharacters(value) + value = replaceSingleAsterixCharacters(value) + value = strings.Replace(value, `"`, `\\"`, -1) + value = fmt.Sprintf(`monitoring.regex.full_match("^%s$")`, value) + } + + logger.Info("filter", "filter", value) + + return value +} + func buildFilterString(metricType string, filterParts []interface{}) string { filterString := "" for i, part := range filterParts { @@ -166,7 +215,11 @@ func buildFilterString(metricType string, filterParts []interface{}) string { if part == "AND" { filterString += " " } else if mod == 2 { - filterString += fmt.Sprintf(`"%s"`, part) + if strings.Contains(part.(string), "*") { + filterString += interpolateFilterWildcards(part.(string)) + } else { + filterString += fmt.Sprintf(`"%s"`, part) + } } else { filterString += part.(string) } @@ -231,6 +284,7 @@ func (e *StackdriverExecutor) executeQuery(ctx context.Context, query *Stackdriv } req.URL.RawQuery = query.Params.Encode() + logger.Info("req.URL.RawQuery", "req.URL.RawQuery", req.URL.RawQuery) queryResult.Meta.Set("rawQuery", req.URL.RawQuery) alignmentPeriod, ok := req.URL.Query()["aggregation.alignmentPeriod"] From 2e665fba0f6c8a9b83f58e10922a9538d1ede966 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 2 Oct 2018 17:11:05 +0200 Subject: [PATCH 02/11] stackdriver: remove not necessary helper functions --- pkg/tsdb/stackdriver/stackdriver.go | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/pkg/tsdb/stackdriver/stackdriver.go b/pkg/tsdb/stackdriver/stackdriver.go index e802a85fc24..962b238de4c 100644 --- a/pkg/tsdb/stackdriver/stackdriver.go +++ b/pkg/tsdb/stackdriver/stackdriver.go @@ -169,23 +169,6 @@ func reverse(s string) string { return string(chars) } -func escapeDoubleBackslash(target string) string { - var re = regexp.MustCompile(`\\`) - return re.ReplaceAllString(target, `\\\\`) - // return strings.Replace(target, `\`, "", -1) -} - -func escapeIllegalCharacters(target string) string { - var re = regexp.MustCompile(`[-\/^$+?.()|[\]{}]`) - return string(re.ReplaceAllFunc([]byte(target), func(in []byte) []byte { - return []byte(strings.Replace(string(in), string(in), `\\`+string(in), 1)) - })) -} - -func replaceSingleAsterixCharacters(target string) string { - return strings.Replace(target, "*", ".*", -1) -} - func interpolateFilterWildcards(value string) string { if strings.HasSuffix(value, "*") && strings.HasPrefix(value, "*") { value = strings.Replace(value, "*", "", 1) @@ -197,8 +180,11 @@ func interpolateFilterWildcards(value string) string { value = reverse(strings.Replace(reverse(value), "*", "", 1)) value = fmt.Sprintf(`starts_with("%s")`, value) } else if strings.Contains(value, "*") { - value = escapeIllegalCharacters(value) - value = replaceSingleAsterixCharacters(value) + re := regexp.MustCompile(`[-\/^$+?.()|[\]{}]`) + value = string(re.ReplaceAllFunc([]byte(value), func(in []byte) []byte { + return []byte(strings.Replace(string(in), string(in), `\\`+string(in), 1)) + })) + value = strings.Replace(value, "*", ".*", -1) value = strings.Replace(value, `"`, `\\"`, -1) value = fmt.Sprintf(`monitoring.regex.full_match("^%s$")`, value) } From 68332c595171a1ba83dc9193411d2ac0d3c69490 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 2 Oct 2018 17:29:51 +0200 Subject: [PATCH 03/11] stackdriver: fix broken substring. also adds tests --- pkg/tsdb/stackdriver/stackdriver.go | 7 +++++-- pkg/tsdb/stackdriver/stackdriver_test.go | 13 +++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/pkg/tsdb/stackdriver/stackdriver.go b/pkg/tsdb/stackdriver/stackdriver.go index 962b238de4c..ec698a77ce0 100644 --- a/pkg/tsdb/stackdriver/stackdriver.go +++ b/pkg/tsdb/stackdriver/stackdriver.go @@ -170,8 +170,11 @@ func reverse(s string) string { } func interpolateFilterWildcards(value string) string { - if strings.HasSuffix(value, "*") && strings.HasPrefix(value, "*") { - value = strings.Replace(value, "*", "", 1) + re := regexp.MustCompile("[*]") + matches := re.FindAllStringIndex(value, -1) + logger.Info("len", "len", len(matches)) + if len(matches) == 2 && strings.HasSuffix(value, "*") && strings.HasPrefix(value, "*") { + value = strings.Replace(value, "*", "", -1) value = fmt.Sprintf(`has_substring("%s")`, value) } else if strings.HasPrefix(value, "*") { value = strings.Replace(value, "*", "", 1) diff --git a/pkg/tsdb/stackdriver/stackdriver_test.go b/pkg/tsdb/stackdriver/stackdriver_test.go index da4d6890207..59bda5a98b4 100644 --- a/pkg/tsdb/stackdriver/stackdriver_test.go +++ b/pkg/tsdb/stackdriver/stackdriver_test.go @@ -342,6 +342,19 @@ func TestStackdriver(t *testing.T) { }) }) }) + + Convey("when interpolating filter wildcards", func() { + Convey("and wildcard is used in the beginning and the end of the word", func() { + Convey("and theres no wildcard in the middle of the word", func() { + value := interpolateFilterWildcards("*-central1*") + So(value, ShouldEqual, `has_substring("-central1")`) + }) + Convey("and there is a wildcard in the middle of the word", func() { + value := interpolateFilterWildcards("*-cent*ral1*") + So(value, ShouldNotStartWith, `has_substring`) + }) + }) + }) }) } From 035be6cbbe5354aa4f0c2b0db2f09b228e2effe7 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 2 Oct 2018 17:52:26 +0200 Subject: [PATCH 04/11] stackdriver: add more tests --- pkg/tsdb/stackdriver/stackdriver.go | 12 +++---- pkg/tsdb/stackdriver/stackdriver_test.go | 44 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/pkg/tsdb/stackdriver/stackdriver.go b/pkg/tsdb/stackdriver/stackdriver.go index ec698a77ce0..0f09de61644 100644 --- a/pkg/tsdb/stackdriver/stackdriver.go +++ b/pkg/tsdb/stackdriver/stackdriver.go @@ -171,18 +171,18 @@ func reverse(s string) string { func interpolateFilterWildcards(value string) string { re := regexp.MustCompile("[*]") - matches := re.FindAllStringIndex(value, -1) - logger.Info("len", "len", len(matches)) - if len(matches) == 2 && strings.HasSuffix(value, "*") && strings.HasPrefix(value, "*") { + matches := len(re.FindAllStringIndex(value, -1)) + logger.Info("len", "len", matches) + if matches == 2 && strings.HasSuffix(value, "*") && strings.HasPrefix(value, "*") { value = strings.Replace(value, "*", "", -1) value = fmt.Sprintf(`has_substring("%s")`, value) - } else if strings.HasPrefix(value, "*") { + } else if matches == 1 && strings.HasPrefix(value, "*") { value = strings.Replace(value, "*", "", 1) value = fmt.Sprintf(`ends_with("%s")`, value) - } else if strings.HasSuffix(value, "*") { + } else if matches == 1 && strings.HasSuffix(value, "*") { value = reverse(strings.Replace(reverse(value), "*", "", 1)) value = fmt.Sprintf(`starts_with("%s")`, value) - } else if strings.Contains(value, "*") { + } else if matches == 1 { re := regexp.MustCompile(`[-\/^$+?.()|[\]{}]`) value = string(re.ReplaceAllFunc([]byte(value), func(in []byte) []byte { return []byte(strings.Replace(string(in), string(in), `\\`+string(in), 1)) diff --git a/pkg/tsdb/stackdriver/stackdriver_test.go b/pkg/tsdb/stackdriver/stackdriver_test.go index 59bda5a98b4..5184c6fc3bb 100644 --- a/pkg/tsdb/stackdriver/stackdriver_test.go +++ b/pkg/tsdb/stackdriver/stackdriver_test.go @@ -354,6 +354,50 @@ func TestStackdriver(t *testing.T) { So(value, ShouldNotStartWith, `has_substring`) }) }) + + Convey("and wildcard is used in the beginning of the word", func() { + Convey("and there is not a wildcard elsewhere in the word", func() { + value := interpolateFilterWildcards("*-central1") + So(value, ShouldEqual, `ends_with("-central1")`) + }) + Convey("and there is a wildcard elsewhere in the word", func() { + value := interpolateFilterWildcards("*-cent*al1") + So(value, ShouldNotStartWith, `ends_with`) + }) + }) + + Convey("and wildcard is used at the end of the word", func() { + Convey("and there is not a wildcard elsewhere in the word", func() { + value := interpolateFilterWildcards("us-central*") + So(value, ShouldEqual, `starts_with("us-central")`) + }) + Convey("and there is a wildcard elsewhere in the word", func() { + value := interpolateFilterWildcards("*us-central*") + So(value, ShouldNotStartWith, `starts_with`) + }) + }) + + Convey("and wildcard is used in the middle of the word", func() { + Convey("and there is only one wildcard", func() { + value := interpolateFilterWildcards("us-ce*tral1-b") + So(value, ShouldEqual, `monitoring.regex.full_match("^us\\-ce.*tral1\\-b$")`) + }) + + Convey("and there is more than one wildcard", func() { + value := interpolateFilterWildcards("us-ce*tra*1-b") + So(value, ShouldEqual, `monitoring.regex.full_match("^us\\-ce.*tra.*1\\-b$")`) + }) + }) + + Convey("and wildcard is used in the middle of the word and in the beginning of the word", func() { + value := interpolateFilterWildcards("*s-ce*tral1-b") + So(value, ShouldEqual, `monitoring.regex.full_match("^.*s\\-ce.*tral1\\-b$")`) + }) + + Convey("and wildcard is used in the middle of the word and in the ending of the word", func() { + value := interpolateFilterWildcards("us-ce*tral1-*") + So(value, ShouldEqual, `monitoring.regex.full_match("^us\\-ce.*tral1\\-.*$")`) + }) }) }) } From 2a0d7a88039224627acee3291b70dbc5b1bd814c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 2 Oct 2018 17:53:19 +0200 Subject: [PATCH 05/11] stackdriver: remove debug logging --- pkg/tsdb/stackdriver/stackdriver.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/tsdb/stackdriver/stackdriver.go b/pkg/tsdb/stackdriver/stackdriver.go index 0f09de61644..ce7ca8fdee4 100644 --- a/pkg/tsdb/stackdriver/stackdriver.go +++ b/pkg/tsdb/stackdriver/stackdriver.go @@ -15,8 +15,6 @@ import ( "strings" "time" - "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" - "golang.org/x/net/context/ctxhttp" "github.com/grafana/grafana/pkg/api/pluginproxy" @@ -172,7 +170,6 @@ func reverse(s string) string { func interpolateFilterWildcards(value string) string { re := regexp.MustCompile("[*]") matches := len(re.FindAllStringIndex(value, -1)) - logger.Info("len", "len", matches) if matches == 2 && strings.HasSuffix(value, "*") && strings.HasPrefix(value, "*") { value = strings.Replace(value, "*", "", -1) value = fmt.Sprintf(`has_substring("%s")`, value) @@ -192,8 +189,6 @@ func interpolateFilterWildcards(value string) string { value = fmt.Sprintf(`monitoring.regex.full_match("^%s$")`, value) } - logger.Info("filter", "filter", value) - return value } @@ -273,7 +268,6 @@ func (e *StackdriverExecutor) executeQuery(ctx context.Context, query *Stackdriv } req.URL.RawQuery = query.Params.Encode() - logger.Info("req.URL.RawQuery", "req.URL.RawQuery", req.URL.RawQuery) queryResult.Meta.Set("rawQuery", req.URL.RawQuery) alignmentPeriod, ok := req.URL.Query()["aggregation.alignmentPeriod"] From 5f7795aa1f525e34f5aba659175827887ede3a91 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 2 Oct 2018 17:58:31 +0200 Subject: [PATCH 06/11] stackdriver: test that no interpolation is done when there are no wildcards --- pkg/tsdb/stackdriver/stackdriver.go | 2 +- pkg/tsdb/stackdriver/stackdriver_test.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/tsdb/stackdriver/stackdriver.go b/pkg/tsdb/stackdriver/stackdriver.go index ce7ca8fdee4..8b5d71ba830 100644 --- a/pkg/tsdb/stackdriver/stackdriver.go +++ b/pkg/tsdb/stackdriver/stackdriver.go @@ -179,7 +179,7 @@ func interpolateFilterWildcards(value string) string { } else if matches == 1 && strings.HasSuffix(value, "*") { value = reverse(strings.Replace(reverse(value), "*", "", 1)) value = fmt.Sprintf(`starts_with("%s")`, value) - } else if matches == 1 { + } else if matches != 0 { re := regexp.MustCompile(`[-\/^$+?.()|[\]{}]`) value = string(re.ReplaceAllFunc([]byte(value), func(in []byte) []byte { return []byte(strings.Replace(string(in), string(in), `\\`+string(in), 1)) diff --git a/pkg/tsdb/stackdriver/stackdriver_test.go b/pkg/tsdb/stackdriver/stackdriver_test.go index 5184c6fc3bb..5840514b993 100644 --- a/pkg/tsdb/stackdriver/stackdriver_test.go +++ b/pkg/tsdb/stackdriver/stackdriver_test.go @@ -398,7 +398,13 @@ func TestStackdriver(t *testing.T) { value := interpolateFilterWildcards("us-ce*tral1-*") So(value, ShouldEqual, `monitoring.regex.full_match("^us\\-ce.*tral1\\-.*$")`) }) + + Convey("and no wildcard is used", func() { + value := interpolateFilterWildcards("us-central1-a}") + So(value, ShouldEqual, `us-central1-a}`) + }) }) + }) } From a3122a4b854672f210892f6f158f7a074dd1d8f5 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 2 Oct 2018 18:09:42 +0200 Subject: [PATCH 07/11] stackdriver: test build filter string --- pkg/tsdb/stackdriver/stackdriver_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pkg/tsdb/stackdriver/stackdriver_test.go b/pkg/tsdb/stackdriver/stackdriver_test.go index 5840514b993..ec311c9f50f 100644 --- a/pkg/tsdb/stackdriver/stackdriver_test.go +++ b/pkg/tsdb/stackdriver/stackdriver_test.go @@ -405,6 +405,19 @@ func TestStackdriver(t *testing.T) { }) }) + Convey("when building filter string", func() { + Convey("and there are wildcards in a filter value", func() { + filterParts := []interface{}{"zone", "=", "*-central1*"} + value := buildFilterString("somemetrictype", filterParts) + So(value, ShouldEqual, `metric.type="somemetrictype" zone=has_substring("-central1")`) + }) + + Convey("and there are no wildcards in any filter value", func() { + filterParts := []interface{}{"zone", "=", "us-central1-a"} + value := buildFilterString("somemetrictype", filterParts) + So(value, ShouldEqual, `metric.type="somemetrictype" zone="us-central1-a"`) + }) + }) }) } From 46ca306c2f742223d3f6aa546f4805c8d30cb31f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 8 Oct 2018 10:52:18 +0200 Subject: [PATCH 08/11] stackdriver: always use regex full match for =~ and !=~operator --- .../features/datasources/stackdriver.md | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/sources/features/datasources/stackdriver.md b/docs/sources/features/datasources/stackdriver.md index 96f3ba3382e..6c493829e50 100644 --- a/docs/sources/features/datasources/stackdriver.md +++ b/docs/sources/features/datasources/stackdriver.md @@ -74,8 +74,12 @@ Click on the links above and click the `Enable` button: Choose a metric from the `Metric` dropdown. +### Filter + To add a filter, click the plus icon and choose a field to filter by and enter a filter value e.g. `instance_name = grafana-1` +It is also possible to add wildcards to the filter value field. E.g `us-*` to capture all values that starts with "us-", `*central-a` to capture all that ends with "central-a". `*-central-*` captures values that has the substring of -central-. + ### Aggregation The aggregation field lets you combine time series based on common statistics. Read more about this option [here](https://cloud.google.com/monitoring/charts/metrics-selector#aggregation-options). @@ -105,20 +109,20 @@ The Alias By field allows you to control the format of the legend keys. The defa #### Metric Type Patterns -Alias Pattern | Description | Example Result ------------------ | ---------------------------- | ------------- -`{{metric.type}}` | returns the full Metric Type | `compute.googleapis.com/instance/cpu/utilization` -`{{metric.name}}` | returns the metric name part | `instance/cpu/utilization` -`{{metric.service}}` | returns the service part | `compute` +| Alias Pattern | Description | Example Result | +| -------------------- | ---------------------------- | ------------------------------------------------- | +| `{{metric.type}}` | returns the full Metric Type | `compute.googleapis.com/instance/cpu/utilization` | +| `{{metric.name}}` | returns the metric name part | `instance/cpu/utilization` | +| `{{metric.service}}` | returns the service part | `compute` | #### Label Patterns In the Group By dropdown, you can see a list of metric and resource labels for a metric. These can be included in the legend key using alias patterns. -Alias Pattern Format | Description | Alias Pattern Example | Example Result ----------------------- | ---------------------------------- | ---------------------------- | ------------- -`{{metric.label.xxx}}` | returns the metric label value | `{{metric.label.instance_name}}` | `grafana-1-prod` -`{{resource.label.xxx}}` | returns the resource label value | `{{resource.label.zone}}` | `us-east1-b` +| Alias Pattern Format | Description | Alias Pattern Example | Example Result | +| ------------------------ | -------------------------------- | -------------------------------- | ---------------- | +| `{{metric.label.xxx}}` | returns the metric label value | `{{metric.label.instance_name}}` | `grafana-1-prod` | +| `{{resource.label.xxx}}` | returns the resource label value | `{{resource.label.zone}}` | `us-east1-b` | Example Alias By: `{{metric.type}} - {{metric.labels.instance_name}}` From 7e6a5c0a7436e175383dbbe93e9f869d15c4ccbb Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 8 Oct 2018 11:08:14 +0200 Subject: [PATCH 09/11] stackdriver: add tests from regex matching --- pkg/tsdb/stackdriver/stackdriver_test.go | 29 ++++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/pkg/tsdb/stackdriver/stackdriver_test.go b/pkg/tsdb/stackdriver/stackdriver_test.go index ec311c9f50f..8b1e8308ef7 100644 --- a/pkg/tsdb/stackdriver/stackdriver_test.go +++ b/pkg/tsdb/stackdriver/stackdriver_test.go @@ -406,16 +406,31 @@ func TestStackdriver(t *testing.T) { }) Convey("when building filter string", func() { - Convey("and there are wildcards in a filter value", func() { - filterParts := []interface{}{"zone", "=", "*-central1*"} - value := buildFilterString("somemetrictype", filterParts) - So(value, ShouldEqual, `metric.type="somemetrictype" zone=has_substring("-central1")`) + Convey("and theres no regex operator", func() { + Convey("and there are wildcards in a filter value", func() { + filterParts := []interface{}{"zone", "=", "*-central1*"} + value := buildFilterString("somemetrictype", filterParts) + So(value, ShouldEqual, `metric.type="somemetrictype" zone=has_substring("-central1")`) + }) + + Convey("and there are no wildcards in any filter value", func() { + filterParts := []interface{}{"zone", "!=", "us-central1-a"} + value := buildFilterString("somemetrictype", filterParts) + So(value, ShouldEqual, `metric.type="somemetrictype" zone!="us-central1-a"`) + }) }) - Convey("and there are no wildcards in any filter value", func() { - filterParts := []interface{}{"zone", "=", "us-central1-a"} + Convey("and there is a regex operator", func() { + filterParts := []interface{}{"zone", "=~", "us-central1-a~"} value := buildFilterString("somemetrictype", filterParts) - So(value, ShouldEqual, `metric.type="somemetrictype" zone="us-central1-a"`) + Convey("it should remove the ~ character from the operator that belongs to the value", func() { + So(value, ShouldNotContainSubstring, `=~`) + So(value, ShouldContainSubstring, `zone=`) + }) + + Convey("it should insert monitoring.regex.full_match before filter value", func() { + So(value, ShouldContainSubstring, `zone=monitoring.regex.full_match("us-central1-a~")`) + }) }) }) }) From 8d53799bcdd2f7ce434ef20e389df44464271828 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 8 Oct 2018 11:12:26 +0200 Subject: [PATCH 10/11] stackdriver: always use regex full match for =~ and !=~operator --- pkg/tsdb/stackdriver/stackdriver.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/tsdb/stackdriver/stackdriver.go b/pkg/tsdb/stackdriver/stackdriver.go index 8b5d71ba830..9023ef7735e 100644 --- a/pkg/tsdb/stackdriver/stackdriver.go +++ b/pkg/tsdb/stackdriver/stackdriver.go @@ -199,7 +199,11 @@ func buildFilterString(metricType string, filterParts []interface{}) string { if part == "AND" { filterString += " " } else if mod == 2 { - if strings.Contains(part.(string), "*") { + operator := filterParts[i-1] + if operator == "=~" || operator == "!=~" { + filterString = reverse(strings.Replace(reverse(filterString), "~", "", 1)) + filterString += fmt.Sprintf(`monitoring.regex.full_match("%s")`, part) + } else if strings.Contains(part.(string), "*") { filterString += interpolateFilterWildcards(part.(string)) } else { filterString += fmt.Sprintf(`"%s"`, part) From 11b9f9691cb181f7b3322ef24cd85b2d616e73dc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 8 Oct 2018 12:01:11 +0200 Subject: [PATCH 11/11] stackdriver: improve filter docs for wildcards and regular expressions --- docs/sources/features/datasources/stackdriver.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/sources/features/datasources/stackdriver.md b/docs/sources/features/datasources/stackdriver.md index 6c493829e50..c525130aebb 100644 --- a/docs/sources/features/datasources/stackdriver.md +++ b/docs/sources/features/datasources/stackdriver.md @@ -76,9 +76,15 @@ Choose a metric from the `Metric` dropdown. ### Filter -To add a filter, click the plus icon and choose a field to filter by and enter a filter value e.g. `instance_name = grafana-1` +To add a filter, click the plus icon and choose a field to filter by and enter a filter value e.g. `instance_name = grafana-1`. You can remove the filter by clicking on the filter name and select `--remove filter--`. -It is also possible to add wildcards to the filter value field. E.g `us-*` to capture all values that starts with "us-", `*central-a` to capture all that ends with "central-a". `*-central-*` captures values that has the substring of -central-. +#### Simple wildcards + +When the operator is set to `=` or `!=` it is possible to add wildcards to the filter value field. E.g `us-*` will capture all values that starts with "us-" and `*central-a` will capture all values that ends with "central-a". `*-central-*` captures all values that has the substring of -central-. Simple wildcards are less expensive than regular expressions. + +#### Regular expressions + +When the operator is set to `=~` or `!=~` it is possible to add regular expressions to the filter value field. E.g `us-central[1-3]-[af]` would match all values that starts with "us-central", is followed by a number in the range of 1 to 3, a dash and then either an "a" or an "f". Leading and trailing slashes are not needed when creating regular expressions. ### Aggregation