Skip to content

Commit 0a493f2

Browse files
skolodyazhnyytuannvmjrhouston
authored
Add behavior field to horizontal_pod_autoscaler (#1030)
Co-authored-by: tuannvm <[email protected]> Co-authored-by: John Houston <[email protected]>
1 parent aeb99cf commit 0a493f2

5 files changed

+377
-0
lines changed

kubernetes/resource_kubernetes_horizontal_pod_autoscaler.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func resourceKubernetesHorizontalPodAutoscaler() *schema.Resource {
3838
},
3939
"metric": {
4040
Type: schema.TypeList,
41+
Computed: true,
4142
Optional: true,
4243
Description: "The specifications for which to use to calculate the desired replica count (the maximum replica count across all metrics will be used). The desired replica count is calculated multiplying the ratio between the target value and the current value by the current number of pods. Ergo, metrics used must decrease as the pod count is increased, and vice-versa. See the individual metric source types for more information about how each type of metric must respond. If not set, the default metric will be set to 80% average CPU utilization.",
4344
Elem: metricSpecFields(),
@@ -48,6 +49,28 @@ func resourceKubernetesHorizontalPodAutoscaler() *schema.Resource {
4849
Optional: true,
4950
Default: 1,
5051
},
52+
"behavior": {
53+
Type: schema.TypeList,
54+
Description: "Behavior configures the scaling behavior of the target in both Up and Down directions (scale_up and scale_down fields respectively).",
55+
Optional: true,
56+
MaxItems: 1,
57+
Elem: &schema.Resource{
58+
Schema: map[string]*schema.Schema{
59+
"scale_up": {
60+
Type: schema.TypeList,
61+
Description: "Scaling policy for scaling Up",
62+
Optional: true,
63+
Elem: scalingRulesSpecFields(),
64+
},
65+
"scale_down": {
66+
Type: schema.TypeList,
67+
Description: "Scaling policy for scaling Down",
68+
Optional: true,
69+
Elem: scalingRulesSpecFields(),
70+
},
71+
},
72+
},
73+
},
5174
"scale_target_ref": {
5275
Type: schema.TypeList,
5376
Description: "Reference to scaled resource. e.g. Replication Controller",
@@ -260,5 +283,11 @@ func useV2(d *schema.ResourceData) bool {
260283
log.Printf("[INFO] Using autoscaling/v2beta2 because this resource has a metric field")
261284
return true
262285
}
286+
287+
if len(d.Get("spec.0.behavior").([]interface{})) > 0 {
288+
log.Printf("[INFO] Using autoscaling/v2beta2 because this resource has a behavior field")
289+
return true
290+
}
291+
263292
return false
264293
}

kubernetes/resource_kubernetes_horizontal_pod_autoscaler_v2_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,27 @@ func TestAccKubernetesHorizontalPodAutoscalerV2_basic(t *testing.T) {
3434
resource.TestCheckResourceAttrSet("kubernetes_horizontal_pod_autoscaler.test", "metadata.0.resource_version"),
3535
resource.TestCheckResourceAttrSet("kubernetes_horizontal_pod_autoscaler.test", "metadata.0.uid"),
3636
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.#", "1"),
37+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.#", "1"),
38+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.#", "1"),
39+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.policy.#", "2"),
40+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.policy.0.period_seconds", "120"),
41+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.policy.0.type", "Pods"),
42+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.policy.0.value", "1"),
43+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.policy.1.period_seconds", "310"),
44+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.policy.1.type", "Percent"),
45+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.policy.1.value", "100"),
46+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.select_policy", "Min"),
47+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.stabilization_window_seconds", "300"),
48+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.#", "1"),
49+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.policy.#", "2"),
50+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.policy.0.period_seconds", "180"),
51+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.policy.0.type", "Percent"),
52+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.policy.0.value", "100"),
53+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.policy.1.period_seconds", "600"),
54+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.policy.1.type", "Pods"),
55+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.policy.1.value", "5"),
56+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.select_policy", "Max"),
57+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.stabilization_window_seconds", "600"),
3758
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.max_replicas", "10"),
3859
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.min_replicas", "1"),
3960
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.scale_target_ref.#", "1"),
@@ -76,6 +97,20 @@ func TestAccKubernetesHorizontalPodAutoscalerV2_basic(t *testing.T) {
7697
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "metadata.0.labels.test", "test"),
7798
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "metadata.0.name", name),
7899
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.#", "1"),
100+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.#", "1"),
101+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.#", "1"),
102+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.policy.#", "1"),
103+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.policy.0.period_seconds", "120"),
104+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.policy.0.type", "Pods"),
105+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.policy.0.value", "10"),
106+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.select_policy", "Max"),
107+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_down.0.stabilization_window_seconds", "100"),
108+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.#", "1"),
109+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.select_policy", "Disabled"),
110+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.policy.#", "1"),
111+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.policy.0.period_seconds", "60"),
112+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.policy.0.type", "Pods"),
113+
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.behavior.0.scale_up.0.policy.0.value", "1"),
79114
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.max_replicas", "100"),
80115
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.min_replicas", "50"),
81116
resource.TestCheckResourceAttr("kubernetes_horizontal_pod_autoscaler.test", "spec.0.scale_target_ref.#", "1"),
@@ -142,6 +177,42 @@ func testAccKubernetesHorizontalPodAutoscalerV2Config_basic(name string) string
142177
name = "TerraformAccTest"
143178
}
144179
180+
behavior {
181+
scale_down {
182+
stabilization_window_seconds = 300
183+
select_policy = "Min"
184+
185+
policy {
186+
period_seconds = 120
187+
type = "Pods"
188+
value = 1
189+
}
190+
191+
policy {
192+
period_seconds = 310
193+
type = "Percent"
194+
value = 100
195+
}
196+
}
197+
198+
scale_up {
199+
stabilization_window_seconds = 600
200+
select_policy = "Max"
201+
202+
policy {
203+
period_seconds = 180
204+
type = "Percent"
205+
value = 100
206+
}
207+
208+
policy {
209+
period_seconds = 600
210+
type = "Pods"
211+
value = 5
212+
}
213+
}
214+
}
215+
145216
metric {
146217
type = "Resource"
147218
resource {
@@ -229,6 +300,29 @@ func testAccKubernetesHorizontalPodAutoscalerV2Config_modified(name string) stri
229300
name = "TerraformAccTest"
230301
}
231302
303+
behavior {
304+
scale_down {
305+
stabilization_window_seconds = 100
306+
select_policy = "Max"
307+
308+
policy {
309+
period_seconds = 120
310+
type = "Pods"
311+
value = 10
312+
}
313+
}
314+
315+
scale_up {
316+
select_policy = "Disabled"
317+
318+
policy {
319+
period_seconds = 60
320+
type = "Pods"
321+
value = 1
322+
}
323+
}
324+
}
325+
232326
metric {
233327
type = "External"
234328
external {

kubernetes/schema_horizontal_pod_autoscaler.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,49 @@ func metricSpecFields() *schema.Resource {
199199
},
200200
}
201201
}
202+
203+
func scalingRulesSpecFields() *schema.Resource {
204+
return &schema.Resource{
205+
Schema: map[string]*schema.Schema{
206+
"policy": {
207+
Type: schema.TypeList,
208+
Required: true,
209+
MinItems: 1,
210+
Elem: scalingPolicySpecFields(),
211+
Description: "List of potential scaling polices which can be used during scaling. At least one policy must be specified, otherwise the scaling rule will be discarded as invalid.",
212+
},
213+
"select_policy": {
214+
Type: schema.TypeString,
215+
Optional: true,
216+
Description: "Used to specify which policy should be used. If not set, the default value Max is used.",
217+
},
218+
"stabilization_window_seconds": {
219+
Type: schema.TypeInt,
220+
Optional: true,
221+
Description: "Number of seconds for which past recommendations should be considered while scaling up or scaling down. This value must be greater than or equal to zero and less than or equal to 3600 (one hour). If not set, use the default values: - For scale up: 0 (i.e. no stabilization is done). - For scale down: 300 (i.e. the stabilization window is 300 seconds long).",
222+
},
223+
},
224+
}
225+
}
226+
227+
func scalingPolicySpecFields() *schema.Resource {
228+
return &schema.Resource{
229+
Schema: map[string]*schema.Schema{
230+
"period_seconds": {
231+
Type: schema.TypeInt,
232+
Required: true,
233+
Description: "Period specifies the window of time for which the policy should hold true. PeriodSeconds must be greater than zero and less than or equal to 1800 (30 min).",
234+
},
235+
"type": {
236+
Type: schema.TypeString,
237+
Required: true,
238+
Description: "Type is used to specify the scaling policy: Percent or Pods",
239+
},
240+
"value": {
241+
Type: schema.TypeInt,
242+
Required: true,
243+
Description: "Value contains the amount of change which is permitted by the policy. It must be greater than zero.",
244+
},
245+
},
246+
}
247+
}

kubernetes/structure_horizontal_pod_autoscaler_v2.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ func expandHorizontalPodAutoscalerV2Spec(in []interface{}) (*autoscalingv2beta2.
3333
spec.Metrics = expandV2Metrics(v)
3434
}
3535

36+
if v, ok := m["behavior"].([]interface{}); ok {
37+
spec.Behavior = expandV2Behavior(v)
38+
}
39+
3640
return spec, nil
3741
}
3842

@@ -170,6 +174,76 @@ func expandV2MetricSpec(m map[string]interface{}) autoscalingv2beta2.MetricSpec
170174
return spec
171175
}
172176

177+
func expandV2Behavior(in []interface{}) *autoscalingv2beta2.HorizontalPodAutoscalerBehavior {
178+
spec := &autoscalingv2beta2.HorizontalPodAutoscalerBehavior{}
179+
180+
if len(in) == 0 || in[0] == nil {
181+
return spec
182+
}
183+
184+
b := in[0].(map[string]interface{})
185+
186+
if v, ok := b["scale_up"].([]interface{}); ok {
187+
spec.ScaleUp = expandV2ScalingRules(v)
188+
}
189+
190+
if v, ok := b["scale_down"].([]interface{}); ok {
191+
spec.ScaleDown = expandV2ScalingRules(v)
192+
}
193+
194+
return spec
195+
}
196+
197+
func expandV2ScalingRules(in []interface{}) *autoscalingv2beta2.HPAScalingRules {
198+
spec := &autoscalingv2beta2.HPAScalingRules{}
199+
200+
if len(in) == 0 || in[0] == nil {
201+
return spec
202+
}
203+
204+
r := in[0].(map[string]interface{})
205+
206+
spec.Policies = expandV2ScalingPolicies(r["policy"].([]interface{}))
207+
208+
if v, ok := r["select_policy"].(string); ok {
209+
spec.SelectPolicy = (*autoscalingv2beta2.ScalingPolicySelect)(&v)
210+
}
211+
212+
if v, ok := r["stabilization_window_seconds"].(int); ok {
213+
spec.StabilizationWindowSeconds = ptrToInt32(int32(v))
214+
}
215+
216+
return spec
217+
}
218+
219+
func expandV2ScalingPolicies(in []interface{}) []autoscalingv2beta2.HPAScalingPolicy {
220+
policies := []autoscalingv2beta2.HPAScalingPolicy{}
221+
222+
for _, m := range in {
223+
policies = append(policies, expandV2ScalingPolicy(m.(map[string]interface{})))
224+
}
225+
226+
return policies
227+
}
228+
229+
func expandV2ScalingPolicy(in map[string]interface{}) autoscalingv2beta2.HPAScalingPolicy {
230+
spec := autoscalingv2beta2.HPAScalingPolicy{}
231+
232+
if v, ok := in["period_seconds"].(int); ok {
233+
spec.PeriodSeconds = int32(v)
234+
}
235+
236+
if v, ok := in["type"].(string); ok {
237+
spec.Type = autoscalingv2beta2.HPAScalingPolicyType(v)
238+
}
239+
240+
if v, ok := in["value"].(int); ok {
241+
spec.Value = int32(v)
242+
}
243+
244+
return spec
245+
}
246+
173247
func expandV2CrossVersionObjectReference(in []interface{}) autoscalingv2beta2.CrossVersionObjectReference {
174248
ref := autoscalingv2beta2.CrossVersionObjectReference{}
175249

@@ -296,6 +370,10 @@ func flattenHorizontalPodAutoscalerV2Spec(spec autoscalingv2beta2.HorizontalPodA
296370
}
297371
m["metric"] = metrics
298372

373+
if spec.Behavior != nil {
374+
m["behavior"] = flattenV2Behavior(*spec.Behavior)
375+
}
376+
299377
return []interface{}{m}
300378
}
301379

@@ -317,6 +395,51 @@ func flattenV2CrossVersionObjectReference(ref autoscalingv2beta2.CrossVersionObj
317395
return []interface{}{m}
318396
}
319397

398+
func flattenV2Behavior(spec autoscalingv2beta2.HorizontalPodAutoscalerBehavior) []interface{} {
399+
b := map[string]interface{}{}
400+
401+
if spec.ScaleUp != nil {
402+
b["scale_up"] = flattenV2ScalingRules(*spec.ScaleUp)
403+
}
404+
405+
if spec.ScaleDown != nil {
406+
b["scale_down"] = flattenV2ScalingRules(*spec.ScaleDown)
407+
}
408+
409+
return []interface{}{b}
410+
}
411+
412+
func flattenV2ScalingRules(spec autoscalingv2beta2.HPAScalingRules) []interface{} {
413+
r := map[string]interface{}{}
414+
415+
if spec.Policies != nil {
416+
policies := []interface{}{}
417+
for _, m := range spec.Policies {
418+
policies = append(policies, flattenV2ScalingPolicy(m))
419+
}
420+
421+
r["policy"] = policies
422+
}
423+
424+
if spec.SelectPolicy != nil {
425+
r["select_policy"] = string(*spec.SelectPolicy)
426+
}
427+
428+
if spec.StabilizationWindowSeconds != nil {
429+
r["stabilization_window_seconds"] = int(*spec.StabilizationWindowSeconds)
430+
}
431+
432+
return []interface{}{r}
433+
}
434+
435+
func flattenV2ScalingPolicy(spec autoscalingv2beta2.HPAScalingPolicy) map[string]interface{} {
436+
return map[string]interface{}{
437+
"type": string(spec.Type),
438+
"value": int(spec.Value),
439+
"period_seconds": int(spec.PeriodSeconds),
440+
}
441+
}
442+
320443
func patchHorizontalPodAutoscalerV2Spec(prefix string, pathPrefix string, d *schema.ResourceData) []PatchOperation {
321444
ops := make([]PatchOperation, 0)
322445

@@ -348,5 +471,12 @@ func patchHorizontalPodAutoscalerV2Spec(prefix string, pathPrefix string, d *sch
348471
})
349472
}
350473

474+
if d.HasChange(prefix + "behavior") {
475+
ops = append(ops, &ReplaceOperation{
476+
Path: pathPrefix + "/behavior",
477+
Value: expandV2Behavior(d.Get(prefix + "behavior").([]interface{})),
478+
})
479+
}
480+
351481
return ops
352482
}

0 commit comments

Comments
 (0)