github.com/bengesoff/terraform@v0.3.1-0.20141018223233-b25a53629922/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 return resource.Retry(5*time.Minute, func() error { 85 _, err := ec2conn.DeleteInternetGateway(s.ID) 86 if err != nil { 87 ec2err, ok := err.(*ec2.Error) 88 if !ok { 89 return err 90 } 91 92 switch ec2err.Code { 93 case "InvalidInternetGatewayID.NotFound": 94 return nil 95 case "DependencyViolation": 96 return err // retry 97 default: 98 return resource.RetryError{err} 99 } 100 } 101 102 return fmt.Errorf("Error deleting internet gateway: %s", err) 103 }) 104 105 // Wait for the internet gateway to actually delete 106 log.Printf("[DEBUG] Waiting for internet gateway (%s) to delete", s.ID) 107 stateConf := &resource.StateChangeConf{ 108 Pending: []string{"available"}, 109 Target: "", 110 Refresh: IGStateRefreshFunc(ec2conn, s.ID), 111 Timeout: 10 * time.Minute, 112 } 113 if _, err := stateConf.WaitForState(); err != nil { 114 return fmt.Errorf( 115 "Error waiting for internet gateway (%s) to destroy: %s", 116 s.ID, err) 117 } 118 119 return nil 120 } 121 122 func resource_aws_internet_gateway_refresh( 123 s *terraform.InstanceState, 124 meta interface{}) (*terraform.InstanceState, error) { 125 p := meta.(*ResourceProvider) 126 ec2conn := p.ec2conn 127 128 igRaw, _, err := IGStateRefreshFunc(ec2conn, s.ID)() 129 if err != nil { 130 return s, err 131 } 132 if igRaw == nil { 133 return nil, nil 134 } 135 136 ig := igRaw.(*ec2.InternetGateway) 137 return resource_aws_internet_gateway_update_state(s, ig) 138 } 139 140 func resource_aws_internet_gateway_diff( 141 s *terraform.InstanceState, 142 c *terraform.ResourceConfig, 143 meta interface{}) (*terraform.InstanceDiff, error) { 144 b := &diff.ResourceBuilder{ 145 Attrs: map[string]diff.AttrType{ 146 "vpc_id": diff.AttrTypeUpdate, 147 }, 148 } 149 150 return b.Diff(s, c) 151 } 152 153 func resource_aws_internet_gateway_attach( 154 ec2conn *ec2.EC2, 155 s *terraform.InstanceState, 156 vpcId string) error { 157 log.Printf( 158 "[INFO] Attaching Internet Gateway '%s' to VPC '%s'", 159 s.ID, 160 vpcId) 161 _, err := ec2conn.AttachInternetGateway(s.ID, vpcId) 162 if err != nil { 163 return err 164 } 165 166 // Wait for it to be fully attached before continuing 167 log.Printf("[DEBUG] Waiting for internet gateway (%s) to attach", s.ID) 168 stateConf := &resource.StateChangeConf{ 169 Pending: []string{"detached", "attaching"}, 170 Target: "available", 171 Refresh: IGAttachStateRefreshFunc(ec2conn, s.ID, "available"), 172 Timeout: 1 * time.Minute, 173 } 174 if _, err := stateConf.WaitForState(); err != nil { 175 return fmt.Errorf( 176 "Error waiting for internet gateway (%s) to attach: %s", 177 s.ID, err) 178 } 179 180 return nil 181 } 182 183 func resource_aws_internet_gateway_detach( 184 ec2conn *ec2.EC2, 185 s *terraform.InstanceState) error { 186 if s.Attributes["vpc_id"] == "" { 187 return nil 188 } 189 190 log.Printf( 191 "[INFO] Detaching Internet Gateway '%s' from VPC '%s'", 192 s.ID, 193 s.Attributes["vpc_id"]) 194 wait := true 195 _, err := ec2conn.DetachInternetGateway(s.ID, s.Attributes["vpc_id"]) 196 if err != nil { 197 ec2err, ok := err.(*ec2.Error) 198 if ok { 199 if ec2err.Code == "InvalidInternetGatewayID.NotFound" { 200 err = nil 201 wait = false 202 } else if ec2err.Code == "Gateway.NotAttached" { 203 err = nil 204 wait = false 205 } 206 } 207 208 if err != nil { 209 return err 210 } 211 } 212 213 delete(s.Attributes, "vpc_id") 214 215 if !wait { 216 return nil 217 } 218 219 // Wait for it to be fully detached before continuing 220 log.Printf("[DEBUG] Waiting for internet gateway (%s) to detach", s.ID) 221 stateConf := &resource.StateChangeConf{ 222 Pending: []string{"attached", "detaching", "available"}, 223 Target: "detached", 224 Refresh: IGAttachStateRefreshFunc(ec2conn, s.ID, "detached"), 225 Timeout: 1 * time.Minute, 226 } 227 if _, err := stateConf.WaitForState(); err != nil { 228 return fmt.Errorf( 229 "Error waiting for internet gateway (%s) to detach: %s", 230 s.ID, err) 231 } 232 233 return nil 234 } 235 236 func resource_aws_internet_gateway_update_state( 237 s *terraform.InstanceState, 238 ig *ec2.InternetGateway) (*terraform.InstanceState, error) { 239 return s, nil 240 } 241 242 // IGStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 243 // an internet gateway. 244 func IGStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 245 return func() (interface{}, string, error) { 246 resp, err := conn.DescribeInternetGateways([]string{id}, ec2.NewFilter()) 247 if err != nil { 248 ec2err, ok := err.(*ec2.Error) 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(conn *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 := conn.DescribeInternetGateways([]string{id}, ec2.NewFilter()) 278 if err != nil { 279 ec2err, ok := err.(*ec2.Error) 280 if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" { 281 resp = nil 282 } else { 283 log.Printf("[ERROR] Error on IGStateRefresh: %s", err) 284 return nil, "", err 285 } 286 } 287 288 if resp == nil { 289 // Sometimes AWS just has consistency issues and doesn't see 290 // our instance yet. Return an empty state. 291 return nil, "", nil 292 } 293 294 ig := &resp.InternetGateways[0] 295 296 if time.Now().Sub(start) > 10*time.Second { 297 return ig, expected, nil 298 } 299 300 if len(ig.Attachments) == 0 { 301 // No attachments, we're detached 302 return ig, "detached", nil 303 } 304 305 return ig, ig.Attachments[0].State, nil 306 } 307 }