Merge branch 'andskli-appautoscaling'

This commit is contained in:
stack72 2016-07-26 10:43:30 +01:00
commit 28e073ec41
No known key found for this signature in database
GPG Key ID: 8619A619B085CB16
11 changed files with 2708 additions and 0 deletions

View File

@ -18,6 +18,7 @@ import (
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/apigateway"
"github.com/aws/aws-sdk-go/service/applicationautoscaling"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/aws/aws-sdk-go/service/cloudfront"
@ -94,6 +95,7 @@ type AWSClient struct {
emrconn *emr.EMR
esconn *elasticsearch.ElasticsearchService
apigateway *apigateway.APIGateway
appautoscalingconn *applicationautoscaling.ApplicationAutoScaling
autoscalingconn *autoscaling.AutoScaling
s3conn *s3.S3
sesConn *ses.SES
@ -213,6 +215,7 @@ func (c *Config) Client() (interface{}, error) {
}
client.apigateway = apigateway.New(sess)
client.appautoscalingconn = applicationautoscaling.New(sess)
client.autoscalingconn = autoscaling.New(sess)
client.cfconn = cloudformation.New(sess)
client.cloudfrontconn = cloudfront.New(sess)

View File

@ -135,6 +135,8 @@ func Provider() terraform.ResourceProvider {
"aws_api_gateway_resource": resourceAwsApiGatewayResource(),
"aws_api_gateway_rest_api": resourceAwsApiGatewayRestApi(),
"aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(),
"aws_appautoscaling_target": resourceAwsAppautoscalingTarget(),
"aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(),
"aws_autoscaling_group": resourceAwsAutoscalingGroup(),
"aws_autoscaling_notification": resourceAwsAutoscalingNotification(),
"aws_autoscaling_policy": resourceAwsAutoscalingPolicy(),

View File

@ -0,0 +1,331 @@
package aws
import (
"bytes"
"fmt"
"log"
"strconv"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/applicationautoscaling"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsAppautoscalingPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceAwsAppautoscalingPolicyCreate,
Read: resourceAwsAppautoscalingPolicyRead,
Update: resourceAwsAppautoscalingPolicyUpdate,
Delete: resourceAwsAppautoscalingPolicyDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
// https://github.com/boto/botocore/blob/9f322b1/botocore/data/autoscaling/2011-01-01/service-2.json#L1862-L1873
value := v.(string)
if len(value) > 255 {
errors = append(errors, fmt.Errorf("q cannot be longer than 255 characters", k))
}
return
},
},
"arn": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"policy_type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "StepScaling",
},
"resource_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"scalable_dimension": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "ecs:service:DesiredCount",
ForceNew: true,
},
"service_namespace": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "ecs",
ForceNew: true,
},
"adjustment_type": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"cooldown": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"metric_aggregation_type": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"min_adjustment_magnitude": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"alarms": &schema.Schema{
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"step_adjustment": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"metric_interval_lower_bound": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"metric_interval_upper_bound": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"scaling_adjustment": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
},
},
Set: resourceAwsAppautoscalingAdjustmentHash,
},
},
}
}
func resourceAwsAppautoscalingPolicyCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).appautoscalingconn
params, err := getAwsAppautoscalingPutScalingPolicyInput(d)
if err != nil {
return err
}
log.Printf("[DEBUG] ApplicationAutoScaling PutScalingPolicy: %#v", params)
resp, err := conn.PutScalingPolicy(&params)
if err != nil {
return fmt.Errorf("Error putting scaling policy: %s", err)
}
d.Set("arn", resp.PolicyARN)
d.SetId(d.Get("name").(string))
log.Printf("[INFO] ApplicationAutoScaling scaling PolicyARN: %s", d.Get("arn").(string))
return resourceAwsAppautoscalingPolicyRead(d, meta)
}
func resourceAwsAppautoscalingPolicyRead(d *schema.ResourceData, meta interface{}) error {
p, err := getAwsAppautoscalingPolicy(d, meta)
if err != nil {
return err
}
if p == nil {
d.SetId("")
return nil
}
log.Printf("[DEBUG] Read ApplicationAutoScaling policy: %s, SP: %s, Obj: %s", d.Get("name"), d.Get("name"), p)
d.Set("arn", p.PolicyARN)
d.Set("name", p.PolicyName)
d.Set("policy_type", p.PolicyType)
d.Set("resource_id", p.ResourceId)
d.Set("scalable_dimension", p.ScalableDimension)
d.Set("service_namespace", p.ServiceNamespace)
d.Set("alarms", p.Alarms)
d.Set("step_scaling_policy_configuration", p.StepScalingPolicyConfiguration)
return nil
}
func resourceAwsAppautoscalingPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).appautoscalingconn
params, inputErr := getAwsAppautoscalingPutScalingPolicyInput(d)
if inputErr != nil {
return inputErr
}
log.Printf("[DEBUG] Application Autoscaling Update Scaling Policy: %#v", params)
_, err := conn.PutScalingPolicy(&params)
if err != nil {
return err
}
return resourceAwsAppautoscalingPolicyRead(d, meta)
}
func resourceAwsAppautoscalingPolicyDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).appautoscalingconn
p, err := getAwsAppautoscalingPolicy(d, meta)
if err != nil {
return fmt.Errorf("Error getting policy: %s", err)
}
if p == nil {
return nil
}
params := applicationautoscaling.DeleteScalingPolicyInput{
PolicyName: aws.String(d.Get("name").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)),
ScalableDimension: aws.String(d.Get("scalable_dimension").(string)),
ServiceNamespace: aws.String(d.Get("service_namespace").(string)),
}
log.Printf("[DEBUG] Deleting Application AutoScaling Policy opts: %#v", params)
if _, err := conn.DeleteScalingPolicy(&params); err != nil {
return fmt.Errorf("Application AutoScaling Policy: %s", err)
}
d.SetId("")
return nil
}
// Takes the result of flatmap.Expand for an array of step adjustments and
// returns a []*applicationautoscaling.StepAdjustment.
func expandAppautoscalingStepAdjustments(configured []interface{}) ([]*applicationautoscaling.StepAdjustment, error) {
var adjustments []*applicationautoscaling.StepAdjustment
// Loop over our configured step adjustments and create an array
// of aws-sdk-go compatible objects. We're forced to convert strings
// to floats here because there's no way to detect whether or not
// an uninitialized, optional schema element is "0.0" deliberately.
// With strings, we can test for "", which is definitely an empty
// struct value.
for _, raw := range configured {
data := raw.(map[string]interface{})
a := &applicationautoscaling.StepAdjustment{
ScalingAdjustment: aws.Int64(int64(data["scaling_adjustment"].(int))),
}
if data["metric_interval_lower_bound"] != "" {
bound := data["metric_interval_lower_bound"]
switch bound := bound.(type) {
case string:
f, err := strconv.ParseFloat(bound, 64)
if err != nil {
return nil, fmt.Errorf(
"metric_interval_lower_bound must be a float value represented as a string")
}
a.MetricIntervalLowerBound = aws.Float64(f)
default:
return nil, fmt.Errorf(
"metric_interval_lower_bound isn't a string. This is a bug. Please file an issue.")
}
}
if data["metric_interval_upper_bound"] != "" {
bound := data["metric_interval_upper_bound"]
switch bound := bound.(type) {
case string:
f, err := strconv.ParseFloat(bound, 64)
if err != nil {
return nil, fmt.Errorf(
"metric_interval_upper_bound must be a float value represented as a string")
}
a.MetricIntervalUpperBound = aws.Float64(f)
default:
return nil, fmt.Errorf(
"metric_interval_upper_bound isn't a string. This is a bug. Please file an issue.")
}
}
adjustments = append(adjustments, a)
}
return adjustments, nil
}
func getAwsAppautoscalingPutScalingPolicyInput(d *schema.ResourceData) (applicationautoscaling.PutScalingPolicyInput, error) {
var params = applicationautoscaling.PutScalingPolicyInput{
PolicyName: aws.String(d.Get("name").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)),
}
if v, ok := d.GetOk("policy_type"); ok {
params.PolicyType = aws.String(v.(string))
}
if v, ok := d.GetOk("service_namespace"); ok {
params.ServiceNamespace = aws.String(v.(string))
}
if v, ok := d.GetOk("policy_type"); ok {
params.PolicyType = aws.String(v.(string))
}
if v, ok := d.GetOk("scalable_dimension"); ok {
params.ScalableDimension = aws.String(v.(string))
}
var adjustmentSteps []*applicationautoscaling.StepAdjustment
if v, ok := d.GetOk("step_adjustment"); ok {
steps, err := expandAppautoscalingStepAdjustments(v.(*schema.Set).List())
if err != nil {
return params, fmt.Errorf("metric_interval_lower_bound and metric_interval_upper_bound must be strings!")
}
adjustmentSteps = steps
}
// build StepScalingPolicyConfiguration
params.StepScalingPolicyConfiguration = &applicationautoscaling.StepScalingPolicyConfiguration{
AdjustmentType: aws.String(d.Get("adjustment_type").(string)),
Cooldown: aws.Int64(int64(d.Get("cooldown").(int))),
MetricAggregationType: aws.String(d.Get("metric_aggregation_type").(string)),
StepAdjustments: adjustmentSteps,
}
if v, ok := d.GetOk("min_adjustment_magnitude"); ok {
params.StepScalingPolicyConfiguration.MinAdjustmentMagnitude = aws.Int64(int64(v.(int)))
}
return params, nil
}
func getAwsAppautoscalingPolicy(d *schema.ResourceData, meta interface{}) (*applicationautoscaling.ScalingPolicy, error) {
conn := meta.(*AWSClient).appautoscalingconn
params := applicationautoscaling.DescribeScalingPoliciesInput{
PolicyNames: []*string{aws.String(d.Get("name").(string))},
ServiceNamespace: aws.String(d.Get("service_namespace").(string)),
}
log.Printf("[DEBUG] Application AutoScaling Policy Describe Params: %#v", params)
resp, err := conn.DescribeScalingPolicies(&params)
if err != nil {
return nil, fmt.Errorf("Error retrieving scaling policies: %s", err)
}
// find scaling policy
name := d.Get("name")
for idx, sp := range resp.ScalingPolicies {
if *sp.PolicyName == name {
return resp.ScalingPolicies[idx], nil
}
}
// policy not found
return nil, nil
}
func resourceAwsAppautoscalingAdjustmentHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
if v, ok := m["metric_interval_lower_bound"]; ok {
buf.WriteString(fmt.Sprintf("%f-", v))
}
if v, ok := m["metric_interval_upper_bound"]; ok {
buf.WriteString(fmt.Sprintf("%f-", v))
}
buf.WriteString(fmt.Sprintf("%d-", m["scaling_adjustment"].(int)))
return hashcode.String(buf.String())
}

View File

@ -0,0 +1,173 @@
package aws
import (
"fmt"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/applicationautoscaling"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSAppautoScalingPolicy_basic(t *testing.T) {
var policy applicationautoscaling.ScalingPolicy
randClusterName := fmt.Sprintf("cluster%s", acctest.RandString(10))
randPolicyName := fmt.Sprintf("terraform-test-foobar-%s", acctest.RandString(5))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAppautoscalingPolicyDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSAppautoscalingPolicyConfig(randClusterName, randPolicyName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAppautoscalingPolicyExists("aws_appautoscaling_policy.foobar_simple", &policy),
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "adjustment_type", "ChangeInCapacity"),
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "policy_type", "StepScaling"),
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "cooldown", "60"),
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "name", randPolicyName),
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "resource_id", fmt.Sprintf("service/%s/foobar", randClusterName)),
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "service_namespace", "ecs"),
resource.TestCheckResourceAttr("aws_appautoscaling_policy.foobar_simple", "scalable_dimension", "ecs:service:DesiredCount"),
),
},
},
})
}
func testAccCheckAWSAppautoscalingPolicyExists(n string, policy *applicationautoscaling.ScalingPolicy) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn
params := &applicationautoscaling.DescribeScalingPoliciesInput{
ServiceNamespace: aws.String(rs.Primary.Attributes["service_namespace"]),
PolicyNames: []*string{aws.String(rs.Primary.ID)},
}
resp, err := conn.DescribeScalingPolicies(params)
if err != nil {
return err
}
if len(resp.ScalingPolicies) == 0 {
return fmt.Errorf("ScalingPolicy %s not found", rs.Primary.ID)
}
return nil
}
}
func testAccCheckAWSAppautoscalingPolicyDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn
for _, rs := range s.RootModule().Resources {
params := applicationautoscaling.DescribeScalingPoliciesInput{
ServiceNamespace: aws.String(rs.Primary.Attributes["service_namespace"]),
PolicyNames: []*string{aws.String(rs.Primary.ID)},
}
resp, err := conn.DescribeScalingPolicies(&params)
if err == nil {
if len(resp.ScalingPolicies) != 0 &&
*resp.ScalingPolicies[0].PolicyName == rs.Primary.ID {
return fmt.Errorf("Application autoscaling policy still exists: %s", rs.Primary.ID)
}
}
}
return nil
}
func testAccAWSAppautoscalingPolicyConfig(
randClusterName string,
randPolicyName string) string {
return fmt.Sprintf(`
resource "aws_iam_role" "autoscale_role" {
name = "%s"
path = "/"
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"sts:AssumeRole\"]}]}"
}
resource "aws_iam_role_policy" "autoscale_role_policy" {
name = "%s"
role = "${aws_iam_role.autoscale_role.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:DescribeServices",
"ecs:UpdateService",
"cloudwatch:DescribeAlarms"
],
"Resource": ["*"]
}
]
}
EOF
}
resource "aws_ecs_cluster" "foo" {
name = "%s"
}
resource "aws_ecs_task_definition" "task" {
family = "foobar"
container_definitions = <<EOF
[
{
"name": "busybox",
"image": "busybox:latest",
"cpu": 10,
"memory": 128,
"essential": true
}
]
EOF
}
resource "aws_ecs_service" "service" {
name = "foobar"
cluster = "${aws_ecs_cluster.foo.id}"
task_definition = "${aws_ecs_task_definition.task.arn}"
desired_count = 1
deployment_maximum_percent = 200
deployment_minimum_healthy_percent = 50
}
resource "aws_appautoscaling_target" "tgt" {
service_namespace = "ecs"
resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
scalable_dimension = "ecs:service:DesiredCount"
role_arn = "${aws_iam_role.autoscale_role.arn}"
min_capacity = 1
max_capacity = 4
}
resource "aws_appautoscaling_policy" "foobar_simple" {
name = "%s"
service_namespace = "ecs"
resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
scalable_dimension = "ecs:service:DesiredCount"
adjustment_type = "ChangeInCapacity"
cooldown = 60
metric_aggregation_type = "Average"
step_adjustment {
metric_interval_lower_bound = 0
scaling_adjustment = 1
}
depends_on = ["aws_appautoscaling_target.tgt"]
}
`, randClusterName, randClusterName, randClusterName, randPolicyName)
}

View File

@ -0,0 +1,216 @@
package aws
import (
"fmt"
"log"
"time"
"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/applicationautoscaling"
)
func resourceAwsAppautoscalingTarget() *schema.Resource {
return &schema.Resource{
Create: resourceAwsAppautoscalingTargetCreate,
Read: resourceAwsAppautoscalingTargetRead,
Delete: resourceAwsAppautoscalingTargetDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
// https://github.com/boto/botocore/blob/9f322b1/botocore/data/autoscaling/2011-01-01/service-2.json#L1862-L1873
value := v.(string)
if len(value) > 255 {
errors = append(errors, fmt.Errorf(
"%q cannot be longer than 255 characters", k))
}
return
},
},
"arn": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"max_capacity": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"min_capacity": &schema.Schema{
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
"resource_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"role_arn": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"scalable_dimension": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "ecs:service:DesiredCount",
ForceNew: true,
},
"service_namespace": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "ecs",
ForceNew: true,
},
},
}
}
func resourceAwsAppautoscalingTargetCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).appautoscalingconn
var targetOpts applicationautoscaling.RegisterScalableTargetInput
targetOpts.MaxCapacity = aws.Int64(int64(d.Get("max_capacity").(int)))
targetOpts.MinCapacity = aws.Int64(int64(d.Get("min_capacity").(int)))
targetOpts.ResourceId = aws.String(d.Get("resource_id").(string))
targetOpts.RoleARN = aws.String(d.Get("role_arn").(string))
targetOpts.ScalableDimension = aws.String(d.Get("scalable_dimension").(string))
targetOpts.ServiceNamespace = aws.String(d.Get("service_namespace").(string))
log.Printf("[DEBUG] Application autoscaling target create configuration %#v", targetOpts)
var out *applicationautoscaling.RegisterScalableTargetOutput
var err error
err = resource.Retry(1*time.Minute, func() *resource.RetryError {
out, err = conn.RegisterScalableTarget(&targetOpts)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "ValidationException" {
log.Printf("[DEBUG] Retrying creation of Application Autoscaling Scalable Target due to possible issues with IAM: %s", awsErr)
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
if err != nil {
return fmt.Errorf("Error creating application autoscaling target: %s", err)
}
d.SetId(d.Get("resource_id").(string))
log.Printf("[INFO] Application AutoScaling Target ID: %s", d.Id())
return resourceAwsAppautoscalingTargetRead(d, meta)
}
func resourceAwsAppautoscalingTargetRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).appautoscalingconn
t, err := getAwsAppautoscalingTarget(d, conn)
if err != nil {
return err
}
if t == nil {
log.Printf("[INFO] Application AutoScaling Target %q not found", d.Id())
d.SetId("")
return nil
}
d.Set("max_capacity", t.MaxCapacity)
d.Set("min_capacity", t.MinCapacity)
d.Set("resource_id", t.ResourceId)
d.Set("role_arn", t.RoleARN)
d.Set("scalable_dimension", t.ScalableDimension)
d.Set("service_namespace", t.ServiceNamespace)
return nil
}
// Updating Target is not supported
// func getAwsAppautoscalingTargetUpdate(d *schema.ResourceData, meta interface{}) error {
// conn := meta.(*AWSClient).appautoscalingconn
// }
func resourceAwsAppautoscalingTargetDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).appautoscalingconn
t, err := getAwsAppautoscalingTarget(d, conn)
if err != nil {
return err
}
if t == nil {
log.Printf("[INFO] Application AutoScaling Target %q not found", d.Id())
d.SetId("")
return nil
}
log.Printf("[DEBUG] Application AutoScaling Target destroy: %#v", d.Id())
deleteOpts := applicationautoscaling.DeregisterScalableTargetInput{
ResourceId: aws.String(d.Get("resource_id").(string)),
ServiceNamespace: aws.String(d.Get("service_namespace").(string)),
ScalableDimension: aws.String(d.Get("scalable_dimension").(string)),
}
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
if _, err := conn.DeregisterScalableTarget(&deleteOpts); err != nil {
if awserr, ok := err.(awserr.Error); ok {
// @TODO: We should do stuff here depending on the actual error returned
return resource.RetryableError(awserr)
}
// Non recognized error, no retry.
return resource.NonRetryableError(err)
}
// Successful delete
return nil
})
if err != nil {
return err
}
return resource.Retry(5*time.Minute, func() *resource.RetryError {
if t, _ = getAwsAppautoscalingTarget(d, conn); t != nil {
return resource.RetryableError(
fmt.Errorf("Application AutoScaling Target still exists"))
}
return nil
})
}
func getAwsAppautoscalingTarget(
d *schema.ResourceData,
conn *applicationautoscaling.ApplicationAutoScaling) (*applicationautoscaling.ScalableTarget, error) {
tgtName := d.Id()
describeOpts := applicationautoscaling.DescribeScalableTargetsInput{
ResourceIds: []*string{aws.String(tgtName)},
ServiceNamespace: aws.String(d.Get("service_namespace").(string)),
}
log.Printf("[DEBUG] Application AutoScaling Target describe configuration: %#v", describeOpts)
describeTargets, err := conn.DescribeScalableTargets(&describeOpts)
if err != nil {
// @TODO: We should probably send something else back if we're trying to access an unknown Resource ID
// targetserr, ok := err.(awserr.Error)
// if ok && targetserr.Code() == ""
return nil, fmt.Errorf("Error retrieving Application AutoScaling Target: %s", err)
}
for idx, tgt := range describeTargets.ScalableTargets {
if *tgt.ResourceId == tgtName {
return describeTargets.ScalableTargets[idx], nil
}
}
return nil, nil
}

View File

@ -0,0 +1,301 @@
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/applicationautoscaling"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSAppautoScalingTarget_basic(t *testing.T) {
var target applicationautoscaling.ScalableTarget
randClusterName := fmt.Sprintf("cluster-%s", acctest.RandString(10))
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
IDRefreshName: "aws_appautoscaling_target.bar",
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAppautoscalingTargetDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSAppautoscalingTargetConfig(randClusterName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAppautoscalingTargetExists("aws_appautoscaling_target.bar", &target),
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "service_namespace", "ecs"),
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "scalable_dimension", "ecs:service:DesiredCount"),
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "min_capacity", "1"),
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "max_capacity", "3"),
),
},
resource.TestStep{
Config: testAccAWSAppautoscalingTargetConfigUpdate(randClusterName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAppautoscalingTargetExists("aws_appautoscaling_target.bar", &target),
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "min_capacity", "2"),
resource.TestCheckResourceAttr("aws_appautoscaling_target.bar", "max_capacity", "8"),
),
},
},
})
}
func testAccCheckAWSAppautoscalingTargetDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_appautoscaling_target" {
continue
}
// Try to find the target
describeTargets, err := conn.DescribeScalableTargets(
&applicationautoscaling.DescribeScalableTargetsInput{
ResourceIds: []*string{aws.String(rs.Primary.ID)},
ServiceNamespace: aws.String(rs.Primary.Attributes["service_namespace"]),
},
)
if err == nil {
if len(describeTargets.ScalableTargets) != 0 &&
*describeTargets.ScalableTargets[0].ResourceId == rs.Primary.ID {
return fmt.Errorf("Application AutoScaling Target still exists")
}
}
// Verify error
e, ok := err.(awserr.Error)
if !ok {
return err
}
if e.Code() != "" {
return e
}
}
return nil
}
func testAccCheckAWSAppautoscalingTargetExists(n string, target *applicationautoscaling.ScalableTarget) 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 Application AutoScaling Target ID is set")
}
conn := testAccProvider.Meta().(*AWSClient).appautoscalingconn
describeTargets, err := conn.DescribeScalableTargets(
&applicationautoscaling.DescribeScalableTargetsInput{
ResourceIds: []*string{aws.String(rs.Primary.ID)},
ServiceNamespace: aws.String(rs.Primary.Attributes["service_namespace"]),
},
)
if err != nil {
return err
}
if len(describeTargets.ScalableTargets) != 1 || *describeTargets.ScalableTargets[0].ResourceId != rs.Primary.ID {
return fmt.Errorf("Application AutoScaling ResourceId not found")
}
target = describeTargets.ScalableTargets[0]
return nil
}
}
func testAccAWSAppautoscalingTargetConfig(
randClusterName string) string {
return fmt.Sprintf(`
resource "aws_iam_role" "autoscale_role" {
name = "autoscalerole%s"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "application-autoscaling.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "autoscale_role_policy" {
name = "autoscalepolicy%s"
role = "${aws_iam_role.autoscale_role.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:DescribeServices",
"ecs:UpdateService"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"cloudwatch:DescribeAlarms"
],
"Resource": [
"*"
]
}
]
}
EOF
}
resource "aws_ecs_cluster" "foo" {
name = "%s"
}
resource "aws_ecs_task_definition" "task" {
family = "foobar"
container_definitions = <<EOF
[
{
"name": "busybox",
"image": "busybox:latest",
"cpu": 10,
"memory": 128,
"essential": true
}
]
EOF
}
resource "aws_ecs_service" "service" {
name = "foobar"
cluster = "${aws_ecs_cluster.foo.id}"
task_definition = "${aws_ecs_task_definition.task.arn}"
desired_count = 1
deployment_maximum_percent = 200
deployment_minimum_healthy_percent = 50
}
resource "aws_appautoscaling_target" "bar" {
service_namespace = "ecs"
resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
scalable_dimension = "ecs:service:DesiredCount"
role_arn = "${aws_iam_role.autoscale_role.arn}"
min_capacity = 1
max_capacity = 3
}
`, randClusterName, randClusterName, randClusterName)
}
func testAccAWSAppautoscalingTargetConfigUpdate(
randClusterName string) string {
return fmt.Sprintf(`
resource "aws_iam_role" "autoscale_role" {
name = "autoscalerole%s"
path = "/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "application-autoscaling.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "autoscale_role_policy" {
name = "autoscalepolicy%s"
role = "${aws_iam_role.autoscale_role.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:DescribeServices",
"ecs:UpdateService"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"cloudwatch:DescribeAlarms"
],
"Resource": [
"*"
]
}
]
}
EOF
}
resource "aws_ecs_cluster" "foo" {
name = "%s"
}
resource "aws_ecs_task_definition" "task" {
family = "foobar"
container_definitions = <<EOF
[
{
"name": "busybox",
"image": "busybox:latest",
"cpu": 10,
"memory": 128,
"essential": true
}
]
EOF
}
resource "aws_ecs_service" "service" {
name = "foobar"
cluster = "${aws_ecs_cluster.foo.id}"
task_definition = "${aws_ecs_task_definition.task.arn}"
desired_count = 1
deployment_maximum_percent = 200
deployment_minimum_healthy_percent = 50
}
resource "aws_appautoscaling_target" "bar" {
service_namespace = "ecs"
resource_id = "service/${aws_ecs_cluster.foo.name}/${aws_ecs_service.service.name}"
scalable_dimension = "ecs:service:DesiredCount"
role_arn = "${aws_iam_role.autoscale_role.arn}"
min_capacity = 2
max_capacity = 8
}
`, randClusterName, randClusterName, randClusterName)
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,112 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
package applicationautoscaling
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/signer/v4"
"github.com/aws/aws-sdk-go/private/protocol/jsonrpc"
)
// Application Auto Scaling is a general purpose Auto Scaling service for supported
// elastic AWS resources. With Application Auto Scaling, you can automatically
// scale your AWS resources, with an experience similar to that of Auto Scaling.
//
// At this time, Application Auto Scaling only supports scaling Amazon ECS
// services.
//
// For example, you can use Application Auto Scaling to accomplish the following
// tasks:
//
// Define scaling policies for automatically adjusting your applications
// resources
//
// Scale your resources in response to CloudWatch alarms
//
// View history of your scaling events
//
// Application Auto Scaling is available in the following regions:
//
// us-east-1
//
// us-west-2
//
// eu-west-1
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type ApplicationAutoScaling struct {
*client.Client
}
// Used for custom client initialization logic
var initClient func(*client.Client)
// Used for custom request initialization logic
var initRequest func(*request.Request)
// A ServiceName is the name of the service the client will make API calls to.
const ServiceName = "autoscaling"
// New creates a new instance of the ApplicationAutoScaling client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a ApplicationAutoScaling client from just a session.
// svc := applicationautoscaling.New(mySession)
//
// // Create a ApplicationAutoScaling client with additional configuration
// svc := applicationautoscaling.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *ApplicationAutoScaling {
c := p.ClientConfig(ServiceName, cfgs...)
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *ApplicationAutoScaling {
svc := &ApplicationAutoScaling{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
SigningName: "application-autoscaling",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2016-02-06",
JSONVersion: "1.1",
TargetPrefix: "AnyScaleFrontendService",
},
handlers,
),
}
// Handlers
svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler)
svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler)
svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler)
svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler)
svc.Handlers.UnmarshalError.PushBackNamed(jsonrpc.UnmarshalErrorHandler)
// Run custom client initialization if present
if initClient != nil {
initClient(svc.Client)
}
return svc
}
// newRequest creates a new request for a ApplicationAutoScaling operation and runs any
// custom request initialization.
func (c *ApplicationAutoScaling) newRequest(op *request.Operation, params, data interface{}) *request.Request {
req := c.NewRequest(op, params, data)
// Run custom request initialization if present
if initRequest != nil {
initRequest(req)
}
return req
}

6
vendor/vendor.json vendored
View File

@ -532,6 +532,12 @@
"version": "v1.2.7",
"versionExact": "v1.2.7"
},
{
"checksumSHA1": "Td30Frd+lrCLlkMAirUTbjBXq5Q=",
"path": "github.com/aws/aws-sdk-go/service/applicationautoscaling",
"revision": "90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6",
"revisionTime": "2016-07-08T00:08:20Z"
},
{
"checksumSHA1": "AUA6op9dlm0X4vv1YPFnIFs6404=",
"comment": "v1.1.23",

View File

@ -0,0 +1,74 @@
---
layout: "aws"
page_title: "AWS: aws_appautoscaling_policy"
sidebar_current: "docs-aws-resource-appautoscaling-policy"
description: |-
Provides an Application AutoScaling Policy resource.
---
# aws\_appautoscaling\_policy
Provides an Application AutoScaling Policy resource.
## Example Usage
```
resource "aws_appautoscaling_policy" "down" {
name = "scale-down"
service_namespace = "ecs"
resource_id = "service/ecsclustername/servicename"
scalable_dimension = "ecs:service:DesiredCount"
adjustment_type = "ChangeInCapacity"
cooldown = 60
metric_aggregation_type = "Maximum"
step_adjustment {
metric_interval_lower_bound = 0
scaling_adjustment = -1
}
depends_on = ["aws_appautoscaling_target.target"]
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the policy.
* `policy_type` - (Optional) Defaults to "StepScaling" because it is the only option available.
* `resource_id` - (Required) The Resource ID on which you want the Application AutoScaling policy to apply to. For Amazon ECS services, this value is the resource type, followed by the cluster name and service name, such as `service/default/sample-webapp`.
* `scalable_dimension` - (Optional) The scalable dimension of the scalable target that this scaling policy applies to. The scalable dimension contains the service names- pace, resource type, and scaling property, such as `ecs:service:DesiredCount` for the desired task count of an Amazon ECS service. Defaults to `ecs:service:DesiredCount` since that is the only allowed value.
* `service_namespace` - (Optional) The AWS service namespace of the scalable target that this scaling policy applies to. Defaults to `ecs`, because that is currently the only supported option.
* `adjustment_type` - (Required) Specifies whether the adjustment is an absolute number or a percentage of the current capacity. Valid values are `ChangeInCapacity`, `ExactCapacity`, and `PercentChangeInCapacity`.
* `cooldown` - (Required) The amount of time, in seconds, after a scaling activity completes and before the next scaling activity can start.
* `metric_aggregation_type` - (Required) The aggregation type for the policy's metrics. Valid values are "Minimum", "Maximum", and "Average". Without a value, AWS will treat the aggregation type as "Average".
* `step_adjustments` - (Optional) A set of adjustments that manage scaling. These have the following structure:
```
step_adjustment {
scaling_adjustment = -1
metric_interval_lower_bound = 1.0
metric_interval_upper_bound = 2.0
}
step_adjustment {
scaling_adjustment = 1
metric_interval_lower_bound = 2.0
metric_interval_upper_bound = 3.0
}
```
* `scaling_adjustment` - (Required) The number of members by which to
scale, when the adjustment bounds are breached. A positive value scales
up. A negative value scales down.
* `metric_interval_lower_bound` - (Optional) The lower bound for the
difference between the alarm threshold and the CloudWatch metric.
Without a value, AWS will treat this bound as infinity.
* `metric_interval_upper_bound` - (Optional) The upper bound for the
difference between the alarm threshold and the CloudWatch metric.
Without a value, AWS will treat this bound as infinity. The upper bound
must be greater than the lower bound.
## Attribute Reference
* `arn` - The ARN assigned by AWS to the scaling policy.
* `name` - The scaling policy's name.
* `adjustment_type` - The scaling policy's adjustment type.
* `policy_type` - The scaling policy's type.

View File

@ -0,0 +1,40 @@
---
layout: "aws"
page_title: "AWS: aws_appautoscaling_target"
sidebar_current: "docs-aws-resource-appautoscaling-target"
description: |-
Provides an Application AutoScaling ScalableTarget resource.
---
# aws\_appautoscaling\_target
Provides an Application AutoScaling ScalableTarget resource.
## Example Usage
```
resource "aws_appautoscaling_target" "tgt" {
service_namespace = "ecs"
resource_id = "service/clusterName/serviceName"
scalable_dimension = "ecs:service:DesiredCount"
role_arn = "${var.ecs_iam_role}"
min_capacity = 1
max_capacity = 4
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the policy.
* `resource_id` - (Required) The Resource ID on which you want the Application AutoScaling policy to apply to. For Amazon ECS services, this value is the resource type, followed by the cluster name and service name, such as `service/default/sample-webapp`.
* `scalable_dimension` - (Optional) The scalable dimension of the scalable target. The scalable dimension contains the service namespace, resource type, and scaling property, such as `ecs:service:DesiredCount` for the desired task count of an Amazon ECS service. Defaults to `ecs:service:DesiredCount` since that is the only allowed value.
* `service_namespace` - (Optional) The AWS service namespace of the scalable target. Defaults to `ecs`, because that is currently the only supported option.
* `max_capacity` - (Required) The max capacity of the scalable target.
* `min_capacity` - (Required) The min capacity of the scalable target.
* `role_arn` - (Required) The ARN of the IAM role that allows Application AutoScaling to modify your scalable target on your behalf.
## Attribute Reference
* `arn` - The ARN assigned by AWS to the scaling policy.
* `name` - The scaling policy's name.