diff --git a/builtin/providers/aws/resource_aws_cloudwatch_log_group.go b/builtin/providers/aws/resource_aws_cloudwatch_log_group.go index 08ffc26a5..015538381 100644 --- a/builtin/providers/aws/resource_aws_cloudwatch_log_group.go +++ b/builtin/providers/aws/resource_aws_cloudwatch_log_group.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/hashicorp/errwrap" ) func resourceAwsCloudWatchLogGroup() *schema.Resource { @@ -21,23 +22,25 @@ func resourceAwsCloudWatchLogGroup() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "name": &schema.Schema{ + "name": { Type: schema.TypeString, Required: true, ForceNew: true, ValidateFunc: validateLogGroupName, }, - "retention_in_days": &schema.Schema{ + "retention_in_days": { Type: schema.TypeInt, Optional: true, Default: 0, }, - "arn": &schema.Schema{ + "arn": { Type: schema.TypeString, Computed: true, }, + + "tags": tagsSchema(), }, } } @@ -46,6 +49,7 @@ func resourceAwsCloudWatchLogGroupCreate(d *schema.ResourceData, meta interface{ conn := meta.(*AWSClient).cloudwatchlogsconn log.Printf("[DEBUG] Creating CloudWatch Log Group: %s", d.Get("name").(string)) + _, err := conn.CreateLogGroup(&cloudwatchlogs.CreateLogGroupInput{ LogGroupName: aws.String(d.Get("name").(string)), }) @@ -83,6 +87,12 @@ func resourceAwsCloudWatchLogGroupRead(d *schema.ResourceData, meta interface{}) d.Set("retention_in_days", lg.RetentionInDays) } + tags, err := flattenCloudWatchTags(d, conn) + if err != nil { + return err + } + d.Set("tags", tags) + return nil } @@ -138,9 +148,55 @@ func resourceAwsCloudWatchLogGroupUpdate(d *schema.ResourceData, meta interface{ } } + if d.HasChange("tags") { + oraw, nraw := d.GetChange("tags") + o := oraw.(map[string]interface{}) + n := nraw.(map[string]interface{}) + create, remove := diffCloudWatchTags(o, n) + + if len(remove) > 0 { + log.Printf("[DEBUG] Removing tags from %s", name) + _, err := conn.UntagLogGroup(&cloudwatchlogs.UntagLogGroupInput{ + LogGroupName: aws.String(name), + Tags: remove, + }) + if err != nil { + return err + } + } + + if len(create) > 0 { + log.Printf("[DEBUG] Creating tags on %s", name) + _, err := conn.TagLogGroup(&cloudwatchlogs.TagLogGroupInput{ + LogGroupName: aws.String(name), + Tags: create, + }) + if err != nil { + return err + } + } + } + return resourceAwsCloudWatchLogGroupRead(d, meta) } +func diffCloudWatchTags(oldTags map[string]interface{}, newTags map[string]interface{}) (map[string]*string, []*string) { + create := make(map[string]*string) + for k, v := range newTags { + create[k] = aws.String(v.(string)) + } + + var remove []*string + for _, t := range oldTags { + old, ok := create[t.(string)] + if !ok || *old != t.(string) { + remove = append(remove, aws.String(t.(string))) + } + } + + return create, remove +} + func resourceAwsCloudWatchLogGroupDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cloudwatchlogsconn log.Printf("[INFO] Deleting CloudWatch Log Group: %s", d.Id()) @@ -156,3 +212,33 @@ func resourceAwsCloudWatchLogGroupDelete(d *schema.ResourceData, meta interface{ return nil } + +func expandCloudWatchLogGroupTags(tagsMap map[string]interface{}) map[string]*string { + output := make(map[string]*string, len(tagsMap)) + + for i, v := range tagsMap { + output[i] = aws.String(v.(string)) + } + + return output +} + +func flattenCloudWatchTags(d *schema.ResourceData, conn *cloudwatchlogs.CloudWatchLogs) (map[string]interface{}, error) { + tagsOutput, err := conn.ListTagsLogGroup(&cloudwatchlogs.ListTagsLogGroupInput{ + LogGroupName: aws.String(d.Get("name").(string)), + }) + if err != nil { + return nil, errwrap.Wrapf("Error Getting CloudWatch Logs Tag List: %s", err) + } + if tagsOutput != nil { + output := make(map[string]interface{}, len(tagsOutput.Tags)) + + for i, v := range tagsOutput.Tags { + output[i] = *v + } + + return output, nil + } + + return make(map[string]interface{}), nil +} diff --git a/builtin/providers/aws/resource_aws_cloudwatch_log_group_test.go b/builtin/providers/aws/resource_aws_cloudwatch_log_group_test.go index e9aeca2c0..ca66dc25f 100644 --- a/builtin/providers/aws/resource_aws_cloudwatch_log_group_test.go +++ b/builtin/providers/aws/resource_aws_cloudwatch_log_group_test.go @@ -19,7 +19,7 @@ func TestAccAWSCloudWatchLogGroup_basic(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSCloudWatchLogGroupConfig(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.foobar", &lg), @@ -39,14 +39,14 @@ func TestAccAWSCloudWatchLogGroup_retentionPolicy(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSCloudWatchLogGroupConfig_withRetention(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.foobar", &lg), resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "retention_in_days", "365"), ), }, - resource.TestStep{ + { Config: testAccAWSCloudWatchLogGroupConfigModified_withRetention(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.foobar", &lg), @@ -66,7 +66,7 @@ func TestAccAWSCloudWatchLogGroup_multiple(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSCloudWatchLogGroupConfig_multiple(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.alpha", &lg), @@ -90,7 +90,7 @@ func TestAccAWSCloudWatchLogGroup_disappears(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccAWSCloudWatchLogGroupConfig(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.foobar", &lg), @@ -102,6 +102,37 @@ func TestAccAWSCloudWatchLogGroup_disappears(t *testing.T) { }) } +func TestAccAWSCloudWatchLogGroup_tagging(t *testing.T) { + var lg cloudwatchlogs.LogGroup + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudWatchLogGroupConfigWithTags(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.foobar", &lg), + resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "tags.%", "2"), + resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "tags.Environment", "Production"), + resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "tags.Foo", "Bar"), + ), + }, + { + Config: testAccAWSCloudWatchLogGroupConfigWithTagsUpdated(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.foobar", &lg), + resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "tags.%", "3"), + resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "tags.Environment", "Development"), + resource.TestCheckResourceAttr("aws_cloudwatch_log_group.foobar", "tags.Bar", "baz"), + ), + }, + }, + }) +} + func testAccCheckCloudWatchLogGroupDisappears(lg *cloudwatchlogs.LogGroup) resource.TestCheckFunc { return func(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).cloudwatchlogsconn @@ -166,6 +197,33 @@ resource "aws_cloudwatch_log_group" "foobar" { `, rInt) } +func testAccAWSCloudWatchLogGroupConfigWithTags(rInt int) string { + return fmt.Sprintf(` +resource "aws_cloudwatch_log_group" "foobar" { + name = "foo-bar-%d" + + tags { + Environment = "Production" + Foo = "Bar" + } +} +`, rInt) +} + +func testAccAWSCloudWatchLogGroupConfigWithTagsUpdated(rInt int) string { + return fmt.Sprintf(` +resource "aws_cloudwatch_log_group" "foobar" { + name = "foo-bar-%d" + + tags { + Environment = "Development" + Foo = "Bar" + Bar = "baz" + } +} +`, rInt) +} + func testAccAWSCloudWatchLogGroupConfig_withRetention(rInt int) string { return fmt.Sprintf(` resource "aws_cloudwatch_log_group" "foobar" { diff --git a/website/source/docs/providers/aws/r/cloudwatch_log_group.html.markdown b/website/source/docs/providers/aws/r/cloudwatch_log_group.html.markdown index 7293a0c17..e704e9e06 100644 --- a/website/source/docs/providers/aws/r/cloudwatch_log_group.html.markdown +++ b/website/source/docs/providers/aws/r/cloudwatch_log_group.html.markdown @@ -15,6 +15,11 @@ Provides a CloudWatch Log Group resource. ``` resource "aws_cloudwatch_log_group" "yada" { name = "Yada" + + tags { + Environment = "production" + Application = "serviceA" + } } ``` @@ -25,6 +30,7 @@ The following arguments are supported: * `name` - (Required) The name of the log group * `retention_in_days` - (Optional) Specifies the number of days you want to retain log events in the specified log group. +* `tags` - (Optional) A mapping of tags to assign to the resource. ## Attributes Reference