diff --git a/.changelog/19804.txt b/.changelog/19804.txt new file mode 100644 index 00000000000..1cddbbd9d6e --- /dev/null +++ b/.changelog/19804.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_cloudwatch_log_metric_filter: Add support for `unit` in the `metric_transformation` block. +``` \ No newline at end of file diff --git a/aws/resource_aws_cloudwatch_log_metric_filter.go b/aws/resource_aws_cloudwatch_log_metric_filter.go index fff90aab0c9..f6fac4c9e3d 100644 --- a/aws/resource_aws_cloudwatch_log_metric_filter.go +++ b/aws/resource_aws_cloudwatch_log_metric_filter.go @@ -3,10 +3,10 @@ package aws import ( "fmt" "log" + "strconv" "strings" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -83,6 +83,12 @@ func resourceAwsCloudWatchLogMetricFilter() *schema.Resource { Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "unit": { + Type: schema.TypeString, + Optional: true, + Default: cloudwatchlogs.StandardUnitNone, + ValidateFunc: validation.StringInSlice(cloudwatchlogs.StandardUnit_Values(), false), + }, }, }, }, @@ -115,7 +121,7 @@ func resourceAwsCloudWatchLogMetricFilterUpdate(d *schema.ResourceData, meta int log.Printf("[DEBUG] Creating/Updating CloudWatch Log Metric Filter: %s", input) _, err := conn.PutMetricFilter(&input) if err != nil { - return fmt.Errorf("Creating/Updating CloudWatch Log Metric Filter failed: %s", err) + return fmt.Errorf("Creating/Updating CloudWatch Log Metric Filter failed: %w", err) } d.SetId(d.Get("name").(string)) @@ -137,7 +143,7 @@ func resourceAwsCloudWatchLogMetricFilterRead(d *schema.ResourceData, meta inter return nil } - return fmt.Errorf("Failed reading CloudWatch Log Metric Filter: %s", err) + return fmt.Errorf("Failed reading CloudWatch Log Metric Filter: %w", err) } log.Printf("[DEBUG] Found CloudWatch Log Metric Filter: %s", mf) @@ -145,7 +151,7 @@ func resourceAwsCloudWatchLogMetricFilterRead(d *schema.ResourceData, meta inter d.Set("name", mf.FilterName) d.Set("pattern", mf.FilterPattern) if err := d.Set("metric_transformation", flattenCloudWatchLogMetricTransformations(mf.MetricTransformations)); err != nil { - return fmt.Errorf("error setting metric_transformation: %s", err) + return fmt.Errorf("error setting metric_transformation: %w", err) } return nil @@ -162,7 +168,7 @@ func lookupCloudWatchLogMetricFilter(conn *cloudwatchlogs.CloudWatchLogs, log.Printf("[DEBUG] Reading CloudWatch Log Metric Filter: %s", input) resp, err := conn.DescribeMetricFilters(&input) if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "ResourceNotFoundException" { + if isAWSErr(err, cloudwatchlogs.ErrCodeResourceNotFoundException, "") { return nil, &resource.NotFoundError{ Message: fmt.Sprintf("CloudWatch Log Metric Filter %q / %q not found via"+ " initial DescribeMetricFilters call", name, logGroupName), @@ -171,7 +177,7 @@ func lookupCloudWatchLogMetricFilter(conn *cloudwatchlogs.CloudWatchLogs, } } - return nil, fmt.Errorf("Failed describing CloudWatch Log Metric Filter: %s", err) + return nil, fmt.Errorf("Failed describing CloudWatch Log Metric Filter: %w", err) } for _, mf := range resp.MetricFilters { @@ -208,7 +214,7 @@ func resourceAwsCloudWatchLogMetricFilterDelete(d *schema.ResourceData, meta int log.Printf("[INFO] Deleting CloudWatch Log Metric Filter: %s", d.Id()) _, err := conn.DeleteMetricFilter(&input) if err != nil { - return fmt.Errorf("Error deleting CloudWatch Log Metric Filter: %s", err) + return fmt.Errorf("Error deleting CloudWatch Log Metric Filter: %w", err) } log.Println("[INFO] CloudWatch Log Metric Filter deleted") @@ -227,3 +233,56 @@ func resourceAwsCloudWatchLogMetricFilterImport(d *schema.ResourceData, meta int d.SetId(name) return []*schema.ResourceData{d}, nil } + +func expandCloudWatchLogMetricTransformations(m map[string]interface{}) []*cloudwatchlogs.MetricTransformation { + transformation := cloudwatchlogs.MetricTransformation{ + MetricName: aws.String(m["name"].(string)), + MetricNamespace: aws.String(m["namespace"].(string)), + MetricValue: aws.String(m["value"].(string)), + } + + if m["default_value"].(string) != "" { + value, _ := strconv.ParseFloat(m["default_value"].(string), 64) + transformation.DefaultValue = aws.Float64(value) + } + + if dims := m["dimensions"].(map[string]interface{}); len(dims) > 0 { + transformation.Dimensions = expandStringMap(dims) + } + + if v, ok := m["unit"].(string); ok && v != "" { + transformation.Unit = aws.String(v) + } + + return []*cloudwatchlogs.MetricTransformation{&transformation} +} + +func flattenCloudWatchLogMetricTransformations(ts []*cloudwatchlogs.MetricTransformation) []interface{} { + mts := make([]interface{}, 0) + m := make(map[string]interface{}) + + transform := ts[0] + m["name"] = aws.StringValue(transform.MetricName) + m["namespace"] = aws.StringValue(transform.MetricNamespace) + m["value"] = aws.StringValue(transform.MetricValue) + + if transform.DefaultValue == nil { + m["default_value"] = "" + } else { + m["default_value"] = strconv.FormatFloat(aws.Float64Value(transform.DefaultValue), 'f', -1, 64) + } + + if dims := transform.Dimensions; len(dims) > 0 { + m["dimensions"] = pointersMapToStringList(dims) + } else { + m["dimensions"] = nil + } + + if transform.Unit != nil { + m["unit"] = aws.StringValue(transform.Unit) + } + + mts = append(mts, m) + + return mts +} diff --git a/aws/resource_aws_cloudwatch_log_metric_filter_test.go b/aws/resource_aws_cloudwatch_log_metric_filter_test.go index 6823121a15d..2e2e5c7d614 100644 --- a/aws/resource_aws_cloudwatch_log_metric_filter_test.go +++ b/aws/resource_aws_cloudwatch_log_metric_filter_test.go @@ -14,7 +14,7 @@ import ( func TestAccAWSCloudWatchLogMetricFilter_basic(t *testing.T) { var mf cloudwatchlogs.MetricFilter rInt := acctest.RandInt() - resourceName := "aws_cloudwatch_log_metric_filter.foobar" + resourceName := "aws_cloudwatch_log_metric_filter.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -98,7 +98,53 @@ func TestAccAWSCloudWatchLogMetricFilter_basic(t *testing.T) { }, { Config: testAccAWSCloudwatchLogMetricFilterConfigMany(rInt), - Check: testAccCheckCloudwatchLogMetricFilterManyExist("aws_cloudwatch_log_metric_filter.count_dracula", &mf), + Check: testAccCheckCloudwatchLogMetricFilterManyExist("aws_cloudwatch_log_metric_filter.test", &mf), + }, + }, + }) +} + +func TestAccAWSCloudWatchLogMetricFilter_disappears(t *testing.T) { + var mf cloudwatchlogs.MetricFilter + rInt := acctest.RandInt() + resourceName := "aws_cloudwatch_log_metric_filter.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, cloudwatchlogs.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCloudWatchLogMetricFilterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudWatchLogMetricFilterConfig(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchLogMetricFilterExists(resourceName, &mf), + testAccCheckResourceDisappears(testAccProvider, resourceAwsCloudWatchLogMetricFilter(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSCloudWatchLogMetricFilter_disappears_logGroup(t *testing.T) { + var mf cloudwatchlogs.MetricFilter + rInt := acctest.RandInt() + resourceName := "aws_cloudwatch_log_metric_filter.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, cloudwatchlogs.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCloudWatchLogMetricFilterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudWatchLogMetricFilterConfig(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchLogMetricFilterExists(resourceName, &mf), + testAccCheckResourceDisappears(testAccProvider, resourceAwsCloudWatchLogGroup(), "aws_cloudwatch_log_group.test"), + ), + ExpectNonEmptyPlan: true, }, }, }) @@ -233,10 +279,10 @@ func testAccCheckCloudwatchLogMetricFilterManyExist(basename string, mf *cloudwa func testAccAWSCloudWatchLogMetricFilterConfig(rInt int) string { return fmt.Sprintf(` -resource "aws_cloudwatch_log_metric_filter" "foobar" { +resource "aws_cloudwatch_log_metric_filter" "test" { name = "MyAppAccessCount-%d" pattern = "" - log_group_name = aws_cloudwatch_log_group.dada.name + log_group_name = aws_cloudwatch_log_group.test.name metric_transformation { name = "EventCount" @@ -245,7 +291,7 @@ resource "aws_cloudwatch_log_metric_filter" "foobar" { } } -resource "aws_cloudwatch_log_group" "dada" { +resource "aws_cloudwatch_log_group" "test" { name = "MyApp/access-%d.log" } `, rInt, rInt) @@ -253,7 +299,7 @@ resource "aws_cloudwatch_log_group" "dada" { func testAccAWSCloudWatchLogMetricFilterConfigModified(rInt int) string { return fmt.Sprintf(` -resource "aws_cloudwatch_log_metric_filter" "foobar" { +resource "aws_cloudwatch_log_metric_filter" "test" { name = "MyAppAccessCount-%d" pattern = < 0 { - transformation.Dimensions = expandStringMap(dims) - } - - return []*cloudwatchlogs.MetricTransformation{&transformation} -} - -func flattenCloudWatchLogMetricTransformations(ts []*cloudwatchlogs.MetricTransformation) []interface{} { - mts := make([]interface{}, 0) - m := make(map[string]interface{}) - - m["name"] = aws.StringValue(ts[0].MetricName) - m["namespace"] = aws.StringValue(ts[0].MetricNamespace) - m["value"] = aws.StringValue(ts[0].MetricValue) - - if ts[0].DefaultValue == nil { - m["default_value"] = "" - } else { - m["default_value"] = strconv.FormatFloat(aws.Float64Value(ts[0].DefaultValue), 'f', -1, 64) - } - - if dims := ts[0].Dimensions; len(dims) > 0 { - m["dimensions"] = pointersMapToStringList(dims) - } else { - m["dimensions"] = nil - } - - mts = append(mts, m) - - return mts -} - func flattenBeanstalkAsg(list []*elasticbeanstalk.AutoScalingGroup) []string { strs := make([]string, 0, len(list)) for _, r := range list { diff --git a/website/docs/r/cloudwatch_log_metric_filter.html.markdown b/website/docs/r/cloudwatch_log_metric_filter.html.markdown index 0d9721a5d86..e3c034090ba 100644 --- a/website/docs/r/cloudwatch_log_metric_filter.html.markdown +++ b/website/docs/r/cloudwatch_log_metric_filter.html.markdown @@ -47,6 +47,7 @@ The `metric_transformation` block supports the following arguments: * `value` - (Required) What to publish to the metric. For example, if you're counting the occurrences of a particular term like "Error", the value will be "1" for each occurrence. If you're counting the bytes transferred the published value will be the value in the log event. * `default_value` - (Optional) The value to emit when a filter pattern does not match a log event. Conflicts with `dimensions`. * `dimensions` - (Optional) Map of fields to use as dimensions for the metric. Up to 3 dimensions are allowed. Conflicts with `default_value`. +* `unit` - (Optional) The unit to assign to the metric. If you omit this, the unit is set as `None`. ## Attributes Reference