github.com/bengesoff/terraform@v0.3.1-0.20141018223233-b25a53629922/builtin/providers/aws/resource_aws_subnet.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	"github.com/hashicorp/terraform/helper/diff"
     9  	"github.com/hashicorp/terraform/helper/resource"
    10  	"github.com/hashicorp/terraform/terraform"
    11  	"github.com/mitchellh/goamz/ec2"
    12  )
    13  
    14  func resource_aws_subnet_create(
    15  	s *terraform.InstanceState,
    16  	d *terraform.InstanceDiff,
    17  	meta interface{}) (*terraform.InstanceState, error) {
    18  	p := meta.(*ResourceProvider)
    19  	ec2conn := p.ec2conn
    20  
    21  	// Merge the diff so that we have all the proper attributes
    22  	s = s.MergeDiff(d)
    23  
    24  	// Create the Subnet
    25  	createOpts := &ec2.CreateSubnet{
    26  		AvailabilityZone: s.Attributes["availability_zone"],
    27  		CidrBlock:        s.Attributes["cidr_block"],
    28  		VpcId:            s.Attributes["vpc_id"],
    29  	}
    30  	log.Printf("[DEBUG] Subnet create config: %#v", createOpts)
    31  	resp, err := ec2conn.CreateSubnet(createOpts)
    32  	if err != nil {
    33  		return nil, fmt.Errorf("Error creating subnet: %s", err)
    34  	}
    35  
    36  	// Get the ID and store it
    37  	subnet := &resp.Subnet
    38  	s.ID = subnet.SubnetId
    39  	log.Printf("[INFO] Subnet ID: %s", s.ID)
    40  
    41  	// Wait for the Subnet to become available
    42  	log.Printf(
    43  		"[DEBUG] Waiting for subnet (%s) to become available",
    44  		s.ID)
    45  	stateConf := &resource.StateChangeConf{
    46  		Pending: []string{"pending"},
    47  		Target:  "available",
    48  		Refresh: SubnetStateRefreshFunc(ec2conn, s.ID),
    49  		Timeout: 10 * time.Minute,
    50  	}
    51  	subnetRaw, err := stateConf.WaitForState()
    52  	if err != nil {
    53  		return s, fmt.Errorf(
    54  			"Error waiting for subnet (%s) to become available: %s",
    55  			s.ID, err)
    56  	}
    57  
    58  	// Map public ip on launch must be set in another API call
    59  	if attr := s.Attributes["map_public_ip_on_launch"]; attr == "true" {
    60  		modifyOpts := &ec2.ModifySubnetAttribute{
    61  			SubnetId:            s.ID,
    62  			MapPublicIpOnLaunch: true,
    63  		}
    64  		log.Printf("[DEBUG] Subnet modify attributes: %#v", modifyOpts)
    65  		_, err := ec2conn.ModifySubnetAttribute(modifyOpts)
    66  		if err != nil {
    67  			return nil, fmt.Errorf("Error modify subnet attributes: %s", err)
    68  		}
    69  	}
    70  
    71  	// Update our attributes and return
    72  	return resource_aws_subnet_update_state(s, subnetRaw.(*ec2.Subnet))
    73  }
    74  
    75  func resource_aws_subnet_update(
    76  	s *terraform.InstanceState,
    77  	d *terraform.InstanceDiff,
    78  	meta interface{}) (*terraform.InstanceState, error) {
    79  	// This should never be called because we have no update-able
    80  	// attributes
    81  	panic("Update for subnet is not supported")
    82  }
    83  
    84  func resource_aws_subnet_destroy(
    85  	s *terraform.InstanceState,
    86  	meta interface{}) error {
    87  	p := meta.(*ResourceProvider)
    88  	ec2conn := p.ec2conn
    89  
    90  	log.Printf("[INFO] Deleting Subnet: %s", s.ID)
    91  	return resource.Retry(5*time.Minute, func() error {
    92  		_, err := ec2conn.DeleteSubnet(s.ID)
    93  		if err != nil {
    94  			ec2err, ok := err.(*ec2.Error)
    95  			if !ok {
    96  				return err
    97  			}
    98  
    99  			switch ec2err.Code {
   100  			case "InvalidSubnetID.NotFound":
   101  				return nil
   102  			case "DependencyViolation":
   103  				return err // retry
   104  			default:
   105  				return resource.RetryError{err}
   106  			}
   107  		}
   108  
   109  		return fmt.Errorf("Error deleting subnet: %s", err)
   110  	})
   111  
   112  	// Wait for the Subnet to actually delete
   113  	log.Printf("[DEBUG] Waiting for subnet (%s) to delete", s.ID)
   114  	stateConf := &resource.StateChangeConf{
   115  		Pending: []string{"available", "pending"},
   116  		Target:  "",
   117  		Refresh: SubnetStateRefreshFunc(ec2conn, s.ID),
   118  		Timeout: 10 * time.Minute,
   119  	}
   120  	if _, err := stateConf.WaitForState(); err != nil {
   121  		return fmt.Errorf(
   122  			"Error waiting for subnet (%s) to destroy: %s",
   123  			s.ID, err)
   124  	}
   125  
   126  	return nil
   127  }
   128  
   129  func resource_aws_subnet_refresh(
   130  	s *terraform.InstanceState,
   131  	meta interface{}) (*terraform.InstanceState, error) {
   132  	p := meta.(*ResourceProvider)
   133  	ec2conn := p.ec2conn
   134  
   135  	subnetRaw, _, err := SubnetStateRefreshFunc(ec2conn, s.ID)()
   136  	if err != nil {
   137  		return s, err
   138  	}
   139  	if subnetRaw == nil {
   140  		return nil, nil
   141  	}
   142  
   143  	subnet := subnetRaw.(*ec2.Subnet)
   144  	return resource_aws_subnet_update_state(s, subnet)
   145  }
   146  
   147  func resource_aws_subnet_diff(
   148  	s *terraform.InstanceState,
   149  	c *terraform.ResourceConfig,
   150  	meta interface{}) (*terraform.InstanceDiff, error) {
   151  	b := &diff.ResourceBuilder{
   152  		Attrs: map[string]diff.AttrType{
   153  			"availability_zone":       diff.AttrTypeCreate,
   154  			"cidr_block":              diff.AttrTypeCreate,
   155  			"vpc_id":                  diff.AttrTypeCreate,
   156  			"map_public_ip_on_launch": diff.AttrTypeCreate,
   157  		},
   158  
   159  		ComputedAttrs: []string{
   160  			"availability_zone",
   161  		},
   162  	}
   163  
   164  	return b.Diff(s, c)
   165  }
   166  
   167  func resource_aws_subnet_update_state(
   168  	s *terraform.InstanceState,
   169  	subnet *ec2.Subnet) (*terraform.InstanceState, error) {
   170  	s.Attributes["availability_zone"] = subnet.AvailabilityZone
   171  	s.Attributes["cidr_block"] = subnet.CidrBlock
   172  	s.Attributes["vpc_id"] = subnet.VpcId
   173  	if subnet.MapPublicIpOnLaunch {
   174  		s.Attributes["map_public_ip_on_launch"] = "true"
   175  	}
   176  
   177  	return s, nil
   178  }
   179  
   180  // SubnetStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch
   181  // a Subnet.
   182  func SubnetStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc {
   183  	return func() (interface{}, string, error) {
   184  		resp, err := conn.DescribeSubnets([]string{id}, ec2.NewFilter())
   185  		if err != nil {
   186  			if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidSubnetID.NotFound" {
   187  				resp = nil
   188  			} else {
   189  				log.Printf("Error on SubnetStateRefresh: %s", err)
   190  				return nil, "", err
   191  			}
   192  		}
   193  
   194  		if resp == nil {
   195  			// Sometimes AWS just has consistency issues and doesn't see
   196  			// our instance yet. Return an empty state.
   197  			return nil, "", nil
   198  		}
   199  
   200  		subnet := &resp.Subnets[0]
   201  		return subnet, subnet.State, nil
   202  	}
   203  }