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  }