Skip to content

Commit

Permalink
Merge pull request #5444 from TimeIncOSS/f-aws-logs-metric-filter
Browse files Browse the repository at this point in the history
provider/aws: Add support for CloudWatch Log Metric Filter
  • Loading branch information
jen20 committed Mar 21, 2016
2 parents 26f4467 + af93183 commit f946695
Show file tree
Hide file tree
Showing 10 changed files with 633 additions and 3 deletions.
1 change: 1 addition & 0 deletions builtin/providers/aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ func Provider() terraform.ResourceProvider {
"aws_cloudwatch_event_rule": resourceAwsCloudWatchEventRule(),
"aws_cloudwatch_event_target": resourceAwsCloudWatchEventTarget(),
"aws_cloudwatch_log_group": resourceAwsCloudWatchLogGroup(),
"aws_cloudwatch_log_metric_filter": resourceAwsCloudWatchLogMetricFilter(),
"aws_autoscaling_lifecycle_hook": resourceAwsAutoscalingLifecycleHook(),
"aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(),
"aws_codedeploy_app": resourceAwsCodeDeployApp(),
Expand Down
7 changes: 4 additions & 3 deletions builtin/providers/aws/resource_aws_cloudwatch_log_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ func resourceAwsCloudWatchLogGroup() *schema.Resource {

Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateLogGroupName,
},

"retention_in_days": &schema.Schema{
Expand Down
187 changes: 187 additions & 0 deletions builtin/providers/aws/resource_aws_cloudwatch_log_metric_filter.go
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
}
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"
}
`
Loading

0 comments on commit f946695

Please sign in to comment.