From e29bca741909f9266d80540f4ecf1676e1087461 Mon Sep 17 00:00:00 2001 From: David Bennett <71459415+Jagularr@users.noreply.github.com> Date: Tue, 20 Apr 2021 17:29:58 -0400 Subject: [PATCH] Add Glob / Wildcard support to Cloudwatch input for 'Dimensions' configuration (#9136) I believe this will resolve #4046 --- plugins/inputs/cloudwatch/README.md | 1 + plugins/inputs/cloudwatch/cloudwatch.go | 22 ++++++++++++++++---- plugins/inputs/cloudwatch/cloudwatch_test.go | 6 ++++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/plugins/inputs/cloudwatch/README.md b/plugins/inputs/cloudwatch/README.md index c86e66e67..d7c803c8c 100644 --- a/plugins/inputs/cloudwatch/README.md +++ b/plugins/inputs/cloudwatch/README.md @@ -101,6 +101,7 @@ API endpoint. In the following order the plugin will attempt to authenticate. # # ## Dimension filters for Metric. All dimensions defined for the metric names # ## must be specified in order to retrieve the metric statistics. + # ## 'value' has wildcard / 'glob' matching support such as `p-*`. # [[inputs.cloudwatch.metrics.dimensions]] # name = "LoadBalancerName" # value = "p-example" diff --git a/plugins/inputs/cloudwatch/cloudwatch.go b/plugins/inputs/cloudwatch/cloudwatch.go index 22fdcab38..f108aceb6 100644 --- a/plugins/inputs/cloudwatch/cloudwatch.go +++ b/plugins/inputs/cloudwatch/cloudwatch.go @@ -66,8 +66,9 @@ type Metric struct { // Dimension defines a simplified Cloudwatch dimension (provides metric filtering). type Dimension struct { - Name string `toml:"name"` - Value string `toml:"value"` + Name string `toml:"name"` + Value string `toml:"value"` + valueMatcher filter.Filter } // metricCache caches metrics, their filters, and generated queries. @@ -170,6 +171,7 @@ func (c *CloudWatch) SampleConfig() string { # # ## Dimension filters for Metric. All dimensions defined for the metric names # ## must be specified in order to retrieve the metric statistics. + # ## 'value' has wildcard / 'glob' matching support. such as 'p-*'. # [[inputs.cloudwatch.metrics.dimensions]] # name = "LoadBalancerName" # value = "p-example" @@ -294,6 +296,18 @@ func (c *CloudWatch) initializeCloudWatch() error { loglevel := aws.LogOff c.client = cloudwatch.New(configProvider, cfg.WithLogLevel(loglevel)) + // Initialize regex matchers for each Dimension value. + for _, m := range c.Metrics { + for _, dimension := range m.Dimensions { + matcher, err := filter.NewIncludeExcludeFilter([]string{dimension.Value}, nil) + if err != nil { + return err + } + + dimension.valueMatcher = matcher + } + } + return nil } @@ -633,7 +647,7 @@ func (f *metricCache) isValid() bool { func hasWildcard(dimensions []*Dimension) bool { for _, d := range dimensions { - if d.Value == "" || d.Value == "*" { + if d.Value == "" || strings.ContainsAny(d.Value, "*?[") { return true } } @@ -651,7 +665,7 @@ func isSelected(name string, metric *cloudwatch.Metric, dimensions []*Dimension) selected := false for _, d2 := range metric.Dimensions { if d.Name == *d2.Name { - if d.Value == "" || d.Value == "*" || d.Value == *d2.Value { + if d.Value == "" || d.valueMatcher.Match(*d2.Value) { selected = true } } diff --git a/plugins/inputs/cloudwatch/cloudwatch_test.go b/plugins/inputs/cloudwatch/cloudwatch_test.go index 43fb01f05..56aee3468 100644 --- a/plugins/inputs/cloudwatch/cloudwatch_test.go +++ b/plugins/inputs/cloudwatch/cloudwatch_test.go @@ -201,16 +201,18 @@ func TestSelectMetrics(t *testing.T) { Dimensions: []*Dimension{ { Name: "LoadBalancerName", - Value: "*", + Value: "lb*", }, { Name: "AvailabilityZone", - Value: "*", + Value: "us-east*", }, }, }, }, } + err := c.initializeCloudWatch() + assert.NoError(t, err) c.client = &mockSelectMetricsCloudWatchClient{} filtered, err := getFilteredMetrics(c) // We've asked for 2 (out of 4) metrics, over all 3 load balancers in all 2