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