Merge pull request #3756 from hashicorp/pr-3708

providers/aws: Add `access_logs` to ELB resource [GH-3756]
This commit is contained in:
Clint 2015-11-11 09:54:43 -06:00
commit caa0baaf87
4 changed files with 210 additions and 1 deletions

View File

@ -107,6 +107,29 @@ func resourceAwsElb() *schema.Resource {
Default: 300,
},
"access_logs": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"interval": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 60,
},
"bucket": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"bucket_prefix": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
},
Set: resourceAwsElbAccessLogsHash,
},
"listener": &schema.Schema{
Type: schema.TypeSet,
Required: true,
@ -323,6 +346,11 @@ func resourceAwsElbRead(d *schema.ResourceData, meta interface{}) error {
d.Set("idle_timeout", lbAttrs.ConnectionSettings.IdleTimeout)
d.Set("connection_draining", lbAttrs.ConnectionDraining.Enabled)
d.Set("connection_draining_timeout", lbAttrs.ConnectionDraining.Timeout)
if lbAttrs.AccessLog != nil {
if err := d.Set("access_logs", flattenAccessLog(lbAttrs.AccessLog)); err != nil {
return err
}
}
resp, err := elbconn.DescribeTags(&elb.DescribeTagsInput{
LoadBalancerNames: []*string{lb.LoadBalancerName},
@ -423,7 +451,7 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error {
d.SetPartial("instances")
}
if d.HasChange("cross_zone_load_balancing") || d.HasChange("idle_timeout") {
if d.HasChange("cross_zone_load_balancing") || d.HasChange("idle_timeout") || d.HasChange("access_logs") {
attrs := elb.ModifyLoadBalancerAttributesInput{
LoadBalancerName: aws.String(d.Get("name").(string)),
LoadBalancerAttributes: &elb.LoadBalancerAttributes{
@ -436,6 +464,30 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error {
},
}
logs := d.Get("access_logs").(*schema.Set).List()
if len(logs) > 1 {
return fmt.Errorf("Only one access logs config per ELB is supported")
} else if len(logs) == 1 {
log := logs[0].(map[string]interface{})
accessLog := &elb.AccessLog{
Enabled: aws.Bool(true),
EmitInterval: aws.Int64(int64(log["interval"].(int))),
S3BucketName: aws.String(log["bucket"].(string)),
}
if log["bucket_prefix"] != "" {
accessLog.S3BucketPrefix = aws.String(log["bucket_prefix"].(string))
}
attrs.LoadBalancerAttributes.AccessLog = accessLog
} else if len(logs) == 0 {
// disable access logs
attrs.LoadBalancerAttributes.AccessLog = &elb.AccessLog{
Enabled: aws.Bool(false),
}
}
log.Printf("[DEBUG] ELB Modify Load Balancer Attributes Request: %#v", attrs)
_, err := elbconn.ModifyLoadBalancerAttributes(&attrs)
if err != nil {
return fmt.Errorf("Failure configuring ELB attributes: %s", err)
@ -568,6 +620,19 @@ func resourceAwsElbHealthCheckHash(v interface{}) int {
return hashcode.String(buf.String())
}
func resourceAwsElbAccessLogsHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%d-", m["interval"].(int)))
buf.WriteString(fmt.Sprintf("%s-",
strings.ToLower(m["bucket"].(string))))
if v, ok := m["bucket_prefix"]; ok {
buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(v.(string))))
}
return hashcode.String(buf.String())
}
func resourceAwsElbListenerHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})

View File

@ -75,6 +75,52 @@ func TestAccAWSELB_fullCharacterRange(t *testing.T) {
})
}
func TestAccAWSELB_AccessLogs(t *testing.T) {
var conf elb.LoadBalancerDescription
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSELBDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSELBAccessLogs,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSELBExists("aws_elb.foo", &conf),
resource.TestCheckResourceAttr(
"aws_elb.foo", "name", "FoobarTerraform-test123"),
),
},
resource.TestStep{
Config: testAccAWSELBAccessLogsOn,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSELBExists("aws_elb.foo", &conf),
resource.TestCheckResourceAttr(
"aws_elb.foo", "name", "FoobarTerraform-test123"),
resource.TestCheckResourceAttr(
"aws_elb.foo", "access_logs.#", "1"),
resource.TestCheckResourceAttr(
"aws_elb.foo", "access_logs.1713209538.bucket", "terraform-access-logs-bucket"),
resource.TestCheckResourceAttr(
"aws_elb.foo", "access_logs.1713209538.interval", "5"),
),
},
resource.TestStep{
Config: testAccAWSELBAccessLogs,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSELBExists("aws_elb.foo", &conf),
resource.TestCheckResourceAttr(
"aws_elb.foo", "name", "FoobarTerraform-test123"),
resource.TestCheckResourceAttr(
"aws_elb.foo", "access_logs.#", "0"),
),
},
},
})
}
func TestAccAWSELB_generatedName(t *testing.T) {
var conf elb.LoadBalancerDescription
generatedNameRegexp := regexp.MustCompile("^tf-lb-")
@ -659,6 +705,64 @@ resource "aws_elb" "foo" {
}
`
const testAccAWSELBAccessLogs = `
resource "aws_elb" "foo" {
name = "FoobarTerraform-test123"
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
listener {
instance_port = 8000
instance_protocol = "http"
lb_port = 80
lb_protocol = "http"
}
}
`
const testAccAWSELBAccessLogsOn = `
# an S3 bucket configured for Access logs
# The 797873946194 is the AWS ID for us-west-2, so this test
# must be ran in us-west-2
resource "aws_s3_bucket" "acceslogs_bucket" {
bucket = "terraform-access-logs-bucket"
acl = "private"
force_destroy = true
policy = <<EOF
{
"Id": "Policy1446577137248",
"Statement": [
{
"Action": "s3:PutObject",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::797873946194:root"
},
"Resource": "arn:aws:s3:::terraform-access-logs-bucket/*",
"Sid": "Stmt1446575236270"
}
],
"Version": "2012-10-17"
}
EOF
}
resource "aws_elb" "foo" {
name = "FoobarTerraform-test123"
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
listener {
instance_port = 8000
instance_protocol = "http"
lb_port = 80
lb_protocol = "http"
}
access_logs {
interval = 5
bucket = "${aws_s3_bucket.acceslogs_bucket.bucket}"
}
}
`
const testAccAWSELBGeneratedName = `
resource "aws_elb" "foo" {
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]

View File

@ -238,6 +238,33 @@ func expandElastiCacheParameters(configured []interface{}) ([]*elasticache.Param
return parameters, nil
}
// Flattens an access log into something that flatmap.Flatten() can handle
func flattenAccessLog(log *elb.AccessLog) []map[string]interface{} {
result := make([]map[string]interface{}, 0, 1)
if log != nil {
r := make(map[string]interface{})
// enabled is the only value we can rely on to not be nil
r["enabled"] = *log.Enabled
if log.S3BucketName != nil {
r["bucket"] = *log.S3BucketName
}
if log.S3BucketPrefix != nil {
r["bucket_prefix"] = *log.S3BucketPrefix
}
if log.EmitInterval != nil {
r["interval"] = *log.EmitInterval
}
result = append(result, r)
}
return result
}
// Flattens a health check into something that flatmap.Flatten()
// can handle
func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} {

View File

@ -18,6 +18,12 @@ resource "aws_elb" "bar" {
name = "foobar-terraform-elb"
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
access_logs {
bucket = "foo"
bucket_prefix = "bar"
interval = 60
}
listener {
instance_port = 8000
instance_protocol = "http"
@ -58,6 +64,7 @@ resource "aws_elb" "bar" {
The following arguments are supported:
* `name` - (Optional) The name of the ELB. By default generated by terraform.
* `access_logs` - (Optional) An Access Logs block. Access Logs documented below.
* `availability_zones` - (Required for an EC2-classic ELB) The AZ's to serve traffic in.
* `security_groups` - (Optional) A list of security group IDs to assign to the ELB.
* `subnets` - (Required for a VPC ELB) A list of subnet IDs to attach to the ELB.
@ -74,6 +81,12 @@ The following arguments are supported:
Exactly one of `availability_zones` or `subnets` must be specified: this
determines if the ELB exists in a VPC or in EC2-classic.
Access Logs support the following:
* `bucket` - (Required) The S3 bucket name to store the logs in.
* `bucket_prefix` - (Optional) The S3 bucket prefix. Logs are stored in the root if not configured.
* `interval` - (Optional) The publishing interval in minutes. Default: 60 minutes.
Listeners support the following:
* `instance_port` - (Required) The port on the instance to route to