github.com/bengesoff/terraform@v0.3.1-0.20141018223233-b25a53629922/builtin/providers/aws/resource_aws_route53_record.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "strconv" 7 "strings" 8 "time" 9 10 "github.com/hashicorp/terraform/flatmap" 11 "github.com/hashicorp/terraform/helper/config" 12 "github.com/hashicorp/terraform/helper/diff" 13 "github.com/hashicorp/terraform/helper/resource" 14 "github.com/hashicorp/terraform/terraform" 15 "github.com/mitchellh/goamz/route53" 16 ) 17 18 func resource_aws_r53_record_validation() *config.Validator { 19 return &config.Validator{ 20 Required: []string{ 21 "zone_id", 22 "name", 23 "type", 24 "ttl", 25 "records.*", 26 }, 27 } 28 } 29 30 func resource_aws_r53_record_create( 31 s *terraform.InstanceState, 32 d *terraform.InstanceDiff, 33 meta interface{}) (*terraform.InstanceState, error) { 34 p := meta.(*ResourceProvider) 35 conn := p.route53 36 37 // Merge the diff into the state so that we have all the attributes 38 // properly. 39 rs := s.MergeDiff(d) 40 41 // Get the record 42 rec, err := resource_aws_r53_build_record_set(rs) 43 if err != nil { 44 return rs, err 45 } 46 47 // Create the new records. We abuse StateChangeConf for this to 48 // retry for us since Route53 sometimes returns errors about another 49 // operation happening at the same time. 50 req := &route53.ChangeResourceRecordSetsRequest{ 51 Comment: "Managed by Terraform", 52 Changes: []route53.Change{ 53 route53.Change{ 54 Action: "UPSERT", 55 Record: *rec, 56 }, 57 }, 58 } 59 zone := rs.Attributes["zone_id"] 60 log.Printf("[DEBUG] Creating resource records for zone: %s, name: %s", 61 zone, rs.Attributes["name"]) 62 wait := resource.StateChangeConf{ 63 Pending: []string{"rejected"}, 64 Target: "accepted", 65 Timeout: 5 * time.Minute, 66 MinTimeout: 1 * time.Second, 67 Refresh: func() (interface{}, string, error) { 68 resp, err := conn.ChangeResourceRecordSets(zone, req) 69 if err != nil { 70 if strings.Contains(err.Error(), "PriorRequestNotComplete") { 71 // There is some pending operation, so just retry 72 // in a bit. 73 return nil, "rejected", nil 74 } 75 76 return nil, "failure", err 77 } 78 79 return resp.ChangeInfo, "accepted", nil 80 }, 81 } 82 respRaw, err := wait.WaitForState() 83 if err != nil { 84 return rs, err 85 } 86 changeInfo := respRaw.(route53.ChangeInfo) 87 88 // Generate an ID 89 rs.ID = fmt.Sprintf("%s_%s_%s", zone, rs.Attributes["name"], rs.Attributes["type"]) 90 91 // Wait until we are done 92 wait = resource.StateChangeConf{ 93 Delay: 30 * time.Second, 94 Pending: []string{"PENDING"}, 95 Target: "INSYNC", 96 Timeout: 10 * time.Minute, 97 MinTimeout: 5 * time.Second, 98 Refresh: func() (result interface{}, state string, err error) { 99 return resource_aws_r53_wait(conn, changeInfo.ID) 100 }, 101 } 102 _, err = wait.WaitForState() 103 if err != nil { 104 return rs, err 105 } 106 return rs, nil 107 } 108 109 func resource_aws_r53_build_record_set(s *terraform.InstanceState) (*route53.ResourceRecordSet, error) { 110 // Parse the TTL 111 ttl, err := strconv.ParseInt(s.Attributes["ttl"], 10, 32) 112 if err != nil { 113 return nil, err 114 } 115 116 // Expand the records 117 recRaw := flatmap.Expand(s.Attributes, "records") 118 var records []string 119 for _, raw := range recRaw.([]interface{}) { 120 records = append(records, raw.(string)) 121 } 122 123 rec := &route53.ResourceRecordSet{ 124 Name: s.Attributes["name"], 125 Type: s.Attributes["type"], 126 TTL: int(ttl), 127 Records: records, 128 } 129 return rec, nil 130 } 131 132 func resource_aws_r53_record_destroy( 133 s *terraform.InstanceState, 134 meta interface{}) error { 135 p := meta.(*ResourceProvider) 136 conn := p.route53 137 138 // Get the record 139 rec, err := resource_aws_r53_build_record_set(s) 140 if err != nil { 141 return err 142 } 143 144 // Create the new records 145 req := &route53.ChangeResourceRecordSetsRequest{ 146 Comment: "Deleted by Terraform", 147 Changes: []route53.Change{ 148 route53.Change{ 149 Action: "DELETE", 150 Record: *rec, 151 }, 152 }, 153 } 154 zone := s.Attributes["zone_id"] 155 log.Printf("[DEBUG] Deleting resource records for zone: %s, name: %s", 156 zone, s.Attributes["name"]) 157 wait := resource.StateChangeConf{ 158 Pending: []string{"rejected"}, 159 Target: "accepted", 160 Timeout: 5 * time.Minute, 161 MinTimeout: 1 * time.Second, 162 Refresh: func() (interface{}, string, error) { 163 _, err := conn.ChangeResourceRecordSets(zone, req) 164 if err != nil { 165 if strings.Contains(err.Error(), "PriorRequestNotComplete") { 166 // There is some pending operation, so just retry 167 // in a bit. 168 return 42, "rejected", nil 169 } 170 171 if strings.Contains(err.Error(), "InvalidChangeBatch") { 172 // This means that the record is already gone. 173 return 42, "accepted", nil 174 } 175 176 return 42, "failure", err 177 } 178 179 return 42, "accepted", nil 180 }, 181 } 182 if _, err := wait.WaitForState(); err != nil { 183 return err 184 } 185 186 return nil 187 } 188 189 func resource_aws_r53_record_refresh( 190 s *terraform.InstanceState, 191 meta interface{}) (*terraform.InstanceState, error) { 192 p := meta.(*ResourceProvider) 193 conn := p.route53 194 195 zone := s.Attributes["zone_id"] 196 lopts := &route53.ListOpts{ 197 Name: s.Attributes["name"], 198 Type: s.Attributes["type"], 199 } 200 resp, err := conn.ListResourceRecordSets(zone, lopts) 201 if err != nil { 202 return s, err 203 } 204 205 // Scan for a matching record 206 found := false 207 for _, record := range resp.Records { 208 if route53.FQDN(record.Name) != route53.FQDN(lopts.Name) { 209 continue 210 } 211 if strings.ToUpper(record.Type) != strings.ToUpper(lopts.Type) { 212 continue 213 } 214 215 found = true 216 resource_aws_r53_record_update_state(s, &record) 217 break 218 } 219 if !found { 220 s.ID = "" 221 } 222 return s, nil 223 } 224 225 func resource_aws_r53_record_update_state( 226 s *terraform.InstanceState, 227 rec *route53.ResourceRecordSet) { 228 229 flatRec := flatmap.Flatten(map[string]interface{}{ 230 "records": rec.Records, 231 }) 232 for k, v := range flatRec { 233 s.Attributes[k] = v 234 } 235 236 s.Attributes["ttl"] = strconv.FormatInt(int64(rec.TTL), 10) 237 } 238 239 func resource_aws_r53_record_diff( 240 s *terraform.InstanceState, 241 c *terraform.ResourceConfig, 242 meta interface{}) (*terraform.InstanceDiff, error) { 243 b := &diff.ResourceBuilder{ 244 Attrs: map[string]diff.AttrType{ 245 "zone_id": diff.AttrTypeCreate, 246 "name": diff.AttrTypeCreate, 247 "type": diff.AttrTypeCreate, 248 "ttl": diff.AttrTypeUpdate, 249 "records": diff.AttrTypeUpdate, 250 }, 251 } 252 return b.Diff(s, c) 253 }