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