github.com/i0n/terraform@v0.4.3-0.20150506151324-010a39a58ec1/builtin/providers/aws/resource_aws_vpc_dhcp_options.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 8 9 "github.com/awslabs/aws-sdk-go/aws" 10 "github.com/awslabs/aws-sdk-go/service/ec2" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceAwsVpcDhcpOptions() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceAwsVpcDhcpOptionsCreate, 18 Read: resourceAwsVpcDhcpOptionsRead, 19 Update: resourceAwsVpcDhcpOptionsUpdate, 20 Delete: resourceAwsVpcDhcpOptionsDelete, 21 22 Schema: map[string]*schema.Schema{ 23 "domain_name": &schema.Schema{ 24 Type: schema.TypeString, 25 Optional: true, 26 ForceNew: true, 27 }, 28 29 "domain_name_servers": &schema.Schema{ 30 Type: schema.TypeList, 31 Optional: true, 32 ForceNew: true, 33 Elem: &schema.Schema{Type: schema.TypeString}, 34 }, 35 36 "ntp_servers": &schema.Schema{ 37 Type: schema.TypeList, 38 Optional: true, 39 ForceNew: true, 40 Elem: &schema.Schema{Type: schema.TypeString}, 41 }, 42 43 "netbios_node_type": &schema.Schema{ 44 Type: schema.TypeString, 45 Optional: true, 46 ForceNew: true, 47 }, 48 49 "netbios_name_servers": &schema.Schema{ 50 Type: schema.TypeList, 51 Optional: true, 52 ForceNew: true, 53 Elem: &schema.Schema{Type: schema.TypeString}, 54 }, 55 56 "tags": &schema.Schema{ 57 Type: schema.TypeMap, 58 Optional: true, 59 }, 60 }, 61 } 62 } 63 64 func resourceAwsVpcDhcpOptionsCreate(d *schema.ResourceData, meta interface{}) error { 65 conn := meta.(*AWSClient).ec2conn 66 67 setDHCPOption := func(key string) *ec2.NewDHCPConfiguration { 68 log.Printf("[DEBUG] Setting DHCP option %s...", key) 69 tfKey := strings.Replace(key, "-", "_", -1) 70 71 value, ok := d.GetOk(tfKey) 72 if !ok { 73 return nil 74 } 75 76 if v, ok := value.(string); ok { 77 return &ec2.NewDHCPConfiguration{ 78 Key: aws.String(key), 79 Values: []*string{ 80 aws.String(v), 81 }, 82 } 83 } 84 85 if v, ok := value.([]interface{}); ok { 86 var s []*string 87 for _, attr := range v { 88 s = append(s, aws.String(attr.(string))) 89 } 90 91 return &ec2.NewDHCPConfiguration{ 92 Key: aws.String(key), 93 Values: s, 94 } 95 } 96 97 return nil 98 } 99 100 createOpts := &ec2.CreateDHCPOptionsInput{ 101 DHCPConfigurations: []*ec2.NewDHCPConfiguration{ 102 setDHCPOption("domain-name"), 103 setDHCPOption("domain-name-servers"), 104 setDHCPOption("ntp-servers"), 105 setDHCPOption("netbios-node-type"), 106 setDHCPOption("netbios-name-servers"), 107 }, 108 } 109 110 resp, err := conn.CreateDHCPOptions(createOpts) 111 if err != nil { 112 return fmt.Errorf("Error creating DHCP Options Set: %s", err) 113 } 114 115 dos := resp.DHCPOptions 116 d.SetId(*dos.DHCPOptionsID) 117 log.Printf("[INFO] DHCP Options Set ID: %s", d.Id()) 118 119 // Wait for the DHCP Options to become available 120 log.Printf("[DEBUG] Waiting for DHCP Options (%s) to become available", d.Id()) 121 stateConf := &resource.StateChangeConf{ 122 Pending: []string{"pending"}, 123 Target: "", 124 Refresh: DHCPOptionsStateRefreshFunc(conn, d.Id()), 125 Timeout: 1 * time.Minute, 126 } 127 if _, err := stateConf.WaitForState(); err != nil { 128 return fmt.Errorf( 129 "Error waiting for DHCP Options (%s) to become available: %s", 130 d.Id(), err) 131 } 132 133 return resourceAwsVpcDhcpOptionsUpdate(d, meta) 134 } 135 136 func resourceAwsVpcDhcpOptionsRead(d *schema.ResourceData, meta interface{}) error { 137 conn := meta.(*AWSClient).ec2conn 138 req := &ec2.DescribeDHCPOptionsInput{ 139 DHCPOptionsIDs: []*string{ 140 aws.String(d.Id()), 141 }, 142 } 143 144 resp, err := conn.DescribeDHCPOptions(req) 145 if err != nil { 146 return fmt.Errorf("Error retrieving DHCP Options: %s", err) 147 } 148 149 if len(resp.DHCPOptions) == 0 { 150 return nil 151 } 152 153 opts := resp.DHCPOptions[0] 154 d.Set("tags", tagsToMapSDK(opts.Tags)) 155 156 for _, cfg := range opts.DHCPConfigurations { 157 tfKey := strings.Replace(*cfg.Key, "-", "_", -1) 158 159 if _, ok := d.Get(tfKey).(string); ok { 160 d.Set(tfKey, cfg.Values[0].Value) 161 } else { 162 values := make([]string, 0, len(cfg.Values)) 163 for _, v := range cfg.Values { 164 values = append(values, *v.Value) 165 } 166 167 d.Set(tfKey, values) 168 } 169 } 170 171 return nil 172 } 173 174 func resourceAwsVpcDhcpOptionsUpdate(d *schema.ResourceData, meta interface{}) error { 175 conn := meta.(*AWSClient).ec2conn 176 return setTagsSDK(conn, d) 177 } 178 179 func resourceAwsVpcDhcpOptionsDelete(d *schema.ResourceData, meta interface{}) error { 180 conn := meta.(*AWSClient).ec2conn 181 182 return resource.Retry(3*time.Minute, func() error { 183 log.Printf("[INFO] Deleting DHCP Options ID %s...", d.Id()) 184 _, err := conn.DeleteDHCPOptions(&ec2.DeleteDHCPOptionsInput{ 185 DHCPOptionsID: aws.String(d.Id()), 186 }) 187 188 if err == nil { 189 return nil 190 } 191 192 log.Printf("[WARN] %s", err) 193 194 ec2err, ok := err.(aws.APIError) 195 if !ok { 196 return err 197 } 198 199 switch ec2err.Code { 200 case "InvalidDhcpOptionsID.NotFound": 201 return nil 202 case "DependencyViolation": 203 // If it is a dependency violation, we want to disassociate 204 // all VPCs using the given DHCP Options ID, and retry deleting. 205 vpcs, err2 := findVPCsByDHCPOptionsID(conn, d.Id()) 206 if err2 != nil { 207 log.Printf("[ERROR] %s", err2) 208 return err2 209 } 210 211 for _, vpc := range vpcs { 212 log.Printf("[INFO] Disassociating DHCP Options Set %s from VPC %s...", d.Id(), *vpc.VPCID) 213 if _, err := conn.AssociateDHCPOptions(&ec2.AssociateDHCPOptionsInput{ 214 DHCPOptionsID: aws.String("default"), 215 VPCID: vpc.VPCID, 216 }); err != nil { 217 return err 218 } 219 } 220 return err //retry 221 default: 222 // Any other error, we want to quit the retry loop immediately 223 return resource.RetryError{Err: err} 224 } 225 226 return nil 227 }) 228 } 229 230 func findVPCsByDHCPOptionsID(conn *ec2.EC2, id string) ([]*ec2.VPC, error) { 231 req := &ec2.DescribeVPCsInput{ 232 Filters: []*ec2.Filter{ 233 &ec2.Filter{ 234 Name: aws.String("dhcp-options-id"), 235 Values: []*string{ 236 aws.String(id), 237 }, 238 }, 239 }, 240 } 241 242 resp, err := conn.DescribeVPCs(req) 243 if err != nil { 244 if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidVpcID.NotFound" { 245 return nil, nil 246 } 247 return nil, err 248 } 249 250 return resp.VPCs, nil 251 } 252 253 func DHCPOptionsStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { 254 return func() (interface{}, string, error) { 255 DescribeDhcpOpts := &ec2.DescribeDHCPOptionsInput{ 256 DHCPOptionsIDs: []*string{ 257 aws.String(id), 258 }, 259 } 260 261 resp, err := conn.DescribeDHCPOptions(DescribeDhcpOpts) 262 if err != nil { 263 if ec2err, ok := err.(aws.APIError); ok && ec2err.Code == "InvalidDhcpOptionsID.NotFound" { 264 resp = nil 265 } else { 266 log.Printf("Error on DHCPOptionsStateRefresh: %s", err) 267 return nil, "", err 268 } 269 } 270 271 if resp == nil { 272 // Sometimes AWS just has consistency issues and doesn't see 273 // our instance yet. Return an empty state. 274 return nil, "", nil 275 } 276 277 dos := resp.DHCPOptions[0] 278 return dos, "", nil 279 } 280 }