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  }