provider/aws: Fix wildcard support in Route53 record

Fixes a bug in Route53 and wildcard entries. Refs #501.
Also fixes:
- an issue in the library where we don't fully wait for the results, because the
  error code/condition changed with the migration to aws-sdk-go
- a limitation in the test, where we only consider the first record returned
This commit is contained in:
Clint Shryock 2015-03-17 14:57:45 -05:00
parent dc4abb48fa
commit 49e6c8fd87
2 changed files with 119 additions and 16 deletions

View File

@ -173,7 +173,8 @@ func resourceAwsRoute53RecordRead(d *schema.ResourceData, meta interface{}) erro
// Scan for a matching record
found := false
for _, record := range resp.ResourceRecordSets {
if FQDN(*record.Name) != FQDN(*lopts.StartRecordName) {
name := cleanRecordName(*record.Name)
if FQDN(name) != FQDN(*lopts.StartRecordName) {
continue
}
if strings.ToUpper(*record.Type) != strings.ToUpper(*lopts.StartRecordType) {
@ -232,15 +233,17 @@ func resourceAwsRoute53RecordDelete(d *schema.ResourceData, meta interface{}) er
Refresh: func() (interface{}, string, error) {
_, err := conn.ChangeResourceRecordSets(req)
if err != nil {
if strings.Contains(err.Error(), "PriorRequestNotComplete") {
// There is some pending operation, so just retry
// in a bit.
return 42, "rejected", nil
}
if r53err, ok := err.(aws.APIError); ok {
if r53err.Code == "PriorRequestNotComplete" {
// There is some pending operation, so just retry
// in a bit.
return 42, "rejected", nil
}
if strings.Contains(err.Error(), "InvalidChangeBatch") {
// This means that the record is already gone.
return 42, "accepted", nil
if r53err.Code == "InvalidChangeBatch" {
// This means that the record is already gone.
return 42, "accepted", nil
}
}
return 42, "failure", err
@ -282,3 +285,15 @@ func FQDN(name string) string {
return name + "."
}
}
// Route 53 stores the "*" wildcard indicator as ASCII 42 and returns the
// octal equivalent, "\\052". Here we look for that, and convert back to "*"
// as needed.
func cleanRecordName(name string) string {
str := name
if strings.HasPrefix(name, "\\052") {
str = strings.Replace(name, "\\052", "*", 1)
log.Printf("[DEBUG] Replacing octal \\052 for * in: %s", name)
}
return str
}

View File

@ -9,9 +9,26 @@ import (
"github.com/hashicorp/terraform/terraform"
"github.com/hashicorp/aws-sdk-go/aws"
awsr53 "github.com/hashicorp/aws-sdk-go/gen/route53"
route53 "github.com/hashicorp/aws-sdk-go/gen/route53"
)
func TestCleanRecordName(t *testing.T) {
cases := []struct {
Input, Output string
}{
{"www.nonexample.com", "www.nonexample.com"},
{"\\052.nonexample.com", "*.nonexample.com"},
{"nonexample.com", "nonexample.com"},
}
for _, tc := range cases {
actual := cleanRecordName(tc.Input)
if actual != tc.Output {
t.Fatalf("input: %s\noutput: %s", tc.Input, actual)
}
}
}
func TestAccRoute53Record(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
@ -44,6 +61,30 @@ func TestAccRoute53Record_generatesSuffix(t *testing.T) {
})
}
func TestAccRoute53Record_wildcard(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckRoute53RecordDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccRoute53WildCardRecordConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckRoute53RecordExists("aws_route53_record.wildcard"),
),
},
// Cause a change, which will trigger a refresh
resource.TestStep{
Config: testAccRoute53WildCardRecordConfigUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckRoute53RecordExists("aws_route53_record.wildcard"),
),
},
},
})
}
func testAccCheckRoute53RecordDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).r53conn
for _, rs := range s.RootModule().Resources {
@ -56,7 +97,7 @@ func testAccCheckRoute53RecordDestroy(s *terraform.State) error {
name := parts[1]
rType := parts[2]
lopts := &awsr53.ListResourceRecordSetsRequest{
lopts := &route53.ListResourceRecordSetsRequest{
HostedZoneID: aws.String(cleanZoneID(zone)),
StartRecordName: aws.String(name),
StartRecordType: aws.String(rType),
@ -94,7 +135,7 @@ func testAccCheckRoute53RecordExists(n string) resource.TestCheckFunc {
name := parts[1]
rType := parts[2]
lopts := &awsr53.ListResourceRecordSetsRequest{
lopts := &route53.ListResourceRecordSetsRequest{
HostedZoneID: aws.String(cleanZoneID(zone)),
StartRecordName: aws.String(name),
StartRecordType: aws.String(rType),
@ -107,11 +148,14 @@ func testAccCheckRoute53RecordExists(n string) resource.TestCheckFunc {
if len(resp.ResourceRecordSets) == 0 {
return fmt.Errorf("Record does not exist")
}
rec := resp.ResourceRecordSets[0]
if FQDN(*rec.Name) == FQDN(name) && *rec.Type == rType {
return nil
// rec := resp.ResourceRecordSets[0]
for _, rec := range resp.ResourceRecordSets {
recName := cleanRecordName(*rec.Name)
if FQDN(recName) == FQDN(name) && *rec.Type == rType {
return nil
}
}
return fmt.Errorf("Record does not exist: %#v", rec)
return fmt.Errorf("Record does not exist: %#v", rs.Primary.ID)
}
}
@ -142,3 +186,47 @@ resource "aws_route53_record" "default" {
records = ["127.0.0.1", "127.0.0.27"]
}
`
const testAccRoute53WildCardRecordConfig = `
resource "aws_route53_zone" "main" {
name = "notexample.com"
}
resource "aws_route53_record" "default" {
zone_id = "${aws_route53_zone.main.zone_id}"
name = "subdomain"
type = "A"
ttl = "30"
records = ["127.0.0.1", "127.0.0.27"]
}
resource "aws_route53_record" "wildcard" {
zone_id = "${aws_route53_zone.main.zone_id}"
name = "*.notexample.com"
type = "A"
ttl = "30"
records = ["127.0.0.1"]
}
`
const testAccRoute53WildCardRecordConfigUpdate = `
resource "aws_route53_zone" "main" {
name = "notexample.com"
}
resource "aws_route53_record" "default" {
zone_id = "${aws_route53_zone.main.zone_id}"
name = "subdomain"
type = "A"
ttl = "30"
records = ["127.0.0.1", "127.0.0.27"]
}
resource "aws_route53_record" "wildcard" {
zone_id = "${aws_route53_zone.main.zone_id}"
name = "*.notexample.com"
type = "A"
ttl = "60"
records = ["127.0.0.1"]
}
`