github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/builtin/providers/aws/resource_aws_eip.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 8 9 "github.com/hashicorp/aws-sdk-go/aws" 10 "github.com/hashicorp/aws-sdk-go/gen/ec2" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceAwsEip() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsEipCreate, 18 Read: resourceAwsEipRead, 19 Update: resourceAwsEipUpdate, 20 Delete: resourceAwsEipDelete, 21 22 Schema: map[string]*schema.Schema{ 23 "vpc": &schema.Schema{ 24 Type: schema.TypeBool, 25 Optional: true, 26 ForceNew: true, 27 }, 28 29 "instance": &schema.Schema{ 30 Type: schema.TypeString, 31 Optional: true, 32 }, 33 34 "allocation_id": &schema.Schema{ 35 Type: schema.TypeString, 36 Computed: true, 37 }, 38 39 "association_id": &schema.Schema{ 40 Type: schema.TypeString, 41 Computed: true, 42 }, 43 44 "domain": &schema.Schema{ 45 Type: schema.TypeString, 46 Computed: true, 47 }, 48 49 "public_ip": &schema.Schema{ 50 Type: schema.TypeString, 51 Computed: true, 52 }, 53 54 "private_ip": &schema.Schema{ 55 Type: schema.TypeString, 56 Computed: true, 57 }, 58 }, 59 } 60 } 61 62 func resourceAwsEipCreate(d *schema.ResourceData, meta interface{}) error { 63 ec2conn := meta.(*AWSClient).ec2conn 64 65 // By default, we're not in a VPC 66 domainOpt := "" 67 if v := d.Get("vpc"); v != nil && v.(bool) { 68 domainOpt = "vpc" 69 } 70 71 allocOpts := &ec2.AllocateAddressRequest{ 72 Domain: aws.String(domainOpt), 73 } 74 75 log.Printf("[DEBUG] EIP create configuration: %#v", allocOpts) 76 allocResp, err := ec2conn.AllocateAddress(allocOpts) 77 if err != nil { 78 return fmt.Errorf("Error creating EIP: %s", err) 79 } 80 81 // The domain tells us if we're in a VPC or not 82 d.Set("domain", allocResp.Domain) 83 84 // Assign the eips (unique) allocation id for use later 85 // the EIP api has a conditional unique ID (really), so 86 // if we're in a VPC we need to save the ID as such, otherwise 87 // it defaults to using the public IP 88 log.Printf("[DEBUG] EIP Allocate: %#v", allocResp) 89 if d.Get("domain").(string) == "vpc" { 90 d.SetId(*allocResp.AllocationID) 91 } else { 92 d.SetId(*allocResp.PublicIP) 93 } 94 95 log.Printf("[INFO] EIP ID: %s (domain: %v)", d.Id(), *allocResp.Domain) 96 return resourceAwsEipUpdate(d, meta) 97 } 98 99 func resourceAwsEipRead(d *schema.ResourceData, meta interface{}) error { 100 ec2conn := meta.(*AWSClient).ec2conn 101 102 domain := resourceAwsEipDomain(d) 103 id := d.Id() 104 105 assocIds := []string{} 106 publicIps := []string{} 107 if domain == "vpc" { 108 assocIds = []string{id} 109 } else { 110 publicIps = []string{id} 111 } 112 113 log.Printf( 114 "[DEBUG] EIP describe configuration: %#v, %#v (domain: %s)", 115 assocIds, publicIps, domain) 116 117 req := &ec2.DescribeAddressesRequest{ 118 AllocationIDs: assocIds, 119 PublicIPs: publicIps, 120 } 121 describeAddresses, err := ec2conn.DescribeAddresses(req) 122 if err != nil { 123 if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidAllocationID.NotFound" { 124 d.SetId("") 125 return nil 126 } 127 128 return fmt.Errorf("Error retrieving EIP: %s", err) 129 } 130 131 // Verify AWS returned our EIP 132 if len(describeAddresses.Addresses) != 1 || 133 *describeAddresses.Addresses[0].AllocationID != id || 134 *describeAddresses.Addresses[0].PublicIP != id { 135 if err != nil { 136 return fmt.Errorf("Unable to find EIP: %#v", describeAddresses.Addresses) 137 } 138 } 139 140 address := describeAddresses.Addresses[0] 141 142 d.Set("association_id", address.AssociationID) 143 d.Set("instance", address.InstanceID) 144 d.Set("private_ip", address.PrivateIPAddress) 145 d.Set("public_ip", address.PublicIP) 146 147 return nil 148 } 149 150 func resourceAwsEipUpdate(d *schema.ResourceData, meta interface{}) error { 151 ec2conn := meta.(*AWSClient).ec2conn 152 153 domain := resourceAwsEipDomain(d) 154 155 // Only register with an instance if we have one 156 if v, ok := d.GetOk("instance"); ok { 157 instanceId := v.(string) 158 159 assocOpts := &ec2.AssociateAddressRequest{ 160 InstanceID: aws.String(instanceId), 161 PublicIP: aws.String(d.Id()), 162 } 163 164 // more unique ID conditionals 165 if domain == "vpc" { 166 assocOpts = &ec2.AssociateAddressRequest{ 167 InstanceID: aws.String(instanceId), 168 AllocationID: aws.String(d.Id()), 169 PublicIP: aws.String(""), 170 } 171 } 172 173 log.Printf("[DEBUG] EIP associate configuration: %#v (domain: %v)", assocOpts, domain) 174 _, err := ec2conn.AssociateAddress(assocOpts) 175 if err != nil { 176 return fmt.Errorf("Failure associating instances: %s", err) 177 } 178 } 179 180 return resourceAwsEipRead(d, meta) 181 } 182 183 func resourceAwsEipDelete(d *schema.ResourceData, meta interface{}) error { 184 ec2conn := meta.(*AWSClient).ec2conn 185 186 if err := resourceAwsEipRead(d, meta); err != nil { 187 return err 188 } 189 if d.Id() == "" { 190 // This might happen from the read 191 return nil 192 } 193 194 // If we are attached to an instance, detach first. 195 if d.Get("instance").(string) != "" { 196 log.Printf("[DEBUG] Disassociating EIP: %s", d.Id()) 197 var err error 198 switch resourceAwsEipDomain(d) { 199 case "vpc": 200 err = ec2conn.DisassociateAddress(&ec2.DisassociateAddressRequest{ 201 AssociationID: aws.String(d.Get("association_id").(string)), 202 }) 203 case "standard": 204 err = ec2conn.DisassociateAddress(&ec2.DisassociateAddressRequest{ 205 PublicIP: aws.String(d.Get("public_ip").(string)), 206 }) 207 } 208 if err != nil { 209 return err 210 } 211 } 212 213 domain := resourceAwsEipDomain(d) 214 return resource.Retry(3*time.Minute, func() error { 215 var err error 216 switch domain { 217 case "vpc": 218 log.Printf( 219 "[DEBUG] EIP release (destroy) address allocation: %v", 220 d.Id()) 221 err = ec2conn.ReleaseAddress(&ec2.ReleaseAddressRequest{ 222 AllocationID: aws.String(d.Id()), 223 }) 224 case "standard": 225 log.Printf("[DEBUG] EIP release (destroy) address: %v", d.Id()) 226 err = ec2conn.ReleaseAddress(&ec2.ReleaseAddressRequest{ 227 PublicIP: aws.String(d.Id()), 228 }) 229 } 230 231 if err == nil { 232 return nil 233 } 234 if _, ok := err.(aws.APIError); !ok { 235 return resource.RetryError{Err: err} 236 } 237 238 return err 239 }) 240 } 241 242 func resourceAwsEipDomain(d *schema.ResourceData) string { 243 if v, ok := d.GetOk("domain"); ok { 244 return v.(string) 245 } else if strings.Contains(d.Id(), "eipalloc") { 246 // We have to do this for backwards compatibility since TF 0.1 247 // didn't have the "domain" computed attribute. 248 return "vpc" 249 } 250 251 return "standard" 252 }