-
Notifications
You must be signed in to change notification settings - Fork 9.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5444 from TimeIncOSS/f-aws-logs-metric-filter
provider/aws: Add support for CloudWatch Log Metric Filter
- Loading branch information
Showing
10 changed files
with
633 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
187 changes: 187 additions & 0 deletions
187
builtin/providers/aws/resource_aws_cloudwatch_log_metric_filter.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/awserr" | ||
"github.com/aws/aws-sdk-go/service/cloudwatchlogs" | ||
) | ||
|
||
func resourceAwsCloudWatchLogMetricFilter() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceAwsCloudWatchLogMetricFilterUpdate, | ||
Read: resourceAwsCloudWatchLogMetricFilterRead, | ||
Update: resourceAwsCloudWatchLogMetricFilterUpdate, | ||
Delete: resourceAwsCloudWatchLogMetricFilterDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validateLogMetricFilterName, | ||
}, | ||
|
||
"pattern": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ValidateFunc: validateMaxLength(512), | ||
StateFunc: func(v interface{}) string { | ||
s, ok := v.(string) | ||
if !ok { | ||
return "" | ||
} | ||
return strings.TrimSpace(s) | ||
}, | ||
}, | ||
|
||
"log_group_name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validateLogGroupName, | ||
}, | ||
|
||
"metric_transformation": &schema.Schema{ | ||
Type: schema.TypeList, | ||
Required: true, | ||
MaxItems: 1, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"name": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ValidateFunc: validateLogMetricFilterTransformationName, | ||
}, | ||
"namespace": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ValidateFunc: validateLogMetricFilterTransformationName, | ||
}, | ||
"value": &schema.Schema{ | ||
Type: schema.TypeString, | ||
Required: true, | ||
ValidateFunc: validateMaxLength(100), | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsCloudWatchLogMetricFilterUpdate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).cloudwatchlogsconn | ||
|
||
input := cloudwatchlogs.PutMetricFilterInput{ | ||
FilterName: aws.String(d.Get("name").(string)), | ||
FilterPattern: aws.String(strings.TrimSpace(d.Get("pattern").(string))), | ||
LogGroupName: aws.String(d.Get("log_group_name").(string)), | ||
} | ||
|
||
transformations := d.Get("metric_transformation").([]interface{}) | ||
o := transformations[0].(map[string]interface{}) | ||
input.MetricTransformations = expandCloudWachLogMetricTransformations(o) | ||
|
||
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) | ||
} | ||
|
||
d.SetId(d.Get("name").(string)) | ||
|
||
log.Println("[INFO] CloudWatch Log Metric Filter created/updated") | ||
|
||
return resourceAwsCloudWatchLogMetricFilterRead(d, meta) | ||
} | ||
|
||
func resourceAwsCloudWatchLogMetricFilterRead(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).cloudwatchlogsconn | ||
|
||
mf, err := lookupCloudWatchLogMetricFilter(conn, d.Get("name").(string), | ||
d.Get("log_group_name").(string), nil) | ||
if err != nil { | ||
if _, ok := err.(*resource.NotFoundError); ok { | ||
log.Printf("[WARN] Removing CloudWatch Log Metric Filter as it is gone") | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("Failed reading CloudWatch Log Metric Filter: %s", err) | ||
} | ||
|
||
log.Printf("[DEBUG] Found CloudWatch Log Metric Filter: %s", mf) | ||
|
||
d.Set("name", mf.FilterName) | ||
d.Set("pattern", mf.FilterPattern) | ||
d.Set("metric_transformation", flattenCloudWachLogMetricTransformations(mf.MetricTransformations)) | ||
|
||
return nil | ||
} | ||
|
||
func lookupCloudWatchLogMetricFilter(conn *cloudwatchlogs.CloudWatchLogs, | ||
name, logGroupName string, nextToken *string) (*cloudwatchlogs.MetricFilter, error) { | ||
|
||
input := cloudwatchlogs.DescribeMetricFiltersInput{ | ||
FilterNamePrefix: aws.String(name), | ||
LogGroupName: aws.String(logGroupName), | ||
NextToken: nextToken, | ||
} | ||
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" { | ||
return nil, &resource.NotFoundError{ | ||
Message: fmt.Sprintf("CloudWatch Log Metric Filter %q / %q not found via"+ | ||
" initial DescribeMetricFilters call", name, logGroupName), | ||
LastError: err, | ||
LastRequest: input, | ||
} | ||
} | ||
|
||
return nil, fmt.Errorf("Failed describing CloudWatch Log Metric Filter: %s", err) | ||
} | ||
|
||
for _, mf := range resp.MetricFilters { | ||
if *mf.FilterName == name { | ||
return mf, nil | ||
} | ||
} | ||
|
||
if resp.NextToken != nil { | ||
return lookupCloudWatchLogMetricFilter(conn, name, logGroupName, resp.NextToken) | ||
} | ||
|
||
return nil, &resource.NotFoundError{ | ||
Message: fmt.Sprintf("CloudWatch Log Metric Filter %q / %q not found "+ | ||
"in given results from DescribeMetricFilters", name, logGroupName), | ||
LastResponse: resp, | ||
LastRequest: input, | ||
} | ||
} | ||
|
||
func resourceAwsCloudWatchLogMetricFilterDelete(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).cloudwatchlogsconn | ||
|
||
input := cloudwatchlogs.DeleteMetricFilterInput{ | ||
FilterName: aws.String(d.Get("name").(string)), | ||
LogGroupName: aws.String(d.Get("log_group_name").(string)), | ||
} | ||
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) | ||
} | ||
log.Println("[INFO] CloudWatch Log Metric Filter deleted") | ||
|
||
d.SetId("") | ||
|
||
return nil | ||
} |
185 changes: 185 additions & 0 deletions
185
builtin/providers/aws/resource_aws_cloudwatch_log_metric_filter_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/cloudwatchlogs" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func TestAccAWSCloudWatchLogMetricFilter_basic(t *testing.T) { | ||
var mf cloudwatchlogs.MetricFilter | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckAWSCloudWatchLogMetricFilterDestroy, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: testAccAWSCloudWatchLogMetricFilterConfig, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckCloudWatchLogMetricFilterExists("aws_cloudwatch_log_metric_filter.foobar", &mf), | ||
resource.TestCheckResourceAttr("aws_cloudwatch_log_metric_filter.foobar", "name", "MyAppAccessCount"), | ||
testAccCheckCloudWatchLogMetricFilterName(&mf, "MyAppAccessCount"), | ||
resource.TestCheckResourceAttr("aws_cloudwatch_log_metric_filter.foobar", "pattern", ""), | ||
testAccCheckCloudWatchLogMetricFilterPattern(&mf, ""), | ||
resource.TestCheckResourceAttr("aws_cloudwatch_log_metric_filter.foobar", "log_group_name", "MyApp/access.log"), | ||
resource.TestCheckResourceAttr("aws_cloudwatch_log_metric_filter.foobar", "metric_transformation.0.name", "EventCount"), | ||
resource.TestCheckResourceAttr("aws_cloudwatch_log_metric_filter.foobar", "metric_transformation.0.namespace", "YourNamespace"), | ||
resource.TestCheckResourceAttr("aws_cloudwatch_log_metric_filter.foobar", "metric_transformation.0.value", "1"), | ||
testAccCheckCloudWatchLogMetricFilterTransformation(&mf, &cloudwatchlogs.MetricTransformation{ | ||
MetricName: aws.String("EventCount"), | ||
MetricNamespace: aws.String("YourNamespace"), | ||
MetricValue: aws.String("1"), | ||
}), | ||
), | ||
}, | ||
resource.TestStep{ | ||
Config: testAccAWSCloudWatchLogMetricFilterConfigModified, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckCloudWatchLogMetricFilterExists("aws_cloudwatch_log_metric_filter.foobar", &mf), | ||
resource.TestCheckResourceAttr("aws_cloudwatch_log_metric_filter.foobar", "name", "MyAppAccessCount"), | ||
testAccCheckCloudWatchLogMetricFilterName(&mf, "MyAppAccessCount"), | ||
resource.TestCheckResourceAttr("aws_cloudwatch_log_metric_filter.foobar", "pattern", "{ $.errorCode = \"AccessDenied\" }"), | ||
testAccCheckCloudWatchLogMetricFilterPattern(&mf, "{ $.errorCode = \"AccessDenied\" }"), | ||
resource.TestCheckResourceAttr("aws_cloudwatch_log_metric_filter.foobar", "log_group_name", "MyApp/access.log"), | ||
resource.TestCheckResourceAttr("aws_cloudwatch_log_metric_filter.foobar", "metric_transformation.0.name", "AccessDeniedCount"), | ||
resource.TestCheckResourceAttr("aws_cloudwatch_log_metric_filter.foobar", "metric_transformation.0.namespace", "MyNamespace"), | ||
resource.TestCheckResourceAttr("aws_cloudwatch_log_metric_filter.foobar", "metric_transformation.0.value", "2"), | ||
testAccCheckCloudWatchLogMetricFilterTransformation(&mf, &cloudwatchlogs.MetricTransformation{ | ||
MetricName: aws.String("AccessDeniedCount"), | ||
MetricNamespace: aws.String("MyNamespace"), | ||
MetricValue: aws.String("2"), | ||
}), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckCloudWatchLogMetricFilterName(mf *cloudwatchlogs.MetricFilter, name string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
if name != *mf.FilterName { | ||
return fmt.Errorf("Expected filter name: %q, given: %q", name, *mf.FilterName) | ||
} | ||
return nil | ||
} | ||
} | ||
|
||
func testAccCheckCloudWatchLogMetricFilterPattern(mf *cloudwatchlogs.MetricFilter, pattern string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
if mf.FilterPattern == nil { | ||
if pattern != "" { | ||
return fmt.Errorf("Received empty filter pattern, expected: %q", pattern) | ||
} | ||
return nil | ||
} | ||
|
||
if pattern != *mf.FilterPattern { | ||
return fmt.Errorf("Expected filter pattern: %q, given: %q", pattern, *mf.FilterPattern) | ||
} | ||
return nil | ||
} | ||
} | ||
|
||
func testAccCheckCloudWatchLogMetricFilterTransformation(mf *cloudwatchlogs.MetricFilter, | ||
t *cloudwatchlogs.MetricTransformation) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
given := mf.MetricTransformations[0] | ||
expected := t | ||
|
||
if *given.MetricName != *expected.MetricName { | ||
return fmt.Errorf("Expected metric name: %q, received: %q", | ||
*expected.MetricName, *given.MetricName) | ||
} | ||
|
||
if *given.MetricNamespace != *expected.MetricNamespace { | ||
return fmt.Errorf("Expected metric namespace: %q, received: %q", | ||
*expected.MetricNamespace, *given.MetricNamespace) | ||
} | ||
|
||
if *given.MetricValue != *expected.MetricValue { | ||
return fmt.Errorf("Expected metric value: %q, received: %q", | ||
*expected.MetricValue, *given.MetricValue) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccCheckCloudWatchLogMetricFilterExists(n string, mf *cloudwatchlogs.MetricFilter) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources[n] | ||
if !ok { | ||
return fmt.Errorf("Not found: %s", n) | ||
} | ||
|
||
conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn | ||
metricFilter, err := lookupCloudWatchLogMetricFilter(conn, rs.Primary.ID, rs.Primary.Attributes["log_group_name"], nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
*mf = *metricFilter | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccCheckAWSCloudWatchLogMetricFilterDestroy(s *terraform.State) error { | ||
conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "aws_cloudwatch_log_metric_filter" { | ||
continue | ||
} | ||
|
||
_, err := lookupCloudWatchLogMetricFilter(conn, rs.Primary.ID, rs.Primary.Attributes["log_group_name"], nil) | ||
if err == nil { | ||
return fmt.Errorf("MetricFilter Still Exists: %s", rs.Primary.ID) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
var testAccAWSCloudWatchLogMetricFilterConfig = ` | ||
resource "aws_cloudwatch_log_metric_filter" "foobar" { | ||
name = "MyAppAccessCount" | ||
pattern = "" | ||
log_group_name = "${aws_cloudwatch_log_group.dada.name}" | ||
metric_transformation { | ||
name = "EventCount" | ||
namespace = "YourNamespace" | ||
value = "1" | ||
} | ||
} | ||
resource "aws_cloudwatch_log_group" "dada" { | ||
name = "MyApp/access.log" | ||
} | ||
` | ||
|
||
var testAccAWSCloudWatchLogMetricFilterConfigModified = ` | ||
resource "aws_cloudwatch_log_metric_filter" "foobar" { | ||
name = "MyAppAccessCount" | ||
pattern = <<PATTERN | ||
{ $.errorCode = "AccessDenied" } | ||
PATTERN | ||
log_group_name = "${aws_cloudwatch_log_group.dada.name}" | ||
metric_transformation { | ||
name = "AccessDeniedCount" | ||
namespace = "MyNamespace" | ||
value = "2" | ||
} | ||
} | ||
resource "aws_cloudwatch_log_group" "dada" { | ||
name = "MyApp/access.log" | ||
} | ||
` |
Oops, something went wrong.