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  }