diff --git a/aws/resource_aws_cloudwatch_log_metric_filter.go b/aws/resource_aws_cloudwatch_log_metric_filter.go index 7abaec78f36..92a361266a7 100644 --- a/aws/resource_aws_cloudwatch_log_metric_filter.go +++ b/aws/resource_aws_cloudwatch_log_metric_filter.go @@ -71,8 +71,9 @@ func resourceAwsCloudWatchLogMetricFilter() *schema.Resource { ValidateFunc: validation.StringLenBetween(0, 100), }, "default_value": { - Type: schema.TypeFloat, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateTypeStringNullableFloat, }, }, }, @@ -92,14 +93,10 @@ func resourceAwsCloudWatchLogMetricFilterUpdate(d *schema.ResourceData, meta int transformations := d.Get("metric_transformation").([]interface{}) o := transformations[0].(map[string]interface{}) - metricsTransformations, err := expandCloudWachLogMetricTransformations(o) - if err != nil { - return err - } - input.MetricTransformations = metricsTransformations + input.MetricTransformations = expandCloudWatchLogMetricTransformations(o) log.Printf("[DEBUG] Creating/Updating CloudWatch Log Metric Filter: %s", input) - _, err = conn.PutMetricFilter(&input) + _, err := conn.PutMetricFilter(&input) if err != nil { return fmt.Errorf("Creating/Updating CloudWatch Log Metric Filter failed: %s", err) } @@ -130,7 +127,7 @@ func resourceAwsCloudWatchLogMetricFilterRead(d *schema.ResourceData, meta inter d.Set("name", mf.FilterName) d.Set("pattern", mf.FilterPattern) - d.Set("metric_transformation", flattenCloudWachLogMetricTransformations(mf.MetricTransformations)) + d.Set("metric_transformation", flattenCloudWatchLogMetricTransformations(mf.MetricTransformations)) return nil } diff --git a/aws/resource_aws_cloudwatch_log_metric_filter_test.go b/aws/resource_aws_cloudwatch_log_metric_filter_test.go index 031eacd453d..9ef085ee408 100644 --- a/aws/resource_aws_cloudwatch_log_metric_filter_test.go +++ b/aws/resource_aws_cloudwatch_log_metric_filter_test.go @@ -56,6 +56,7 @@ func TestAccAWSCloudWatchLogMetricFilter_basic(t *testing.T) { MetricName: aws.String("AccessDeniedCount"), MetricNamespace: aws.String("MyNamespace"), MetricValue: aws.String("2"), + DefaultValue: aws.Float64(1), }), ), }, @@ -109,6 +110,14 @@ func testAccCheckCloudWatchLogMetricFilterTransformation(mf *cloudwatchlogs.Metr *expected.MetricValue, *given.MetricValue) } + if (given.DefaultValue != nil) != (expected.DefaultValue != nil) { + return fmt.Errorf("Expected default value to be present: %t, received: %t", + expected.DefaultValue != nil, given.DefaultValue != nil) + } else if (given.DefaultValue != nil) && *given.DefaultValue != *expected.DefaultValue { + return fmt.Errorf("Expected metric value: %g, received: %g", + *expected.DefaultValue, *given.DefaultValue) + } + return nil } } diff --git a/aws/structure.go b/aws/structure.go index 4fa0cbdbf82..9f2ce1061e3 100644 --- a/aws/structure.go +++ b/aws/structure.go @@ -1780,21 +1780,22 @@ func expandApiGatewayStageKeyOperations(d *schema.ResourceData) []*apigateway.Pa return operations } -func expandCloudWachLogMetricTransformations(m map[string]interface{}) ([]*cloudwatchlogs.MetricTransformation, error) { +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"] != "" { - transformation.DefaultValue = aws.Float64(m["default_value"].(float64)) + if m["default_value"].(string) != "" { + value, _ := strconv.ParseFloat(m["default_value"].(string), 64) + transformation.DefaultValue = aws.Float64(value) } - return []*cloudwatchlogs.MetricTransformation{&transformation}, nil + return []*cloudwatchlogs.MetricTransformation{&transformation} } -func flattenCloudWachLogMetricTransformations(ts []*cloudwatchlogs.MetricTransformation) []interface{} { +func flattenCloudWatchLogMetricTransformations(ts []*cloudwatchlogs.MetricTransformation) []interface{} { mts := make([]interface{}, 0) m := make(map[string]interface{}, 0) @@ -1802,7 +1803,9 @@ func flattenCloudWachLogMetricTransformations(ts []*cloudwatchlogs.MetricTransfo m["namespace"] = *ts[0].MetricNamespace m["value"] = *ts[0].MetricValue - if ts[0].DefaultValue != nil { + if ts[0].DefaultValue == nil { + m["default_value"] = "" + } else { m["default_value"] = *ts[0].DefaultValue } diff --git a/aws/validators.go b/aws/validators.go index 3b41e20bdf4..7cda482c844 100644 --- a/aws/validators.go +++ b/aws/validators.go @@ -62,6 +62,26 @@ func validateTypeStringNullableBoolean(v interface{}, k string) (ws []string, es return } +// validateTypeStringNullableFloat provides custom error messaging for TypeString floats +// Some arguments require a floating point value or an unspecified, empty field. +func validateTypeStringNullableFloat(v interface{}, k string) (ws []string, es []error) { + value, ok := v.(string) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be string", k)) + return + } + + if value == "" { + return + } + + if _, err := strconv.ParseFloat(value, 64); err != nil { + es = append(es, fmt.Errorf("%s: cannot parse '%s' as float: %s", k, value, err)) + } + + return +} + func validateRdsIdentifier(v interface{}, k string) (ws []string, errors []error) { value := v.(string) if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { diff --git a/aws/validators_test.go b/aws/validators_test.go index 552d6ec252c..960969c8262 100644 --- a/aws/validators_test.go +++ b/aws/validators_test.go @@ -130,6 +130,57 @@ func TestValidateTypeStringNullableBoolean(t *testing.T) { } } +func TestValidateTypeStringNullableFloat(t *testing.T) { + testCases := []struct { + val interface{} + expectedErr *regexp.Regexp + }{ + { + val: "", + }, + { + val: "0", + }, + { + val: "1", + }, + { + val: "42.0", + }, + { + val: "threeve", + expectedErr: regexp.MustCompile(`cannot parse`), + }, + } + + matchErr := func(errs []error, r *regexp.Regexp) bool { + // err must match one provided + for _, err := range errs { + if r.MatchString(err.Error()) { + return true + } + } + + return false + } + + for i, tc := range testCases { + _, errs := validateTypeStringNullableFloat(tc.val, "test_property") + + if len(errs) == 0 && tc.expectedErr == nil { + continue + } + + if len(errs) != 0 && tc.expectedErr == nil { + t.Fatalf("expected test case %d to produce no errors, got %v", i, errs) + } + + if !matchErr(errs, tc.expectedErr) { + t.Fatalf("expected test case %d to produce error matching \"%s\", got %v", i, tc.expectedErr, errs) + } + } +} + func TestValidateCloudWatchDashboardName(t *testing.T) { validNames := []string{ "HelloWorl_d",