github.com/jefferai/terraform@v0.3.7-0.20150310153852-f7512ca29fcf/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/aws-sdk-go/aws" 9 "github.com/hashicorp/aws-sdk-go/gen/ec2" 10 "github.com/hashicorp/terraform/helper/resource" 11 "github.com/hashicorp/terraform/helper/schema" 12 ) 13 14 func resourceAwsInternetGateway() *schema.Resource { 15 return &schema.Resource{ 16 Create: resourceAwsInternetGatewayCreate, 17 Read: resourceAwsInternetGatewayRead, 18 Update: resourceAwsInternetGatewayUpdate, 19 Delete: resourceAwsInternetGatewayDelete, 20 21 Schema: map[string]*schema.Schema{ 22 "vpc_id": &schema.Schema{ 23 Type: schema.TypeString, 24 Optional: true, 25 }, 26 "tags": tagsSchema(), 27 }, 28 } 29 } 30 31 func resourceAwsInternetGatewayCreate(d *schema.ResourceData, meta interface{}) error { 32 ec2conn := meta.(*AWSClient).awsEC2conn 33 34 // Create the gateway 35 log.Printf("[DEBUG] Creating internet gateway") 36 resp, err := ec2conn.CreateInternetGateway(nil) 37 if err != nil { 38 return fmt.Errorf("Error creating internet gateway: %s", err) 39 } 40 41 // Get the ID and store it 42 ig := resp.InternetGateway 43 d.SetId(*ig.InternetGatewayID) 44 log.Printf("[INFO] InternetGateway ID: %s", d.Id()) 45 46 // Attach the new gateway to the correct vpc 47 return resourceAwsInternetGatewayAttach(d, meta) 48 } 49 50 func resourceAwsInternetGatewayRead(d *schema.ResourceData, meta interface{}) error { 51 ec2conn := meta.(*AWSClient).awsEC2conn 52 53 igRaw, _, err := IGStateRefreshFunc(ec2conn, d.Id())() 54 if err != nil { 55 return err 56 } 57 if igRaw == nil { 58 // Seems we have lost our internet gateway 59 d.SetId("") 60 return nil 61 } 62 63 ig := igRaw.(*ec2.InternetGateway) 64 if len(ig.Attachments) == 0 { 65 // Gateway exists but not attached to the VPC 66 d.Set("vpc_id", "") 67 } else { 68 d.Set("vpc_id", ig.Attachments[0].VPCID) 69 } 70 71 d.Set("tags", tagsToMapSDK(ig.Tags)) 72 73 return nil 74 } 75 76 func resourceAwsInternetGatewayUpdate(d *schema.ResourceData, meta interface{}) error { 77 if d.HasChange("vpc_id") { 78 // If we're already attached, detach it first 79 if err := resourceAwsInternetGatewayDetach(d, meta); err != nil { 80 return err 81 } 82 83 // Attach the gateway to the new vpc 84 if err := resourceAwsInternetGatewayAttach(d, meta); err != nil { 85 return err 86 } 87 } 88 89 ec2conn := meta.(*AWSClient).awsEC2conn 90 91 if err := setTagsSDK(ec2conn, d); err != nil { 92 return err 93 } 94 95 d.SetPartial("tags") 96 97 return nil 98 } 99 100 func resourceAwsInternetGatewayDelete(d *schema.ResourceData, meta interface{}) error { 101 ec2conn := meta.(*AWSClient).awsEC2conn 102 103 // Detach if it is attached 104 if err := resourceAwsInternetGatewayDetach(d, meta); err != nil { 105 return err 106 } 107 108 log.Printf("[INFO] Deleting Internet Gateway: %s", d.Id()) 109 110 return resource.Retry(5*time.Minute, func() error { 111 err := ec2conn.DeleteInternetGateway(&ec2.DeleteInternetGatewayRequest{ 112 InternetGatewayID: aws.String(d.Id()), 113 }) 114 if err == nil { 115 return nil 116 } 117 118 ec2err, ok := err.(aws.APIError) 119 if !ok { 120 return err 121 } 122 123 switch ec2err.Code { 124 case "InvalidInternetGatewayID.NotFound": 125 return nil 126 case "DependencyViolation": 127 return err // retry 128 } 129 130 return resource.RetryError{Err: err} 131 }) 132 } 133 134 func resourceAwsInternetGatewayAttach(d *schema.ResourceData, meta interface{}) error { 135 ec2conn := meta.(*AWSClient).awsEC2conn 136 137 if d.Get("vpc_id").(string) == "" { 138 log.Printf( 139 "[DEBUG] Not attaching Internet Gateway '%s' as no VPC ID is set", 140 d.Id()) 141 return nil 142 } 143 144 log.Printf( 145 "[INFO] Attaching Internet Gateway '%s' to VPC '%s'", 146 d.Id(), 147 d.Get("vpc_id").(string)) 148 149 err := ec2conn.AttachInternetGateway(&ec2.AttachInternetGatewayRequest{ 150 InternetGatewayID: aws.String(d.Id()), 151 VPCID: aws.String(d.Get("vpc_id").(string)), 152 }) 153 if err != nil { 154 return err 155 } 156 157 // A note on the states below: the AWS docs (as of July, 2014) say 158 // that the states would be: attached, attaching, detached, detaching, 159 // but when running, I noticed that the state is usually "available" when 160 // it is attached. 161 162 // Wait for it to be fully attached before continuing 163 log.Printf("[DEBUG] Waiting for internet gateway (%s) to attach", d.Id()) 164 stateConf := &resource.StateChangeConf{ 165 Pending: []string{"detached", "attaching"}, 166 Target: "available", 167 Refresh: IGAttachStateRefreshFunc(ec2conn, d.Id(), "available"), 168 Timeout: 1 * time.Minute, 169 } 170 if _, err := stateConf.WaitForState(); err != nil { 171 return fmt.Errorf( 172 "Error waiting for internet gateway (%s) to attach: %s", 173 d.Id(), err) 174 } 175 176 return nil 177 } 178 179 func resourceAwsInternetGatewayDetach(d *schema.ResourceData, meta interface{}) error { 180 ec2conn := meta.(*AWSClient).awsEC2conn 181 182 // Get the old VPC ID to detach from 183 vpcID, _ := d.GetChange("vpc_id") 184 185 if vpcID.(string) == "" { 186 log.Printf( 187 "[DEBUG] Not detaching Internet Gateway '%s' as no VPC ID is set", 188 d.Id()) 189 return nil 190 } 191 192 log.Printf( 193 "[INFO] Detaching Internet Gateway '%s' from VPC '%s'", 194 d.Id(), 195 vpcID.(string)) 196 197 wait := true 198 err := ec2conn.DetachInternetGateway(&ec2.DetachInternetGatewayRequest{ 199 InternetGatewayID: aws.String(d.Id()), 200 VPCID: aws.String(vpcID.(string)), 201 }) 202 if err != nil { 203 ec2err, ok := err.(aws.APIError) 204 if ok { 205 if ec2err.Code == "InvalidInternetGatewayID.NotFound" { 206 err = nil 207 wait = false 208 } else if ec2err.Code == "Gateway.NotAttached" { 209 err = nil 210 wait = false 211 } 212 } 213 214 if err != nil { 215 return err 216 } 217 } 218 219 if !wait { 220 return nil 221 } 222 223 // Wait for it to be fully detached before continuing 224 log.Printf("[DEBUG] Waiting for internet gateway (%s) to detach", d.Id()) 225 stateConf := &resource.StateChangeConf{ 226 Pending: []string{"attached", "detaching", "available"}, 227 Target: "detached", 228 Refresh: IGAttachStateRefreshFunc(ec2conn, d.Id(), "detached"), 229 Timeout: 1 * time.Minute, 230 } 231 if _, err := stateConf.WaitForState(); err != nil { 232 return fmt.Errorf( 233 "Error waiting for internet gateway (%s) to detach: %s", 234 d.Id(), err) 235 } 236 237 return nil 238 } 239 240 // IGStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch 241 // an internet gateway. 242 func IGStateRefreshFunc(ec2conn *ec2.EC2, id string) resource.StateRefreshFunc { 243 return func() (interface{}, string, error) { 244 resp, err := ec2conn.DescribeInternetGateways(&ec2.DescribeInternetGatewaysRequest{ 245 InternetGatewayIDs: []string{id}, 246 }) 247 if err != nil { 248 ec2err, ok := err.(aws.APIError) 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(ec2conn *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 := ec2conn.DescribeInternetGateways(&ec2.DescribeInternetGatewaysRequest{ 278 InternetGatewayIDs: []string{id}, 279 }) 280 if err != nil { 281 ec2err, ok := err.(aws.APIError) 282 if ok && ec2err.Code == "InvalidInternetGatewayID.NotFound" { 283 resp = nil 284 } else { 285 log.Printf("[ERROR] Error on IGStateRefresh: %s", err) 286 return nil, "", err 287 } 288 } 289 290 if resp == nil { 291 // Sometimes AWS just has consistency issues and doesn't see 292 // our instance yet. Return an empty state. 293 return nil, "", nil 294 } 295 296 ig := &resp.InternetGateways[0] 297 298 if time.Now().Sub(start) > 10*time.Second { 299 return ig, expected, nil 300 } 301 302 if len(ig.Attachments) == 0 { 303 // No attachments, we're detached 304 return ig, "detached", nil 305 } 306 307 return ig, *ig.Attachments[0].State, nil 308 } 309 }