github.com/adamar/terraform@v0.2.2-0.20141016210445-2e703afdad0e/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 f := func() error { 92 _, err := ec2conn.DeleteSubnet(s.ID) 93 return err 94 } 95 if err := resource.Retry(10*time.Second, f); err != nil { 96 ec2err, ok := err.(*ec2.Error) 97 if ok && ec2err.Code == "InvalidSubnetID.NotFound" { 98 return nil 99 } 100 101 return fmt.Errorf("Error deleting subnet: %s", err) 102 } 103 104 // Wait for the Subnet to actually delete 105 log.Printf("[DEBUG] Waiting for subnet (%s) to delete", s.ID) 106 stateConf := &resource.StateChangeConf{ 107 Pending: []string{"available", "pending"}, 108 Target: "", 109 Refresh: SubnetStateRefreshFunc(ec2conn, s.ID), 110 Timeout: 10 * time.Minute, 111 } 112 if _, err := stateConf.WaitForState(); err != nil { 113 return fmt.Errorf( 114 "Error waiting for subnet (%s) to destroy: %s", 115 s.ID, err) 116 } 117 118 return nil 119 } 120 121 func resource_aws_subnet_refresh( 122 s *terraform.InstanceState, 123 meta interface{}) (*terraform.InstanceState, error) { 124 p := meta.(*ResourceProvider) 125 ec2conn := p.ec2conn 126 127 subnetRaw, _, err := SubnetStateRefreshFunc(ec2conn, s.ID)() 128 if err != nil { 129 return s, err 130 } 131 if subnetRaw == nil { 132 return nil, nil 133 } 134 135 subnet := subnetRaw.(*ec2.Subnet) 136 return resource_aws_subnet_update_state(s, subnet) 137 } 138 139 func resource_aws_subnet_diff( 140 s *terraform.InstanceState, 141 c *terraform.ResourceConfig, 142 meta interface{}) (*terraform.InstanceDiff, error) { 143 b := &diff.ResourceBuilder{ 144 Attrs: map[string]diff.AttrType{ 145 "availability_zone": diff.AttrTypeCreate, 146 "cidr_block": diff.AttrTypeCreate, 147 "vpc_id": diff.AttrTypeCreate, 148 "map_public_ip_on_launch": diff.AttrTypeCreate, 149 }, 150 151 ComputedAttrs: []string{ 152 "availability_zone", 153 }, 154 } 155 156 return b.Diff(s, c) 157 } 158 159 func resource_aws_subnet_update_state( 160 s *terraform.InstanceState, 161 subnet *ec2.Subnet) (*terraform.InstanceState, error) { 162 s.Attributes["availability_zone"] = subnet.AvailabilityZone 163 s.Attributes["cidr_block"] = subnet.CidrBlock 164 s.Attributes["vpc_id"] = subnet.VpcId 165 if subnet.MapPublicIpOnLaunch { 166 s.Attributes["map_public_ip_on_launch"] = "true" 167 } 168 169 return s, nil 170 } 171 172 // SubnetStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 173 // a Subnet. 174 func SubnetStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 175 return func() (interface{}, string, error) { 176 resp, err := conn.DescribeSubnets([]string{id}, ec2.NewFilter()) 177 if err != nil { 178 if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidSubnetID.NotFound" { 179 resp = nil 180 } else { 181 log.Printf("Error on SubnetStateRefresh: %s", err) 182 return nil, "", err 183 } 184 } 185 186 if resp == nil { 187 // Sometimes AWS just has consistency issues and doesn't see 188 // our instance yet. Return an empty state. 189 return nil, "", nil 190 } 191 192 subnet := &resp.Subnets[0] 193 return subnet, subnet.State, nil 194 } 195 }