support aws resource ElastiCache
This commit is contained in:
parent
3d367794e9
commit
f3d9a287e1
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/awslabs/aws-sdk-go/aws"
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
"github.com/awslabs/aws-sdk-go/service/autoscaling"
|
"github.com/awslabs/aws-sdk-go/service/autoscaling"
|
||||||
"github.com/awslabs/aws-sdk-go/service/ec2"
|
"github.com/awslabs/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/elasticache"
|
||||||
"github.com/awslabs/aws-sdk-go/service/elb"
|
"github.com/awslabs/aws-sdk-go/service/elb"
|
||||||
"github.com/awslabs/aws-sdk-go/service/iam"
|
"github.com/awslabs/aws-sdk-go/service/iam"
|
||||||
"github.com/awslabs/aws-sdk-go/service/rds"
|
"github.com/awslabs/aws-sdk-go/service/rds"
|
||||||
|
@ -36,6 +37,7 @@ type AWSClient struct {
|
||||||
region string
|
region string
|
||||||
rdsconn *rds.RDS
|
rdsconn *rds.RDS
|
||||||
iamconn *iam.IAM
|
iamconn *iam.IAM
|
||||||
|
elasticacheconn *elasticache.ElastiCache
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client configures and returns a fully initailized AWSClient
|
// Client configures and returns a fully initailized AWSClient
|
||||||
|
@ -96,6 +98,8 @@ func (c *Config) Client() (interface{}, error) {
|
||||||
Region: "us-east-1",
|
Region: "us-east-1",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
log.Println("[INFO] Initializing Elasticache Connection")
|
||||||
|
client.elasticacheconn = elasticache.New(awsConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
|
|
|
@ -80,6 +80,9 @@ func Provider() terraform.ResourceProvider {
|
||||||
"aws_db_parameter_group": resourceAwsDbParameterGroup(),
|
"aws_db_parameter_group": resourceAwsDbParameterGroup(),
|
||||||
"aws_db_security_group": resourceAwsDbSecurityGroup(),
|
"aws_db_security_group": resourceAwsDbSecurityGroup(),
|
||||||
"aws_db_subnet_group": resourceAwsDbSubnetGroup(),
|
"aws_db_subnet_group": resourceAwsDbSubnetGroup(),
|
||||||
|
"aws_elasticache": resourceAwsElasticache(),
|
||||||
|
"aws_elasticache_subnet_group": resourceAwsElasticacheSubnetGroup(),
|
||||||
|
"aws_elasticache_security_group": resourceAwsElasticacheSecurityGroup(),
|
||||||
"aws_eip": resourceAwsEip(),
|
"aws_eip": resourceAwsEip(),
|
||||||
"aws_elb": resourceAwsElb(),
|
"aws_elb": resourceAwsElb(),
|
||||||
"aws_instance": resourceAwsInstance(),
|
"aws_instance": resourceAwsInstance(),
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/elasticache"
|
||||||
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsElasticache() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsElasticacheCreate,
|
||||||
|
Read: resourceAwsElasticacheRead,
|
||||||
|
Delete: resourceAwsElasticacheDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"cluster_id": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"engine": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"node_type": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"num_cache_nodes": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"parameter_group_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"port": &schema.Schema{
|
||||||
|
Type: schema.TypeInt,
|
||||||
|
Default: 11211,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"engine_version": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"subnet_group_name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"security_group_names": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: func(v interface{}) int {
|
||||||
|
return hashcode.String(v.(string))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"security_group_ids": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: func(v interface{}) int {
|
||||||
|
return hashcode.String(v.(string))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticacheCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).elasticacheconn
|
||||||
|
|
||||||
|
clusterId := d.Get("cluster_id").(string)
|
||||||
|
nodeType := d.Get("node_type").(string) // e.g) cache.m1.small
|
||||||
|
numNodes := int64(d.Get("num_cache_nodes").(int)) // 2
|
||||||
|
engine := d.Get("engine").(string) // memcached
|
||||||
|
engineVersion := d.Get("engine_version").(string) // 1.4.14
|
||||||
|
port := int64(d.Get("port").(int)) // 11211
|
||||||
|
subnetGroupName := d.Get("subnet_group_name").(string)
|
||||||
|
securityNameSet := d.Get("security_group_names").(*schema.Set)
|
||||||
|
securityIdSet := d.Get("security_group_ids").(*schema.Set)
|
||||||
|
paramGroupName := d.Get("parameter_group_name").(string) // default.memcached1.4
|
||||||
|
|
||||||
|
securityNames := expandStringList(securityNameSet.List())
|
||||||
|
securityIds := expandStringList(securityIdSet.List())
|
||||||
|
|
||||||
|
req := &elasticache.CreateCacheClusterInput{
|
||||||
|
CacheClusterID: aws.String(clusterId),
|
||||||
|
CacheNodeType: aws.String(nodeType),
|
||||||
|
NumCacheNodes: aws.Long(numNodes),
|
||||||
|
Engine: aws.String(engine),
|
||||||
|
EngineVersion: aws.String(engineVersion),
|
||||||
|
Port: aws.Long(port),
|
||||||
|
CacheSubnetGroupName: aws.String(subnetGroupName),
|
||||||
|
CacheSecurityGroupNames: securityNames,
|
||||||
|
SecurityGroupIDs: securityIds,
|
||||||
|
CacheParameterGroupName: aws.String(paramGroupName),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := conn.CreateCacheCluster(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating Elasticache: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pending := []string{"creating"}
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: pending,
|
||||||
|
Target: "available",
|
||||||
|
Refresh: CacheClusterStateRefreshFunc(conn, d.Id(), "available", pending),
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
Delay: 10 * time.Second,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Waiting for state to become available: %v", d.Id())
|
||||||
|
_, sterr := stateConf.WaitForState()
|
||||||
|
if sterr != nil {
|
||||||
|
return fmt.Errorf("Error waiting for elasticache (%s) to be created: %s", d.Id(), sterr)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(clusterId)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticacheRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).elasticacheconn
|
||||||
|
req := &elasticache.DescribeCacheClustersInput{
|
||||||
|
CacheClusterID: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := conn.DescribeCacheClusters(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(res.CacheClusters) == 1 {
|
||||||
|
c := res.CacheClusters[0]
|
||||||
|
d.Set("cluster_id", c.CacheClusterID)
|
||||||
|
d.Set("node_type", c.CacheNodeType)
|
||||||
|
d.Set("num_cache_nodes", c.NumCacheNodes)
|
||||||
|
d.Set("engine", c.Engine)
|
||||||
|
d.Set("engine_version", c.EngineVersion)
|
||||||
|
if c.ConfigurationEndpoint != nil {
|
||||||
|
d.Set("port", c.ConfigurationEndpoint.Port)
|
||||||
|
}
|
||||||
|
d.Set("subnet_group_name", c.CacheSubnetGroupName)
|
||||||
|
d.Set("security_group_names", c.CacheSecurityGroups)
|
||||||
|
d.Set("security_group_ids", c.SecurityGroups)
|
||||||
|
d.Set("parameter_group_name", c.CacheParameterGroup)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticacheDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).elasticacheconn
|
||||||
|
|
||||||
|
req := &elasticache.DeleteCacheClusterInput{
|
||||||
|
CacheClusterID: aws.String(d.Id()),
|
||||||
|
}
|
||||||
|
_, err := conn.DeleteCacheCluster(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Waiting for deletion: %v", d.Id())
|
||||||
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"creating", "available", "deleting", "incompatible-parameters", "incompatible-network", "restore-failed"},
|
||||||
|
Target: "",
|
||||||
|
Refresh: CacheClusterStateRefreshFunc(conn, d.Id(), "", []string{}),
|
||||||
|
Timeout: 10 * time.Minute,
|
||||||
|
Delay: 10 * time.Second,
|
||||||
|
MinTimeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, sterr := stateConf.WaitForState()
|
||||||
|
if sterr != nil {
|
||||||
|
return fmt.Errorf("Error waiting for elasticache (%s) to delete: %s", d.Id(), sterr)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId("")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CacheClusterStateRefreshFunc(conn *elasticache.ElastiCache, clusterID, givenState string, pending []string) resource.StateRefreshFunc {
|
||||||
|
return func() (interface{}, string, error) {
|
||||||
|
resp, err := conn.DescribeCacheClusters(&elasticache.DescribeCacheClustersInput{
|
||||||
|
CacheClusterID: aws.String(clusterID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
apierr := err.(aws.APIError)
|
||||||
|
log.Printf("[DEBUG] message: %v, code: %v", apierr.Message, apierr.Code)
|
||||||
|
if apierr.Message == fmt.Sprintf("CacheCluster not found: %v", clusterID) {
|
||||||
|
log.Printf("[DEBUG] Detect deletion")
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[ERROR] CacheClusterStateRefreshFunc: %s", err)
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := resp.CacheClusters[0]
|
||||||
|
log.Printf("[DEBUG] status: %v", *c.CacheClusterStatus)
|
||||||
|
|
||||||
|
// return the current state if it's in the pending array
|
||||||
|
for _, p := range pending {
|
||||||
|
s := *c.CacheClusterStatus
|
||||||
|
if p == s {
|
||||||
|
log.Printf("[DEBUG] Return with status: %v", *c.CacheClusterStatus)
|
||||||
|
return c, p, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return given state if it's not in pending
|
||||||
|
if givenState != "" {
|
||||||
|
return c, givenState, nil
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] current status: %v", *c.CacheClusterStatus)
|
||||||
|
return c, *c.CacheClusterStatus, nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/elasticache"
|
||||||
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsElasticacheSecurityGroup() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsElasticacheSecurityGroupCreate,
|
||||||
|
Read: resourceAwsElasticacheSecurityGroupRead,
|
||||||
|
Delete: resourceAwsElasticacheSecurityGroupDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"description": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"security_group_names": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: func(v interface{}) int {
|
||||||
|
return hashcode.String(v.(string))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticacheSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).elasticacheconn
|
||||||
|
|
||||||
|
name := d.Get("name").(string)
|
||||||
|
desc := d.Get("description").(string)
|
||||||
|
nameSet := d.Get("security_group_names").(*schema.Set)
|
||||||
|
|
||||||
|
names := make([]string, nameSet.Len())
|
||||||
|
for i, name := range nameSet.List() {
|
||||||
|
names[i] = name.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Cache security group create: name: %s, description: %s, security_group_names: %v", name, desc, names)
|
||||||
|
res, err := conn.CreateCacheSecurityGroup(&elasticache.CreateCacheSecurityGroupInput{
|
||||||
|
Description: aws.String(desc),
|
||||||
|
CacheSecurityGroupName: aws.String(name),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating CacheSecurityGroup: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, n := range names {
|
||||||
|
log.Printf("[DEBUG] Authorize cache security group ingress name: %v, ec2 security group name: %v", name, n)
|
||||||
|
_, err = conn.AuthorizeCacheSecurityGroupIngress(&elasticache.AuthorizeCacheSecurityGroupIngressInput{
|
||||||
|
CacheSecurityGroupName: aws.String(name),
|
||||||
|
EC2SecurityGroupName: aws.String(n),
|
||||||
|
EC2SecurityGroupOwnerID: aws.String(*res.CacheSecurityGroup.OwnerID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed to authorize: %v", err)
|
||||||
|
_, err := conn.DeleteCacheSecurityGroup(&elasticache.DeleteCacheSecurityGroupInput{
|
||||||
|
CacheSecurityGroupName: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
log.Printf("[ERROR] Revert cache security group: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId(name)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticacheSecurityGroupRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).elasticacheconn
|
||||||
|
req := &elasticache.DescribeCacheSecurityGroupsInput{
|
||||||
|
CacheSecurityGroupName: aws.String(d.Get("name").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := conn.DescribeCacheSecurityGroups(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(res.CacheSecurityGroups) == 0 {
|
||||||
|
return fmt.Errorf("Error missing %v", d.Get("name"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var group *elasticache.CacheSecurityGroup
|
||||||
|
for _, g := range res.CacheSecurityGroups {
|
||||||
|
log.Printf("[DEBUG] CacheSecurityGroupName: %v, id: %v", g.CacheSecurityGroupName, d.Id())
|
||||||
|
if *g.CacheSecurityGroupName == d.Id() {
|
||||||
|
group = g
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if group == nil {
|
||||||
|
return fmt.Errorf("Error retrieving cache security group: %v", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("name", group.CacheSecurityGroupName)
|
||||||
|
d.Set("description", group.Description)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticacheSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).elasticacheconn
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Cache security group delete: %s", d.Id())
|
||||||
|
|
||||||
|
return resource.Retry(5*time.Minute, func() error {
|
||||||
|
_, err := conn.DeleteCacheSecurityGroup(&elasticache.DeleteCacheSecurityGroupInput{
|
||||||
|
CacheSecurityGroupName: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
apierr, ok := err.(aws.APIError)
|
||||||
|
if !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] APIError.Code: %v", apierr.Code)
|
||||||
|
switch apierr.Code {
|
||||||
|
case "InvalidCacheSecurityGroupState":
|
||||||
|
return err
|
||||||
|
case "DependencyViolation":
|
||||||
|
// If it is a dependency violation, we want to retry
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
return resource.RetryError{Err: err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/elasticache"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSEcacheSecurityGroup(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSEcacheSecurityGroupDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSEcacheSecurityGroupConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSEcacheSecurityGroupExists("aws_elasticache_security_group.bar"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSEcacheSecurityGroupDestroy(s *terraform.State) error {
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).elasticacheconn
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_elasticache_security_group" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res, err := conn.DescribeCacheSecurityGroups(&elasticache.DescribeCacheSecurityGroupsInput{
|
||||||
|
CacheSecurityGroupName: aws.String(rs.Primary.ID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(res.CacheSecurityGroups) > 0 {
|
||||||
|
return fmt.Errorf("still exist.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSEcacheSecurityGroupExists(n string) 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 security group ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).elasticacheconn
|
||||||
|
_, err := conn.DescribeCacheSecurityGroups(&elasticache.DescribeCacheSecurityGroupsInput{
|
||||||
|
CacheSecurityGroupName: aws.String(rs.Primary.ID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("CacheSecurityGroup error: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testAccAWSEcacheSecurityGroupConfig = fmt.Sprintf(`
|
||||||
|
resource "aws_security_group" "bar" {
|
||||||
|
name = "tf-test-security-group-%03d"
|
||||||
|
description = "tf-test-security-group-descr"
|
||||||
|
ingress {
|
||||||
|
from_port = -1
|
||||||
|
to_port = -1
|
||||||
|
protocol = "icmp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elasticache_security_group" "bar" {
|
||||||
|
name = "tf-test-security-group-%03d"
|
||||||
|
description = "tf-test-security-group-descr"
|
||||||
|
security_group_names = ["${aws_security_group.bar.name}"]
|
||||||
|
}
|
||||||
|
`, genRandInt(), genRandInt())
|
|
@ -0,0 +1,137 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/elasticache"
|
||||||
|
"github.com/hashicorp/aws-sdk-go/aws"
|
||||||
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceAwsElasticacheSubnetGroup() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceAwsElasticacheSubnetGroupCreate,
|
||||||
|
Read: resourceAwsElasticacheSubnetGroupRead,
|
||||||
|
Delete: resourceAwsElasticacheSubnetGroupDelete,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"description": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"name": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
ForceNew: true,
|
||||||
|
},
|
||||||
|
"subnet_ids": &schema.Schema{
|
||||||
|
Type: schema.TypeSet,
|
||||||
|
Optional: true,
|
||||||
|
Computed: true,
|
||||||
|
ForceNew: true,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Set: func(v interface{}) int {
|
||||||
|
return hashcode.String(v.(string))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticacheSubnetGroupCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).elasticacheconn
|
||||||
|
|
||||||
|
// Get the group properties
|
||||||
|
name := d.Get("name").(string)
|
||||||
|
desc := d.Get("description").(string)
|
||||||
|
subnetIdsSet := d.Get("subnet_ids").(*schema.Set)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Cache subnet group create: name: %s, description: %s", name, desc)
|
||||||
|
|
||||||
|
subnetIds := expandStringList(subnetIdsSet.List())
|
||||||
|
|
||||||
|
req := &elasticache.CreateCacheSubnetGroupInput{
|
||||||
|
CacheSubnetGroupDescription: aws.String(desc),
|
||||||
|
CacheSubnetGroupName: aws.String(name),
|
||||||
|
SubnetIDs: subnetIds,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := conn.CreateCacheSubnetGroup(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error creating CacheSubnetGroup: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign the group name as the resource ID
|
||||||
|
d.SetId(name)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticacheSubnetGroupRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).elasticacheconn
|
||||||
|
req := &elasticache.DescribeCacheSubnetGroupsInput{
|
||||||
|
CacheSubnetGroupName: aws.String(d.Get("name").(string)),
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := conn.DescribeCacheSubnetGroups(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(res.CacheSubnetGroups) == 0 {
|
||||||
|
return fmt.Errorf("Error missing %v", d.Get("name"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var group *elasticache.CacheSubnetGroup
|
||||||
|
for _, g := range res.CacheSubnetGroups {
|
||||||
|
log.Printf("[DEBUG] %v %v", g.CacheSubnetGroupName, d.Id())
|
||||||
|
if *g.CacheSubnetGroupName == d.Id() {
|
||||||
|
group = g
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if group == nil {
|
||||||
|
return fmt.Errorf("Error retrieving cache subnet group: %v", res)
|
||||||
|
}
|
||||||
|
|
||||||
|
ids := make([]string, len(group.Subnets))
|
||||||
|
for i, s := range group.Subnets {
|
||||||
|
ids[i] = *s.SubnetIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Set("name", group.CacheSubnetGroupName)
|
||||||
|
d.Set("description", group.CacheSubnetGroupDescription)
|
||||||
|
d.Set("subnet_ids", ids)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceAwsElasticacheSubnetGroupDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
conn := meta.(*AWSClient).elasticacheconn
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Cache subnet group delete: %s", d.Id())
|
||||||
|
|
||||||
|
return resource.Retry(5*time.Minute, func() error {
|
||||||
|
_, err := conn.DeleteCacheSubnetGroup(&elasticache.DeleteCacheSubnetGroupInput{
|
||||||
|
CacheSubnetGroupName: aws.String(d.Id()),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
apierr, ok := err.(aws.APIError)
|
||||||
|
if !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] APIError.Code: %v", apierr.Code)
|
||||||
|
switch apierr.Code {
|
||||||
|
case "DependencyViolation":
|
||||||
|
// If it is a dependency violation, we want to retry
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
return resource.RetryError{Err: err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/elasticache"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSEcacheSubnetGroup(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSEcacheSubnetGroupDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSEcacheSubnetGroupConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSEcacheSubnetGroupExists("aws_elasticache_subnet_group.bar"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSEcacheSubnetGroupDestroy(s *terraform.State) error {
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).elasticacheconn
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_elasticache_subnet_group" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res, err := conn.DescribeCacheSubnetGroups(&elasticache.DescribeCacheSubnetGroupsInput{
|
||||||
|
CacheSubnetGroupName: aws.String(rs.Primary.ID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(res.CacheSubnetGroups) > 0 {
|
||||||
|
return fmt.Errorf("still exist.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSEcacheSubnetGroupExists(n string) 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 subnet group ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).elasticacheconn
|
||||||
|
_, err := conn.DescribeCacheSubnetGroups(&elasticache.DescribeCacheSubnetGroupsInput{
|
||||||
|
CacheSubnetGroupName: aws.String(rs.Primary.ID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("CacheSubnetGroup error: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testAccAWSEcacheSubnetGroupConfig = fmt.Sprintf(`
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "192.168.1.1/16"
|
||||||
|
tags {
|
||||||
|
Name = "tf-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
cidr_block = "192.168.1.1/20"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
tags {
|
||||||
|
Name = "tf-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elasticache_subnet_group" "bar" {
|
||||||
|
name = "tf-test-cache-subnet-%03d"
|
||||||
|
description = "tf-test-cache-subnet-group-descr"
|
||||||
|
subnet_ids = ["${aws_subnet.foo.id}"]
|
||||||
|
}
|
||||||
|
`, genRandInt())
|
|
@ -0,0 +1,159 @@
|
||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/awslabs/aws-sdk-go/aws"
|
||||||
|
"github.com/awslabs/aws-sdk-go/service/elasticache"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccAWSElasticache(t *testing.T) {
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckAWSElasticacheDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSElasticacheConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSEcacheSecurityGroupExists("aws_elasticache_security_group.bar"),
|
||||||
|
testAccCheckAWSElasticacheExists("aws_elasticache.bar"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccAWSElasticacheInVPCConfig,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckAWSEcacheSubnetGroupExists("aws_elasticache_subnet_group.bar"),
|
||||||
|
testAccCheckAWSElasticacheExists("aws_elasticache.bar"),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSElasticacheDestroy(s *terraform.State) error {
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).elasticacheconn
|
||||||
|
|
||||||
|
for _, rs := range s.RootModule().Resources {
|
||||||
|
if rs.Type != "aws_elasticache" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res, err := conn.DescribeCacheClusters(&elasticache.DescribeCacheClustersInput{
|
||||||
|
CacheClusterID: aws.String(rs.Primary.ID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(res.CacheClusters) > 0 {
|
||||||
|
return fmt.Errorf("still exist.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckAWSElasticacheExists(n string) 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 cluster ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := testAccProvider.Meta().(*AWSClient).elasticacheconn
|
||||||
|
_, err := conn.DescribeCacheClusters(&elasticache.DescribeCacheClustersInput{
|
||||||
|
CacheClusterID: aws.String(rs.Primary.ID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Elasticache error: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func genRandInt() int {
|
||||||
|
return rand.New(rand.NewSource(time.Now().UnixNano())).Int() % 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
var testAccAWSElasticacheConfig = fmt.Sprintf(`
|
||||||
|
resource "aws_security_group" "bar" {
|
||||||
|
name = "tf-test-security-group-%03d"
|
||||||
|
description = "tf-test-security-group-descr"
|
||||||
|
ingress {
|
||||||
|
from_port = -1
|
||||||
|
to_port = -1
|
||||||
|
protocol = "icmp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elasticache_security_group" "bar" {
|
||||||
|
name = "tf-test-security-group-%03d"
|
||||||
|
description = "tf-test-security-group-descr"
|
||||||
|
security_group_names = ["${aws_security_group.bar.name}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elasticache" "bar" {
|
||||||
|
cluster_id = "tf-test-%03d"
|
||||||
|
engine = "memcached"
|
||||||
|
node_type = "cache.m1.small"
|
||||||
|
num_cache_nodes = 1
|
||||||
|
parameter_group_name = "default.memcached1.4"
|
||||||
|
security_group_names = ["${aws_elasticache_security_group.bar.name}"]
|
||||||
|
}
|
||||||
|
`, genRandInt(), genRandInt(), genRandInt())
|
||||||
|
|
||||||
|
var testAccAWSElasticacheInVPCConfig = fmt.Sprintf(`
|
||||||
|
resource "aws_vpc" "foo" {
|
||||||
|
cidr_block = "192.168.0.0/16"
|
||||||
|
tags {
|
||||||
|
Name = "tf-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_subnet" "foo" {
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
cidr_block = "192.168.0.0/20"
|
||||||
|
availability_zone = "us-west-2a"
|
||||||
|
tags {
|
||||||
|
Name = "tf-test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elasticache_subnet_group" "bar" {
|
||||||
|
name = "tf-test-cache-subnet-%03d"
|
||||||
|
description = "tf-test-cache-subnet-group-descr"
|
||||||
|
subnet_ids = ["${aws_subnet.foo.id}"]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_security_group" "bar" {
|
||||||
|
name = "tf-test-security-group-%03d"
|
||||||
|
description = "tf-test-security-group-descr"
|
||||||
|
vpc_id = "${aws_vpc.foo.id}"
|
||||||
|
ingress {
|
||||||
|
from_port = -1
|
||||||
|
to_port = -1
|
||||||
|
protocol = "icmp"
|
||||||
|
cidr_blocks = ["0.0.0.0/0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_elasticache" "bar" {
|
||||||
|
cluster_id = "tf-test-%03d"
|
||||||
|
node_type = "cache.m1.small"
|
||||||
|
num_cache_nodes = 1
|
||||||
|
engine = "redis"
|
||||||
|
engine_version = "2.8.19"
|
||||||
|
port = 6379
|
||||||
|
subnet_group_name = "${aws_elasticache_subnet_group.bar.name}"
|
||||||
|
security_group_ids = ["${aws_security_group.bar.id}"]
|
||||||
|
parameter_group_name = "default.redis2.8"
|
||||||
|
}
|
||||||
|
`, genRandInt(), genRandInt(), genRandInt())
|
Loading…
Reference in New Issue