github.com/daveadams/terraform@v0.6.4-0.20160830094355-13ce74975936/builtin/providers/aws/resource_aws_db_parameter_group.go (about)

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