diff --git a/builtin/providers/aws/resource_aws_cloudwatch_log_group.go b/builtin/providers/aws/resource_aws_cloudwatch_log_group.go index 7bbd30621b66..b0cafe8d30de 100644 --- a/builtin/providers/aws/resource_aws_cloudwatch_log_group.go +++ b/builtin/providers/aws/resource_aws_cloudwatch_log_group.go @@ -4,6 +4,7 @@ import ( "fmt" "log" + "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/aws/aws-sdk-go/aws" @@ -24,10 +25,18 @@ func resourceAwsCloudWatchLogGroup() *schema.Resource { Schema: map[string]*schema.Schema{ "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, + ValidateFunc: validateLogGroupName, + }, + "name_prefix": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, - ValidateFunc: validateLogGroupName, + ValidateFunc: validateLogGroupNamePrefix, }, "retention_in_days": { @@ -49,10 +58,19 @@ func resourceAwsCloudWatchLogGroup() *schema.Resource { func resourceAwsCloudWatchLogGroupCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).cloudwatchlogsconn - log.Printf("[DEBUG] Creating CloudWatch Log Group: %s", d.Get("name").(string)) + var logGroupName string + if v, ok := d.GetOk("name"); ok { + logGroupName = v.(string) + } else if v, ok := d.GetOk("name_prefix"); ok { + logGroupName = resource.PrefixedUniqueId(v.(string)) + } else { + logGroupName = resource.UniqueId() + } + + log.Printf("[DEBUG] Creating CloudWatch Log Group: %s", logGroupName) _, err := conn.CreateLogGroup(&cloudwatchlogs.CreateLogGroupInput{ - LogGroupName: aws.String(d.Get("name").(string)), + LogGroupName: aws.String(logGroupName), }) if err != nil { if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "ResourceAlreadyExistsException" { @@ -61,7 +79,7 @@ func resourceAwsCloudWatchLogGroupCreate(d *schema.ResourceData, meta interface{ return fmt.Errorf("Creating CloudWatch Log Group failed: %s '%s'", err, d.Get("name")) } - d.SetId(d.Get("name").(string)) + d.SetId(logGroupName) log.Println("[INFO] CloudWatch Log Group created") 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 5c59f849985e..1e8a4ecb8a53 100644 --- a/builtin/providers/aws/resource_aws_cloudwatch_log_group_test.go +++ b/builtin/providers/aws/resource_aws_cloudwatch_log_group_test.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" @@ -30,6 +31,43 @@ func TestAccAWSCloudWatchLogGroup_basic(t *testing.T) { }) } +func TestAccAWSCloudWatchLogGroup_namePrefix(t *testing.T) { + var lg cloudwatchlogs.LogGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudWatchLogGroup_namePrefix, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.test", &lg), + resource.TestMatchResourceAttr("aws_cloudwatch_log_group.test", "name", regexp.MustCompile("^tf-test-")), + ), + }, + }, + }) +} + +func TestAccAWSCloudWatchLogGroup_generatedName(t *testing.T) { + var lg cloudwatchlogs.LogGroup + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCloudWatchLogGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCloudWatchLogGroup_generatedName, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudWatchLogGroupExists("aws_cloudwatch_log_group.test", &lg), + ), + }, + }, + }) +} + func TestAccAWSCloudWatchLogGroup_retentionPolicy(t *testing.T) { var lg cloudwatchlogs.LogGroup rInt := acctest.RandInt() @@ -256,3 +294,13 @@ resource "aws_cloudwatch_log_group" "charlie" { } `, rInt, rInt+1, rInt+2) } + +const testAccAWSCloudWatchLogGroup_namePrefix = ` +resource "aws_cloudwatch_log_group" "test" { + name_prefix = "tf-test-" +} +` + +const testAccAWSCloudWatchLogGroup_generatedName = ` +resource "aws_cloudwatch_log_group" "test" {} +` diff --git a/builtin/providers/aws/validators.go b/builtin/providers/aws/validators.go index 7ff0e6f389ba..a9e711f1e681 100644 --- a/builtin/providers/aws/validators.go +++ b/builtin/providers/aws/validators.go @@ -482,6 +482,26 @@ func validateLogGroupName(v interface{}, k string) (ws []string, errors []error) return } +func validateLogGroupNamePrefix(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if len(value) > 483 { + errors = append(errors, fmt.Errorf( + "%q cannot be longer than 483 characters: %q", k, value)) + } + + // http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogGroup.html + pattern := `^[\.\-_/#A-Za-z0-9]+$` + if !regexp.MustCompile(pattern).MatchString(value) { + errors = append(errors, fmt.Errorf( + "%q isn't a valid log group name (alphanumeric characters, underscores,"+ + " hyphens, slashes, hash signs and dots are allowed): %q", + k, value)) + } + + return +} + func validateS3BucketLifecycleTimestamp(v interface{}, k string) (ws []string, errors []error) { value := v.(string) _, err := time.Parse(time.RFC3339, fmt.Sprintf("%sT00:00:00Z", value)) diff --git a/builtin/providers/aws/validators_test.go b/builtin/providers/aws/validators_test.go index 7fe451a8785a..9792653ed8d8 100644 --- a/builtin/providers/aws/validators_test.go +++ b/builtin/providers/aws/validators_test.go @@ -410,7 +410,7 @@ func TestValidateLogGroupName(t *testing.T) { for _, v := range validNames { _, errors := validateLogGroupName(v, "name") if len(errors) != 0 { - t.Fatalf("%q should be a valid Log Metric Filter Transformation Name: %q", v, errors) + t.Fatalf("%q should be a valid Log Group name: %q", v, errors) } } @@ -427,7 +427,42 @@ func TestValidateLogGroupName(t *testing.T) { for _, v := range invalidNames { _, errors := validateLogGroupName(v, "name") if len(errors) == 0 { - t.Fatalf("%q should be an invalid Log Metric Filter Transformation Name", v) + t.Fatalf("%q should be an invalid Log Group name", v) + } + } +} + +func TestValidateLogGroupNamePrefix(t *testing.T) { + validNames := []string{ + "ValidLogGroupName", + "ValidLogGroup.Name", + "valid/Log-group", + "1234", + "YadaValid#0123", + "Also_valid-name", + strings.Repeat("W", 483), + } + for _, v := range validNames { + _, errors := validateLogGroupNamePrefix(v, "name_prefix") + if len(errors) != 0 { + t.Fatalf("%q should be a valid Log Group name prefix: %q", v, errors) + } + } + + invalidNames := []string{ + "Here is a name with: colon", + "and here is another * invalid name", + "also $ invalid", + "This . is also %% invalid@!)+(", + "*", + "", + // length > 483 + strings.Repeat("W", 484), + } + for _, v := range invalidNames { + _, errors := validateLogGroupNamePrefix(v, "name_prefix") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid Log Group name prefix", v) } } } 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 80b3a420cfbd..bfa9fc4ad94c 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 @@ -27,7 +27,8 @@ resource "aws_cloudwatch_log_group" "yada" { The following arguments are supported: -* `name` - (Required) The name of the log group +* `name` - (Optional, Forces new resource) The name of the log group. If omitted, Terraform will assign a random, unique name. +* `name_prefix` - (Optional, Forces new resource) Creates a unique name beginning with the specified prefix. Conflicts with `name`. * `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. @@ -45,4 +46,4 @@ Cloudwatch Log Groups can be imported using the `name`, e.g. ``` $ terraform import aws_cloudwatch_log_group.test_group yada -``` \ No newline at end of file +```