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