github.com/arvindram03/terraform@v0.3.7-0.20150212015210-408f838db36d/builtin/providers/aws/resource_aws_route53_record.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 8 9 "github.com/hashicorp/terraform/helper/hashcode" 10 "github.com/hashicorp/terraform/helper/resource" 11 "github.com/hashicorp/terraform/helper/schema" 12 "github.com/mitchellh/goamz/route53" 13 ) 14 15 func resourceAwsRoute53Record() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsRoute53RecordCreate, 18 Read: resourceAwsRoute53RecordRead, 19 Delete: resourceAwsRoute53RecordDelete, 20 21 Schema: map[string]*schema.Schema{ 22 "name": &schema.Schema{ 23 Type: schema.TypeString, 24 Required: true, 25 ForceNew: true, 26 }, 27 28 "type": &schema.Schema{ 29 Type: schema.TypeString, 30 Required: true, 31 ForceNew: true, 32 }, 33 34 "zone_id": &schema.Schema{ 35 Type: schema.TypeString, 36 Required: true, 37 ForceNew: true, 38 }, 39 40 "ttl": &schema.Schema{ 41 Type: schema.TypeInt, 42 Required: true, 43 ForceNew: true, 44 }, 45 46 "records": &schema.Schema{ 47 Type: schema.TypeSet, 48 Elem: &schema.Schema{Type: schema.TypeString}, 49 Required: true, 50 ForceNew: true, 51 Set: func(v interface{}) int { 52 return hashcode.String(v.(string)) 53 }, 54 }, 55 }, 56 } 57 } 58 59 func resourceAwsRoute53RecordCreate(d *schema.ResourceData, meta interface{}) error { 60 conn := meta.(*AWSClient).route53 61 62 zone := d.Get("zone_id").(string) 63 64 zoneRecord, err := conn.GetHostedZone(zone) 65 if err != nil { 66 return err 67 } 68 69 // Check if the current record name contains the zone suffix. 70 // If it does not, add the zone name to form a fully qualified name 71 // and keep AWS happy. 72 recordName := d.Get("name").(string) 73 zoneName := strings.Trim(zoneRecord.HostedZone.Name, ".") 74 if !strings.HasSuffix(recordName, zoneName) { 75 d.Set("name", strings.Join([]string{recordName, zoneName}, ".")) 76 } 77 78 // Get the record 79 rec, err := resourceAwsRoute53RecordBuildSet(d) 80 if err != nil { 81 return err 82 } 83 84 // Create the new records. We abuse StateChangeConf for this to 85 // retry for us since Route53 sometimes returns errors about another 86 // operation happening at the same time. 87 req := &route53.ChangeResourceRecordSetsRequest{ 88 Comment: "Managed by Terraform", 89 Changes: []route53.Change{ 90 route53.Change{ 91 Action: "UPSERT", 92 Record: *rec, 93 }, 94 }, 95 } 96 97 log.Printf("[DEBUG] Creating resource records for zone: %s, name: %s", 98 zone, d.Get("name").(string)) 99 100 wait := resource.StateChangeConf{ 101 Pending: []string{"rejected"}, 102 Target: "accepted", 103 Timeout: 5 * time.Minute, 104 MinTimeout: 1 * time.Second, 105 Refresh: func() (interface{}, string, error) { 106 resp, err := conn.ChangeResourceRecordSets(zone, req) 107 if err != nil { 108 if strings.Contains(err.Error(), "PriorRequestNotComplete") { 109 // There is some pending operation, so just retry 110 // in a bit. 111 return nil, "rejected", nil 112 } 113 114 return nil, "failure", err 115 } 116 117 return resp.ChangeInfo, "accepted", nil 118 }, 119 } 120 121 respRaw, err := wait.WaitForState() 122 if err != nil { 123 return err 124 } 125 changeInfo := respRaw.(route53.ChangeInfo) 126 127 // Generate an ID 128 d.SetId(fmt.Sprintf("%s_%s_%s", zone, d.Get("name").(string), d.Get("type").(string))) 129 130 // Wait until we are done 131 wait = resource.StateChangeConf{ 132 Delay: 30 * time.Second, 133 Pending: []string{"PENDING"}, 134 Target: "INSYNC", 135 Timeout: 10 * time.Minute, 136 MinTimeout: 5 * time.Second, 137 Refresh: func() (result interface{}, state string, err error) { 138 return resourceAwsRoute53Wait(conn, changeInfo.ID) 139 }, 140 } 141 _, err = wait.WaitForState() 142 if err != nil { 143 return err 144 } 145 146 return nil 147 } 148 149 func resourceAwsRoute53RecordRead(d *schema.ResourceData, meta interface{}) error { 150 conn := meta.(*AWSClient).route53 151 152 zone := d.Get("zone_id").(string) 153 lopts := &route53.ListOpts{ 154 Name: d.Get("name").(string), 155 Type: d.Get("type").(string), 156 } 157 resp, err := conn.ListResourceRecordSets(zone, lopts) 158 if err != nil { 159 return err 160 } 161 162 // Scan for a matching record 163 found := false 164 for _, record := range resp.Records { 165 if route53.FQDN(record.Name) != route53.FQDN(lopts.Name) { 166 continue 167 } 168 if strings.ToUpper(record.Type) != strings.ToUpper(lopts.Type) { 169 continue 170 } 171 172 found = true 173 174 d.Set("records", record.Records) 175 d.Set("ttl", record.TTL) 176 177 break 178 } 179 180 if !found { 181 d.SetId("") 182 } 183 184 return nil 185 } 186 187 func resourceAwsRoute53RecordDelete(d *schema.ResourceData, meta interface{}) error { 188 conn := meta.(*AWSClient).route53 189 190 // Get the records 191 rec, err := resourceAwsRoute53RecordBuildSet(d) 192 if err != nil { 193 return err 194 } 195 196 // Create the new records 197 req := &route53.ChangeResourceRecordSetsRequest{ 198 Comment: "Deleted by Terraform", 199 Changes: []route53.Change{ 200 route53.Change{ 201 Action: "DELETE", 202 Record: *rec, 203 }, 204 }, 205 } 206 zone := d.Get("zone_id").(string) 207 log.Printf("[DEBUG] Deleting resource records for zone: %s, name: %s", 208 zone, d.Get("name").(string)) 209 210 wait := resource.StateChangeConf{ 211 Pending: []string{"rejected"}, 212 Target: "accepted", 213 Timeout: 5 * time.Minute, 214 MinTimeout: 1 * time.Second, 215 Refresh: func() (interface{}, string, error) { 216 _, err := conn.ChangeResourceRecordSets(zone, req) 217 if err != nil { 218 if strings.Contains(err.Error(), "PriorRequestNotComplete") { 219 // There is some pending operation, so just retry 220 // in a bit. 221 return 42, "rejected", nil 222 } 223 224 if strings.Contains(err.Error(), "InvalidChangeBatch") { 225 // This means that the record is already gone. 226 return 42, "accepted", nil 227 } 228 229 return 42, "failure", err 230 } 231 232 return 42, "accepted", nil 233 }, 234 } 235 236 if _, err := wait.WaitForState(); err != nil { 237 return err 238 } 239 240 return nil 241 } 242 243 func resourceAwsRoute53RecordBuildSet(d *schema.ResourceData) (*route53.ResourceRecordSet, error) { 244 recs := d.Get("records").(*schema.Set).List() 245 records := make([]string, 0, len(recs)) 246 247 for _, r := range recs { 248 records = append(records, r.(string)) 249 } 250 251 rec := &route53.ResourceRecordSet{ 252 Name: d.Get("name").(string), 253 Type: d.Get("type").(string), 254 TTL: d.Get("ttl").(int), 255 Records: records, 256 } 257 return rec, nil 258 }