Lokasi ngalangkungan proxy:   [ UP ]  
[Ngawartoskeun bug]   [Panyetelan cookie]                
Skip to content

Commit 305a450

Browse files
committed
aws_autoscaling_policy: Add support for StepScaling policies.
Unlike SimpleScaling policies, StepScaling policies require one or more "steps", which are interval ranges in which a tracked metric can lie. Policies can then execute scaling adjustments wedded to these steps. This commit also adds a slew of additional policy attributes which are only applicable to step policies.
1 parent 17ce605 commit 305a450

2 files changed

Lines changed: 194 additions & 8 deletions

File tree

builtin/providers/aws/resource_aws_autoscaling_policy.go

Lines changed: 123 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package aws
22

33
import (
4+
"bytes"
45
"fmt"
56
"log"
67

78
"github.com/aws/aws-sdk-go/aws"
89
"github.com/aws/aws-sdk-go/service/autoscaling"
10+
"github.com/hashicorp/terraform/helper/hashcode"
911
"github.com/hashicorp/terraform/helper/schema"
1012
)
1113

@@ -35,17 +37,59 @@ func resourceAwsAutoscalingPolicy() *schema.Resource {
3537
Required: true,
3638
ForceNew: true,
3739
},
40+
"policy_type": &schema.Schema{
41+
Type: schema.TypeString,
42+
Optional: true,
43+
Default: "SimpleScaling", // preserve AWS's default to make validation easier.
44+
},
3845
"cooldown": &schema.Schema{
3946
Type: schema.TypeInt,
4047
Optional: true,
4148
},
42-
"min_adjustment_step": &schema.Schema{
49+
"estimated_instance_warmup": &schema.Schema{
4350
Type: schema.TypeInt,
4451
Optional: true,
4552
},
46-
"scaling_adjustment": &schema.Schema{
53+
"metric_aggregation_type": &schema.Schema{
54+
Type: schema.TypeString,
55+
Optional: true,
56+
},
57+
"min_adjustment_magnitude": &schema.Schema{
4758
Type: schema.TypeInt,
48-
Required: true,
59+
Optional: true,
60+
},
61+
"min_adjustment_step": &schema.Schema{
62+
Type: schema.TypeInt,
63+
Optional: true,
64+
Deprecated: "Use min_adjustment_magnitude instead.",
65+
ConflictsWith: []string{"min_adjustment_magnitude"},
66+
},
67+
"scaling_adjustment": &schema.Schema{
68+
Type: schema.TypeInt,
69+
Optional: true,
70+
ConflictsWith: []string{"step_adjustment"},
71+
},
72+
"step_adjustment": &schema.Schema{
73+
Type: schema.TypeSet,
74+
Optional: true,
75+
ConflictsWith: []string{"scaling_adjustment"},
76+
Elem: &schema.Resource{
77+
Schema: map[string]*schema.Schema{
78+
"metric_interval_lower_bound": &schema.Schema{
79+
Type: schema.TypeString,
80+
Optional: true,
81+
},
82+
"metric_interval_upper_bound": &schema.Schema{
83+
Type: schema.TypeString,
84+
Optional: true,
85+
},
86+
"scaling_adjustment": &schema.Schema{
87+
Type: schema.TypeInt,
88+
Required: true,
89+
},
90+
},
91+
},
92+
Set: resourceAwsAutoscalingScalingAdjustmentHash,
4993
},
5094
},
5195
}
@@ -54,7 +98,10 @@ func resourceAwsAutoscalingPolicy() *schema.Resource {
5498
func resourceAwsAutoscalingPolicyCreate(d *schema.ResourceData, meta interface{}) error {
5599
autoscalingconn := meta.(*AWSClient).autoscalingconn
56100

57-
params := getAwsAutoscalingPutScalingPolicyInput(d)
101+
params, err := getAwsAutoscalingPutScalingPolicyInput(d)
102+
if err != nil {
103+
return err
104+
}
58105

59106
log.Printf("[DEBUG] AutoScaling PutScalingPolicy: %#v", params)
60107
resp, err := autoscalingconn.PutScalingPolicy(&params)
@@ -84,18 +131,26 @@ func resourceAwsAutoscalingPolicyRead(d *schema.ResourceData, meta interface{})
84131
d.Set("adjustment_type", p.AdjustmentType)
85132
d.Set("autoscaling_group_name", p.AutoScalingGroupName)
86133
d.Set("cooldown", p.Cooldown)
134+
d.Set("estimated_instance_warmup", p.EstimatedInstanceWarmup)
135+
d.Set("metric_aggregation_type", p.MetricAggregationType)
136+
d.Set("policy_type", p.PolicyType)
137+
d.Set("min_adjustment_magnitude", p.MinAdjustmentMagnitude)
87138
d.Set("min_adjustment_step", p.MinAdjustmentStep)
88139
d.Set("arn", p.PolicyARN)
89140
d.Set("name", p.PolicyName)
90141
d.Set("scaling_adjustment", p.ScalingAdjustment)
142+
d.Set("step_adjustment", flattenStepAdjustments(p.StepAdjustments))
91143

92144
return nil
93145
}
94146

95147
func resourceAwsAutoscalingPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
96148
autoscalingconn := meta.(*AWSClient).autoscalingconn
97149

98-
params := getAwsAutoscalingPutScalingPolicyInput(d)
150+
params, inputErr := getAwsAutoscalingPutScalingPolicyInput(d)
151+
if inputErr != nil {
152+
return inputErr
153+
}
99154

100155
log.Printf("[DEBUG] Autoscaling Update Scaling Policy: %#v", params)
101156
_, err := autoscalingconn.PutScalingPolicy(&params)
@@ -128,8 +183,10 @@ func resourceAwsAutoscalingPolicyDelete(d *schema.ResourceData, meta interface{}
128183
return nil
129184
}
130185

131-
// PutScalingPolicy seems to require all params to be resent, so create and update can share this common function
132-
func getAwsAutoscalingPutScalingPolicyInput(d *schema.ResourceData) autoscaling.PutScalingPolicyInput {
186+
// PutScalingPolicy can safely resend all parameters without destroying the
187+
// resource, so create and update can share this common function. It will error
188+
// if certain mutually exclusive values are set.
189+
func getAwsAutoscalingPutScalingPolicyInput(d *schema.ResourceData) (autoscaling.PutScalingPolicyInput, error) {
133190
var params = autoscaling.PutScalingPolicyInput{
134191
AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
135192
PolicyName: aws.String(d.Get("name").(string)),
@@ -143,15 +200,59 @@ func getAwsAutoscalingPutScalingPolicyInput(d *schema.ResourceData) autoscaling.
143200
params.Cooldown = aws.Int64(int64(v.(int)))
144201
}
145202

203+
if v, ok := d.GetOk("estimated_instance_warmup"); ok {
204+
params.EstimatedInstanceWarmup = aws.Int64(int64(v.(int)))
205+
}
206+
207+
if v, ok := d.GetOk("metric_aggregation_type"); ok {
208+
params.MetricAggregationType = aws.String(v.(string))
209+
}
210+
211+
if v, ok := d.GetOk("policy_type"); ok {
212+
params.PolicyType = aws.String(v.(string))
213+
}
214+
146215
if v, ok := d.GetOk("scaling_adjustment"); ok {
147216
params.ScalingAdjustment = aws.Int64(int64(v.(int)))
148217
}
149218

219+
if v, ok := d.GetOk("step_adjustment"); ok {
220+
steps, err := expandStepAdjustments(v.(*schema.Set).List())
221+
if err != nil {
222+
return params, fmt.Errorf("metric_interval_lower_bound and metric_interval_upper_bound must be strings!")
223+
}
224+
params.StepAdjustments = steps
225+
}
226+
227+
if v, ok := d.GetOk("min_adjustment_magnitude"); ok {
228+
params.MinAdjustmentMagnitude = aws.Int64(int64(v.(int)))
229+
}
230+
150231
if v, ok := d.GetOk("min_adjustment_step"); ok {
151232
params.MinAdjustmentStep = aws.Int64(int64(v.(int)))
152233
}
153234

154-
return params
235+
// Validate our final input to confirm it won't error when sent to AWS.
236+
// First, SimpleScaling policy types...
237+
if *params.PolicyType == "SimpleScaling" && params.StepAdjustments != nil {
238+
return params, fmt.Errorf("SimpleScaling policy types cannot use step_adjustments!")
239+
}
240+
if *params.PolicyType == "SimpleScaling" && params.MetricAggregationType != nil {
241+
return params, fmt.Errorf("SimpleScaling policy types cannot use metric_aggregation_type!")
242+
}
243+
if *params.PolicyType == "SimpleScaling" && params.EstimatedInstanceWarmup != nil {
244+
return params, fmt.Errorf("SimpleScaling policy types cannot use estimated_instance_warmup!")
245+
}
246+
247+
// Second, StepScaling policy types...
248+
if *params.PolicyType == "StepScaling" && params.ScalingAdjustment != nil {
249+
return params, fmt.Errorf("StepScaling policy types cannot use scaling_adjustment!")
250+
}
251+
if *params.PolicyType == "StepScaling" && params.Cooldown != nil {
252+
return params, fmt.Errorf("StepScaling policy types cannot use cooldown!")
253+
}
254+
255+
return params, nil
155256
}
156257

157258
func getAwsAutoscalingPolicy(d *schema.ResourceData, meta interface{}) (*autoscaling.ScalingPolicy, error) {
@@ -179,3 +280,17 @@ func getAwsAutoscalingPolicy(d *schema.ResourceData, meta interface{}) (*autosca
179280
// policy not found
180281
return nil, nil
181282
}
283+
284+
func resourceAwsAutoscalingScalingAdjustmentHash(v interface{}) int {
285+
var buf bytes.Buffer
286+
m := v.(map[string]interface{})
287+
if v, ok := m["metric_interval_lower_bound"]; ok {
288+
buf.WriteString(fmt.Sprintf("%f-", v))
289+
}
290+
if v, ok := m["metric_interval_upper_bound"]; ok {
291+
buf.WriteString(fmt.Sprintf("%f-", v))
292+
}
293+
buf.WriteString(fmt.Sprintf("%d-", m["scaling_adjustment"].(int)))
294+
295+
return hashcode.String(buf.String())
296+
}

builtin/providers/aws/structure.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"fmt"
77
"sort"
8+
"strconv"
89
"strings"
910

1011
"github.com/aws/aws-sdk-go/aws"
@@ -305,6 +306,58 @@ func flattenAccessLog(l *elb.AccessLog) []map[string]interface{} {
305306
return result
306307
}
307308

309+
// Takes the result of flatmap.Expand for an array of step adjustments and
310+
// returns a []*autoscaling.StepAdjustment.
311+
func expandStepAdjustments(configured []interface{}) ([]*autoscaling.StepAdjustment, error) {
312+
var adjustments []*autoscaling.StepAdjustment
313+
314+
// Loop over our configured step adjustments and create an array
315+
// of aws-sdk-go compatible objects. We're forced to convert strings
316+
// to floats here because there's no way to detect whether or not
317+
// an uninitialized, optional schema element is "0.0" deliberately.
318+
// With strings, we can test for "", which is definitely an empty
319+
// struct value.
320+
for _, raw := range configured {
321+
data := raw.(map[string]interface{})
322+
a := &autoscaling.StepAdjustment{
323+
ScalingAdjustment: aws.Int64(int64(data["scaling_adjustment"].(int))),
324+
}
325+
if data["metric_interval_lower_bound"] != "" {
326+
bound := data["metric_interval_lower_bound"]
327+
switch bound := bound.(type) {
328+
case string:
329+
f, err := strconv.ParseFloat(bound, 64)
330+
if err != nil {
331+
return nil, fmt.Errorf(
332+
"metric_interval_lower_bound must be a float value represented as a string")
333+
}
334+
a.MetricIntervalLowerBound = aws.Float64(f)
335+
default:
336+
return nil, fmt.Errorf(
337+
"metric_interval_lower_bound isn't a string. This is a bug. Please file an issue.")
338+
}
339+
}
340+
if data["metric_interval_upper_bound"] != "" {
341+
bound := data["metric_interval_upper_bound"]
342+
switch bound := bound.(type) {
343+
case string:
344+
f, err := strconv.ParseFloat(bound, 64)
345+
if err != nil {
346+
return nil, fmt.Errorf(
347+
"metric_interval_upper_bound must be a float value represented as a string")
348+
}
349+
a.MetricIntervalUpperBound = aws.Float64(f)
350+
default:
351+
return nil, fmt.Errorf(
352+
"metric_interval_upper_bound isn't a string. This is a bug. Please file an issue.")
353+
}
354+
}
355+
adjustments = append(adjustments, a)
356+
}
357+
358+
return adjustments, nil
359+
}
360+
308361
// Flattens a health check into something that flatmap.Flatten()
309362
// can handle
310363
func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} {
@@ -564,6 +617,24 @@ func flattenAttachment(a *ec2.NetworkInterfaceAttachment) map[string]interface{}
564617
return att
565618
}
566619

620+
// Flattens step adjustments into a list of map[string]interface.
621+
func flattenStepAdjustments(adjustments []*autoscaling.StepAdjustment) []map[string]interface{} {
622+
result := make([]map[string]interface{}, 0, len(adjustments))
623+
for _, raw := range adjustments {
624+
a := map[string]interface{}{
625+
"scaling_adjustment": *raw.ScalingAdjustment,
626+
}
627+
if raw.MetricIntervalUpperBound != nil {
628+
a["metric_interval_upper_bound"] = *raw.MetricIntervalUpperBound
629+
}
630+
if raw.MetricIntervalLowerBound != nil {
631+
a["metric_interval_lower_bound"] = *raw.MetricIntervalLowerBound
632+
}
633+
result = append(result, a)
634+
}
635+
return result
636+
}
637+
567638
func flattenResourceRecords(recs []*route53.ResourceRecord) []string {
568639
strs := make([]string, 0, len(recs))
569640
for _, r := range recs {

0 commit comments

Comments
 (0)