Merge pull request #2226 from Banno/add-aws-route53-health-checks-squashed

provider/aws: add aws_route53_health_check (rebase,squash+docs)
This commit is contained in:
Paul Hinze 2015-06-15 11:06:03 -05:00
commit 924278c33f
8 changed files with 445 additions and 10 deletions

View File

@ -125,8 +125,9 @@ func Provider() terraform.ResourceProvider {
"aws_route53_record": resourceAwsRoute53Record(),
"aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(),
"aws_route53_zone": resourceAwsRoute53Zone(),
"aws_route_table_association": resourceAwsRouteTableAssociation(),
"aws_route53_health_check": resourceAwsRoute53HealthCheck(),
"aws_route_table": resourceAwsRouteTable(),
"aws_route_table_association": resourceAwsRouteTableAssociation(),
"aws_s3_bucket": resourceAwsS3Bucket(),
"aws_security_group": resourceAwsSecurityGroup(),
"aws_security_group_rule": resourceAwsSecurityGroupRule(),

View File

@ -0,0 +1,211 @@
package aws
import (
"log"
"time"
"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/route53"
)
func resourceAwsRoute53HealthCheck() *schema.Resource {
return &schema.Resource{
Create: resourceAwsRoute53HealthCheckCreate,
Read: resourceAwsRoute53HealthCheckRead,
Update: resourceAwsRoute53HealthCheckUpdate,
Delete: resourceAwsRoute53HealthCheckDelete,
Schema: map[string]*schema.Schema{
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"failure_threshold": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"request_interval": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true, // todo this should be updateable but the awslabs route53 service doesnt have the ability
},
"ip_address": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"fqdn": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"port": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"resource_path": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"search_string": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"tags": tagsSchema(),
},
}
}
func resourceAwsRoute53HealthCheckUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).r53conn
updateHealthCheck := &route53.UpdateHealthCheckInput{
HealthCheckID: aws.String(d.Id()),
}
if d.HasChange("failure_threshold") {
updateHealthCheck.FailureThreshold = aws.Long(int64(d.Get("failure_threshold").(int)))
}
if d.HasChange("fqdn") {
updateHealthCheck.FullyQualifiedDomainName = aws.String(d.Get("fqdn").(string))
}
if d.HasChange("port") {
updateHealthCheck.Port = aws.Long(int64(d.Get("port").(int)))
}
if d.HasChange("resource_path") {
updateHealthCheck.ResourcePath = aws.String(d.Get("resource_path").(string))
}
if d.HasChange("search_string") {
updateHealthCheck.SearchString = aws.String(d.Get("search_string").(string))
}
_, err := conn.UpdateHealthCheck(updateHealthCheck)
if err != nil {
return err
}
if err := setTagsR53(conn, d, "healthcheck"); err != nil {
return err
}
return resourceAwsRoute53HealthCheckRead(d, meta)
}
func resourceAwsRoute53HealthCheckCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).r53conn
healthConfig := &route53.HealthCheckConfig{
Type: aws.String(d.Get("type").(string)),
FailureThreshold: aws.Long(int64(d.Get("failure_threshold").(int))),
RequestInterval: aws.Long(int64(d.Get("request_interval").(int))),
}
if v, ok := d.GetOk("fqdn"); ok {
healthConfig.FullyQualifiedDomainName = aws.String(v.(string))
}
if v, ok := d.GetOk("search_string"); ok {
healthConfig.SearchString = aws.String(v.(string))
}
if v, ok := d.GetOk("ip_address"); ok {
healthConfig.IPAddress = aws.String(v.(string))
}
if v, ok := d.GetOk("port"); ok {
healthConfig.Port = aws.Long(int64(v.(int)))
}
if v, ok := d.GetOk("resource_path"); ok {
healthConfig.ResourcePath = aws.String(v.(string))
}
input := &route53.CreateHealthCheckInput{
CallerReference: aws.String(time.Now().Format(time.RFC3339Nano)),
HealthCheckConfig: healthConfig,
}
resp, err := conn.CreateHealthCheck(input)
if err != nil {
return err
}
d.SetId(*resp.HealthCheck.ID)
if err := setTagsR53(conn, d, "healthcheck"); err != nil {
return err
}
return resourceAwsRoute53HealthCheckRead(d, meta)
}
func resourceAwsRoute53HealthCheckRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).r53conn
read, err := conn.GetHealthCheck(&route53.GetHealthCheckInput{HealthCheckID: aws.String(d.Id())})
if err != nil {
if r53err, ok := err.(awserr.Error); ok && r53err.Code() == "NoSuchHealthCheck" {
d.SetId("")
return nil
}
return err
}
if read == nil {
return nil
}
updated := read.HealthCheck.HealthCheckConfig
d.Set("type", updated.Type)
d.Set("failure_threshold", updated.FailureThreshold)
d.Set("request_interval", updated.RequestInterval)
d.Set("fqdn", updated.FullyQualifiedDomainName)
d.Set("search_string", updated.SearchString)
d.Set("ip_address", updated.IPAddress)
d.Set("port", updated.Port)
d.Set("resource_path", updated.ResourcePath)
// read the tags
req := &route53.ListTagsForResourceInput{
ResourceID: aws.String(d.Id()),
ResourceType: aws.String("healthcheck"),
}
resp, err := conn.ListTagsForResource(req)
if err != nil {
return err
}
var tags []*route53.Tag
if resp.ResourceTagSet != nil {
tags = resp.ResourceTagSet.Tags
}
if err := d.Set("tags", tagsToMapR53(tags)); err != nil {
return err
}
return nil
}
func resourceAwsRoute53HealthCheckDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).r53conn
log.Printf("[DEBUG] Deleteing Route53 health check: %s", d.Id())
_, err := conn.DeleteHealthCheck(&route53.DeleteHealthCheckInput{HealthCheckID: aws.String(d.Id())})
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,162 @@
package aws
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/aws/aws-sdk-go/service/route53"
)
func TestAccRoute53HealthCheck_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckRoute53HealthCheckDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccRoute53HealthCheckConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckRoute53HealthCheckExists("aws_route53_health_check.foo"),
),
},
resource.TestStep{
Config: testAccRoute53HealthCheckConfigUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckRoute53HealthCheckExists("aws_route53_health_check.foo"),
resource.TestCheckResourceAttr(
"aws_route53_health_check.foo", "failure_threshold", "5"),
),
},
},
})
}
func TestAccRoute53HealthCheck_IpConfig(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckRoute53HealthCheckDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccRoute53HealthCheckIpConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckRoute53HealthCheckExists("aws_route53_health_check.bar"),
),
},
},
})
}
func testAccCheckRoute53HealthCheckDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).r53conn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_route53_health_check" {
continue
}
lopts := &route53.ListHealthChecksInput{}
resp, err := conn.ListHealthChecks(lopts)
if err != nil {
return err
}
if len(resp.HealthChecks) == 0 {
return nil
}
for _, check := range resp.HealthChecks {
if *check.ID == rs.Primary.ID {
return fmt.Errorf("Record still exists: %#v", check)
}
}
}
return nil
}
func testAccCheckRoute53HealthCheckExists(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).r53conn
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
fmt.Print(rs.Primary.ID)
if rs.Primary.ID == "" {
return fmt.Errorf("No health check ID is set")
}
lopts := &route53.ListHealthChecksInput{}
resp, err := conn.ListHealthChecks(lopts)
if err != nil {
return err
}
if len(resp.HealthChecks) == 0 {
return fmt.Errorf("Health Check does not exist")
}
for _, check := range resp.HealthChecks {
if *check.ID == rs.Primary.ID {
return nil
}
}
return fmt.Errorf("Health Check does not exist")
}
}
func testUpdateHappened(n string) resource.TestCheckFunc {
return nil
}
const testAccRoute53HealthCheckConfig = `
resource "aws_route53_health_check" "foo" {
fqdn = "dev.notexample.com"
port = 80
type = "HTTP"
resource_path = "/"
failure_threshold = "2"
request_interval = "30"
tags = {
Name = "tf-test-health-check"
}
}
`
const testAccRoute53HealthCheckConfigUpdate = `
resource "aws_route53_health_check" "foo" {
fqdn = "dev.notexample.com"
port = 80
type = "HTTP"
resource_path = "/"
failure_threshold = "5"
request_interval = "30"
tags = {
Name = "tf-test-health-check"
}
}
`
const testAccRoute53HealthCheckIpConfig = `
resource "aws_route53_health_check" "bar" {
ip_address = "1.2.3.4"
port = 80
type = "HTTP"
resource_path = "/"
failure_threshold = "2"
request_interval = "30"
tags = {
Name = "tf-test-health-check"
}
}
`

View File

@ -89,6 +89,16 @@ func resourceAwsRoute53Record() *schema.Resource {
Set: resourceAwsRoute53AliasRecordHash,
},
"failover": &schema.Schema{ // PRIMARY | SECONDARY
Type: schema.TypeString,
Optional: true,
},
"health_check_id": &schema.Schema{ // ID of health check
Type: schema.TypeString,
Optional: true,
},
"records": &schema.Schema{
Type: schema.TypeSet,
ConflictsWith: []string{"alias"},
@ -232,10 +242,6 @@ func resourceAwsRoute53RecordRead(d *schema.ResourceData, meta interface{}) erro
StartRecordType: aws.String(d.Get("type").(string)),
}
if v, ok := d.GetOk("set_identifier"); ok {
lopts.StartRecordIdentifier = aws.String(v.(string))
}
resp, err := conn.ListResourceRecordSets(lopts)
if err != nil {
return err
@ -252,7 +258,7 @@ func resourceAwsRoute53RecordRead(d *schema.ResourceData, meta interface{}) erro
continue
}
if lopts.StartRecordIdentifier != nil && *record.SetIdentifier != *lopts.StartRecordIdentifier {
if record.SetIdentifier != nil && *record.SetIdentifier != d.Get("set_identifier") {
continue
}
@ -262,9 +268,12 @@ func resourceAwsRoute53RecordRead(d *schema.ResourceData, meta interface{}) erro
if err != nil {
return fmt.Errorf("[DEBUG] Error setting records for: %s, error: %#v", en, err)
}
d.Set("ttl", record.TTL)
d.Set("weight", record.Weight)
d.Set("set_identifier", record.SetIdentifier)
d.Set("failover", record.Failover)
d.Set("health_check_id", record.HealthCheckID)
break
}
@ -390,6 +399,14 @@ func resourceAwsRoute53RecordBuildSet(d *schema.ResourceData, zoneName string) (
}
}
if v, ok := d.GetOk("failover"); ok {
rec.Failover = aws.String(v.(string))
}
if v, ok := d.GetOk("health_check_id"); ok {
rec.HealthCheckID = aws.String(v.(string))
}
if v, ok := d.GetOk("weight"); ok {
rec.Weight = aws.Long(int64(v.(int)))
}

View File

@ -183,7 +183,7 @@ func resourceAwsRoute53ZoneRead(d *schema.ResourceData, meta interface{}) error
func resourceAwsRoute53ZoneUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).r53conn
if err := setTagsR53(conn, d); err != nil {
if err := setTagsR53(conn, d, "hostedzone"); err != nil {
return err
} else {
d.SetPartial("tags")

View File

@ -10,7 +10,7 @@ import (
// setTags is a helper to set the tags for a resource. It expects the
// tags field to be named "tags"
func setTagsR53(conn *route53.Route53, d *schema.ResourceData) error {
func setTagsR53(conn *route53.Route53, d *schema.ResourceData, resourceType string) error {
if d.HasChange("tags") {
oraw, nraw := d.GetChange("tags")
o := oraw.(map[string]interface{})
@ -25,7 +25,7 @@ func setTagsR53(conn *route53.Route53, d *schema.ResourceData) error {
log.Printf("[DEBUG] Changing tags: \n\tadding: %#v\n\tremoving:%#v", create, remove)
req := &route53.ChangeTagsForResourceInput{
ResourceID: aws.String(d.Id()),
ResourceType: aws.String("hostedzone"),
ResourceType: aws.String(resourceType),
}
if len(create) > 0 {

View File

@ -0,0 +1,42 @@
---
layout: "aws"
page_title: "AWS: aws_route53_health_check"
sidebar_current: "docs-aws-resource-health-check"
description: |-
Provides a Route53 health check.
---
# aws\_route53\_health\_check
Provides a Route53 health check.
## Example Usage
```
resource "aws_route53_health_check" "foo" {
fqdn = "foobar.terraform.com"
port = 80
type = "HTTP"
resource_path = "/"
failure_threshold = "5"
request_interval = "30"
tags = {
Name = "tf-test-health-check"
}
}
```
## Argument Reference
The following arguments are supported:
* `fqdn` - (Optional) The fully qualified domain name of the endpoint to be checked.
* `ip_address` - (Optional) The IP address of the endpoint to be checked.
* `failure_threshold` - (Required) The number of consecutive health checks that an endpoint must pass or fai.
* `request_interval` - (Required) The number of seconds between the time that Amazon Route 53 gets a response from your endpoint and the time that it sends the next health-check request.
* `resource_path` - (Optional) The path that you want Amazon Route 53 to request when performing health checks.
* `search_string` - (Optional) String searched in respoonse body for check to considered healthy.
* `tags` - (Optional) A mapping of tags to assign to the health check.
Exactly one of `fqdn` or `ip_address` must be specified.

View File

@ -93,7 +93,9 @@ The following arguments are supported:
* `records` - (Required for non-alias records) A string list of records.
* `weight` - (Optional) The weight of weighted record (0-255).
* `set_identifier` - (Optional) Unique identifier to differentiate weighted
record from one another. Required for each weighted record.
record from one another. Required for each weighted record.
* `failover` - (Optional) The routing behavior when associated health check fails. Must be PRIMARY or SECONDARY.
* `health_check_id` - (Optional) The health check the record should be associated with.
* `alias` - (Optional) An alias block. Conflicts with `ttl` & `records`.
Alias record documented below.