github.com/adamar/terraform@v0.2.2-0.20141016210445-2e703afdad0e/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/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_internet_gateway_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 // Create the gateway 22 log.Printf("[DEBUG] Creating internet gateway") 23 resp, err := ec2conn.CreateInternetGateway(nil) 24 if err != nil { 25 return nil, fmt.Errorf("Error creating subnet: %s", err) 26 } 27 28 // Get the ID and store it 29 ig := &resp.InternetGateway 30 s.ID = ig.InternetGatewayId 31 log.Printf("[INFO] InternetGateway ID: %s", s.ID) 32 33 // Update our attributes and return 34 return resource_aws_internet_gateway_update(s, d, meta) 35 } 36 37 func resource_aws_internet_gateway_update( 38 s *terraform.InstanceState, 39 d *terraform.InstanceDiff, 40 meta interface{}) (*terraform.InstanceState, error) { 41 p := meta.(*ResourceProvider) 42 ec2conn := p.ec2conn 43 44 // Merge the diff so we have the latest attributes 45 rs := s.MergeDiff(d) 46 47 // A note on the states below: the AWS docs (as of July, 2014) say 48 // that the states would be: attached, attaching, detached, detaching, 49 // but when running, I noticed that the state is usually "available" when 50 // it is attached. 51 52 // If we're already attached, detach it first 53 if err := resource_aws_internet_gateway_detach(ec2conn, s); err != nil { 54 return s, err 55 } 56 57 // Set the VPC ID to empty since we're detached at this point 58 delete(rs.Attributes, "vpc_id") 59 60 if attr, ok := d.Attributes["vpc_id"]; ok && attr.New != "" { 61 err := resource_aws_internet_gateway_attach(ec2conn, s, attr.New) 62 if err != nil { 63 return rs, err 64 } 65 66 rs.Attributes["vpc_id"] = attr.New 67 } 68 69 return resource_aws_internet_gateway_update_state(rs, nil) 70 } 71 72 func resource_aws_internet_gateway_destroy( 73 s *terraform.InstanceState, 74 meta interface{}) error { 75 p := meta.(*ResourceProvider) 76 ec2conn := p.ec2conn 77 78 // Detach if it is attached 79 if err := resource_aws_internet_gateway_detach(ec2conn, s); err != nil { 80 return err 81 } 82 83 log.Printf("[INFO] Deleting Internet Gateway: %s", s.ID) 84 if _, err := ec2conn.DeleteInternetGateway(s.ID); err != nil { 85 ec2err, ok := err.(*ec2.Error) 86 if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" { 87 return nil 88 } 89 90 return fmt.Errorf("Error deleting internet gateway: %s", err) 91 } 92 93 // Wait for the internet gateway to actually delete 94 log.Printf("[DEBUG] Waiting for internet gateway (%s) to delete", s.ID) 95 stateConf := &resource.StateChangeConf{ 96 Pending: []string{"available"}, 97 Target: "", 98 Refresh: IGStateRefreshFunc(ec2conn, s.ID), 99 Timeout: 10 * time.Minute, 100 } 101 if _, err := stateConf.WaitForState(); err != nil { 102 return fmt.Errorf( 103 "Error waiting for internet gateway (%s) to destroy: %s", 104 s.ID, err) 105 } 106 107 return nil 108 } 109 110 func resource_aws_internet_gateway_refresh( 111 s *terraform.InstanceState, 112 meta interface{}) (*terraform.InstanceState, error) { 113 p := meta.(*ResourceProvider) 114 ec2conn := p.ec2conn 115 116 igRaw, _, err := IGStateRefreshFunc(ec2conn, s.ID)() 117 if err != nil { 118 return s, err 119 } 120 if igRaw == nil { 121 return nil, nil 122 } 123 124 ig := igRaw.(*ec2.InternetGateway) 125 return resource_aws_internet_gateway_update_state(s, ig) 126 } 127 128 func resource_aws_internet_gateway_diff( 129 s *terraform.InstanceState, 130 c *terraform.ResourceConfig, 131 meta interface{}) (*terraform.InstanceDiff, error) { 132 b := &diff.ResourceBuilder{ 133 Attrs: map[string]diff.AttrType{ 134 "vpc_id": diff.AttrTypeUpdate, 135 }, 136 } 137 138 return b.Diff(s, c) 139 } 140 141 func resource_aws_internet_gateway_attach( 142 ec2conn *ec2.EC2, 143 s *terraform.InstanceState, 144 vpcId string) error { 145 log.Printf( 146 "[INFO] Attaching Internet Gateway '%s' to VPC '%s'", 147 s.ID, 148 vpcId) 149 _, err := ec2conn.AttachInternetGateway(s.ID, vpcId) 150 if err != nil { 151 return err 152 } 153 154 // Wait for it to be fully attached before continuing 155 log.Printf("[DEBUG] Waiting for internet gateway (%s) to attach", s.ID) 156 stateConf := &resource.StateChangeConf{ 157 Pending: []string{"detached", "attaching"}, 158 Target: "available", 159 Refresh: IGAttachStateRefreshFunc(ec2conn, s.ID, "available"), 160 Timeout: 1 * time.Minute, 161 } 162 if _, err := stateConf.WaitForState(); err != nil { 163 return fmt.Errorf( 164 "Error waiting for internet gateway (%s) to attach: %s", 165 s.ID, err) 166 } 167 168 return nil 169 } 170 171 func resource_aws_internet_gateway_detach( 172 ec2conn *ec2.EC2, 173 s *terraform.InstanceState) error { 174 if s.Attributes["vpc_id"] == "" { 175 return nil 176 } 177 178 log.Printf( 179 "[INFO] Detaching Internet Gateway '%s' from VPC '%s'", 180 s.ID, 181 s.Attributes["vpc_id"]) 182 wait := true 183 _, err := ec2conn.DetachInternetGateway(s.ID, s.Attributes["vpc_id"]) 184 if err != nil { 185 ec2err, ok := err.(*ec2.Error) 186 if ok { 187 if ec2err.Code == "InvalidInternetGatewayID.NotFound" { 188 err = nil 189 wait = false 190 } else if ec2err.Code == "Gateway.NotAttached" { 191 err = nil 192 wait = false 193 } 194 } 195 196 if err != nil { 197 return err 198 } 199 } 200 201 delete(s.Attributes, "vpc_id") 202 203 if !wait { 204 return nil 205 } 206 207 // Wait for it to be fully detached before continuing 208 log.Printf("[DEBUG] Waiting for internet gateway (%s) to detach", s.ID) 209 stateConf := &resource.StateChangeConf{ 210 Pending: []string{"attached", "detaching", "available"}, 211 Target: "detached", 212 Refresh: IGAttachStateRefreshFunc(ec2conn, s.ID, "detached"), 213 Timeout: 1 * time.Minute, 214 } 215 if _, err := stateConf.WaitForState(); err != nil { 216 return fmt.Errorf( 217 "Error waiting for internet gateway (%s) to detach: %s", 218 s.ID, err) 219 } 220 221 return nil 222 } 223 224 func resource_aws_internet_gateway_update_state( 225 s *terraform.InstanceState, 226 ig *ec2.InternetGateway) (*terraform.InstanceState, error) { 227 return s, nil 228 } 229 230 // IGStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 231 // an internet gateway. 232 func IGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 233 return func() (interface{}, string, error) { 234 resp, err := conn.DescribeInternetGateways([]string{id}, ec2.NewFilter()) 235 if err != nil { 236 ec2err, ok := err.(*ec2.Error) 237 if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" { 238 resp = nil 239 } else { 240 log.Printf("[ERROR] Error on IGStateRefresh: %s", err) 241 return nil, "", err 242 } 243 } 244 245 if resp == nil { 246 // Sometimes AWS just has consistency issues and doesn't see 247 // our instance yet. Return an empty state. 248 return nil, "", nil 249 } 250 251 ig := &resp.InternetGateways[0] 252 return ig, "available", nil 253 } 254 } 255 256 // IGAttachStateRefreshFunc returns a resource.StateRefreshFunc that is used 257 // watch the state of an internet gateway's attachment. 258 func IGAttachStateRefreshFunc(conn *ec2.EC2, id string, expected string) resource.StateRefreshFunc { 259 var start time.Time 260 return func() (interface{}, string, error) { 261 if start.IsZero() { 262 start = time.Now() 263 } 264 265 resp, err := conn.DescribeInternetGateways([]string{id}, ec2.NewFilter()) 266 if err != nil { 267 ec2err, ok := err.(*ec2.Error) 268 if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" { 269 resp = nil 270 } else { 271 log.Printf("[ERROR] Error on IGStateRefresh: %s", err) 272 return nil, "", err 273 } 274 } 275 276 if resp == nil { 277 // Sometimes AWS just has consistency issues and doesn't see 278 // our instance yet. Return an empty state. 279 return nil, "", nil 280 } 281 282 ig := &resp.InternetGateways[0] 283 284 if time.Now().Sub(start) > 10*time.Second { 285 return ig, expected, nil 286 } 287 288 if len(ig.Attachments) == 0 { 289 // No attachments, we're detached 290 return ig, "detached", nil 291 } 292 293 return ig, ig.Attachments[0].State, nil 294 } 295 }