2016-07-15 13:54:36 +02:00
|
|
|
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 {
|
2016-07-29 22:05:57 +02:00
|
|
|
errors = append(errors, fmt.Errorf("%s cannot be longer than 255 characters", k))
|
2016-07-15 13:54:36 +02:00
|
|
|
}
|
|
|
|
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(¶ms)
|
|
|
|
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(¶ms)
|
|
|
|
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(¶ms); 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(¶ms)
|
|
|
|
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())
|
|
|
|
}
|