Add `name_prefix` support to `aws_cloudwatch_log_group` (#13273)
This commit is contained in:
parent
1da9a06a64
commit
8d5fdeae57
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
@ -25,10 +26,18 @@ func resourceAwsCloudWatchLogGroup() *schema.Resource {
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
"name": {
|
"name": {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
ForceNew: true,
|
ForceNew: true,
|
||||||
|
ConflictsWith: []string{"name_prefix"},
|
||||||
ValidateFunc: validateLogGroupName,
|
ValidateFunc: validateLogGroupName,
|
||||||
},
|
},
|
||||||
|
"name_prefix": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
ValidateFunc: validateLogGroupNamePrefix,
|
||||||
|
},
|
||||||
|
|
||||||
"retention_in_days": {
|
"retention_in_days": {
|
||||||
Type: schema.TypeInt,
|
Type: schema.TypeInt,
|
||||||
|
@ -49,10 +58,19 @@ func resourceAwsCloudWatchLogGroup() *schema.Resource {
|
||||||
func resourceAwsCloudWatchLogGroupCreate(d *schema.ResourceData, meta interface{}) error {
|
func resourceAwsCloudWatchLogGroupCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
conn := meta.(*AWSClient).cloudwatchlogsconn
|
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{
|
_, err := conn.CreateLogGroup(&cloudwatchlogs.CreateLogGroupInput{
|
||||||
LogGroupName: aws.String(d.Get("name").(string)),
|
LogGroupName: aws.String(logGroupName),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "ResourceAlreadyExistsException" {
|
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"))
|
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")
|
log.Println("[INFO] CloudWatch Log Group created")
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package aws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
"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) {
|
func TestAccAWSCloudWatchLogGroup_retentionPolicy(t *testing.T) {
|
||||||
var lg cloudwatchlogs.LogGroup
|
var lg cloudwatchlogs.LogGroup
|
||||||
rInt := acctest.RandInt()
|
rInt := acctest.RandInt()
|
||||||
|
@ -256,3 +294,13 @@ resource "aws_cloudwatch_log_group" "charlie" {
|
||||||
}
|
}
|
||||||
`, rInt, rInt+1, rInt+2)
|
`, 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" {}
|
||||||
|
`
|
||||||
|
|
|
@ -483,6 +483,26 @@ func validateLogGroupName(v interface{}, k string) (ws []string, errors []error)
|
||||||
return
|
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) {
|
func validateS3BucketLifecycleTimestamp(v interface{}, k string) (ws []string, errors []error) {
|
||||||
value := v.(string)
|
value := v.(string)
|
||||||
_, err := time.Parse(time.RFC3339, fmt.Sprintf("%sT00:00:00Z", value))
|
_, err := time.Parse(time.RFC3339, fmt.Sprintf("%sT00:00:00Z", value))
|
||||||
|
|
|
@ -410,7 +410,7 @@ func TestValidateLogGroupName(t *testing.T) {
|
||||||
for _, v := range validNames {
|
for _, v := range validNames {
|
||||||
_, errors := validateLogGroupName(v, "name")
|
_, errors := validateLogGroupName(v, "name")
|
||||||
if len(errors) != 0 {
|
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 {
|
for _, v := range invalidNames {
|
||||||
_, errors := validateLogGroupName(v, "name")
|
_, errors := validateLogGroupName(v, "name")
|
||||||
if len(errors) == 0 {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,8 @@ resource "aws_cloudwatch_log_group" "yada" {
|
||||||
|
|
||||||
The following arguments are supported:
|
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
|
* `retention_in_days` - (Optional) Specifies the number of days
|
||||||
you want to retain log events in the specified log group.
|
you want to retain log events in the specified log group.
|
||||||
* `tags` - (Optional) A mapping of tags to assign to the resource.
|
* `tags` - (Optional) A mapping of tags to assign to the resource.
|
||||||
|
|
Loading…
Reference in New Issue