Merge pull request #2276 from SamClinckspoor/resource-aws-elasticache-parameter-group

provider/aws elasticache parameter group
This commit is contained in:
Clint 2015-06-26 14:01:19 -05:00
commit 079e4505a8
7 changed files with 565 additions and 0 deletions

View File

@ -100,6 +100,7 @@ func Provider() terraform.ResourceProvider {
"aws_ecs_task_definition": resourceAwsEcsTaskDefinition(),
"aws_eip": resourceAwsEip(),
"aws_elasticache_cluster": resourceAwsElasticacheCluster(),
"aws_elasticache_parameter_group": resourceAwsElasticacheParameterGroup(),
"aws_elasticache_security_group": resourceAwsElasticacheSecurityGroup(),
"aws_elasticache_subnet_group": resourceAwsElasticacheSubnetGroup(),
"aws_elb": resourceAwsElb(),

View File

@ -0,0 +1,211 @@
package aws
import (
"bytes"
"fmt"
"log"
"time"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/elasticache"
)
func resourceAwsElasticacheParameterGroup() *schema.Resource {
return &schema.Resource{
Create: resourceAwsElasticacheParameterGroupCreate,
Read: resourceAwsElasticacheParameterGroupRead,
Update: resourceAwsElasticacheParameterGroupUpdate,
Delete: resourceAwsElasticacheParameterGroupDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
"family": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"parameter": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
ForceNew: false,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"value": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
},
},
Set: resourceAwsElasticacheParameterHash,
},
},
}
}
func resourceAwsElasticacheParameterGroupCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).elasticacheconn
createOpts := elasticache.CreateCacheParameterGroupInput{
CacheParameterGroupName: aws.String(d.Get("name").(string)),
CacheParameterGroupFamily: aws.String(d.Get("family").(string)),
Description: aws.String(d.Get("description").(string)),
}
log.Printf("[DEBUG] Create Cache Parameter Group: %#v", createOpts)
_, err := conn.CreateCacheParameterGroup(&createOpts)
if err != nil {
return fmt.Errorf("Error creating DB Parameter Group: %s", err)
}
d.Partial(true)
d.SetPartial("name")
d.SetPartial("family")
d.SetPartial("description")
d.Partial(false)
d.SetId(*createOpts.CacheParameterGroupName)
log.Printf("[INFO] Cache Parameter Group ID: %s", d.Id())
return resourceAwsElasticacheParameterGroupUpdate(d, meta)
}
func resourceAwsElasticacheParameterGroupRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).elasticacheconn
describeOpts := elasticache.DescribeCacheParameterGroupsInput{
CacheParameterGroupName: aws.String(d.Id()),
}
describeResp, err := conn.DescribeCacheParameterGroups(&describeOpts)
if err != nil {
return err
}
if len(describeResp.CacheParameterGroups) != 1 ||
*describeResp.CacheParameterGroups[0].CacheParameterGroupName != d.Id() {
return fmt.Errorf("Unable to find Parameter Group: %#v", describeResp.CacheParameterGroups)
}
d.Set("name", describeResp.CacheParameterGroups[0].CacheParameterGroupName)
d.Set("family", describeResp.CacheParameterGroups[0].CacheParameterGroupFamily)
d.Set("description", describeResp.CacheParameterGroups[0].Description)
// Only include user customized parameters as there's hundreds of system/default ones
describeParametersOpts := elasticache.DescribeCacheParametersInput{
CacheParameterGroupName: aws.String(d.Id()),
Source: aws.String("user"),
}
describeParametersResp, err := conn.DescribeCacheParameters(&describeParametersOpts)
if err != nil {
return err
}
d.Set("parameter", flattenElastiCacheParameters(describeParametersResp.Parameters))
return nil
}
func resourceAwsElasticacheParameterGroupUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).elasticacheconn
d.Partial(true)
if d.HasChange("parameter") {
o, n := d.GetChange("parameter")
if o == nil {
o = new(schema.Set)
}
if n == nil {
n = new(schema.Set)
}
os := o.(*schema.Set)
ns := n.(*schema.Set)
// Expand the "parameter" set to aws-sdk-go compat []elasticacheconn.Parameter
parameters, err := expandElastiCacheParameters(ns.Difference(os).List())
if err != nil {
return err
}
if len(parameters) > 0 {
modifyOpts := elasticache.ModifyCacheParameterGroupInput{
CacheParameterGroupName: aws.String(d.Get("name").(string)),
ParameterNameValues: parameters,
}
log.Printf("[DEBUG] Modify Cache Parameter Group: %#v", modifyOpts)
_, err = conn.ModifyCacheParameterGroup(&modifyOpts)
if err != nil {
return fmt.Errorf("Error modifying Cache Parameter Group: %s", err)
}
}
d.SetPartial("parameter")
}
d.Partial(false)
return resourceAwsElasticacheParameterGroupRead(d, meta)
}
func resourceAwsElasticacheParameterGroupDelete(d *schema.ResourceData, meta interface{}) error {
stateConf := &resource.StateChangeConf{
Pending: []string{"pending"},
Target: "destroyed",
Refresh: resourceAwsElasticacheParameterGroupDeleteRefreshFunc(d, meta),
Timeout: 3 * time.Minute,
MinTimeout: 1 * time.Second,
}
_, err := stateConf.WaitForState()
return err
}
func resourceAwsElasticacheParameterGroupDeleteRefreshFunc(
d *schema.ResourceData,
meta interface{}) resource.StateRefreshFunc {
conn := meta.(*AWSClient).elasticacheconn
return func() (interface{}, string, error) {
deleteOpts := elasticache.DeleteCacheParameterGroupInput{
CacheParameterGroupName: aws.String(d.Id()),
}
if _, err := conn.DeleteCacheParameterGroup(&deleteOpts); err != nil {
elasticahceerr, ok := err.(awserr.Error)
if ok && elasticahceerr.Code() == "CacheParameterGroupNotFoundFault" {
d.SetId("")
return d, "error", err
}
return d, "error", err
}
return d, "destroyed", nil
}
}
func resourceAwsElasticacheParameterHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
buf.WriteString(fmt.Sprintf("%s-", m["value"].(string)))
return hashcode.String(buf.String())
}

View File

@ -0,0 +1,210 @@
package aws
import (
"fmt"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/elasticache"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSElasticacheParameterGroup_basic(t *testing.T) {
var v elasticache.CacheParameterGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSElasticacheParameterGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSElasticacheParameterGroupConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheParameterGroupExists("aws_elasticache_parameter_group.bar", &v),
testAccCheckAWSElasticacheParameterGroupAttributes(&v),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "name", "parameter-group-test-terraform"),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "family", "redis2.8"),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "description", "Test parameter group for terraform"),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "parameter.283487565.name", "appendonly"),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "parameter.283487565.value", "yes"),
),
},
resource.TestStep{
Config: testAccAWSElasticacheParameterGroupAddParametersConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheParameterGroupExists("aws_elasticache_parameter_group.bar", &v),
testAccCheckAWSElasticacheParameterGroupAttributes(&v),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "name", "parameter-group-test-terraform"),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "family", "redis2.8"),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "description", "Test parameter group for terraform"),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "parameter.283487565.name", "appendonly"),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "parameter.283487565.value", "yes"),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "parameter.2196914567.name", "appendfsync"),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "parameter.2196914567.value", "always"),
),
},
},
})
}
func TestAccAWSElasticacheParameterGroupOnly(t *testing.T) {
var v elasticache.CacheParameterGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSElasticacheParameterGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSElasticacheParameterGroupOnlyConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheParameterGroupExists("aws_elasticache_parameter_group.bar", &v),
testAccCheckAWSElasticacheParameterGroupAttributes(&v),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "name", "parameter-group-test-terraform"),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "family", "redis2.8"),
resource.TestCheckResourceAttr(
"aws_elasticache_parameter_group.bar", "description", "Test parameter group for terraform"),
),
},
},
})
}
func testAccCheckAWSElasticacheParameterGroupDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).elasticacheconn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_elasticache_parameter_group" {
continue
}
// Try to find the Group
resp, err := conn.DescribeCacheParameterGroups(
&elasticache.DescribeCacheParameterGroupsInput{
CacheParameterGroupName: aws.String(rs.Primary.ID),
})
if err == nil {
if len(resp.CacheParameterGroups) != 0 &&
*resp.CacheParameterGroups[0].CacheParameterGroupName == rs.Primary.ID {
return fmt.Errorf("Cache Parameter Group still exists")
}
}
// Verify the error
newerr, ok := err.(awserr.Error)
if !ok {
return err
}
if newerr.Code() != "InvalidCacheParameterGroup.NotFound" {
return err
}
}
return nil
}
func testAccCheckAWSElasticacheParameterGroupAttributes(v *elasticache.CacheParameterGroup) resource.TestCheckFunc {
return func(s *terraform.State) error {
if *v.CacheParameterGroupName != "parameter-group-test-terraform" {
return fmt.Errorf("bad name: %#v", v.CacheParameterGroupName)
}
if *v.CacheParameterGroupFamily != "redis2.8" {
return fmt.Errorf("bad family: %#v", v.CacheParameterGroupFamily)
}
if *v.Description != "Test parameter group for terraform" {
return fmt.Errorf("bad description: %#v", v.Description)
}
return nil
}
}
func testAccCheckAWSElasticacheParameterGroupExists(n string, v *elasticache.CacheParameterGroup) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No Cache Parameter Group ID is set")
}
conn := testAccProvider.Meta().(*AWSClient).elasticacheconn
opts := elasticache.DescribeCacheParameterGroupsInput{
CacheParameterGroupName: aws.String(rs.Primary.ID),
}
resp, err := conn.DescribeCacheParameterGroups(&opts)
if err != nil {
return err
}
if len(resp.CacheParameterGroups) != 1 ||
*resp.CacheParameterGroups[0].CacheParameterGroupName != rs.Primary.ID {
return fmt.Errorf("Cache Parameter Group not found")
}
*v = *resp.CacheParameterGroups[0]
return nil
}
}
const testAccAWSElasticacheParameterGroupConfig = `
resource "aws_elasticache_parameter_group" "bar" {
name = "parameter-group-test-terraform"
family = "redis2.8"
description = "Test parameter group for terraform"
parameter {
name = "appendonly"
value = "yes"
}
}
`
const testAccAWSElasticacheParameterGroupAddParametersConfig = `
resource "aws_elasticache_parameter_group" "bar" {
name = "parameter-group-test-terraform"
family = "redis2.8"
description = "Test parameter group for terraform"
parameter {
name = "appendonly"
value = "yes"
}
parameter {
name = "appendfsync"
value = "always"
}
}
`
const testAccAWSElasticacheParameterGroupOnlyConfig = `
resource "aws_elasticache_parameter_group" "bar" {
name = "parameter-group-test-terraform"
family = "redis2.8"
description = "Test parameter group for terraform"
}
`

View File

@ -10,6 +10,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ecs"
"github.com/aws/aws-sdk-go/service/elasticache"
"github.com/aws/aws-sdk-go/service/elb"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go/service/route53"
@ -201,6 +202,27 @@ func expandParameters(configured []interface{}) ([]*rds.Parameter, error) {
return parameters, nil
}
// Takes the result of flatmap.Expand for an array of parameters and
// returns Parameter API compatible objects
func expandElastiCacheParameters(configured []interface{}) ([]*elasticache.ParameterNameValue, error) {
parameters := make([]*elasticache.ParameterNameValue, 0, len(configured))
// Loop over our configured parameters and create
// an array of aws-sdk-go compatabile objects
for _, pRaw := range configured {
data := pRaw.(map[string]interface{})
p := &elasticache.ParameterNameValue{
ParameterName: aws.String(data["name"].(string)),
ParameterValue: aws.String(data["value"].(string)),
}
parameters = append(parameters, p)
}
return parameters, nil
}
// Flattens a health check into something that flatmap.Flatten()
// can handle
func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} {
@ -326,6 +348,18 @@ func flattenParameters(list []*rds.Parameter) []map[string]interface{} {
return result
}
// Flattens an array of Parameters into a []map[string]interface{}
func flattenElastiCacheParameters(list []*elasticache.Parameter) []map[string]interface{} {
result := make([]map[string]interface{}, 0, len(list))
for _, i := range list {
result = append(result, map[string]interface{}{
"name": strings.ToLower(*i.ParameterName),
"value": strings.ToLower(*i.ParameterValue),
})
}
return result
}
// Takes the result of flatmap.Expand for an array of strings
// and returns a []string
func expandStringList(configured []interface{}) []*string {

View File

@ -6,6 +6,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/elasticache"
"github.com/aws/aws-sdk-go/service/elb"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go/service/route53"
@ -393,6 +394,32 @@ func TestexpandParameters(t *testing.T) {
}
}
func TestexpandElasticacheParameters(t *testing.T) {
expanded := []interface{}{
map[string]interface{}{
"name": "character_set_client",
"value": "utf8",
"apply_method": "immediate",
},
}
parameters, err := expandElastiCacheParameters(expanded)
if err != nil {
t.Fatalf("bad: %#v", err)
}
expected := &elasticache.Parameter{
ParameterName: aws.String("activerehashing"),
ParameterValue: aws.String("yes"),
}
if !reflect.DeepEqual(parameters[0], expected) {
t.Fatalf(
"Got:\n\n%#v\n\nExpected:\n\n%#v\n",
parameters[0],
expected)
}
}
func TestflattenParameters(t *testing.T) {
cases := []struct {
Input []*rds.Parameter
@ -422,6 +449,35 @@ func TestflattenParameters(t *testing.T) {
}
}
func TestflattenElasticacheParameters(t *testing.T) {
cases := []struct {
Input []*elasticache.Parameter
Output []map[string]interface{}
}{
{
Input: []*elasticache.Parameter{
&elasticache.Parameter{
ParameterName: aws.String("activerehashing"),
ParameterValue: aws.String("yes"),
},
},
Output: []map[string]interface{}{
map[string]interface{}{
"name": "activerehashing",
"value": "yes",
},
},
},
}
for _, tc := range cases {
output := flattenElastiCacheParameters(tc.Input)
if !reflect.DeepEqual(output, tc.Output) {
t.Fatalf("Got:\n\n%#v\n\nExpected:\n\n%#v", output, tc.Output)
}
}
}
func TestexpandInstanceString(t *testing.T) {
expected := []*elb.Instance{

View File

@ -0,0 +1,49 @@
---
layout: "aws"
page_title: "AWS: aws_elasticache_parameter_group"
sidebar_current: "docs-aws-resource-elasticache-parameter-group"
---
# aws\_elasticache\_parameter\_group
Provides an ElastiCache parameter group resource.
## Example Usage
```
resource "aws_elasticache_parameter_group" "default" {
name = "cache-params"
family = "redis2.8"
description = "Cache cluster default param group"
parameter {
name = "activerehashing"
value = "yes"
}
parameter {
name = "min-slaves-to-write"
value = "2"
}
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the ElastiCache parameter group.
* `family` - (Required) The family of the ElastiCache parameter group.
* `description` - (Required) The description of the ElastiCache parameter group.
* `parameter` - (Optional) A list of ElastiCache parameters to apply.
Parameter blocks support the following:
* `name` - (Required) The name of the ElastiCache parameter.
* `value` - (Required) The value of the ElastiCache parameter.
## Attributes Reference
The following attributes are exported:
* `id` - The ElastiCache parameter group name.

View File

@ -80,6 +80,10 @@
<a href="/docs/providers/aws/r/elasticache_cluster.html">aws_elasticache_cluster</a>
</li>
<li<%= sidebar_current("docs-aws-resource-elasticache-parameter-group") %>>
<a href="/docs/providers/aws/r/elasticache_parameter_group.html">aws_elasticache_parameter_group</a>
</li>
<li<%= sidebar_current("docs-aws-resource-elasticache-security-group") %>>
<a href="/docs/providers/aws/r/elasticache_security_group.html">aws_elasticache_security_group</a>
</li>