Skip to content

Commit 7a45b80

Browse files
committed
feat: Enable predictive scaling via scaling policy
1 parent 81242e5 commit 7a45b80

4 files changed

Lines changed: 277 additions & 21 deletions

File tree

castai/resource_workload_scaling_policy.go

Lines changed: 102 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,12 @@ func resourceWorkloadScalingPolicyCreate(ctx context.Context, d *schema.Resource
513542

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

545+
ps, err := toPredictiveScaling(toSection(d, FieldPredictiveScaling))
546+
if err != nil {
547+
return diag.FromErr(err)
548+
}
549+
req.RecommendationPolicies.PredictiveScaling = ps
550+
516551
ar, err := toAssignmentRules(toSection(d, FieldAssignmentRules))
517552
if err != nil {
518553
return diag.FromErr(err)
@@ -593,6 +628,9 @@ func resourceWorkloadScalingPolicyRead(ctx context.Context, d *schema.ResourceDa
593628
if err := d.Set("anti_affinity", toAntiAffinityMap(sp.RecommendationPolicies.AntiAffinity)); err != nil {
594629
return diag.FromErr(fmt.Errorf("setting anti-affinity: %w", err))
595630
}
631+
if err := d.Set("predictive_scaling", toPredictiveScalingMap(sp.RecommendationPolicies.PredictiveScaling)); err != nil {
632+
return diag.FromErr(fmt.Errorf("setting predictive scaling: %w", err))
633+
}
596634

597635
if err := d.Set(FieldAssignmentRules, toAssignmentRulesMap(getResourceFrom(d, FieldAssignmentRules), sp.AssignmentRules)); err != nil {
598636
return diag.FromErr(fmt.Errorf("setting assignment rules: %w", err))
@@ -640,20 +678,25 @@ func resourceWorkloadScalingPolicyUpdate(ctx context.Context, d *schema.Resource
640678
if err != nil {
641679
return diag.FromErr(err)
642680
}
681+
predictiveScaling, err := toPredictiveScaling(toSection(d, FieldPredictiveScaling))
682+
if err != nil {
683+
return diag.FromErr(err)
684+
}
643685

644686
req := sdk.WorkloadOptimizationAPIUpdateWorkloadScalingPolicyJSONRequestBody{
645687
Name: d.Get("name").(string),
646688
ApplyType: sdk.WorkloadoptimizationV1ApplyType(d.Get("apply_type").(string)),
647689
AssignmentRules: ar,
648690
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)),
691+
ManagementOption: sdk.WorkloadoptimizationV1ManagementOption(d.Get("management_option").(string)),
692+
Cpu: cpu,
693+
Memory: memory,
694+
Startup: toStartup(toSection(d, "startup")),
695+
Downscaling: toDownscaling(toSection(d, "downscaling")),
696+
MemoryEvent: toMemoryEvent(toSection(d, "memory_event")),
697+
AntiAffinity: toAntiAffinity(toSection(d, "anti_affinity")),
698+
Confidence: toConfidence(toSection(d, FieldConfidence)),
699+
PredictiveScaling: predictiveScaling,
657700
},
658701
}
659702

@@ -1161,6 +1204,57 @@ func toAntiAffinityMap(s *sdk.WorkloadoptimizationV1AntiAffinitySettings) []map[
11611204
return []map[string]any{m}
11621205
}
11631206

1207+
func toPredictiveScalingMap(s *sdk.WorkloadoptimizationV1PredictiveScalingSettings) []map[string]any {
1208+
if s == nil {
1209+
return nil
1210+
}
1211+
1212+
return []map[string]any{
1213+
{
1214+
FieldCPU: toPredictiveScalingResourceMap(s.Cpu),
1215+
},
1216+
}
1217+
}
1218+
1219+
func toPredictiveScaling(m map[string]any) (*sdk.WorkloadoptimizationV1PredictiveScalingSettings, error) {
1220+
if len(m) == 0 {
1221+
return nil, nil
1222+
}
1223+
1224+
cpuCfg := getFirstElem(m, FieldCPU)
1225+
cpuResource, err := toPredictiveScalingResource(cpuCfg)
1226+
if err != nil {
1227+
return nil, fmt.Errorf("field %q: field %q: %w", FieldPredictiveScaling, FieldCPU, err)
1228+
}
1229+
1230+
return &sdk.WorkloadoptimizationV1PredictiveScalingSettings{
1231+
Cpu: *cpuResource,
1232+
}, nil
1233+
}
1234+
1235+
func toPredictiveScalingResourceMap(s sdk.WorkloadoptimizationV1PredictiveScaling) []map[string]any {
1236+
return []map[string]any{
1237+
{
1238+
FieldEnabled: s.Enabled,
1239+
},
1240+
}
1241+
}
1242+
1243+
func toPredictiveScalingResource(m map[string]any) (*sdk.WorkloadoptimizationV1PredictiveScaling, error) {
1244+
if len(m) == 0 {
1245+
return nil, nil
1246+
}
1247+
1248+
enabled, err := mustGetValue[bool](m, FieldEnabled)
1249+
if err != nil {
1250+
return nil, err
1251+
}
1252+
1253+
return &sdk.WorkloadoptimizationV1PredictiveScaling{
1254+
Enabled: *enabled,
1255+
}, nil
1256+
}
1257+
11641258
func getWorkloadScalingPolicyByName(ctx context.Context, client sdk.ClientWithResponsesInterface, clusterID, name string) (*sdk.WorkloadoptimizationV1WorkloadScalingPolicy, error) {
11651259
list, err := client.WorkloadOptimizationAPIListWorkloadScalingPoliciesWithResponse(ctx, clusterID)
11661260
if checkErr := sdk.CheckOKResponse(list, err); checkErr != nil {

castai/resource_workload_scaling_policy_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ func TestAccResourceWorkloadScalingPolicy(t *testing.T) {
105105
resource.TestCheckResourceAttr(resourceName, "assignment_rules.0.rules.1.workload.0.gvk.0", "DaemonSet"),
106106
resource.TestCheckResourceAttr(resourceName, "assignment_rules.0.rules.1.workload.0.labels_expressions.0.key", "helm.sh/chart"),
107107
resource.TestCheckResourceAttr(resourceName, "assignment_rules.0.rules.1.workload.0.labels_expressions.0.operator", "DoesNotExist"),
108+
resource.TestCheckResourceAttr(resourceName, "predictive_scaling.0.cpu.0.enabled", "true"),
108109
),
109110
},
110111
},
@@ -213,6 +214,11 @@ func scalingPolicyConfigUpdated(clusterName, projectID, name string) string {
213214
}
214215
}
215216
}
217+
predictive_scaling {
218+
cpu {
219+
enabled = true
220+
}
221+
}
216222
cpu {
217223
function = "QUANTILE"
218224
overhead = 0.15

0 commit comments

Comments
 (0)