Skip to content

Commit 3fa96f2

Browse files
authored
feat: Allow enabling predictive scaling on workload optimization scaling policy (#524)
1 parent ffab911 commit 3fa96f2

5 files changed

Lines changed: 321 additions & 24 deletions

File tree

castai/resource_workload_scaling_policy.go

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const (
3636
FieldLimitStrategyType = "type"
3737
FieldLimitStrategyMultiplier = "multiplier"
3838
FieldConfidence = "confidence"
39+
FieldPredictiveScaling = "predictive_scaling"
3940
FieldConfidenceThreshold = "threshold"
4041
DeprecatedFieldApplyThreshold = "apply_threshold"
4142
FieldApplyThresholdStrategy = "apply_threshold_strategy"
@@ -257,6 +258,16 @@ It can be either:
257258
},
258259
},
259260
},
261+
FieldPredictiveScaling: {
262+
Type: schema.TypeList,
263+
Optional: true,
264+
MaxItems: 1,
265+
Elem: &schema.Resource{
266+
Schema: map[string]*schema.Schema{
267+
FieldCPU: getPredictiveScalingResourceSchema(),
268+
},
269+
},
270+
},
260271
},
261272
Timeouts: &schema.ResourceTimeout{
262273
Create: schema.DefaultTimeout(15 * time.Second),
@@ -267,6 +278,24 @@ It can be either:
267278
}
268279
}
269280

281+
func getPredictiveScalingResourceSchema() *schema.Schema {
282+
return &schema.Schema{
283+
Type: schema.TypeList,
284+
Optional: true,
285+
MaxItems: 1,
286+
Description: "Defines predictive scaling resource configuration.",
287+
Elem: &schema.Resource{
288+
Schema: map[string]*schema.Schema{
289+
FieldEnabled: {
290+
Type: schema.TypeBool,
291+
Required: true,
292+
Description: "Defines if predictive scaling is enabled for resource.",
293+
},
294+
},
295+
},
296+
}
297+
}
298+
270299
func k8sLabelExpressionsSchema() *schema.Schema {
271300
return &schema.Schema{
272301
Type: schema.TypeList,
@@ -513,6 +542,8 @@ func resourceWorkloadScalingPolicyCreate(ctx context.Context, d *schema.Resource
513542

514543
req.RecommendationPolicies.AntiAffinity = toAntiAffinity(toSection(d, "anti_affinity"))
515544

545+
req.RecommendationPolicies.PredictiveScaling = toPredictiveScaling(toSection(d, FieldPredictiveScaling))
546+
516547
ar, err := toAssignmentRules(toSection(d, FieldAssignmentRules))
517548
if err != nil {
518549
return diag.FromErr(err)
@@ -593,6 +624,9 @@ func resourceWorkloadScalingPolicyRead(ctx context.Context, d *schema.ResourceDa
593624
if err := d.Set("anti_affinity", toAntiAffinityMap(sp.RecommendationPolicies.AntiAffinity)); err != nil {
594625
return diag.FromErr(fmt.Errorf("setting anti-affinity: %w", err))
595626
}
627+
if err := d.Set(FieldPredictiveScaling, toPredictiveScalingMap(sp.RecommendationPolicies.PredictiveScaling)); err != nil {
628+
return diag.FromErr(fmt.Errorf("setting predictive scaling: %w", err))
629+
}
596630

597631
if err := d.Set(FieldAssignmentRules, toAssignmentRulesMap(getResourceFrom(d, FieldAssignmentRules), sp.AssignmentRules)); err != nil {
598632
return diag.FromErr(fmt.Errorf("setting assignment rules: %w", err))
@@ -621,6 +655,7 @@ func resourceWorkloadScalingPolicyUpdate(ctx context.Context, d *schema.Resource
621655
"anti_affinity",
622656
FieldConfidence,
623657
FieldAssignmentRules,
658+
FieldPredictiveScaling,
624659
) {
625660
tflog.Info(ctx, "scaling policy up to date")
626661
return nil
@@ -646,14 +681,15 @@ func resourceWorkloadScalingPolicyUpdate(ctx context.Context, d *schema.Resource
646681
ApplyType: sdk.WorkloadoptimizationV1ApplyType(d.Get("apply_type").(string)),
647682
AssignmentRules: ar,
648683
RecommendationPolicies: sdk.WorkloadoptimizationV1RecommendationPolicies{
649-
ManagementOption: sdk.WorkloadoptimizationV1ManagementOption(d.Get("management_option").(string)),
650-
Cpu: cpu,
651-
Memory: memory,
652-
Startup: toStartup(toSection(d, "startup")),
653-
Downscaling: toDownscaling(toSection(d, "downscaling")),
654-
MemoryEvent: toMemoryEvent(toSection(d, "memory_event")),
655-
AntiAffinity: toAntiAffinity(toSection(d, "anti_affinity")),
656-
Confidence: toConfidence(toSection(d, FieldConfidence)),
684+
ManagementOption: sdk.WorkloadoptimizationV1ManagementOption(d.Get("management_option").(string)),
685+
Cpu: cpu,
686+
Memory: memory,
687+
Startup: toStartup(toSection(d, "startup")),
688+
Downscaling: toDownscaling(toSection(d, "downscaling")),
689+
MemoryEvent: toMemoryEvent(toSection(d, "memory_event")),
690+
AntiAffinity: toAntiAffinity(toSection(d, "anti_affinity")),
691+
Confidence: toConfidence(toSection(d, FieldConfidence)),
692+
PredictiveScaling: toPredictiveScaling(toSection(d, FieldPredictiveScaling)),
657693
},
658694
}
659695

@@ -1161,6 +1197,59 @@ func toAntiAffinityMap(s *sdk.WorkloadoptimizationV1AntiAffinitySettings) []map[
11611197
return []map[string]any{m}
11621198
}
11631199

1200+
func toPredictiveScalingMap(s *sdk.WorkloadoptimizationV1PredictiveScalingSettings) []map[string]any {
1201+
if s == nil || s.Cpu == nil {
1202+
return nil
1203+
}
1204+
1205+
return []map[string]any{
1206+
{
1207+
FieldCPU: toPredictiveScalingResourceMap(s.Cpu),
1208+
},
1209+
}
1210+
}
1211+
1212+
func toPredictiveScaling(m map[string]any) *sdk.WorkloadoptimizationV1PredictiveScalingSettings {
1213+
if len(m) == 0 {
1214+
return nil
1215+
}
1216+
1217+
cpuResource := toPredictiveScalingResource(getFirstElem(m, FieldCPU))
1218+
if cpuResource == nil {
1219+
return nil
1220+
}
1221+
1222+
return &sdk.WorkloadoptimizationV1PredictiveScalingSettings{
1223+
Cpu: cpuResource,
1224+
}
1225+
}
1226+
1227+
func toPredictiveScalingResourceMap(s *sdk.WorkloadoptimizationV1PredictiveScaling) []map[string]any {
1228+
if s == nil {
1229+
return nil
1230+
}
1231+
1232+
return []map[string]any{
1233+
{
1234+
FieldEnabled: s.Enabled,
1235+
},
1236+
}
1237+
}
1238+
1239+
func toPredictiveScalingResource(m map[string]any) *sdk.WorkloadoptimizationV1PredictiveScaling {
1240+
if len(m) == 0 {
1241+
return nil
1242+
}
1243+
1244+
r := &sdk.WorkloadoptimizationV1PredictiveScaling{}
1245+
1246+
if v, ok := m[FieldEnabled].(bool); ok {
1247+
r.Enabled = v
1248+
}
1249+
1250+
return r
1251+
}
1252+
11641253
func getWorkloadScalingPolicyByName(ctx context.Context, client sdk.ClientWithResponsesInterface, clusterID, name string) (*sdk.WorkloadoptimizationV1WorkloadScalingPolicy, error) {
11651254
list, err := client.WorkloadOptimizationAPIListWorkloadScalingPoliciesWithResponse(ctx, clusterID)
11661255
if checkErr := sdk.CheckOKResponse(list, err); checkErr != nil {

castai/resource_workload_scaling_policy_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
1313
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
1414
"github.com/stretchr/testify/require"
15+
16+
"github.com/castai/terraform-provider-castai/castai/sdk"
1517
)
1618

1719
func TestAccResourceWorkloadScalingPolicy(t *testing.T) {
@@ -105,6 +107,7 @@ func TestAccResourceWorkloadScalingPolicy(t *testing.T) {
105107
resource.TestCheckResourceAttr(resourceName, "assignment_rules.0.rules.1.workload.0.gvk.0", "DaemonSet"),
106108
resource.TestCheckResourceAttr(resourceName, "assignment_rules.0.rules.1.workload.0.labels_expressions.0.key", "helm.sh/chart"),
107109
resource.TestCheckResourceAttr(resourceName, "assignment_rules.0.rules.1.workload.0.labels_expressions.0.operator", "DoesNotExist"),
110+
resource.TestCheckResourceAttr(resourceName, "predictive_scaling.0.cpu.0.enabled", "true"),
108111
),
109112
},
110113
},
@@ -213,6 +216,11 @@ func scalingPolicyConfigUpdated(clusterName, projectID, name string) string {
213216
}
214217
}
215218
}
219+
predictive_scaling {
220+
cpu {
221+
enabled = true
222+
}
223+
}
216224
cpu {
217225
function = "QUANTILE"
218226
overhead = 0.15
@@ -392,3 +400,36 @@ func Test_validateResourcePolicy(t *testing.T) {
392400
})
393401
}
394402
}
403+
404+
func Test_toPredictiveScaling(t *testing.T) {
405+
tests := map[string]struct {
406+
args map[string]any
407+
exp *sdk.WorkloadoptimizationV1PredictiveScalingSettings
408+
}{
409+
"should return predictive scaling settings": {
410+
args: map[string]any{
411+
FieldCPU: []any{
412+
map[string]any{
413+
FieldEnabled: true,
414+
},
415+
},
416+
},
417+
exp: &sdk.WorkloadoptimizationV1PredictiveScalingSettings{
418+
Cpu: &sdk.WorkloadoptimizationV1PredictiveScaling{
419+
Enabled: true,
420+
},
421+
},
422+
},
423+
"should return nil on empty map": {
424+
args: map[string]any{},
425+
exp: nil,
426+
},
427+
}
428+
for name, tt := range tests {
429+
t.Run(name, func(t *testing.T) {
430+
r := require.New(t)
431+
got := toPredictiveScaling(tt.args)
432+
r.Equal(tt.exp, got)
433+
})
434+
}
435+
}

0 commit comments

Comments
 (0)