Adding the documentation for the Redshift security groups

Creation of the schema, CRUD and acceptance tests for Redshift Parameter Group
This commit is contained in:
stack72 2015-11-11 23:37:56 +00:00
parent 85afc7d614
commit 249e7df76c
9 changed files with 634 additions and 22 deletions

View File

@ -171,6 +171,7 @@ func Provider() terraform.ResourceProvider {
"aws_rds_cluster": resourceAwsRDSCluster(),
"aws_rds_cluster_instance": resourceAwsRDSClusterInstance(),
"aws_redshift_security_group": resourceAwsRedshiftSecurityGroup(),
"aws_redshift_parameter_group": resourceAwsRedshiftParameterGroup(),
"aws_route53_delegation_set": resourceAwsRoute53DelegationSet(),
"aws_route53_record": resourceAwsRoute53Record(),
"aws_route53_zone_association": resourceAwsRoute53ZoneAssociation(),

View File

@ -0,0 +1,241 @@
package aws
import (
"bytes"
"fmt"
"log"
"regexp"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/redshift"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsRedshiftParameterGroup() *schema.Resource {
return &schema.Resource{
Create: resourceAwsRedshiftParameterGroupCreate,
Read: resourceAwsRedshiftParameterGroupRead,
Update: resourceAwsRedshiftParameterGroupUpdate,
Delete: resourceAwsRedshiftParameterGroupDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
ForceNew: true,
Required: true,
ValidateFunc: validateRedshiftParamGroupName,
},
"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: resourceAwsRedshiftParameterHash,
},
"tags": tagsSchema(),
},
}
}
func resourceAwsRedshiftParameterGroupCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).redshiftconn
createOpts := redshift.CreateClusterParameterGroupInput{
ParameterGroupName: aws.String(d.Get("name").(string)),
ParameterGroupFamily: aws.String(d.Get("family").(string)),
Description: aws.String(d.Get("description").(string)),
Tags: tagsFromMapRedshift(d.Get("tags").(map[string]interface{})),
}
log.Printf("[DEBUG] Create Redshift Parameter Group: %#v", createOpts)
_, err := conn.CreateClusterParameterGroup(&createOpts)
if err != nil {
return fmt.Errorf("Error creating Redshift Parameter Group: %s", err)
}
d.SetId(*createOpts.ParameterGroupName)
log.Printf("[INFO] Redshift Parameter Group ID: %s", d.Id())
return resourceAwsRedshiftParameterGroupUpdate(d, meta)
}
func resourceAwsRedshiftParameterGroupRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).redshiftconn
describeOpts := redshift.DescribeClusterParameterGroupsInput{
ParameterGroupName: aws.String(d.Id()),
}
describeResp, err := conn.DescribeClusterParameterGroups(&describeOpts)
if err != nil {
return err
}
if len(describeResp.ParameterGroups) != 1 ||
*describeResp.ParameterGroups[0].ParameterGroupName != d.Id() {
return fmt.Errorf("Unable to find Parameter Group: %#v", describeResp.ParameterGroups)
}
d.Set("name", describeResp.ParameterGroups[0].ParameterGroupName)
d.Set("family", describeResp.ParameterGroups[0].ParameterGroupFamily)
d.Set("description", describeResp.ParameterGroups[0].Description)
d.Set("tags", tagsToMapRedshift(describeResp.ParameterGroups[0].Tags))
describeParametersOpts := redshift.DescribeClusterParametersInput{
ParameterGroupName: aws.String(d.Id()),
Source: aws.String("user"),
}
describeParametersResp, err := conn.DescribeClusterParameters(&describeParametersOpts)
if err != nil {
return err
}
d.Set("parameter", flattenRedshiftParameters(describeParametersResp.Parameters))
return nil
}
func resourceAwsRedshiftParameterGroupUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).redshiftconn
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 []redshift.Parameter
parameters, err := expandRedshiftParameters(ns.Difference(os).List())
if err != nil {
return err
}
if len(parameters) > 0 {
modifyOpts := redshift.ModifyClusterParameterGroupInput{
ParameterGroupName: aws.String(d.Get("name").(string)),
Parameters: parameters,
}
log.Printf("[DEBUG] Modify Redshift Parameter Group: %s", modifyOpts)
_, err = conn.ModifyClusterParameterGroup(&modifyOpts)
if err != nil {
return fmt.Errorf("Error modifying Redshift Parameter Group: %s", err)
}
}
d.SetPartial("parameter")
}
d.Partial(false)
return resourceAwsRedshiftParameterGroupRead(d, meta)
}
func resourceAwsRedshiftParameterGroupDelete(d *schema.ResourceData, meta interface{}) error {
stateConf := &resource.StateChangeConf{
Pending: []string{"pending"},
Target: "destroyed",
Refresh: resourceAwsRedshiftParameterGroupDeleteRefreshFunc(d, meta),
Timeout: 3 * time.Minute,
MinTimeout: 1 * time.Second,
}
_, err := stateConf.WaitForState()
return err
}
func resourceAwsRedshiftParameterGroupDeleteRefreshFunc(
d *schema.ResourceData,
meta interface{}) resource.StateRefreshFunc {
conn := meta.(*AWSClient).redshiftconn
return func() (interface{}, string, error) {
deleteOpts := redshift.DeleteClusterParameterGroupInput{
ParameterGroupName: aws.String(d.Id()),
}
if _, err := conn.DeleteClusterParameterGroup(&deleteOpts); err != nil {
redshiftErr, ok := err.(awserr.Error)
if !ok {
return d, "error", err
}
if redshiftErr.Code() != "RedshiftParameterGroupNotFoundFault" {
return d, "error", err
}
}
return d, "destroyed", nil
}
}
func resourceAwsRedshiftParameterHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%s-", m["name"].(string)))
// Store the value as a lower case string, to match how we store them in flattenParameters
buf.WriteString(fmt.Sprintf("%s-", strings.ToLower(m["value"].(string))))
return hashcode.String(buf.String())
}
func validateRedshiftParamGroupName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only lowercase alphanumeric characters and hyphens allowed in %q", k))
}
if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"first character of %q must be a letter", k))
}
if regexp.MustCompile(`--`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot contain two consecutive hyphens", k))
}
if regexp.MustCompile(`-$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"%q cannot end with a hyphen", k))
}
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be greater than 255 characters", k))
}
return
}

View File

@ -0,0 +1,207 @@
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/redshift"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSRedshiftParameterGroup_withParameters(t *testing.T) {
var v redshift.ClusterParameterGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSRedshiftParameterGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSRedshiftParameterGroupConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSRedshiftParameterGroupExists("aws_redshift_parameter_group.bar", &v),
resource.TestCheckResourceAttr(
"aws_redshift_parameter_group.bar", "name", "parameter-group-test-terraform"),
resource.TestCheckResourceAttr(
"aws_redshift_parameter_group.bar", "family", "redshift-1.0"),
resource.TestCheckResourceAttr(
"aws_redshift_parameter_group.bar", "description", "Test parameter group for terraform"),
resource.TestCheckResourceAttr(
"aws_redshift_parameter_group.bar", "parameter.490804664.name", "require_ssl"),
resource.TestCheckResourceAttr(
"aws_redshift_parameter_group.bar", "parameter.490804664.value", "true"),
resource.TestCheckResourceAttr(
"aws_redshift_parameter_group.bar", "parameter.2036118857.name", "query_group"),
resource.TestCheckResourceAttr(
"aws_redshift_parameter_group.bar", "parameter.2036118857.value", "example"),
resource.TestCheckResourceAttr(
"aws_redshift_parameter_group.bar", "parameter.484080973.name", "enable_user_activity_logging"),
resource.TestCheckResourceAttr(
"aws_redshift_parameter_group.bar", "parameter.484080973.value", "true"),
),
},
},
})
}
func TestAccAWSRedshiftParameterGroup_withoutParameters(t *testing.T) {
var v redshift.ClusterParameterGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSRedshiftParameterGroupDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSRedshiftParameterGroupOnlyConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSRedshiftParameterGroupExists("aws_redshift_parameter_group.bar", &v),
resource.TestCheckResourceAttr(
"aws_redshift_parameter_group.bar", "name", "parameter-group-test-terraform"),
resource.TestCheckResourceAttr(
"aws_redshift_parameter_group.bar", "family", "redshift-1.0"),
resource.TestCheckResourceAttr(
"aws_redshift_parameter_group.bar", "description", "Test parameter group for terraform"),
),
},
},
})
}
func TestResourceAWSRedshiftParameterGroupName_validation(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "tEsting123",
ErrCount: 1,
},
{
Value: "testing123!",
ErrCount: 1,
},
{
Value: "1testing123",
ErrCount: 1,
},
{
Value: "testing--123",
ErrCount: 1,
},
{
Value: "testing123-",
ErrCount: 1,
},
{
Value: randomString(256),
ErrCount: 1,
},
}
for _, tc := range cases {
_, errors := validateRedshiftParamGroupName(tc.Value, "aws_redshift_parameter_group_name")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected the Redshift Parameter Group Name to trigger a validation error")
}
}
}
func testAccCheckAWSRedshiftParameterGroupDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).redshiftconn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_redshift_parameter_group" {
continue
}
// Try to find the Group
resp, err := conn.DescribeClusterParameterGroups(
&redshift.DescribeClusterParameterGroupsInput{
ParameterGroupName: aws.String(rs.Primary.ID),
})
if err == nil {
if len(resp.ParameterGroups) != 0 &&
*resp.ParameterGroups[0].ParameterGroupName == rs.Primary.ID {
return fmt.Errorf("Redshift Parameter Group still exists")
}
}
// Verify the error
newerr, ok := err.(awserr.Error)
if !ok {
return err
}
if newerr.Code() != "InvalidRedshiftParameterGroup.NotFound" {
return err
}
}
return nil
}
func testAccCheckAWSRedshiftParameterGroupExists(n string, v *redshift.ClusterParameterGroup) 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 Redshift Parameter Group ID is set")
}
conn := testAccProvider.Meta().(*AWSClient).redshiftconn
opts := redshift.DescribeClusterParameterGroupsInput{
ParameterGroupName: aws.String(rs.Primary.ID),
}
resp, err := conn.DescribeClusterParameterGroups(&opts)
if err != nil {
return err
}
if len(resp.ParameterGroups) != 1 ||
*resp.ParameterGroups[0].ParameterGroupName != rs.Primary.ID {
return fmt.Errorf("Redshift Parameter Group not found")
}
*v = *resp.ParameterGroups[0]
return nil
}
}
const testAccAWSRedshiftParameterGroupOnlyConfig = `
resource "aws_redshift_parameter_group" "bar" {
name = "parameter-group-test-terraform"
family = "redshift-1.0"
description = "Test parameter group for terraform"
}`
const testAccAWSRedshiftParameterGroupConfig = `
resource "aws_redshift_parameter_group" "bar" {
name = "parameter-group-test-terraform"
family = "redshift-1.0"
description = "Test parameter group for terraform"
parameter {
name = "require_ssl"
value = "true"
}
parameter {
name = "query_group"
value = "example"
}
parameter{
name = "enable_user_activity_logging"
value = "true"
}
}
`

View File

@ -154,6 +154,7 @@ func resourceAwsRedshiftSecurityGroupRead(d *schema.ResourceData, meta interface
d.Set("ingress", rules)
d.Set("name", *sg.ClusterSecurityGroupName)
d.Set("description", *sg.Description)
d.Set("tags", tagsToMapRedshift(sg.Tags))
return nil
}
@ -203,27 +204,6 @@ func resourceAwsRedshiftSecurityGroupRetrieve(d *schema.ResourceData, meta inter
return resp.ClusterSecurityGroups[0], nil
}
func tagsFromMapRedshift(m map[string]interface{}) []*redshift.Tag {
result := make([]*redshift.Tag, 0, len(m))
for k, v := range m {
result = append(result, &redshift.Tag{
Key: aws.String(k),
Value: aws.String(v.(string)),
})
}
return result
}
func tagsToMapRedshift(ts []*redshift.Tag) map[string]string {
result := make(map[string]string)
for _, t := range ts {
result[*t.Key] = *t.Value
}
return result
}
func validateRedshiftSecurityGroupName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if value == "default" {

View File

@ -17,6 +17,7 @@ import (
elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice"
"github.com/aws/aws-sdk-go/service/elb"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go/service/redshift"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/hashicorp/terraform/helper/schema"
)
@ -233,6 +234,29 @@ func expandParameters(configured []interface{}) ([]*rds.Parameter, error) {
return parameters, nil
}
func expandRedshiftParameters(configured []interface{}) ([]*redshift.Parameter, error) {
var parameters []*redshift.Parameter
// Loop over our configured parameters and create
// an array of aws-sdk-go compatabile objects
for _, pRaw := range configured {
data := pRaw.(map[string]interface{})
if data["name"].(string) == "" {
continue
}
p := &redshift.Parameter{
ParameterName: aws.String(data["name"].(string)),
ParameterValue: aws.String(data["value"].(string)),
}
parameters = append(parameters, p)
}
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) {
@ -413,6 +437,18 @@ func flattenParameters(list []*rds.Parameter) []map[string]interface{} {
return result
}
// Flattens an array of Redshift Parameters into a []map[string]interface{}
func flattenRedshiftParameters(list []*redshift.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
}
// 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))

View File

@ -10,6 +10,7 @@ import (
"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/redshift"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/hashicorp/terraform/flatmap"
"github.com/hashicorp/terraform/helper/schema"
@ -426,7 +427,36 @@ func TestExpandParameters(t *testing.T) {
}
}
<<<<<<< HEAD
func TestExpandElasticacheParameters(t *testing.T) {
=======
func TestexpandRedshiftParameters(t *testing.T) {
expanded := []interface{}{
map[string]interface{}{
"name": "character_set_client",
"value": "utf8",
},
}
parameters, err := expandRedshiftParameters(expanded)
if err != nil {
t.Fatalf("bad: %#v", err)
}
expected := &redshift.Parameter{
ParameterName: aws.String("character_set_client"),
ParameterValue: aws.String("utf8"),
}
if !reflect.DeepEqual(parameters[0], expected) {
t.Fatalf(
"Got:\n\n%#v\n\nExpected:\n\n%#v\n",
parameters[0],
expected)
}
}
func TestexpandElasticacheParameters(t *testing.T) {
>>>>>>> Creation of the schema, CRUD and acceptance tests for Redshift Parameter Group
expanded := []interface{}{
map[string]interface{}{
"name": "activerehashing",
@ -481,7 +511,36 @@ func TestFlattenParameters(t *testing.T) {
}
}
func TestFlattenElasticacheParameters(t *testing.T) {
func TestflattenRedshiftParameters(t *testing.T) {
cases := []struct {
Input []*redshift.Parameter
Output []map[string]interface{}
}{
{
Input: []*redshift.Parameter{
&redshift.Parameter{
ParameterName: aws.String("character_set_client"),
ParameterValue: aws.String("utf8"),
},
},
Output: []map[string]interface{}{
map[string]interface{}{
"name": "character_set_client",
"value": "utf8",
},
},
},
}
for _, tc := range cases {
output := flattenRedshiftParameters(tc.Input)
if !reflect.DeepEqual(output, tc.Output) {
t.Fatalf("Got:\n\n%#v\n\nExpected:\n\n%#v", output, tc.Output)
}
}
}
func TestflattenElasticacheParameters(t *testing.T) {
cases := []struct {
Input []*elasticache.Parameter
Output []map[string]interface{}

View File

@ -0,0 +1,27 @@
package aws
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/redshift"
)
func tagsFromMapRedshift(m map[string]interface{}) []*redshift.Tag {
result := make([]*redshift.Tag, 0, len(m))
for k, v := range m {
result = append(result, &redshift.Tag{
Key: aws.String(k),
Value: aws.String(v.(string)),
})
}
return result
}
func tagsToMapRedshift(ts []*redshift.Tag) map[string]string {
result := make(map[string]string)
for _, t := range ts {
result[*t.Key] = *t.Value
}
return result
}

View File

@ -0,0 +1,50 @@
---
layout: "aws"
page_title: "AWS: aws_redshift_security_group"
sidebar_current: "docs-aws-resource-redshift-security-group"
description: |-
Provides a Redshift security group resource.
---
# aws\_redshift\_security\_group
Creates a new Amazon Redshift security group. You use security groups to control access to non-VPC clusters
## Example Usage
```
resource "aws_redshift_security_group" "default" {
name = "redshift_sg"
description = "Redshift Example security group"
ingress {
cidr = "10.0.0.0/24"
}
tags {
Environment = "test"
}
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the Redshift security group.
* `description` - (Required) The description of the Redshift security group.
* `ingress` - (Optional) A list of ingress rules.
Ingress blocks support the following:
* `cidr` - The CIDR block to accept
* `security_group_name` - The name of the security group to authorize
* `security_group_owner_id` - The owner Id of the security group provided
by `security_group_name`.
## Attributes Reference
The following attributes are exported:
* `id` - The Redshift security group ID.

View File

@ -433,6 +433,17 @@
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-redshift/) %>>
<a href="#">Redshift Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-aws-resource-redshift-security-group") %>>
<a href="/docs/providers/aws/r/redshift_security_group.html">aws_redshift_security_group</a>
</li>
</ul>
</li>
<li<%= sidebar_current(/^docs-aws-resource-route53/) %>>
<a href="#">Route53 Resources</a>