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