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