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