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