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