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