github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/builtin/providers/aws/resource_aws_db_parameter_group.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"regexp"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/hashicorp/terraform/helper/hashcode"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  
    15  	"github.com/aws/aws-sdk-go/aws"
    16  	"github.com/aws/aws-sdk-go/aws/awserr"
    17  	"github.com/aws/aws-sdk-go/service/rds"
    18  )
    19  
    20  func resourceAwsDbParameterGroup() *schema.Resource {
    21  	return &schema.Resource{
    22  		Create: resourceAwsDbParameterGroupCreate,
    23  		Read:   resourceAwsDbParameterGroupRead,
    24  		Update: resourceAwsDbParameterGroupUpdate,
    25  		Delete: resourceAwsDbParameterGroupDelete,
    26  		Schema: map[string]*schema.Schema{
    27  			"name": &schema.Schema{
    28  				Type:         schema.TypeString,
    29  				ForceNew:     true,
    30  				Required:     true,
    31  				ValidateFunc: validateDbParamGroupName,
    32  			},
    33  			"family": &schema.Schema{
    34  				Type:     schema.TypeString,
    35  				Required: true,
    36  				ForceNew: true,
    37  			},
    38  			"description": &schema.Schema{
    39  				Type:     schema.TypeString,
    40  				Required: true,
    41  				ForceNew: true,
    42  			},
    43  			"parameter": &schema.Schema{
    44  				Type:     schema.TypeSet,
    45  				Optional: true,
    46  				ForceNew: false,
    47  				Elem: &schema.Resource{
    48  					Schema: map[string]*schema.Schema{
    49  						"name": &schema.Schema{
    50  							Type:     schema.TypeString,
    51  							Required: true,
    52  						},
    53  						"value": &schema.Schema{
    54  							Type:     schema.TypeString,
    55  							Required: true,
    56  						},
    57  						"apply_method": &schema.Schema{
    58  							Type:     schema.TypeString,
    59  							Optional: true,
    60  							Default:  "immediate",
    61  							// this parameter is not actually state, but a
    62  							// meta-parameter describing how the RDS API call
    63  							// to modify the parameter group should be made.
    64  							// Future reads of the resource from AWS don't tell
    65  							// us what we used for apply_method previously, so
    66  							// by squashing state to an empty string we avoid
    67  							// needing to do an update for every future run.
    68  							StateFunc: func(interface{}) string { return "" },
    69  						},
    70  					},
    71  				},
    72  				Set: resourceAwsDbParameterHash,
    73  			},
    74  		},
    75  	}
    76  }
    77  
    78  func resourceAwsDbParameterGroupCreate(d *schema.ResourceData, meta interface{}) error {
    79  	rdsconn := meta.(*AWSClient).rdsconn
    80  
    81  	createOpts := rds.CreateDBParameterGroupInput{
    82  		DBParameterGroupName:   aws.String(d.Get("name").(string)),
    83  		DBParameterGroupFamily: aws.String(d.Get("family").(string)),
    84  		Description:            aws.String(d.Get("description").(string)),
    85  	}
    86  
    87  	log.Printf("[DEBUG] Create DB Parameter Group: %#v", createOpts)
    88  	_, err := rdsconn.CreateDBParameterGroup(&createOpts)
    89  	if err != nil {
    90  		return fmt.Errorf("Error creating DB Parameter Group: %s", err)
    91  	}
    92  
    93  	d.Partial(true)
    94  	d.SetPartial("name")
    95  	d.SetPartial("family")
    96  	d.SetPartial("description")
    97  	d.Partial(false)
    98  
    99  	d.SetId(*createOpts.DBParameterGroupName)
   100  	log.Printf("[INFO] DB Parameter Group ID: %s", d.Id())
   101  
   102  	return resourceAwsDbParameterGroupUpdate(d, meta)
   103  }
   104  
   105  func resourceAwsDbParameterGroupRead(d *schema.ResourceData, meta interface{}) error {
   106  	rdsconn := meta.(*AWSClient).rdsconn
   107  
   108  	describeOpts := rds.DescribeDBParameterGroupsInput{
   109  		DBParameterGroupName: aws.String(d.Id()),
   110  	}
   111  
   112  	describeResp, err := rdsconn.DescribeDBParameterGroups(&describeOpts)
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	if len(describeResp.DBParameterGroups) != 1 ||
   118  		*describeResp.DBParameterGroups[0].DBParameterGroupName != d.Id() {
   119  		return fmt.Errorf("Unable to find Parameter Group: %#v", describeResp.DBParameterGroups)
   120  	}
   121  
   122  	d.Set("name", describeResp.DBParameterGroups[0].DBParameterGroupName)
   123  	d.Set("family", describeResp.DBParameterGroups[0].DBParameterGroupFamily)
   124  	d.Set("description", describeResp.DBParameterGroups[0].Description)
   125  
   126  	// Only include user customized parameters as there's hundreds of system/default ones
   127  	describeParametersOpts := rds.DescribeDBParametersInput{
   128  		DBParameterGroupName: aws.String(d.Id()),
   129  		Source:               aws.String("user"),
   130  	}
   131  
   132  	describeParametersResp, err := rdsconn.DescribeDBParameters(&describeParametersOpts)
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	d.Set("parameter", flattenParameters(describeParametersResp.Parameters))
   138  
   139  	return nil
   140  }
   141  
   142  func resourceAwsDbParameterGroupUpdate(d *schema.ResourceData, meta interface{}) error {
   143  	rdsconn := meta.(*AWSClient).rdsconn
   144  
   145  	d.Partial(true)
   146  
   147  	if d.HasChange("parameter") {
   148  		o, n := d.GetChange("parameter")
   149  		if o == nil {
   150  			o = new(schema.Set)
   151  		}
   152  		if n == nil {
   153  			n = new(schema.Set)
   154  		}
   155  
   156  		os := o.(*schema.Set)
   157  		ns := n.(*schema.Set)
   158  
   159  		// Expand the "parameter" set to aws-sdk-go compat []rds.Parameter
   160  		parameters, err := expandParameters(ns.Difference(os).List())
   161  		if err != nil {
   162  			return err
   163  		}
   164  
   165  		if len(parameters) > 0 {
   166  			modifyOpts := rds.ModifyDBParameterGroupInput{
   167  				DBParameterGroupName: aws.String(d.Get("name").(string)),
   168  				Parameters:           parameters,
   169  			}
   170  
   171  			log.Printf("[DEBUG] Modify DB Parameter Group: %s", modifyOpts)
   172  			_, err = rdsconn.ModifyDBParameterGroup(&modifyOpts)
   173  			if err != nil {
   174  				return fmt.Errorf("Error modifying DB Parameter Group: %s", err)
   175  			}
   176  		}
   177  		d.SetPartial("parameter")
   178  	}
   179  
   180  	d.Partial(false)
   181  
   182  	return resourceAwsDbParameterGroupRead(d, meta)
   183  }
   184  
   185  func resourceAwsDbParameterGroupDelete(d *schema.ResourceData, meta interface{}) error {
   186  	stateConf := &resource.StateChangeConf{
   187  		Pending:    []string{"pending"},
   188  		Target:     "destroyed",
   189  		Refresh:    resourceAwsDbParameterGroupDeleteRefreshFunc(d, meta),
   190  		Timeout:    3 * time.Minute,
   191  		MinTimeout: 1 * time.Second,
   192  	}
   193  	_, err := stateConf.WaitForState()
   194  	return err
   195  }
   196  
   197  func resourceAwsDbParameterGroupDeleteRefreshFunc(
   198  	d *schema.ResourceData,
   199  	meta interface{}) resource.StateRefreshFunc {
   200  	rdsconn := meta.(*AWSClient).rdsconn
   201  
   202  	return func() (interface{}, string, error) {
   203  
   204  		deleteOpts := rds.DeleteDBParameterGroupInput{
   205  			DBParameterGroupName: aws.String(d.Id()),
   206  		}
   207  
   208  		if _, err := rdsconn.DeleteDBParameterGroup(&deleteOpts); err != nil {
   209  			rdserr, ok := err.(awserr.Error)
   210  			if !ok {
   211  				return d, "error", err
   212  			}
   213  
   214  			if rdserr.Code() != "DBParameterGroupNotFoundFault" {
   215  				return d, "error", err
   216  			}
   217  		}
   218  
   219  		return d, "destroyed", nil
   220  	}
   221  }
   222  
   223  func resourceAwsDbParameterHash(v interface{}) int {
   224  	var buf bytes.Buffer
   225  	m := v.(map[string]interface{})
   226  	buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
   227  	// Store the value as a lower case string, to match how we store them in flattenParameters
   228  	buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["value"].(string))))
   229  
   230  	return hashcode.String(buf.String())
   231  }
   232  
   233  func validateDbParamGroupName(v interface{}, k string) (ws []string, errors []error) {
   234  	value := v.(string)
   235  	if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
   236  		errors = append(errors, fmt.Errorf(
   237  			"only lowercase alphanumeric characters and hyphens allowed in %q", k))
   238  	}
   239  	if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
   240  		errors = append(errors, fmt.Errorf(
   241  			"first character of %q must be a letter", k))
   242  	}
   243  	if regexp.MustCompile(`--`).MatchString(value) {
   244  		errors = append(errors, fmt.Errorf(
   245  			"%q cannot contain two consecutive hyphens", k))
   246  	}
   247  	if regexp.MustCompile(`-$`).MatchString(value) {
   248  		errors = append(errors, fmt.Errorf(
   249  			"%q cannot end with a hyphen", k))
   250  	}
   251  	if len(value) > 255 {
   252  		errors = append(errors, fmt.Errorf(
   253  			"%q cannot be greater than 255 characters", k))
   254  	}
   255  	return
   256  
   257  }