github.com/jefferai/terraform@v0.3.7-0.20150310153852-f7512ca29fcf/builtin/providers/aws/resource_aws_internet_gateway.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	"github.com/hashicorp/aws-sdk-go/aws"
     9  	"github.com/hashicorp/aws-sdk-go/gen/ec2"
    10  	"github.com/hashicorp/terraform/helper/resource"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  )
    13  
    14  func resourceAwsInternetGateway() *schema.Resource {
    15  	return &schema.Resource{
    16  		Create: resourceAwsInternetGatewayCreate,
    17  		Read:   resourceAwsInternetGatewayRead,
    18  		Update: resourceAwsInternetGatewayUpdate,
    19  		Delete: resourceAwsInternetGatewayDelete,
    20  
    21  		Schema: map[string]*schema.Schema{
    22  			"vpc_id": &schema.Schema{
    23  				Type:     schema.TypeString,
    24  				Optional: true,
    25  			},
    26  			"tags": tagsSchema(),
    27  		},
    28  	}
    29  }
    30  
    31  func resourceAwsInternetGatewayCreate(d *schema.ResourceData, meta interface{}) error {
    32  	ec2conn := meta.(*AWSClient).awsEC2conn
    33  
    34  	// Create the gateway
    35  	log.Printf("[DEBUG] Creating internet gateway")
    36  	resp, err := ec2conn.CreateInternetGateway(nil)
    37  	if err != nil {
    38  		return fmt.Errorf("Error creating internet gateway: %s", err)
    39  	}
    40  
    41  	// Get the ID and store it
    42  	ig := resp.InternetGateway
    43  	d.SetId(*ig.InternetGatewayID)
    44  	log.Printf("[INFO] InternetGateway ID: %s", d.Id())
    45  
    46  	// Attach the new gateway to the correct vpc
    47  	return resourceAwsInternetGatewayAttach(d, meta)
    48  }
    49  
    50  func resourceAwsInternetGatewayRead(d *schema.ResourceData, meta interface{}) error {
    51  	ec2conn := meta.(*AWSClient).awsEC2conn
    52  
    53  	igRaw, _, err := IGStateRefreshFunc(ec2conn, d.Id())()
    54  	if err != nil {
    55  		return err
    56  	}
    57  	if igRaw == nil {
    58  		// Seems we have lost our internet gateway
    59  		d.SetId("")
    60  		return nil
    61  	}
    62  
    63  	ig := igRaw.(*ec2.InternetGateway)
    64  	if len(ig.Attachments) == 0 {
    65  		// Gateway exists but not attached to the VPC
    66  		d.Set("vpc_id", "")
    67  	} else {
    68  		d.Set("vpc_id", ig.Attachments[0].VPCID)
    69  	}
    70  
    71  	d.Set("tags", tagsToMapSDK(ig.Tags))
    72  
    73  	return nil
    74  }
    75  
    76  func resourceAwsInternetGatewayUpdate(d *schema.ResourceData, meta interface{}) error {
    77  	if d.HasChange("vpc_id") {
    78  		// If we're already attached, detach it first
    79  		if err := resourceAwsInternetGatewayDetach(d, meta); err != nil {
    80  			return err
    81  		}
    82  
    83  		// Attach the gateway to the new vpc
    84  		if err := resourceAwsInternetGatewayAttach(d, meta); err != nil {
    85  			return err
    86  		}
    87  	}
    88  
    89  	ec2conn := meta.(*AWSClient).awsEC2conn
    90  
    91  	if err := setTagsSDK(ec2conn, d); err != nil {
    92  		return err
    93  	}
    94  
    95  	d.SetPartial("tags")
    96  
    97  	return nil
    98  }
    99  
   100  func resourceAwsInternetGatewayDelete(d *schema.ResourceData, meta interface{}) error {
   101  	ec2conn := meta.(*AWSClient).awsEC2conn
   102  
   103  	// Detach if it is attached
   104  	if err := resourceAwsInternetGatewayDetach(d, meta); err != nil {
   105  		return err
   106  	}
   107  
   108  	log.Printf("[INFO] Deleting Internet Gateway: %s", d.Id())
   109  
   110  	return resource.Retry(5*time.Minute, func() error {
   111  		err := ec2conn.DeleteInternetGateway(&ec2.DeleteInternetGatewayRequest{
   112  			InternetGatewayID: aws.String(d.Id()),
   113  		})
   114  		if err == nil {
   115  			return nil
   116  		}
   117  
   118  		ec2err, ok := err.(aws.APIError)
   119  		if !ok {
   120  			return err
   121  		}
   122  
   123  		switch ec2err.Code {
   124  		case "InvalidInternetGatewayID.NotFound":
   125  			return nil
   126  		case "DependencyViolation":
   127  			return err // retry
   128  		}
   129  
   130  		return resource.RetryError{Err: err}
   131  	})
   132  }
   133  
   134  func resourceAwsInternetGatewayAttach(d *schema.ResourceData, meta interface{}) error {
   135  	ec2conn := meta.(*AWSClient).awsEC2conn
   136  
   137  	if d.Get("vpc_id").(string) == "" {
   138  		log.Printf(
   139  			"[DEBUG] Not attaching Internet Gateway '%s' as no VPC ID is set",
   140  			d.Id())
   141  		return nil
   142  	}
   143  
   144  	log.Printf(
   145  		"[INFO] Attaching Internet Gateway '%s' to VPC '%s'",
   146  		d.Id(),
   147  		d.Get("vpc_id").(string))
   148  
   149  	err := ec2conn.AttachInternetGateway(&ec2.AttachInternetGatewayRequest{
   150  		InternetGatewayID: aws.String(d.Id()),
   151  		VPCID:             aws.String(d.Get("vpc_id").(string)),
   152  	})
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	// A note on the states below: the AWS docs (as of July, 2014) say
   158  	// that the states would be: attached, attaching, detached, detaching,
   159  	// but when running, I noticed that the state is usually "available" when
   160  	// it is attached.
   161  
   162  	// Wait for it to be fully attached before continuing
   163  	log.Printf("[DEBUG] Waiting for internet gateway (%s) to attach", d.Id())
   164  	stateConf := &resource.StateChangeConf{
   165  		Pending: []string{"detached", "attaching"},
   166  		Target:  "available",
   167  		Refresh: IGAttachStateRefreshFunc(ec2conn, d.Id(), "available"),
   168  		Timeout: 1 * time.Minute,
   169  	}
   170  	if _, err := stateConf.WaitForState(); err != nil {
   171  		return fmt.Errorf(
   172  			"Error waiting for internet gateway (%s) to attach: %s",
   173  			d.Id(), err)
   174  	}
   175  
   176  	return nil
   177  }
   178  
   179  func resourceAwsInternetGatewayDetach(d *schema.ResourceData, meta interface{}) error {
   180  	ec2conn := meta.(*AWSClient).awsEC2conn
   181  
   182  	// Get the old VPC ID to detach from
   183  	vpcID, _ := d.GetChange("vpc_id")
   184  
   185  	if vpcID.(string) == "" {
   186  		log.Printf(
   187  			"[DEBUG] Not detaching Internet Gateway '%s' as no VPC ID is set",
   188  			d.Id())
   189  		return nil
   190  	}
   191  
   192  	log.Printf(
   193  		"[INFO] Detaching Internet Gateway '%s' from VPC '%s'",
   194  		d.Id(),
   195  		vpcID.(string))
   196  
   197  	wait := true
   198  	err := ec2conn.DetachInternetGateway(&ec2.DetachInternetGatewayRequest{
   199  		InternetGatewayID: aws.String(d.Id()),
   200  		VPCID:             aws.String(vpcID.(string)),
   201  	})
   202  	if err != nil {
   203  		ec2err, ok := err.(aws.APIError)
   204  		if ok {
   205  			if ec2err.Code == "InvalidInternetGatewayID.NotFound" {
   206  				err = nil
   207  				wait = false
   208  			} else if ec2err.Code == "Gateway.NotAttached" {
   209  				err = nil
   210  				wait = false
   211  			}
   212  		}
   213  
   214  		if err != nil {
   215  			return err
   216  		}
   217  	}
   218  
   219  	if !wait {
   220  		return nil
   221  	}
   222  
   223  	// Wait for it to be fully detached before continuing
   224  	log.Printf("[DEBUG] Waiting for internet gateway (%s) to detach", d.Id())
   225  	stateConf := &resource.StateChangeConf{
   226  		Pending: []string{"attached", "detaching", "available"},
   227  		Target:  "detached",
   228  		Refresh: IGAttachStateRefreshFunc(ec2conn, d.Id(), "detached"),
   229  		Timeout: 1 * time.Minute,
   230  	}
   231  	if _, err := stateConf.WaitForState(); err != nil {
   232  		return fmt.Errorf(
   233  			"Error waiting for internet gateway (%s) to detach: %s",
   234  			d.Id(), err)
   235  	}
   236  
   237  	return nil
   238  }
   239  
   240  // IGStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
   241  // an internet gateway.
   242  func IGStateRefreshFunc(ec2conn *ec2.EC2, id string) resource.StateRefreshFunc {
   243  	return func() (interface{}, string, error) {
   244  		resp, err := ec2conn.DescribeInternetGateways(&ec2.DescribeInternetGatewaysRequest{
   245  			InternetGatewayIDs: []string{id},
   246  		})
   247  		if err != nil {
   248  			ec2err, ok := err.(aws.APIError)
   249  			if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" {
   250  				resp = nil
   251  			} else {
   252  				log.Printf("[ERROR] Error on IGStateRefresh: %s", err)
   253  				return nil, "", err
   254  			}
   255  		}
   256  
   257  		if resp == nil {
   258  			// Sometimes AWS just has consistency issues and doesn't see
   259  			// our instance yet. Return an empty state.
   260  			return nil, "", nil
   261  		}
   262  
   263  		ig := &resp.InternetGateways[0]
   264  		return ig, "available", nil
   265  	}
   266  }
   267  
   268  // IGAttachStateRefreshFunc returns a resource.StateRefreshFunc that is used
   269  // watch the state of an internet gateway's attachment.
   270  func IGAttachStateRefreshFunc(ec2conn *ec2.EC2, id string, expected string) resource.StateRefreshFunc {
   271  	var start time.Time
   272  	return func() (interface{}, string, error) {
   273  		if start.IsZero() {
   274  			start = time.Now()
   275  		}
   276  
   277  		resp, err := ec2conn.DescribeInternetGateways(&ec2.DescribeInternetGatewaysRequest{
   278  			InternetGatewayIDs: []string{id},
   279  		})
   280  		if err != nil {
   281  			ec2err, ok := err.(aws.APIError)
   282  			if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" {
   283  				resp = nil
   284  			} else {
   285  				log.Printf("[ERROR] Error on IGStateRefresh: %s", err)
   286  				return nil, "", err
   287  			}
   288  		}
   289  
   290  		if resp == nil {
   291  			// Sometimes AWS just has consistency issues and doesn't see
   292  			// our instance yet. Return an empty state.
   293  			return nil, "", nil
   294  		}
   295  
   296  		ig := &resp.InternetGateways[0]
   297  
   298  		if time.Now().Sub(start) > 10*time.Second {
   299  			return ig, expected, nil
   300  		}
   301  
   302  		if len(ig.Attachments) == 0 {
   303  			// No attachments, we're detached
   304  			return ig, "detached", nil
   305  		}
   306  
   307  		return ig, *ig.Attachments[0].State, nil
   308  	}
   309  }