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