terraform/builtin/providers/aws/resource_aws_route53_record.go

878 lines
25 KiB
Go
Raw Normal View History

package aws
import (
"bytes"
2016-01-29 18:56:19 +01:00
"errors"
"fmt"
"log"
"strings"
"time"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/resource"
"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"
)
2016-01-29 18:56:19 +01:00
var r53NoRecordsFound = errors.New("No matching Hosted Zone found")
var r53NoHostedZoneFound = errors.New("No matching records found")
func resourceAwsRoute53Record() *schema.Resource {
return &schema.Resource{
Create: resourceAwsRoute53RecordCreate,
Read: resourceAwsRoute53RecordRead,
Update: resourceAwsRoute53RecordUpdate,
Delete: resourceAwsRoute53RecordDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
SchemaVersion: 2,
MigrateState: resourceAwsRoute53RecordMigrateState,
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
StateFunc: func(v interface{}) string {
value := strings.TrimSuffix(v.(string), ".")
return strings.ToLower(value)
},
},
"fqdn": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateRoute53RecordType,
},
"zone_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if value == "" {
es = append(es, fmt.Errorf("Cannot have empty zone_id"))
}
return
},
},
"ttl": {
Type: schema.TypeInt,
Optional: true,
ConflictsWith: []string{"alias"},
},
"weight": {
Type: schema.TypeInt,
Optional: true,
Removed: "Now implemented as weighted_routing_policy; Please see https://www.terraform.io/docs/providers/aws/r/route53_record.html",
},
"set_identifier": {
Type: schema.TypeString,
Optional: true,
},
"alias": {
Type: schema.TypeSet,
Optional: true,
ConflictsWith: []string{"records", "ttl"},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"zone_id": {
Type: schema.TypeString,
Required: true,
},
"name": {
provider/aws: Fix aws_route53_record alias perpetual diff (#9704) Fixes #9628 Fixes #9298 When a route53_record alias is updated in the console, AWS prepends `dualstack.` to the name. This is there incase IPV6 is wanted. It is exactly the same without it as it is with it In order to stop perpetual diffs, I introduced a normalizeFunc that will that tke alias name and strip known issues: * dualstack * trailing dot This normalize fun will continue to grow I'm sure ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSRoute53Record_' ✹ ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/10/29 00:28:12 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSRoute53Record_ -timeout 120m === RUN TestAccAWSRoute53Record_basic --- PASS: TestAccAWSRoute53Record_basic (124.64s) === RUN TestAccAWSRoute53Record_basic_fqdn --- PASS: TestAccAWSRoute53Record_basic_fqdn (132.07s) === RUN TestAccAWSRoute53Record_txtSupport --- PASS: TestAccAWSRoute53Record_txtSupport (134.07s) === RUN TestAccAWSRoute53Record_spfSupport --- PASS: TestAccAWSRoute53Record_spfSupport (113.36s) === RUN TestAccAWSRoute53Record_generatesSuffix --- PASS: TestAccAWSRoute53Record_generatesSuffix (112.62s) === RUN TestAccAWSRoute53Record_wildcard --- PASS: TestAccAWSRoute53Record_wildcard (162.84s) === RUN TestAccAWSRoute53Record_failover --- PASS: TestAccAWSRoute53Record_failover (126.18s) === RUN TestAccAWSRoute53Record_weighted_basic --- PASS: TestAccAWSRoute53Record_weighted_basic (121.10s) === RUN TestAccAWSRoute53Record_alias --- PASS: TestAccAWSRoute53Record_alias (118.14s) === RUN TestAccAWSRoute53Record_s3_alias --- PASS: TestAccAWSRoute53Record_s3_alias (155.07s) === RUN TestAccAWSRoute53Record_weighted_alias --- PASS: TestAccAWSRoute53Record_weighted_alias (235.41s) === RUN TestAccAWSRoute53Record_geolocation_basic ^[[C--- PASS: TestAccAWSRoute53Record_geolocation_basic (125.32s) === RUN TestAccAWSRoute53Record_latency_basic --- PASS: TestAccAWSRoute53Record_latency_basic (122.23s) === RUN TestAccAWSRoute53Record_TypeChange --- PASS: TestAccAWSRoute53Record_TypeChange (231.98s) === RUN TestAccAWSRoute53Record_empty --- PASS: TestAccAWSRoute53Record_empty (116.48s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 2131.526s ``` Before this fix, I was getting the following by recreating the code in ``` ~ aws_route53_record.alias alias.1563903989.evaluate_target_health: "true" => "false" alias.1563903989.name: "9828-recreation-106795730.us-west-2.elb.amazonaws.com." => "" alias.1563903989.zone_id: "Z1H1FL5HABSF5" => "" alias.318754017.evaluate_target_health: "" => "true" alias.318754017.name: "" => "9828-recreation-106795730.us-west-2.elb.amazonaws.com" alias.318754017.zone_id: "" => "Z1H1FL5HABSF5" Plan: 0 to add, 1 to change, 0 to destroy. ``` After this fix: ``` No changes. Infrastructure is up-to-date. This means that Terraform could not detect any differences between your configuration and the real physical resources that exist. As a result, Terraform doesn't need to do anything.
2016-10-31 20:18:00 +01:00
Type: schema.TypeString,
Required: true,
StateFunc: normalizeAwsAliasName,
},
"evaluate_target_health": {
Type: schema.TypeBool,
Required: true,
},
},
},
Set: resourceAwsRoute53AliasRecordHash,
},
"failover": { // PRIMARY | SECONDARY
Type: schema.TypeString,
Optional: true,
Removed: "Now implemented as failover_routing_policy; see docs",
},
"failover_routing_policy": {
Type: schema.TypeList,
Optional: true,
ConflictsWith: []string{
"geolocation_routing_policy",
"latency_routing_policy",
"weighted_routing_policy",
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if value != "PRIMARY" && value != "SECONDARY" {
es = append(es, fmt.Errorf("Failover policy type must be PRIMARY or SECONDARY"))
}
return
},
},
},
},
},
"latency_routing_policy": {
Type: schema.TypeList,
Optional: true,
ConflictsWith: []string{
"failover_routing_policy",
"geolocation_routing_policy",
"weighted_routing_policy",
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"region": {
Type: schema.TypeString,
Required: true,
},
},
},
},
"geolocation_routing_policy": { // AWS Geolocation
Type: schema.TypeList,
Optional: true,
ConflictsWith: []string{
"failover_routing_policy",
"latency_routing_policy",
"weighted_routing_policy",
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"continent": {
Type: schema.TypeString,
Optional: true,
},
"country": {
Type: schema.TypeString,
Optional: true,
},
"subdivision": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
"weighted_routing_policy": {
Type: schema.TypeList,
Optional: true,
ConflictsWith: []string{
"failover_routing_policy",
"geolocation_routing_policy",
"latency_routing_policy",
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"weight": {
Type: schema.TypeInt,
Required: true,
},
},
},
},
"health_check_id": { // ID of health check
Type: schema.TypeString,
Optional: true,
},
"records": {
Type: schema.TypeSet,
ConflictsWith: []string{"alias"},
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Set: schema.HashString,
},
},
}
}
func resourceAwsRoute53RecordUpdate(d *schema.ResourceData, meta interface{}) error {
// Route 53 supports CREATE, DELETE, and UPSERT actions. We use UPSERT, and
2015-04-06 17:45:02 +02:00
// AWS dynamically determines if a record should be created or updated.
// Amazon Route 53 can update an existing resource record set only when all
// of the following values match: Name, Type and SetIdentifier
// See http://docs.aws.amazon.com/Route53/latest/APIReference/API_ChangeResourceRecordSets.html
if !d.HasChange("type") && !d.HasChange("set_identifier") {
// If neither type nor set_identifier changed we use UPSERT,
// for resouce update here we simply fall through to
// our resource create function.
return resourceAwsRoute53RecordCreate(d, meta)
}
// Otherwise we delete the existing record and create a new record within
// a transactional change
conn := meta.(*AWSClient).r53conn
zone := cleanZoneID(d.Get("zone_id").(string))
var err error
zoneRecord, err := conn.GetHostedZone(&route53.GetHostedZoneInput{Id: aws.String(zone)})
if err != nil {
return err
}
if zoneRecord.HostedZone == nil {
return fmt.Errorf("[WARN] No Route53 Zone found for id (%s)", zone)
}
// Build the to be deleted record
en := expandRecordName(d.Get("name").(string), *zoneRecord.HostedZone.Name)
typeo, _ := d.GetChange("type")
oldRec := &route53.ResourceRecordSet{
Name: aws.String(en),
Type: aws.String(typeo.(string)),
}
if v, _ := d.GetChange("ttl"); v.(int) != 0 {
oldRec.TTL = aws.Int64(int64(v.(int)))
}
// Resource records
if v, _ := d.GetChange("records"); v != nil {
recs := v.(*schema.Set).List()
if len(recs) > 0 {
oldRec.ResourceRecords = expandResourceRecords(recs, typeo.(string))
}
}
// Alias record
if v, _ := d.GetChange("alias"); v != nil {
aliases := v.(*schema.Set).List()
if len(aliases) == 1 {
alias := aliases[0].(map[string]interface{})
oldRec.AliasTarget = &route53.AliasTarget{
DNSName: aws.String(alias["name"].(string)),
EvaluateTargetHealth: aws.Bool(alias["evaluate_target_health"].(bool)),
HostedZoneId: aws.String(alias["zone_id"].(string)),
}
}
}
if v, _ := d.GetChange("set_identifier"); v.(string) != "" {
oldRec.SetIdentifier = aws.String(v.(string))
}
// Build the to be created record
rec, err := resourceAwsRoute53RecordBuildSet(d, *zoneRecord.HostedZone.Name)
if err != nil {
return err
}
// Delete the old and create the new records in a single batch. We abuse
// StateChangeConf for this to retry for us since Route53 sometimes returns
// errors about another operation happening at the same time.
changeBatch := &route53.ChangeBatch{
Comment: aws.String("Managed by Terraform"),
Changes: []*route53.Change{
{
Action: aws.String("DELETE"),
ResourceRecordSet: oldRec,
},
{
Action: aws.String("CREATE"),
ResourceRecordSet: rec,
},
},
}
req := &route53.ChangeResourceRecordSetsInput{
HostedZoneId: aws.String(cleanZoneID(*zoneRecord.HostedZone.Id)),
ChangeBatch: changeBatch,
}
log.Printf("[DEBUG] Updating resource records for zone: %s, name: %s\n\n%s",
zone, *rec.Name, req)
respRaw, err := changeRoute53RecordSet(conn, req)
if err != nil {
return errwrap.Wrapf("[ERR]: Error building changeset: {{err}}", err)
}
changeInfo := respRaw.(*route53.ChangeResourceRecordSetsOutput).ChangeInfo
// Generate an ID
vars := []string{
zone,
strings.ToLower(d.Get("name").(string)),
d.Get("type").(string),
}
if v, ok := d.GetOk("set_identifier"); ok {
vars = append(vars, v.(string))
}
d.SetId(strings.Join(vars, "_"))
err = waitForRoute53RecordSetToSync(conn, cleanChangeID(*changeInfo.Id))
if err != nil {
return err
}
return resourceAwsRoute53RecordRead(d, meta)
}
func resourceAwsRoute53RecordCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).r53conn
zone := cleanZoneID(d.Get("zone_id").(string))
var err error
zoneRecord, err := conn.GetHostedZone(&route53.GetHostedZoneInput{Id: aws.String(zone)})
if err != nil {
return err
}
if zoneRecord.HostedZone == nil {
return fmt.Errorf("[WARN] No Route53 Zone found for id (%s)", zone)
}
// Build the record
rec, err := resourceAwsRoute53RecordBuildSet(d, *zoneRecord.HostedZone.Name)
if err != nil {
return err
}
// Create the new records. We abuse StateChangeConf for this to
// retry for us since Route53 sometimes returns errors about another
// operation happening at the same time.
changeBatch := &route53.ChangeBatch{
Comment: aws.String("Managed by Terraform"),
2015-04-16 20:42:16 +02:00
Changes: []*route53.Change{
{
Action: aws.String("UPSERT"),
ResourceRecordSet: rec,
},
},
}
2015-04-16 20:42:16 +02:00
req := &route53.ChangeResourceRecordSetsInput{
HostedZoneId: aws.String(cleanZoneID(*zoneRecord.HostedZone.Id)),
ChangeBatch: changeBatch,
}
log.Printf("[DEBUG] Creating resource records for zone: %s, name: %s\n\n%s",
zone, *rec.Name, req)
respRaw, err := changeRoute53RecordSet(conn, req)
if err != nil {
return errwrap.Wrapf("[ERR]: Error building changeset: {{err}}", err)
}
changeInfo := respRaw.(*route53.ChangeResourceRecordSetsOutput).ChangeInfo
// Generate an ID
vars := []string{
zone,
strings.ToLower(d.Get("name").(string)),
d.Get("type").(string),
}
if v, ok := d.GetOk("set_identifier"); ok {
vars = append(vars, v.(string))
}
d.SetId(strings.Join(vars, "_"))
err = waitForRoute53RecordSetToSync(conn, cleanChangeID(*changeInfo.Id))
if err != nil {
return err
}
return resourceAwsRoute53RecordRead(d, meta)
}
func changeRoute53RecordSet(conn *route53.Route53, input *route53.ChangeResourceRecordSetsInput) (interface{}, error) {
wait := resource.StateChangeConf{
Pending: []string{"rejected"},
Target: []string{"accepted"},
Timeout: 5 * time.Minute,
MinTimeout: 1 * time.Second,
Refresh: func() (interface{}, string, error) {
resp, err := conn.ChangeResourceRecordSets(input)
if err != nil {
if r53err, ok := err.(awserr.Error); ok {
if r53err.Code() == "PriorRequestNotComplete" {
// There is some pending operation, so just retry
// in a bit.
return nil, "rejected", nil
}
}
return nil, "failure", err
}
return resp, "accepted", nil
},
}
return wait.WaitForState()
}
func waitForRoute53RecordSetToSync(conn *route53.Route53, requestId string) error {
wait := resource.StateChangeConf{
Delay: 30 * time.Second,
Pending: []string{"PENDING"},
Target: []string{"INSYNC"},
Timeout: 30 * time.Minute,
MinTimeout: 5 * time.Second,
Refresh: func() (result interface{}, state string, err error) {
2015-04-16 20:42:16 +02:00
changeRequest := &route53.GetChangeInput{
Id: aws.String(requestId),
}
return resourceAwsGoRoute53Wait(conn, changeRequest)
},
}
_, err := wait.WaitForState()
return err
}
func resourceAwsRoute53RecordRead(d *schema.ResourceData, meta interface{}) error {
2016-04-21 22:46:37 +02:00
// If we don't have a zone ID we're doing an import. Parse it from the ID.
if _, ok := d.GetOk("zone_id"); !ok {
parts := strings.Split(d.Id(), "_")
//we check that we have parsed the id into the correct number of segments
//we need at least 3 segments!
if len(parts) == 1 || len(parts) < 3 {
return fmt.Errorf("Error Importing aws_route_53 record. Please make sure the record ID is in the form ZONEID_RECORDNAME_TYPE (i.e. Z4KAPRWWNC7JR_dev_A")
}
2016-04-21 22:46:37 +02:00
d.Set("zone_id", parts[0])
d.Set("name", parts[1])
d.Set("type", parts[2])
if len(parts) > 3 {
d.Set("set_identifier", parts[3])
}
}
record, err := findRecord(d, meta)
if err != nil {
2016-01-29 18:56:19 +01:00
switch err {
case r53NoHostedZoneFound, r53NoRecordsFound:
log.Printf("[DEBUG] %s for: %s, removing from state file", err, d.Id())
d.SetId("")
return nil
default:
return err
}
}
err = d.Set("records", flattenResourceRecords(record.ResourceRecords, *record.Type))
if err != nil {
return fmt.Errorf("[DEBUG] Error setting records for: %s, error: %#v", d.Id(), err)
}
2016-04-21 22:46:37 +02:00
if alias := record.AliasTarget; alias != nil {
provider/aws: Fix aws_route53_record alias perpetual diff (#9704) Fixes #9628 Fixes #9298 When a route53_record alias is updated in the console, AWS prepends `dualstack.` to the name. This is there incase IPV6 is wanted. It is exactly the same without it as it is with it In order to stop perpetual diffs, I introduced a normalizeFunc that will that tke alias name and strip known issues: * dualstack * trailing dot This normalize fun will continue to grow I'm sure ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSRoute53Record_' ✹ ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/10/29 00:28:12 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSRoute53Record_ -timeout 120m === RUN TestAccAWSRoute53Record_basic --- PASS: TestAccAWSRoute53Record_basic (124.64s) === RUN TestAccAWSRoute53Record_basic_fqdn --- PASS: TestAccAWSRoute53Record_basic_fqdn (132.07s) === RUN TestAccAWSRoute53Record_txtSupport --- PASS: TestAccAWSRoute53Record_txtSupport (134.07s) === RUN TestAccAWSRoute53Record_spfSupport --- PASS: TestAccAWSRoute53Record_spfSupport (113.36s) === RUN TestAccAWSRoute53Record_generatesSuffix --- PASS: TestAccAWSRoute53Record_generatesSuffix (112.62s) === RUN TestAccAWSRoute53Record_wildcard --- PASS: TestAccAWSRoute53Record_wildcard (162.84s) === RUN TestAccAWSRoute53Record_failover --- PASS: TestAccAWSRoute53Record_failover (126.18s) === RUN TestAccAWSRoute53Record_weighted_basic --- PASS: TestAccAWSRoute53Record_weighted_basic (121.10s) === RUN TestAccAWSRoute53Record_alias --- PASS: TestAccAWSRoute53Record_alias (118.14s) === RUN TestAccAWSRoute53Record_s3_alias --- PASS: TestAccAWSRoute53Record_s3_alias (155.07s) === RUN TestAccAWSRoute53Record_weighted_alias --- PASS: TestAccAWSRoute53Record_weighted_alias (235.41s) === RUN TestAccAWSRoute53Record_geolocation_basic ^[[C--- PASS: TestAccAWSRoute53Record_geolocation_basic (125.32s) === RUN TestAccAWSRoute53Record_latency_basic --- PASS: TestAccAWSRoute53Record_latency_basic (122.23s) === RUN TestAccAWSRoute53Record_TypeChange --- PASS: TestAccAWSRoute53Record_TypeChange (231.98s) === RUN TestAccAWSRoute53Record_empty --- PASS: TestAccAWSRoute53Record_empty (116.48s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 2131.526s ``` Before this fix, I was getting the following by recreating the code in ``` ~ aws_route53_record.alias alias.1563903989.evaluate_target_health: "true" => "false" alias.1563903989.name: "9828-recreation-106795730.us-west-2.elb.amazonaws.com." => "" alias.1563903989.zone_id: "Z1H1FL5HABSF5" => "" alias.318754017.evaluate_target_health: "" => "true" alias.318754017.name: "" => "9828-recreation-106795730.us-west-2.elb.amazonaws.com" alias.318754017.zone_id: "" => "Z1H1FL5HABSF5" Plan: 0 to add, 1 to change, 0 to destroy. ``` After this fix: ``` No changes. Infrastructure is up-to-date. This means that Terraform could not detect any differences between your configuration and the real physical resources that exist. As a result, Terraform doesn't need to do anything.
2016-10-31 20:18:00 +01:00
name := normalizeAwsAliasName(*alias.DNSName)
provider/aws: aws_route53_record alias refresh manually updated record Fixes #9108 When an aws_route53_record alias is created with terraform and then modified via cli or console, terraform wasn't picking up the changes. I had the following config: ``` resource "aws_route53_record" "alias" { zone_id = "${aws_route53_zone.main.zone_id}" name = "www" type = "A" alias { zone_id = "${aws_elb.main.zone_id}" name = "${aws_elb.main.dns_name}" evaluate_target_health = true } } ``` I changed the evaluate_health_target on the AWS console and terraform plan showed me this: ``` % terraform plan Refreshing Terraform state in-memory prior to plan... The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage. aws_route53_zone.main: Refreshing state... (ID: Z32Z9B1UPAIP6X) aws_elb.main: Refreshing state... (ID: foobar-terraform-elb-1111) aws_route53_record.alias: Refreshing state... (ID: Z32Z9B1UPAIP6X_www_A) No changes. Infrastructure is up-to-date. This means that Terraform could not detect any differences between your configuration and the real physical resources that exist. As a result, Terraform doesn't need to do anything. ``` When rebuilding the provider with the changes in the PR, a terraform plan then looks as follows: ``` % terraform plan [WARN] /Users/stacko/Code/go/bin/terraform-provider-aws overrides an internal plugin for aws-provider. If you did not expect to see this message you will need to remove the old plugin. See https://www.terraform.io/docs/internals/internal-plugins.html [WARN] /Users/stacko/Code/go/bin/terraform-provider-azurerm overrides an internal plugin for azurerm-provider. If you did not expect to see this message you will need to remove the old plugin. See https://www.terraform.io/docs/internals/internal-plugins.html Refreshing Terraform state in-memory prior to plan... The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage. aws_route53_zone.main: Refreshing state... (ID: Z32Z9B1UPAIP6X) aws_elb.main: Refreshing state... (ID: foobar-terraform-elb-1111) aws_route53_record.alias: Refreshing state... (ID: Z32Z9B1UPAIP6X_www_A) The Terraform execution plan has been generated and is shown below. Resources are shown in alphabetical order for quick scanning. Green resources will be created (or destroyed and then created if an existing resource exists), yellow resources are being changed in-place, and red resources will be destroyed. Cyan entries are data sources to be read. Note: You didn't specify an "-out" parameter to save this plan, so when "apply" is called, Terraform can't guarantee this is what will execute. ~ aws_route53_record.alias alias.1050468691.evaluate_target_health: "" => "true" alias.1050468691.name: "" => "foobar-terraform-elb-1111-522021794.us-west-2.elb.amazonaws.com" alias.1050468691.zone_id: "" => "Z1H1FL5HABSF5" alias.2906616344.evaluate_target_health: "false" => "false" alias.2906616344.name: "foobar-terraform-elb-1111-522021794.us-west-2.elb.amazonaws.com." => "" alias.2906616344.zone_id: "Z1H1FL5HABSF5" => "" Plan: 0 to add, 1 to change, 0 to destroy. ``` the apply then changed the target back to true ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSRoute53Record_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/09/29 18:17:23 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSRoute53Record_ -timeout 120m === RUN TestAccAWSRoute53Record_basic --- PASS: TestAccAWSRoute53Record_basic (120.63s) === RUN TestAccAWSRoute53Record_basic_fqdn --- PASS: TestAccAWSRoute53Record_basic_fqdn (131.81s) === RUN TestAccAWSRoute53Record_txtSupport --- PASS: TestAccAWSRoute53Record_txtSupport (128.40s) === RUN TestAccAWSRoute53Record_spfSupport --- PASS: TestAccAWSRoute53Record_spfSupport (120.06s) === RUN TestAccAWSRoute53Record_generatesSuffix --- PASS: TestAccAWSRoute53Record_generatesSuffix (114.02s) === RUN TestAccAWSRoute53Record_wildcard --- PASS: TestAccAWSRoute53Record_wildcard (165.54s) === RUN TestAccAWSRoute53Record_failover --- PASS: TestAccAWSRoute53Record_failover (118.10s) === RUN TestAccAWSRoute53Record_weighted_basic --- PASS: TestAccAWSRoute53Record_weighted_basic (128.10s) === RUN TestAccAWSRoute53Record_alias --- PASS: TestAccAWSRoute53Record_alias (132.62s) === RUN TestAccAWSRoute53Record_s3_alias --- PASS: TestAccAWSRoute53Record_s3_alias (132.12s) === RUN TestAccAWSRoute53Record_weighted_alias --- PASS: TestAccAWSRoute53Record_weighted_alias (237.92s) === RUN TestAccAWSRoute53Record_geolocation_basic --- PASS: TestAccAWSRoute53Record_geolocation_basic (121.95s) === RUN TestAccAWSRoute53Record_latency_basic --- PASS: TestAccAWSRoute53Record_latency_basic (123.40s) === RUN TestAccAWSRoute53Record_TypeChange --- PASS: TestAccAWSRoute53Record_TypeChange (198.24s) === RUN TestAccAWSRoute53Record_empty --- PASS: TestAccAWSRoute53Record_empty (119.68s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws2092.597s ```
2016-09-29 15:17:09 +02:00
d.Set("alias", []interface{}{
map[string]interface{}{
"zone_id": *alias.HostedZoneId,
"name": name,
"evaluate_target_health": *alias.EvaluateTargetHealth,
},
})
2016-04-21 22:46:37 +02:00
}
d.Set("ttl", record.TTL)
if record.Failover != nil {
v := []map[string]interface{}{{
"type": aws.StringValue(record.Failover),
}}
if err := d.Set("failover_routing_policy", v); err != nil {
return fmt.Errorf("[DEBUG] Error setting failover records for: %s, error: %#v", d.Id(), err)
}
}
if record.GeoLocation != nil {
v := []map[string]interface{}{{
"continent": aws.StringValue(record.GeoLocation.ContinentCode),
"country": aws.StringValue(record.GeoLocation.CountryCode),
"subdivision": aws.StringValue(record.GeoLocation.SubdivisionCode),
}}
if err := d.Set("geolocation_routing_policy", v); err != nil {
return fmt.Errorf("[DEBUG] Error setting gelocation records for: %s, error: %#v", d.Id(), err)
}
}
if record.Region != nil {
v := []map[string]interface{}{{
"region": aws.StringValue(record.Region),
}}
if err := d.Set("latency_routing_policy", v); err != nil {
return fmt.Errorf("[DEBUG] Error setting latency records for: %s, error: %#v", d.Id(), err)
}
}
if record.Weight != nil {
v := []map[string]interface{}{{
"weight": aws.Int64Value((record.Weight)),
}}
if err := d.Set("weighted_routing_policy", v); err != nil {
return fmt.Errorf("[DEBUG] Error setting weighted records for: %s, error: %#v", d.Id(), err)
}
}
d.Set("set_identifier", record.SetIdentifier)
d.Set("health_check_id", record.HealthCheckId)
return nil
}
2016-01-29 18:56:19 +01:00
// findRecord takes a ResourceData struct for aws_resource_route53_record. It
// uses the referenced zone_id to query Route53 and find information on it's
// records.
//
// If records are found, it returns the matching
// route53.ResourceRecordSet and nil for the error.
//
// If no hosted zone is found, it returns a nil recordset and r53NoHostedZoneFound
// error.
//
// If no matching recordset is found, it returns nil and a r53NoRecordsFound
// error
//
// If there are other errors, it returns nil a nil recordset and passes on the
// error.
func findRecord(d *schema.ResourceData, meta interface{}) (*route53.ResourceRecordSet, error) {
conn := meta.(*AWSClient).r53conn
// Scan for a
zone := cleanZoneID(d.Get("zone_id").(string))
// get expanded name
zoneRecord, err := conn.GetHostedZone(&route53.GetHostedZoneInput{Id: aws.String(zone)})
if err != nil {
if r53err, ok := err.(awserr.Error); ok && r53err.Code() == "NoSuchHostedZone" {
2016-01-29 18:56:19 +01:00
return nil, r53NoHostedZoneFound
}
return nil, err
}
en := expandRecordName(d.Get("name").(string), *zoneRecord.HostedZone.Name)
log.Printf("[DEBUG] Expanded record name: %s", en)
d.Set("fqdn", en)
2015-04-16 20:42:16 +02:00
lopts := &route53.ListResourceRecordSetsInput{
HostedZoneId: aws.String(cleanZoneID(zone)),
StartRecordName: aws.String(en),
StartRecordType: aws.String(d.Get("type").(string)),
}
log.Printf("[DEBUG] List resource records sets for zone: %s, opts: %s",
zone, lopts)
resp, err := conn.ListResourceRecordSets(lopts)
if err != nil {
return nil, err
}
for _, record := range resp.ResourceRecordSets {
name := cleanRecordName(*record.Name)
if FQDN(strings.ToLower(name)) != FQDN(strings.ToLower(*lopts.StartRecordName)) {
continue
}
if strings.ToUpper(*record.Type) != strings.ToUpper(*lopts.StartRecordType) {
continue
}
if record.SetIdentifier != nil && *record.SetIdentifier != d.Get("set_identifier") {
continue
}
2016-01-29 18:56:19 +01:00
// The only safe return where a record is found
return record, nil
}
2016-01-29 18:56:19 +01:00
return nil, r53NoRecordsFound
}
func resourceAwsRoute53RecordDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).r53conn
// Get the records
rec, err := findRecord(d, meta)
if err != nil {
2016-01-29 19:38:22 +01:00
switch err {
case r53NoHostedZoneFound, r53NoRecordsFound:
log.Printf("[DEBUG] %s for: %s, removing from state file", err, d.Id())
d.SetId("")
return nil
default:
return err
}
}
// Change batch for deleting
changeBatch := &route53.ChangeBatch{
Comment: aws.String("Deleted by Terraform"),
2015-04-16 20:42:16 +02:00
Changes: []*route53.Change{
{
Action: aws.String("DELETE"),
ResourceRecordSet: rec,
},
},
}
zone := cleanZoneID(d.Get("zone_id").(string))
2015-04-16 20:42:16 +02:00
req := &route53.ChangeResourceRecordSetsInput{
HostedZoneId: aws.String(cleanZoneID(zone)),
ChangeBatch: changeBatch,
}
provider/aws: Wait for `aws_route_53_record` to be in-sync after a delete Fixes #6679 When we change the type of a record, it forces a new resource. We never waited for the recordset to be in-sync after a deletion. ``` % make testacc TEST=./builtin/providers/aws % TESTARGS='-run=TestAccAWSRoute53Record_' % ✹ ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/09/03 17:55:03 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSRoute53Record_ -timeout 120m === RUN TestAccAWSRoute53Record_basic --- PASS: TestAccAWSRoute53Record_basic (85.54s) === RUN TestAccAWSRoute53Record_basic_fqdn --- PASS: TestAccAWSRoute53Record_basic_fqdn (101.75s) === RUN TestAccAWSRoute53Record_txtSupport --- PASS: TestAccAWSRoute53Record_txtSupport (84.01s) === RUN TestAccAWSRoute53Record_spfSupport --- PASS: TestAccAWSRoute53Record_spfSupport (85.08s) === RUN TestAccAWSRoute53Record_generatesSuffix --- PASS: TestAccAWSRoute53Record_generatesSuffix (97.12s) === RUN TestAccAWSRoute53Record_wildcard --- PASS: TestAccAWSRoute53Record_wildcard (141.08s) === RUN TestAccAWSRoute53Record_failover --- PASS: TestAccAWSRoute53Record_failover (91.25s) === RUN TestAccAWSRoute53Record_weighted_basic --- PASS: TestAccAWSRoute53Record_weighted_basic (89.01s) === RUN TestAccAWSRoute53Record_alias --- PASS: TestAccAWSRoute53Record_alias (88.91s) === RUN TestAccAWSRoute53Record_s3_alias --- PASS: TestAccAWSRoute53Record_s3_alias (103.10s) === RUN TestAccAWSRoute53Record_weighted_alias --- PASS: TestAccAWSRoute53Record_weighted_alias (174.71s) === RUN TestAccAWSRoute53Record_geolocation_basic --- PASS: TestAccAWSRoute53Record_geolocation_basic (89.50s) === RUN TestAccAWSRoute53Record_latency_basic --- PASS: TestAccAWSRoute53Record_latency_basic (89.12s) === RUN TestAccAWSRoute53Record_TypeChange --- PASS: TestAccAWSRoute53Record_TypeChange (138.09s) === RUN TestAccAWSRoute53Record_empty --- PASS: TestAccAWSRoute53Record_empty (88.51s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 1684.774s ```
2016-09-03 17:14:50 +02:00
respRaw, err := deleteRoute53RecordSet(conn, req)
if err != nil {
return errwrap.Wrapf("[ERR]: Error building changeset: {{err}}", err)
}
changeInfo := respRaw.(*route53.ChangeResourceRecordSetsOutput).ChangeInfo
provider/aws: guard against aws_route53_record delete panic Fixes #9025 We were assuming there would always be a changeInfo record and then dereferencing the ID. This wasn't always the case (As noted in #9025) where it was a delete rather than a delete / create action ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSRoute53Record_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/09/26 11:26:43 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSRoute53Record_ -timeout 120m === RUN TestAccAWSRoute53Record_basic --- PASS: TestAccAWSRoute53Record_basic (114.99s) === RUN TestAccAWSRoute53Record_basic_fqdn --- PASS: TestAccAWSRoute53Record_basic_fqdn (126.64s) === RUN TestAccAWSRoute53Record_txtSupport --- PASS: TestAccAWSRoute53Record_txtSupport (113.25s) === RUN TestAccAWSRoute53Record_spfSupport --- PASS: TestAccAWSRoute53Record_spfSupport (112.89s) === RUN TestAccAWSRoute53Record_generatesSuffix --- PASS: TestAccAWSRoute53Record_generatesSuffix (113.29s) === RUN TestAccAWSRoute53Record_wildcard --- PASS: TestAccAWSRoute53Record_wildcard (163.05s) === RUN TestAccAWSRoute53Record_failover --- PASS: TestAccAWSRoute53Record_failover (121.15s) === RUN TestAccAWSRoute53Record_weighted_basic --- PASS: TestAccAWSRoute53Record_weighted_basic (117.06s) === RUN TestAccAWSRoute53Record_alias --- PASS: TestAccAWSRoute53Record_alias (116.97s) === RUN TestAccAWSRoute53Record_s3_alias --- PASS: TestAccAWSRoute53Record_s3_alias (138.79s) === RUN TestAccAWSRoute53Record_weighted_alias --- PASS: TestAccAWSRoute53Record_weighted_alias (241.48s) === RUN TestAccAWSRoute53Record_geolocation_basic --- PASS: TestAccAWSRoute53Record_geolocation_basic (132.51s) === RUN TestAccAWSRoute53Record_latency_basic --- PASS: TestAccAWSRoute53Record_latency_basic (121.29s) === RUN TestAccAWSRoute53Record_TypeChange --- PASS: TestAccAWSRoute53Record_TypeChange (189.31s) === RUN TestAccAWSRoute53Record_empty --- PASS: TestAccAWSRoute53Record_empty (127.31s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 2050.012s ```
2016-09-26 12:27:56 +02:00
if changeInfo == nil {
log.Printf("[INFO] No ChangeInfo Found. Waiting for Sync not required")
return nil
}
provider/aws: Wait for `aws_route_53_record` to be in-sync after a delete Fixes #6679 When we change the type of a record, it forces a new resource. We never waited for the recordset to be in-sync after a deletion. ``` % make testacc TEST=./builtin/providers/aws % TESTARGS='-run=TestAccAWSRoute53Record_' % ✹ ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/09/03 17:55:03 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSRoute53Record_ -timeout 120m === RUN TestAccAWSRoute53Record_basic --- PASS: TestAccAWSRoute53Record_basic (85.54s) === RUN TestAccAWSRoute53Record_basic_fqdn --- PASS: TestAccAWSRoute53Record_basic_fqdn (101.75s) === RUN TestAccAWSRoute53Record_txtSupport --- PASS: TestAccAWSRoute53Record_txtSupport (84.01s) === RUN TestAccAWSRoute53Record_spfSupport --- PASS: TestAccAWSRoute53Record_spfSupport (85.08s) === RUN TestAccAWSRoute53Record_generatesSuffix --- PASS: TestAccAWSRoute53Record_generatesSuffix (97.12s) === RUN TestAccAWSRoute53Record_wildcard --- PASS: TestAccAWSRoute53Record_wildcard (141.08s) === RUN TestAccAWSRoute53Record_failover --- PASS: TestAccAWSRoute53Record_failover (91.25s) === RUN TestAccAWSRoute53Record_weighted_basic --- PASS: TestAccAWSRoute53Record_weighted_basic (89.01s) === RUN TestAccAWSRoute53Record_alias --- PASS: TestAccAWSRoute53Record_alias (88.91s) === RUN TestAccAWSRoute53Record_s3_alias --- PASS: TestAccAWSRoute53Record_s3_alias (103.10s) === RUN TestAccAWSRoute53Record_weighted_alias --- PASS: TestAccAWSRoute53Record_weighted_alias (174.71s) === RUN TestAccAWSRoute53Record_geolocation_basic --- PASS: TestAccAWSRoute53Record_geolocation_basic (89.50s) === RUN TestAccAWSRoute53Record_latency_basic --- PASS: TestAccAWSRoute53Record_latency_basic (89.12s) === RUN TestAccAWSRoute53Record_TypeChange --- PASS: TestAccAWSRoute53Record_TypeChange (138.09s) === RUN TestAccAWSRoute53Record_empty --- PASS: TestAccAWSRoute53Record_empty (88.51s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 1684.774s ```
2016-09-03 17:14:50 +02:00
err = waitForRoute53RecordSetToSync(conn, cleanChangeID(*changeInfo.Id))
if err != nil {
return err
}
return err
}
func deleteRoute53RecordSet(conn *route53.Route53, input *route53.ChangeResourceRecordSetsInput) (interface{}, error) {
wait := resource.StateChangeConf{
Pending: []string{"rejected"},
Target: []string{"accepted"},
Timeout: 5 * time.Minute,
MinTimeout: 1 * time.Second,
Refresh: func() (interface{}, string, error) {
resp, err := conn.ChangeResourceRecordSets(input)
if err != nil {
if r53err, ok := err.(awserr.Error); ok {
if r53err.Code() == "PriorRequestNotComplete" {
// There is some pending operation, so just retry
// in a bit.
return 42, "rejected", nil
}
if r53err.Code() == "InvalidChangeBatch" {
// This means that the record is already gone.
return resp, "accepted", nil
}
}
return 42, "failure", err
}
return resp, "accepted", nil
},
}
return wait.WaitForState()
}
func resourceAwsRoute53RecordBuildSet(d *schema.ResourceData, zoneName string) (*route53.ResourceRecordSet, error) {
// get expanded name
en := expandRecordName(d.Get("name").(string), zoneName)
// Create the RecordSet request with the fully expanded name, e.g.
// sub.domain.com. Route 53 requires a fully qualified domain name, but does
// not require the trailing ".", which it will itself, so we don't call FQDN
// here.
rec := &route53.ResourceRecordSet{
Name: aws.String(en),
Type: aws.String(d.Get("type").(string)),
}
if v, ok := d.GetOk("ttl"); ok {
rec.TTL = aws.Int64(int64(v.(int)))
}
// Resource records
if v, ok := d.GetOk("records"); ok {
recs := v.(*schema.Set).List()
rec.ResourceRecords = expandResourceRecords(recs, d.Get("type").(string))
}
// Alias record
if v, ok := d.GetOk("alias"); ok {
aliases := v.(*schema.Set).List()
if len(aliases) > 1 {
return nil, fmt.Errorf("You can only define a single alias target per record")
}
alias := aliases[0].(map[string]interface{})
rec.AliasTarget = &route53.AliasTarget{
DNSName: aws.String(alias["name"].(string)),
EvaluateTargetHealth: aws.Bool(alias["evaluate_target_health"].(bool)),
HostedZoneId: aws.String(alias["zone_id"].(string)),
}
log.Printf("[DEBUG] Creating alias: %#v", alias)
} else {
if _, ok := d.GetOk("ttl"); !ok {
return nil, fmt.Errorf(`provider.aws: aws_route53_record: %s: "ttl": required field is not set`, d.Get("name").(string))
}
if _, ok := d.GetOk("records"); !ok {
return nil, fmt.Errorf(`provider.aws: aws_route53_record: %s: "records": required field is not set`, d.Get("name").(string))
}
}
if v, ok := d.GetOk("failover_routing_policy"); ok {
if _, ok := d.GetOk("set_identifier"); !ok {
return nil, fmt.Errorf(`provider.aws: aws_route53_record: %s: "set_identifier": required field is not set when "failover_routing_policy" is set`, d.Get("name").(string))
}
records := v.([]interface{})
if len(records) > 1 {
return nil, fmt.Errorf("You can only define a single failover_routing_policy per record")
}
failover := records[0].(map[string]interface{})
rec.Failover = aws.String(failover["type"].(string))
}
if v, ok := d.GetOk("health_check_id"); ok {
rec.HealthCheckId = aws.String(v.(string))
}
if v, ok := d.GetOk("weighted_routing_policy"); ok {
if _, ok := d.GetOk("set_identifier"); !ok {
return nil, fmt.Errorf(`provider.aws: aws_route53_record: %s: "set_identifier": required field is not set when "weight_routing_policy" is set`, d.Get("name").(string))
}
records := v.([]interface{})
if len(records) > 1 {
return nil, fmt.Errorf("You can only define a single weighed_routing_policy per record")
}
weight := records[0].(map[string]interface{})
rec.Weight = aws.Int64(int64(weight["weight"].(int)))
}
if v, ok := d.GetOk("set_identifier"); ok {
rec.SetIdentifier = aws.String(v.(string))
}
if v, ok := d.GetOk("latency_routing_policy"); ok {
if _, ok := d.GetOk("set_identifier"); !ok {
return nil, fmt.Errorf(`provider.aws: aws_route53_record: %s: "set_identifier": required field is not set when "latency_routing_policy" is set`, d.Get("name").(string))
}
records := v.([]interface{})
if len(records) > 1 {
return nil, fmt.Errorf("You can only define a single latency_routing_policy per record")
}
latency := records[0].(map[string]interface{})
rec.Region = aws.String(latency["region"].(string))
}
if v, ok := d.GetOk("geolocation_routing_policy"); ok {
if _, ok := d.GetOk("set_identifier"); !ok {
return nil, fmt.Errorf(`provider.aws: aws_route53_record: %s: "set_identifier": required field is not set when "geolocation_routing_policy" is set`, d.Get("name").(string))
}
geolocations := v.([]interface{})
if len(geolocations) > 1 {
return nil, fmt.Errorf("You can only define a single geolocation_routing_policy per record")
}
geolocation := geolocations[0].(map[string]interface{})
rec.GeoLocation = &route53.GeoLocation{
ContinentCode: nilString(geolocation["continent"].(string)),
CountryCode: nilString(geolocation["country"].(string)),
SubdivisionCode: nilString(geolocation["subdivision"].(string)),
}
log.Printf("[DEBUG] Creating geolocation: %#v", geolocation)
}
return rec, nil
}
func FQDN(name string) string {
n := len(name)
if n == 0 || name[n-1] == '.' {
return name
} else {
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
}
// Check if the current record name contains the zone suffix.
// If it does not, add the zone name to form a fully qualified name
// and keep AWS happy.
func expandRecordName(name, zone string) string {
2015-10-20 23:36:25 +02:00
rn := strings.ToLower(strings.TrimSuffix(name, "."))
zone = strings.TrimSuffix(zone, ".")
if !strings.HasSuffix(rn, zone) {
if len(name) == 0 {
rn = zone
} else {
rn = strings.Join([]string{name, zone}, ".")
}
}
return rn
}
func resourceAwsRoute53AliasRecordHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
provider/aws: Fix aws_route53_record alias perpetual diff (#9704) Fixes #9628 Fixes #9298 When a route53_record alias is updated in the console, AWS prepends `dualstack.` to the name. This is there incase IPV6 is wanted. It is exactly the same without it as it is with it In order to stop perpetual diffs, I introduced a normalizeFunc that will that tke alias name and strip known issues: * dualstack * trailing dot This normalize fun will continue to grow I'm sure ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSRoute53Record_' ✹ ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/10/29 00:28:12 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSRoute53Record_ -timeout 120m === RUN TestAccAWSRoute53Record_basic --- PASS: TestAccAWSRoute53Record_basic (124.64s) === RUN TestAccAWSRoute53Record_basic_fqdn --- PASS: TestAccAWSRoute53Record_basic_fqdn (132.07s) === RUN TestAccAWSRoute53Record_txtSupport --- PASS: TestAccAWSRoute53Record_txtSupport (134.07s) === RUN TestAccAWSRoute53Record_spfSupport --- PASS: TestAccAWSRoute53Record_spfSupport (113.36s) === RUN TestAccAWSRoute53Record_generatesSuffix --- PASS: TestAccAWSRoute53Record_generatesSuffix (112.62s) === RUN TestAccAWSRoute53Record_wildcard --- PASS: TestAccAWSRoute53Record_wildcard (162.84s) === RUN TestAccAWSRoute53Record_failover --- PASS: TestAccAWSRoute53Record_failover (126.18s) === RUN TestAccAWSRoute53Record_weighted_basic --- PASS: TestAccAWSRoute53Record_weighted_basic (121.10s) === RUN TestAccAWSRoute53Record_alias --- PASS: TestAccAWSRoute53Record_alias (118.14s) === RUN TestAccAWSRoute53Record_s3_alias --- PASS: TestAccAWSRoute53Record_s3_alias (155.07s) === RUN TestAccAWSRoute53Record_weighted_alias --- PASS: TestAccAWSRoute53Record_weighted_alias (235.41s) === RUN TestAccAWSRoute53Record_geolocation_basic ^[[C--- PASS: TestAccAWSRoute53Record_geolocation_basic (125.32s) === RUN TestAccAWSRoute53Record_latency_basic --- PASS: TestAccAWSRoute53Record_latency_basic (122.23s) === RUN TestAccAWSRoute53Record_TypeChange --- PASS: TestAccAWSRoute53Record_TypeChange (231.98s) === RUN TestAccAWSRoute53Record_empty --- PASS: TestAccAWSRoute53Record_empty (116.48s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 2131.526s ``` Before this fix, I was getting the following by recreating the code in ``` ~ aws_route53_record.alias alias.1563903989.evaluate_target_health: "true" => "false" alias.1563903989.name: "9828-recreation-106795730.us-west-2.elb.amazonaws.com." => "" alias.1563903989.zone_id: "Z1H1FL5HABSF5" => "" alias.318754017.evaluate_target_health: "" => "true" alias.318754017.name: "" => "9828-recreation-106795730.us-west-2.elb.amazonaws.com" alias.318754017.zone_id: "" => "Z1H1FL5HABSF5" Plan: 0 to add, 1 to change, 0 to destroy. ``` After this fix: ``` No changes. Infrastructure is up-to-date. This means that Terraform could not detect any differences between your configuration and the real physical resources that exist. As a result, Terraform doesn't need to do anything.
2016-10-31 20:18:00 +01:00
buf.WriteString(fmt.Sprintf("%s-", normalizeAwsAliasName(m["name"].(string))))
buf.WriteString(fmt.Sprintf("%s-", m["zone_id"].(string)))
buf.WriteString(fmt.Sprintf("%t-", m["evaluate_target_health"].(bool)))
return hashcode.String(buf.String())
}
// nilString takes a string as an argument and returns a string
// pointer. The returned pointer is nil if the string argument is
// empty, otherwise it is a pointer to a copy of the string.
func nilString(s string) *string {
if s == "" {
return nil
}
return aws.String(s)
}
provider/aws: Fix aws_route53_record alias perpetual diff (#9704) Fixes #9628 Fixes #9298 When a route53_record alias is updated in the console, AWS prepends `dualstack.` to the name. This is there incase IPV6 is wanted. It is exactly the same without it as it is with it In order to stop perpetual diffs, I introduced a normalizeFunc that will that tke alias name and strip known issues: * dualstack * trailing dot This normalize fun will continue to grow I'm sure ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSRoute53Record_' ✹ ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2016/10/29 00:28:12 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSRoute53Record_ -timeout 120m === RUN TestAccAWSRoute53Record_basic --- PASS: TestAccAWSRoute53Record_basic (124.64s) === RUN TestAccAWSRoute53Record_basic_fqdn --- PASS: TestAccAWSRoute53Record_basic_fqdn (132.07s) === RUN TestAccAWSRoute53Record_txtSupport --- PASS: TestAccAWSRoute53Record_txtSupport (134.07s) === RUN TestAccAWSRoute53Record_spfSupport --- PASS: TestAccAWSRoute53Record_spfSupport (113.36s) === RUN TestAccAWSRoute53Record_generatesSuffix --- PASS: TestAccAWSRoute53Record_generatesSuffix (112.62s) === RUN TestAccAWSRoute53Record_wildcard --- PASS: TestAccAWSRoute53Record_wildcard (162.84s) === RUN TestAccAWSRoute53Record_failover --- PASS: TestAccAWSRoute53Record_failover (126.18s) === RUN TestAccAWSRoute53Record_weighted_basic --- PASS: TestAccAWSRoute53Record_weighted_basic (121.10s) === RUN TestAccAWSRoute53Record_alias --- PASS: TestAccAWSRoute53Record_alias (118.14s) === RUN TestAccAWSRoute53Record_s3_alias --- PASS: TestAccAWSRoute53Record_s3_alias (155.07s) === RUN TestAccAWSRoute53Record_weighted_alias --- PASS: TestAccAWSRoute53Record_weighted_alias (235.41s) === RUN TestAccAWSRoute53Record_geolocation_basic ^[[C--- PASS: TestAccAWSRoute53Record_geolocation_basic (125.32s) === RUN TestAccAWSRoute53Record_latency_basic --- PASS: TestAccAWSRoute53Record_latency_basic (122.23s) === RUN TestAccAWSRoute53Record_TypeChange --- PASS: TestAccAWSRoute53Record_TypeChange (231.98s) === RUN TestAccAWSRoute53Record_empty --- PASS: TestAccAWSRoute53Record_empty (116.48s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 2131.526s ``` Before this fix, I was getting the following by recreating the code in ``` ~ aws_route53_record.alias alias.1563903989.evaluate_target_health: "true" => "false" alias.1563903989.name: "9828-recreation-106795730.us-west-2.elb.amazonaws.com." => "" alias.1563903989.zone_id: "Z1H1FL5HABSF5" => "" alias.318754017.evaluate_target_health: "" => "true" alias.318754017.name: "" => "9828-recreation-106795730.us-west-2.elb.amazonaws.com" alias.318754017.zone_id: "" => "Z1H1FL5HABSF5" Plan: 0 to add, 1 to change, 0 to destroy. ``` After this fix: ``` No changes. Infrastructure is up-to-date. This means that Terraform could not detect any differences between your configuration and the real physical resources that exist. As a result, Terraform doesn't need to do anything.
2016-10-31 20:18:00 +01:00
func normalizeAwsAliasName(alias interface{}) string {
input := alias.(string)
if strings.HasPrefix(input, "dualstack.") {
return strings.Replace(input, "dualstack.", "", -1)
}
return strings.TrimRight(input, ".")
}