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  }