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