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  }