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