From deca29752eb71ad2fc3ecaea6b3e1a54c049eb93 Mon Sep 17 00:00:00 2001 From: Sharad Kesarwani <108344822+sharadkesarwani@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:39:57 +0530 Subject: [PATCH 1/9] Fixed duplicate attachment of rightsizing workload --- ...source_spotinst_ocean_right_sizing_rule.go | 165 +++++++++++++++--- 1 file changed, 142 insertions(+), 23 deletions(-) diff --git a/spotinst/resource_spotinst_ocean_right_sizing_rule.go b/spotinst/resource_spotinst_ocean_right_sizing_rule.go index a3e871d1..a87d67f5 100644 --- a/spotinst/resource_spotinst_ocean_right_sizing_rule.go +++ b/spotinst/resource_spotinst_ocean_right_sizing_rule.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "reflect" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -214,7 +215,7 @@ func attachRightSizingRule(resourceData *schema.ResourceData, meta interface{}, "skipping attach workloads for right sizing rule %q", ruleName) } - attachWorkloadsSpec, err := expandAttachWorkloadsConfig(attachWorkloadsSchema, ruleName, oceanId) + attachWorkloadsSpec, err := expandAttachWorkloadsConfig(attachWorkloadsSchema, ruleName, oceanId, meta) if err != nil { return fmt.Errorf("failed expanding attach workloads "+ "configuration for right sizing rule %q, error: %v", ruleName, err) @@ -226,18 +227,20 @@ func attachRightSizingRule(resourceData *schema.ResourceData, meta interface{}, "configuration for right sizing rule %q, error: %v", ruleName, err) } - log.Printf("onUpdate() -> Updating right sizing rule [%v] with configuration %s", ruleName, updateStateJSON) - attachRuleInput := &right_sizing.RightSizingAttachDetachInput{ - RuleName: attachWorkloadsSpec.RuleName, - OceanId: attachWorkloadsSpec.OceanId, - Namespaces: attachWorkloadsSpec.Namespaces, - } - if _, err = meta.(*Client).ocean.RightSizing().AttachRightSizingRule(context.TODO(), - attachRuleInput); err != nil { - return fmt.Errorf("onUpdate() -> Attach workloads failed for right sizing rule [%v], error: %v", - ruleName, err) + if len(attachWorkloadsSpec.Namespaces) > 0 { + log.Printf("onUpdate() -> Updating right sizing rule [%v] with configuration %s", ruleName, updateStateJSON) + attachRuleInput := &right_sizing.RightSizingAttachDetachInput{ + RuleName: attachWorkloadsSpec.RuleName, + OceanId: attachWorkloadsSpec.OceanId, + Namespaces: attachWorkloadsSpec.Namespaces, + } + if _, err = meta.(*Client).ocean.RightSizing().AttachRightSizingRule(context.TODO(), + attachRuleInput); err != nil { + return fmt.Errorf("onUpdate() -> Attach workloads failed for right sizing rule [%v], error: %v", + ruleName, err) + } + log.Printf("onUpdate() -> Successfully attached workloads for right sizing rule [%v]", ruleName) } - log.Printf("onUpdate() -> Successfully attached workloads for right sizing rule [%v]", ruleName) } return nil @@ -289,7 +292,7 @@ func detachRightSizingRule(resourceData *schema.ResourceData, meta interface{}, } func expandAttachWorkloadsConfig(data interface{}, - ruleName string, oceanId *string) (*right_sizing.RightSizingAttachDetachInput, error) { + ruleName string, oceanId *string, meta interface{}) (*right_sizing.RightSizingAttachDetachInput, error) { spec := &right_sizing.RightSizingAttachDetachInput{ OceanId: oceanId, RuleName: spotinst.String(ruleName), @@ -299,7 +302,7 @@ func expandAttachWorkloadsConfig(data interface{}, m := data.(map[string]interface{}) if v, ok := m[string(ocean_right_sizing_rule.Namespaces)]; ok { - namespaces, err := expandNamespaces(v) + namespaces, err := expandNamespaces(v, ruleName, oceanId, meta) if err != nil { return nil, err } @@ -315,10 +318,18 @@ func expandAttachWorkloadsConfig(data interface{}, return spec, nil } -func expandNamespaces(data interface{}) ([]*right_sizing.Namespace, error) { +func expandNamespaces(data interface{}, ruleName string, oceanId *string, meta interface{}) ([]*right_sizing.Namespace, error) { list := data.(*schema.Set).List() namespaces := make([]*right_sizing.Namespace, 0, len(list)) + input := &right_sizing.ReadRightsizingRuleAttachedWorkloadsInput{ + RuleName: spotinst.String(ruleName), + OceanId: oceanId, + } + resp, err := meta.(*Client).ocean.RightSizing().ReadRightsizingRuleAttachedWorkloads(context.Background(), input) + log.Print(resp) + log.Print(err) + for _, item := range list { attr := item.(map[string]interface{}) @@ -329,7 +340,7 @@ func expandNamespaces(data interface{}) ([]*right_sizing.Namespace, error) { } if v, ok := attr[string(ocean_right_sizing_rule.Workloads)]; ok { - workloads, err := expandWorkloads(v) + workloads, err := expandWorkloads(v, namespace.NamespaceName, resp) if err != nil { return nil, err } @@ -342,7 +353,7 @@ func expandNamespaces(data interface{}) ([]*right_sizing.Namespace, error) { } if v, ok := attr[string(ocean_right_sizing_rule.Labels)]; ok { - labels, err := expandLabels(v) + labels, err := expandLabels(v, namespace.NamespaceName, resp) if err != nil { return nil, err } @@ -354,15 +365,23 @@ func expandNamespaces(data interface{}) ([]*right_sizing.Namespace, error) { namespace.Labels = nil } - namespaces = append(namespaces, namespace) + if len(namespace.Labels) == 0 && len(namespace.Workloads) == 0 { + continue + } else { + namespaces = append(namespaces, namespace) + } } return namespaces, nil } -func expandWorkloads(data interface{}) ([]*right_sizing.Workload, error) { +func expandWorkloads(data interface{}, namespaceName *string, response *right_sizing.ReadRightsizingRuleAttachedWorkloadsOutput) ([]*right_sizing.Workload, error) { list := data.(*schema.Set).List() workloads := make([]*right_sizing.Workload, 0, len(list)) + // Fetching details of workload already attached . + responseDataWorkload := response.RightsizingRuleAttachedWorkloads.RightsizingRuleWorkloads + responseDataRegex := response.RightsizingRuleAttachedWorkloads.RightsizingRuleRegex + for _, item := range list { attr := item.(map[string]interface{}) @@ -380,15 +399,82 @@ func expandWorkloads(data interface{}) ([]*right_sizing.Workload, error) { workload.RegexName = spotinst.String(v) } - workloads = append(workloads, workload) + //During first workload API call responseDataWorkload, responseDataRegex and responseDataLabel will be empty list, hence not validating user's workload again GET attachedWorkload response in IF block. + //In case of responseDataWorkload, responseDataRegex empty list not going to validate user's workload again GET attachedWorkload response in IF block. + if len(responseDataWorkload) == 0 && len(responseDataRegex) == 0 { + workloads = append(workloads, workload) + } else { + if v, ok := attr[string(ocean_right_sizing_rule.WorkloadName)].(string); ok && v != "" { + + // Creating list[map] so that we can check whether workload requested by user is already attached on not. + var list []map[string]interface{} + for _, obj := range responseDataWorkload { + item := map[string]interface{}{ + "Name": spotinst.StringValue(obj.Name), + "WorkloadType": spotinst.StringValue(obj.Type), + "Namespace": spotinst.StringValue(obj.Namespace), + } + list = append(list, item) + } + userWorkload := map[string]interface{}{ + "Name": spotinst.StringValue(workload.Name), + "WorkloadType": spotinst.StringValue(workload.WorkloadType), + "Namespace": spotinst.StringValue(namespaceName), + } + alreadyExist := false + for _, item := range list { + if reflect.DeepEqual(item, userWorkload) { + alreadyExist = true + break + } + } + if alreadyExist { + break + } else { + workloads = append(workloads, workload) + } + } + if v, ok := attr[string(ocean_right_sizing_rule.RegexName)].(string); ok && v != "" { + // Creating list[map] so that we can check whether workload requested by user is already attached on not. + var list []map[string]interface{} + for _, obj := range responseDataRegex { + item := map[string]interface{}{ + "Name": spotinst.StringValue(obj.Name), + "WorkloadType": spotinst.StringValue(obj.WorkloadType), + "Namespace": spotinst.StringValue(obj.Namespace), + } + list = append(list, item) + } + userWorkload := map[string]interface{}{ + "Name": spotinst.StringValue(workload.RegexName), + "WorkloadType": spotinst.StringValue(workload.WorkloadType), + "Namespace": spotinst.StringValue(namespaceName), + } + alreadyExist := false + for _, item := range list { + if reflect.DeepEqual(item, userWorkload) { + alreadyExist = true + break + } + } + if alreadyExist { + break + } else { + workloads = append(workloads, workload) + } + } + } } return workloads, nil } -func expandLabels(data interface{}) ([]*right_sizing.Label, error) { +func expandLabels(data interface{}, namespaceName *string, response *right_sizing.ReadRightsizingRuleAttachedWorkloadsOutput) ([]*right_sizing.Label, error) { list := data.(*schema.Set).List() labels := make([]*right_sizing.Label, 0, len(list)) + // Fetching details of workload using labels already attached . + responseDataLabels := response.RightsizingRuleAttachedWorkloads.RightsizingRuleLabels + for _, item := range list { attr := item.(map[string]interface{}) @@ -402,8 +488,41 @@ func expandLabels(data interface{}) ([]*right_sizing.Label, error) { label.Value = spotinst.String(v) } - labels = append(labels, label) - + //In case of responseDataLabel empty list not going to validate user's workload again GET attachedWorkload response in IF block. + if len(responseDataLabels) == 0 { + labels = append(labels, label) + } else { + if v, ok := attr[string(ocean_right_sizing_rule.Key)].(string); ok && v != "" { + + // Creating list[map] so that we can check whether workload having requested label by user is already attached on not. + var list []map[string]interface{} + for _, obj := range responseDataLabels { + item := map[string]interface{}{ + "Key": spotinst.StringValue(obj.Key), + "Value": spotinst.StringValue(obj.Value), + "Namespace": spotinst.StringValue(obj.Namespace), + } + list = append(list, item) + } + userWorkload := map[string]interface{}{ + "Key": spotinst.StringValue(label.Key), + "Value": spotinst.StringValue(label.Value), + "Namespace": spotinst.StringValue(namespaceName), + } + alreadyExist := false + for _, item := range list { + if reflect.DeepEqual(item, userWorkload) { + alreadyExist = true + break + } + } + if alreadyExist { + break + } else { + labels = append(labels, label) + } + } + } } return labels, nil } From 61c4314e259ed32ff535fd8a844c9d0c068b95bc Mon Sep 17 00:00:00 2001 From: Sharad Kesarwani <108344822+sharadkesarwani@users.noreply.github.com> Date: Fri, 8 Nov 2024 22:03:49 +0530 Subject: [PATCH 2/9] Fixed duplicate detachment of rightsizing workload --- ...source_spotinst_ocean_right_sizing_rule.go | 161 +++++++++++++++--- 1 file changed, 138 insertions(+), 23 deletions(-) diff --git a/spotinst/resource_spotinst_ocean_right_sizing_rule.go b/spotinst/resource_spotinst_ocean_right_sizing_rule.go index a87d67f5..34f23d1d 100644 --- a/spotinst/resource_spotinst_ocean_right_sizing_rule.go +++ b/spotinst/resource_spotinst_ocean_right_sizing_rule.go @@ -262,7 +262,7 @@ func detachRightSizingRule(resourceData *schema.ResourceData, meta interface{}, "skipping detach workloads for right sizing rule %q", ruleName) } - detachWorkloadsSpec, err := expandDetachWorkloadsConfig(detachWorkloadsSchema, ruleName, oceanId) + detachWorkloadsSpec, err := expandDetachWorkloadsConfig(detachWorkloadsSchema, ruleName, oceanId, meta) if err != nil { return fmt.Errorf("failed expanding attach workloads "+ "configuration for right sizing rule %q, error: %v", ruleName, err) @@ -274,18 +274,20 @@ func detachRightSizingRule(resourceData *schema.ResourceData, meta interface{}, "configuration for right sizing rule %q, error: %v", ruleName, err) } - log.Printf("onUpdate() -> Updating right sizing rule [%v] with configuration %s", ruleName, updateStateJSON) - detachWorkloadsInput := &right_sizing.RightSizingAttachDetachInput{ - RuleName: detachWorkloadsSpec.RuleName, - OceanId: detachWorkloadsSpec.OceanId, - Namespaces: detachWorkloadsSpec.Namespaces, - } - if _, err = meta.(*Client).ocean.RightSizing().DetachRightSizingRule(context.TODO(), - detachWorkloadsInput); err != nil { - return fmt.Errorf("onUpdate() -> Detach workloads failed for right sizing rule [%v], error: %v", - ruleName, err) + if len(detachWorkloadsSpec.Namespaces) > 0 { + log.Printf("onUpdate() -> Updating right sizing rule [%v] with configuration %s", ruleName, updateStateJSON) + detachWorkloadsInput := &right_sizing.RightSizingAttachDetachInput{ + RuleName: detachWorkloadsSpec.RuleName, + OceanId: detachWorkloadsSpec.OceanId, + Namespaces: detachWorkloadsSpec.Namespaces, + } + if _, err = meta.(*Client).ocean.RightSizing().DetachRightSizingRule(context.TODO(), + detachWorkloadsInput); err != nil { + return fmt.Errorf("onUpdate() -> Detach workloads failed for right sizing rule [%v], error: %v", + ruleName, err) + } + log.Printf("onUpdate() -> Successfully detached workloads for right sizing rule [%v]", ruleName) } - log.Printf("onUpdate() -> Successfully detached workloads for right sizing rule [%v]", ruleName) } return nil @@ -528,7 +530,7 @@ func expandLabels(data interface{}, namespaceName *string, response *right_sizin } func expandDetachWorkloadsConfig(data interface{}, - ruleName string, oceanId *string) (*right_sizing.RightSizingAttachDetachInput, error) { + ruleName string, oceanId *string, meta interface{}) (*right_sizing.RightSizingAttachDetachInput, error) { spec := &right_sizing.RightSizingAttachDetachInput{ OceanId: oceanId, RuleName: spotinst.String(ruleName), @@ -538,7 +540,7 @@ func expandDetachWorkloadsConfig(data interface{}, m := data.(map[string]interface{}) if v, ok := m[string(ocean_right_sizing_rule.NamespacesForDetach)]; ok { - namespaces, err := expandNamespacesForDetach(v) + namespaces, err := expandNamespacesForDetach(v, ruleName, oceanId, meta) if err != nil { return nil, err } @@ -554,9 +556,16 @@ func expandDetachWorkloadsConfig(data interface{}, return spec, nil } -func expandNamespacesForDetach(data interface{}) ([]*right_sizing.Namespace, error) { +func expandNamespacesForDetach(data interface{}, ruleName string, oceanId *string, meta interface{}) ([]*right_sizing.Namespace, error) { list := data.(*schema.Set).List() namespaces := make([]*right_sizing.Namespace, 0, len(list)) + input := &right_sizing.ReadRightsizingRuleAttachedWorkloadsInput{ + RuleName: spotinst.String(ruleName), + OceanId: oceanId, + } + resp, err := meta.(*Client).ocean.RightSizing().ReadRightsizingRuleAttachedWorkloads(context.Background(), input) + log.Print(resp) + log.Print(err) for _, item := range list { attr := item.(map[string]interface{}) @@ -568,7 +577,7 @@ func expandNamespacesForDetach(data interface{}) ([]*right_sizing.Namespace, err } if v, ok := attr[string(ocean_right_sizing_rule.WorkloadsForDetach)]; ok { - workloads, err := expandWorkloadsForDetach(v) + workloads, err := expandWorkloadsForDetach(v, namespace.NamespaceName, resp) if err != nil { return nil, err } @@ -581,7 +590,7 @@ func expandNamespacesForDetach(data interface{}) ([]*right_sizing.Namespace, err } if v, ok := attr[string(ocean_right_sizing_rule.LabelsForDetach)]; ok { - labels, err := expandLabelsForDetach(v) + labels, err := expandLabelsForDetach(v, namespace.NamespaceName, resp) if err != nil { return nil, err } @@ -593,15 +602,24 @@ func expandNamespacesForDetach(data interface{}) ([]*right_sizing.Namespace, err namespace.Labels = nil } - namespaces = append(namespaces, namespace) + if len(namespace.Labels) == 0 && len(namespace.Workloads) == 0 { + continue + } else { + namespaces = append(namespaces, namespace) + } + //namespaces = append(namespaces, namespace) } return namespaces, nil } -func expandWorkloadsForDetach(data interface{}) ([]*right_sizing.Workload, error) { +func expandWorkloadsForDetach(data interface{}, namespaceName *string, response *right_sizing.ReadRightsizingRuleAttachedWorkloadsOutput) ([]*right_sizing.Workload, error) { list := data.(*schema.Set).List() workloads := make([]*right_sizing.Workload, 0, len(list)) + // Fetching details of workload already attached . + responseDataWorkload := response.RightsizingRuleAttachedWorkloads.RightsizingRuleWorkloads + responseDataRegex := response.RightsizingRuleAttachedWorkloads.RightsizingRuleRegex + for _, item := range list { attr := item.(map[string]interface{}) @@ -618,16 +636,81 @@ func expandWorkloadsForDetach(data interface{}) ([]*right_sizing.Workload, error if v, ok := attr[string(ocean_right_sizing_rule.RegexNameForDetach)].(string); ok && v != "" { workload.RegexName = spotinst.String(v) } + if len(responseDataWorkload) == 0 && len(responseDataRegex) == 0 { + //workloads = append(workloads, workload) + continue + } else { + if v, ok := attr[string(ocean_right_sizing_rule.WorkloadName)].(string); ok && v != "" { - workloads = append(workloads, workload) + // Creating list[map] so that we can check whether workload requested by user is already attached on not. + var list []map[string]interface{} + for _, obj := range responseDataWorkload { + item := map[string]interface{}{ + "Name": spotinst.StringValue(obj.Name), + "WorkloadType": spotinst.StringValue(obj.Type), + "Namespace": spotinst.StringValue(obj.Namespace), + } + list = append(list, item) + } + userWorkload := map[string]interface{}{ + "Name": spotinst.StringValue(workload.Name), + "WorkloadType": spotinst.StringValue(workload.WorkloadType), + "Namespace": spotinst.StringValue(namespaceName), + } + alreadyExist := false + for _, item := range list { + if reflect.DeepEqual(item, userWorkload) { + alreadyExist = true + break + } + } + if alreadyExist { + workloads = append(workloads, workload) + } else { + break + } + } + if v, ok := attr[string(ocean_right_sizing_rule.RegexName)].(string); ok && v != "" { + // Creating list[map] so that we can check whether workload requested by user is already attached on not. + var list []map[string]interface{} + for _, obj := range responseDataRegex { + item := map[string]interface{}{ + "Name": spotinst.StringValue(obj.Name), + "WorkloadType": spotinst.StringValue(obj.WorkloadType), + "Namespace": spotinst.StringValue(obj.Namespace), + } + list = append(list, item) + } + userWorkload := map[string]interface{}{ + "Name": spotinst.StringValue(workload.RegexName), + "WorkloadType": spotinst.StringValue(workload.WorkloadType), + "Namespace": spotinst.StringValue(namespaceName), + } + alreadyExist := false + for _, item := range list { + if reflect.DeepEqual(item, userWorkload) { + alreadyExist = true + break + } + } + if alreadyExist { + workloads = append(workloads, workload) + } else { + break + } + } + } } return workloads, nil } -func expandLabelsForDetach(data interface{}) ([]*right_sizing.Label, error) { +func expandLabelsForDetach(data interface{}, namespaceName *string, response *right_sizing.ReadRightsizingRuleAttachedWorkloadsOutput) ([]*right_sizing.Label, error) { list := data.(*schema.Set).List() labels := make([]*right_sizing.Label, 0, len(list)) + // Fetching details of workload using labels already attached . + responseDataLabels := response.RightsizingRuleAttachedWorkloads.RightsizingRuleLabels + for _, item := range list { attr := item.(map[string]interface{}) @@ -640,9 +723,41 @@ func expandLabelsForDetach(data interface{}) ([]*right_sizing.Label, error) { if v, ok := attr[string(ocean_right_sizing_rule.ValueForDetach)].(string); ok && v != "" { label.Value = spotinst.String(v) } + //In case of responseDataLabel empty list not going to validate user's workload again GET attachedWorkload response in IF block. + if len(responseDataLabels) == 0 { + continue + } else { + if v, ok := attr[string(ocean_right_sizing_rule.Key)].(string); ok && v != "" { - labels = append(labels, label) - + // Creating list[map] so that we can check whether workload having requested label by user is already attached on not. + var list []map[string]interface{} + for _, obj := range responseDataLabels { + item := map[string]interface{}{ + "Key": spotinst.StringValue(obj.Key), + "Value": spotinst.StringValue(obj.Value), + "Namespace": spotinst.StringValue(obj.Namespace), + } + list = append(list, item) + } + userWorkload := map[string]interface{}{ + "Key": spotinst.StringValue(label.Key), + "Value": spotinst.StringValue(label.Value), + "Namespace": spotinst.StringValue(namespaceName), + } + alreadyExist := false + for _, item := range list { + if reflect.DeepEqual(item, userWorkload) { + alreadyExist = true + break + } + } + if alreadyExist { + labels = append(labels, label) + } else { + break + } + } + } } return labels, nil } From 2e4c5907493cd051d06daefac52e7c3b42af22e9 Mon Sep 17 00:00:00 2001 From: Sharad Kesarwani <108344822+sharadkesarwani@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:55:15 +0530 Subject: [PATCH 3/9] Added rightsizing attributes and documents --- docs/resources/ocean_automatic_rightsizing.md | 250 ++++++++++++++++++ spotinst/ocean_right_sizing_rule/consts.go | 4 + ...fields_spotinst_ocean_right_sizing_rule.go | 166 ++++++++++++ 3 files changed, 420 insertions(+) create mode 100644 docs/resources/ocean_automatic_rightsizing.md diff --git a/docs/resources/ocean_automatic_rightsizing.md b/docs/resources/ocean_automatic_rightsizing.md new file mode 100644 index 00000000..8a9e9661 --- /dev/null +++ b/docs/resources/ocean_automatic_rightsizing.md @@ -0,0 +1,250 @@ +--- +layout: "spotinst" +page_title: "Spotinst: ocean_right_sizing_rule" +subcategory: "Ocean" +description: |- + Provides a Spotinst Ocean Right Sizing resource. +--- + +# spotinst\_ocean\_right\_sizing\_rule + +Manages a Spotinst Ocean right sizing rule resource. + +## Example Usage + +```hcl +resource "spotinst_ocean_right_sizing_rule" "example" { + ocean_id = "o-123456" + rule_name = "test-rule" + exclude_preliminary_recommendations= true + restart_replicas="ALL_MANIFEST" + + recommendation_application_hpa{ + allow_hpa_recommendation= true + } + + recommendation_application_intervals { + repetition_basis = "WEEKLY" + weekly_repetition_basis { + interval_days = ["MONDAY", "WEDNESDAY"] + interval_hours_start_time = "12:00" + interval_hours_end_time = "14:00" + } + } + + + recommendation_application_intervals { + repetition_basis = "MONTHLY" + monthly_repetition_basis { + interval_months = [2,6,9] + week_of_the_month = ["FIRST","LAST"] + weekly_repetition_basis { + interval_days = ["MONDAY"] + interval_hours_start_time = "03:00" + interval_hours_end_time = "04:00" + } + } + } + + recommendation_application_boundaries { + cpu_min = 120 + cpu_max = 190 + memory_min = 160 + memory_max = 190 + } + + recommendation_application_min_threshold { + cpu_percentage = 0.412 + memory_percentage = 0.36 + } + + recommendation_application_overhead_values { + cpu_percentage = 0.80 + memory_percentage = 0.50 + } + + attach_workloads { + namespaces { + namespace_name = "kube-system" + workloads { + workload_type = "Deployment" + workload_name = "konnectivity-agent" + } + } + + namespaces{ + namespace_name = "kube-system" + workloads { + workload_type = "DaemonSet" + regex_name = "csi-*" + } + } + + namespaces{ + namespace_name = "kube-system" + labels{ + key = "kubernetes.io/cluster-service" + value = "true" + } + } + + } + + + detach_workloads { + + namespaces { + namespace_name = "kube-system" + workloads { + workload_type = "Deployment" + workload_name = "konnectivity-agent" + } + } + + namespaces{ + namespace_name = "kube-system" + workloads { + workload_type = "DaemonSet" + regex_name = "csi-*" + } + } + + namespaces{ + namespace_name = "kube-system" + labels{ + key = "kubernetes.io/cluster-service" + value = "true" + } + } + } +} +``` +``` + +## Argument Reference + +The following arguments are supported: + +* `ocean_id` - (Required) Identifier of the Ocean cluster. +* `rule_name` - (Required) The unique name of the rule. +* `exclude_preliminary_recommendations` - (Optional) Exclude preliminary recommendations (recommendations based on less than 4 full days of data). +* `restart_replicas` - (Optional) Enum: "MORE_THAN_ONE_REPLICA" "ALL_MANIFEST" "NO_RESTART" + Enable to sequentially restart pod batches according to recommendations, for all pods, only more than 1 replica, or not any pod. +* `recommendation_application_boundaries` - (Optional) Describes the Ocean Kubernetes Auto Scaler.Determines the Ocean Rightsizing rule recommendation application boundaries. + * `cpu_min` - (Optional) the minimal value of cpu in vCpu. + * `cpu_max` - (Optional) the maximal value of cpu in vCpu. + * `memory_min` - (Optional) the minimal value of memory in Gib. + * `memory_max` - (Optional) the maximal value of memory in Gib. +* `recommendation_application_hpa` - HPA Rightsizing Rule Recommendation Configuration + * `allow_hpa_recommendation` - Determines by the rule if recommendation application is allowed for workloads with HPA definition. + +* `recommendation_application_intervals` - (Required) Determines the Ocean Rightsizing rule recommendation application intervals. + * `repetition_basis` - (Optional) Enum: "WEEKLY" "MONTHLY". The repetition basis. + * `weekly_repetition_basis` - (Optional) the maximal value of cpu in vCpu. + * `interval_days` - (Optional) Enum: "SUNDAY" "MONDAY" "TUESDAY" "WEDNESDAY" "THURSDAY" "FRIDAY" "SATURDAY". Array of the days of the week, when we want to trigger the apply recommendations. + * `interval_hours_start_time` - (Optional) Start time. + * `interval_hours_end_time` - (Optional) End time. + * `monthly_repetition_basis` - (Optional) the minimal value of memory in Gib. + * `interval_months` - (Optional) Array of the months (in number), when we want to trigger the apply recommendations. + * `week_of_the_month` - (Optional) Enum: "FIRST" "SECOND" "THIRD" "FOURTH" "LAST". Array of the weeks in the month, when we want to trigger the apply recommendations. + * `weekly_repetition_basis` - (Optional) Determines the Ocean Rightsizing rule weekly repetition basis. + * `interval_days` - (Optional) Enum: "SUNDAY" "MONDAY" "TUESDAY" "WEDNESDAY" "THURSDAY" "FRIDAY" "SATURDAY". Array of the days of the week, when we want to trigger the apply recommendations. + * `interval_hours_start_time` - (Optional) Start time. + * `interval_hours_end_time` - (Optional) End time. + +* `recommendation_application_min_threshold` - Determines the extent of difference between current request and recommendation to trigger a change in percentage. + * `cpu_percentage` - (Optional, Default: 0.05) . + * `memory_percentage` - (Optional, Default: 0.05) . + +* `recommendation_application_overhead_values` - Determines the Ocean Rightsizing rule recommendation application overhead values. + * `cpu_percentage` - (Optional, Default: 0.1) . + * `memory_percentage` - (Optional, Default: 0.1) . + + + +## Attach Workloads + +* `attach_workloads` - (Optional) + * `namespaces` - (Optional) + * `namespace_name` - (Optional) List of namespaces. + * `workloads` - (Optional) List of workloads. + * `workload_type` - (Optional). The type of the workload. + * `workload_name` - (Optional). The name of the workload. + * `regex_name` - (Optional). The regex for the workload name. + * `labels` - (Optional). + * `key` - (Optional). + * `value` - (Optional) . + + +```hcl +attach_workloads { + namespaces { + namespace_name = "kube-system" + workloads { + workload_type = "Deployment" + workload_name = "konnectivity-agent" + } + } + + namespaces{ + namespace_name = "kube-system" + workloads { + workload_type = "DaemonSet" + regex_name = "csi-*" + } + } + + namespaces{ + namespace_name = "kube-system" + labels{ + key = "kubernetes.io/cluster-service" + value = "true" + } + } + +} +``` + + +## Detach Workloads + +* `detach_workloads` - (Optional) + * `namespaces` - (Optional) + * `namespace_name` - (Optional) List of namespaces. + * `workloads` - (Optional) List of workloads. + * `workload_type` - (Optional). The type of the workload. + * `workload_name` - (Optional). The name of the workload. + * `regex_name` - (Optional). The regex for the workload name. + * `labels` - (Optional). + * `key` - (Optional). + * `value` - (Optional) . + + +```hcl + detach_workloads { + + namespaces { + namespace_name = "kube-system" + workloads { + workload_type = "Deployment" + workload_name = "konnectivity-agent" + } + } + + namespaces{ + namespace_name = "kube-system" + workloads { + workload_type = "DaemonSet" + regex_name = "csi-*" + } + } + + namespaces{ + namespace_name = "kube-system" + labels{ + key = "kubernetes.io/cluster-service" + value = "true" + } + } +} +``` \ No newline at end of file diff --git a/spotinst/ocean_right_sizing_rule/consts.go b/spotinst/ocean_right_sizing_rule/consts.go index d339dc34..24a301cd 100644 --- a/spotinst/ocean_right_sizing_rule/consts.go +++ b/spotinst/ocean_right_sizing_rule/consts.go @@ -19,6 +19,10 @@ const ( MonthlyWeeklyIntervalDays commons.FieldName = "interval_days" MonthlyWeeklyIntervalHoursStartTime commons.FieldName = "interval_hours_start_time" MonthlyWeeklyIntervalHoursEndTime commons.FieldName = "interval_hours_end_time" + ExcludePreliminaryRecommendations commons.FieldName = "exclude_preliminary_recommendations" + RestartReplicas commons.FieldName = "restart_replicas" + RecommendationApplicationHPA commons.FieldName = "recommendation_application_hpa" + AllowHpaRecommendation commons.FieldName = "allow_hpa_recommendation" RecommendationApplicationBoundaries commons.FieldName = "recommendation_application_boundaries" CpuMin commons.FieldName = "cpu_min" diff --git a/spotinst/ocean_right_sizing_rule/fields_spotinst_ocean_right_sizing_rule.go b/spotinst/ocean_right_sizing_rule/fields_spotinst_ocean_right_sizing_rule.go index efa7e880..812cce77 100644 --- a/spotinst/ocean_right_sizing_rule/fields_spotinst_ocean_right_sizing_rule.go +++ b/spotinst/ocean_right_sizing_rule/fields_spotinst_ocean_right_sizing_rule.go @@ -49,6 +49,86 @@ func Setup(fieldsMap map[commons.FieldName]*commons.GenericField) { nil, ) + fieldsMap[RestartReplicas] = commons.NewGenericField( + commons.OceanRightSizingRule, + RestartReplicas, + &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + func(resourceObject interface{}, resourceData *schema.ResourceData, meta interface{}) error { + rightSizingRuleWrapper := resourceObject.(*commons.RightSizingRuleWrapper) + rightSizingRule := rightSizingRuleWrapper.GetOceanRightSizingRule() + var value *string = nil + if rightSizingRule.RestartReplicas != nil { + value = rightSizingRule.RestartReplicas + } + if err := resourceData.Set(string(RestartReplicas), value); err != nil { + return fmt.Errorf(string(commons.FailureFieldReadPattern), string(RestartReplicas), err) + } + return nil + }, + func(resourceObject interface{}, resourceData *schema.ResourceData, meta interface{}) error { + rightSizingRuleWrapper := resourceObject.(*commons.RightSizingRuleWrapper) + rightSizingRule := rightSizingRuleWrapper.GetOceanRightSizingRule() + if v, ok := resourceData.GetOk(string(RestartReplicas)); ok && v != "" { + rightSizingRule.SetRestartReplicas(spotinst.String(resourceData.Get(string(RestartReplicas)).(string))) + } + return nil + }, + func(resourceObject interface{}, resourceData *schema.ResourceData, meta interface{}) error { + rightSizingRuleWrapper := resourceObject.(*commons.RightSizingRuleWrapper) + rightSizingRule := rightSizingRuleWrapper.GetOceanRightSizingRule() + if v, ok := resourceData.GetOk(string(RestartReplicas)); ok && v != "" { + rightSizingRule.SetRestartReplicas(spotinst.String(resourceData.Get(string(RestartReplicas)).(string))) + } else { + rightSizingRule.SetRestartReplicas(nil) + } + return nil + }, + nil, + ) + + fieldsMap[ExcludePreliminaryRecommendations] = commons.NewGenericField( + commons.OceanRightSizingRule, + ExcludePreliminaryRecommendations, + &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + func(resourceObject interface{}, resourceData *schema.ResourceData, meta interface{}) error { + rightSizingRuleWrapper := resourceObject.(*commons.RightSizingRuleWrapper) + rightSizingRule := rightSizingRuleWrapper.GetOceanRightSizingRule() + var value *bool = nil + if rightSizingRule.ExcludePreliminaryRecommendations != nil { + value = rightSizingRule.ExcludePreliminaryRecommendations + } + if err := resourceData.Set(string(ExcludePreliminaryRecommendations), value); err != nil { + return fmt.Errorf(string(commons.FailureFieldReadPattern), string(ExcludePreliminaryRecommendations), err) + } + return nil + }, + func(resourceObject interface{}, resourceData *schema.ResourceData, meta interface{}) error { + rightSizingRuleWrapper := resourceObject.(*commons.RightSizingRuleWrapper) + rightSizingRule := rightSizingRuleWrapper.GetOceanRightSizingRule() + if _, ok := resourceData.GetOk(string(ExcludePreliminaryRecommendations)); ok { + rightSizingRule.SetExcludePreliminaryRecommendations(spotinst.Bool(resourceData.Get(string(ExcludePreliminaryRecommendations)).(bool))) + } + return nil + }, + func(resourceObject interface{}, resourceData *schema.ResourceData, meta interface{}) error { + rightSizingRuleWrapper := resourceObject.(*commons.RightSizingRuleWrapper) + rightSizingRule := rightSizingRuleWrapper.GetOceanRightSizingRule() + if _, ok := resourceData.GetOk(string(ExcludePreliminaryRecommendations)); ok { + rightSizingRule.SetExcludePreliminaryRecommendations(spotinst.Bool(resourceData.Get(string(ExcludePreliminaryRecommendations)).(bool))) + } else { + rightSizingRule.SetExcludePreliminaryRecommendations(nil) + } + return nil + }, + nil, + ) + fieldsMap[OceanId] = commons.NewGenericField( commons.OrganizationPolicy, OceanId, @@ -541,6 +621,65 @@ func Setup(fieldsMap map[commons.FieldName]*commons.GenericField) { nil, ) + fieldsMap[RecommendationApplicationHPA] = commons.NewGenericField( + commons.OceanRightSizingRule, + RecommendationApplicationHPA, + &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + string(AllowHpaRecommendation): { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + func(resourceObject interface{}, resourceData *schema.ResourceData, meta interface{}) error { + rightSizingRuleWrapper := resourceObject.(*commons.RightSizingRuleWrapper) + rightSizingRule := rightSizingRuleWrapper.GetOceanRightSizingRule() + var result []interface{} = nil + if rightSizingRule.RecommendationApplicationHPA != nil { + recommendationApplicationHPA := rightSizingRule.RecommendationApplicationHPA + result = flattenRecommendationApplicationHPA(recommendationApplicationHPA) + } + if result != nil { + if err := resourceData.Set(string(RecommendationApplicationHPA), result); err != nil { + return fmt.Errorf(string(commons.FailureFieldReadPattern), string(RecommendationApplicationHPA), err) + } + } + return nil + }, + func(resourceObject interface{}, resourceData *schema.ResourceData, meta interface{}) error { + rightSizingRuleWrapper := resourceObject.(*commons.RightSizingRuleWrapper) + rightSizingRule := rightSizingRuleWrapper.GetOceanRightSizingRule() + if v, ok := resourceData.GetOk(string(RecommendationApplicationHPA)); ok { + if recommendationApplicationHPA, err := expandRecommendationApplicationHPA(v); err != nil { + return err + } else { + rightSizingRule.SetRecommendationApplicationHPA(recommendationApplicationHPA) + } + } + return nil + }, + func(resourceObject interface{}, resourceData *schema.ResourceData, meta interface{}) error { + rightSizingRuleWrapper := resourceObject.(*commons.RightSizingRuleWrapper) + rightSizingRule := rightSizingRuleWrapper.GetOceanRightSizingRule() + var value *right_sizing.RecommendationApplicationHPA = nil + if v, ok := resourceData.GetOk(string(RecommendationApplicationHPA)); ok { + if recommendationApplicationHPA, err := expandRecommendationApplicationHPA(v); err != nil { + return err + } else { + value = recommendationApplicationHPA + } + } + rightSizingRule.SetRecommendationApplicationHPA(value) + return nil + }, + nil, + ) + } func flattenRecommendationApplicationIntervals(recommendationApplicationIntervals []*right_sizing.RecommendationApplicationIntervals) []interface{} { @@ -971,3 +1110,30 @@ func expandRecommendationApplicationOverheadValues(data interface{}) (*right_siz } return nil, nil } + +func expandRecommendationApplicationHPA(data interface{}) (*right_sizing.RecommendationApplicationHPA, error) { + list := data.(*schema.Set).List() + recommendationApplicationHPA := &right_sizing.RecommendationApplicationHPA{} + + if len(list) > 0 { + item := list[0] + m := item.(map[string]interface{}) + + if v, ok := m[string(AllowHpaRecommendation)].(bool); ok { + recommendationApplicationHPA.SetAllowHPARecommendations(spotinst.Bool(v)) + } + + return recommendationApplicationHPA, nil + + } + return nil, nil +} + +func flattenRecommendationApplicationHPA(recommendationApplicationHPA *right_sizing.RecommendationApplicationHPA) []interface{} { + result := make(map[string]interface{}) + + if recommendationApplicationHPA.AllowHPARecommendations != nil { + result[string(AllowHpaRecommendation)] = spotinst.BoolValue(recommendationApplicationHPA.AllowHPARecommendations) + } + return []interface{}{result} +} From fed0182a8b58f642912d0bf99a49d759c0419ec5 Mon Sep 17 00:00:00 2001 From: Sharad Kesarwani <108344822+sharadkesarwani@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:27:43 +0530 Subject: [PATCH 4/9] updated doc and corrected attribute name --- docs/resources/ocean_automatic_rightsizing.md | 63 ++----------------- spotinst/ocean_right_sizing_rule/consts.go | 2 +- ...fields_spotinst_ocean_right_sizing_rule.go | 6 +- 3 files changed, 8 insertions(+), 63 deletions(-) diff --git a/docs/resources/ocean_automatic_rightsizing.md b/docs/resources/ocean_automatic_rightsizing.md index 8a9e9661..3e073013 100644 --- a/docs/resources/ocean_automatic_rightsizing.md +++ b/docs/resources/ocean_automatic_rightsizing.md @@ -62,61 +62,7 @@ resource "spotinst_ocean_right_sizing_rule" "example" { cpu_percentage = 0.80 memory_percentage = 0.50 } - - attach_workloads { - namespaces { - namespace_name = "kube-system" - workloads { - workload_type = "Deployment" - workload_name = "konnectivity-agent" - } - } - - namespaces{ - namespace_name = "kube-system" - workloads { - workload_type = "DaemonSet" - regex_name = "csi-*" - } - } - - namespaces{ - namespace_name = "kube-system" - labels{ - key = "kubernetes.io/cluster-service" - value = "true" - } - } - - } - - - detach_workloads { - - namespaces { - namespace_name = "kube-system" - workloads { - workload_type = "Deployment" - workload_name = "konnectivity-agent" - } - } - - namespaces{ - namespace_name = "kube-system" - workloads { - workload_type = "DaemonSet" - regex_name = "csi-*" - } - } - - namespaces{ - namespace_name = "kube-system" - labels{ - key = "kubernetes.io/cluster-service" - value = "true" - } - } - } + } ``` ``` @@ -136,7 +82,7 @@ The following arguments are supported: * `memory_min` - (Optional) the minimal value of memory in Gib. * `memory_max` - (Optional) the maximal value of memory in Gib. * `recommendation_application_hpa` - HPA Rightsizing Rule Recommendation Configuration - * `allow_hpa_recommendation` - Determines by the rule if recommendation application is allowed for workloads with HPA definition. + * `allow_hpa_recommendations` - Determines by the rule if recommendation application is allowed for workloads with HPA definition. * `recommendation_application_intervals` - (Required) Determines the Ocean Rightsizing rule recommendation application intervals. * `repetition_basis` - (Optional) Enum: "WEEKLY" "MONTHLY". The repetition basis. @@ -158,9 +104,8 @@ The following arguments are supported: * `recommendation_application_overhead_values` - Determines the Ocean Rightsizing rule recommendation application overhead values. * `cpu_percentage` - (Optional, Default: 0.1) . - * `memory_percentage` - (Optional, Default: 0.1) . - - + * `memory_percentage` - (Optional, Default: 0.1). +``` ## Attach Workloads diff --git a/spotinst/ocean_right_sizing_rule/consts.go b/spotinst/ocean_right_sizing_rule/consts.go index 24a301cd..49fd393b 100644 --- a/spotinst/ocean_right_sizing_rule/consts.go +++ b/spotinst/ocean_right_sizing_rule/consts.go @@ -22,7 +22,7 @@ const ( ExcludePreliminaryRecommendations commons.FieldName = "exclude_preliminary_recommendations" RestartReplicas commons.FieldName = "restart_replicas" RecommendationApplicationHPA commons.FieldName = "recommendation_application_hpa" - AllowHpaRecommendation commons.FieldName = "allow_hpa_recommendation" + AllowHpaRecommendations commons.FieldName = "allow_hpa_recommendations" RecommendationApplicationBoundaries commons.FieldName = "recommendation_application_boundaries" CpuMin commons.FieldName = "cpu_min" diff --git a/spotinst/ocean_right_sizing_rule/fields_spotinst_ocean_right_sizing_rule.go b/spotinst/ocean_right_sizing_rule/fields_spotinst_ocean_right_sizing_rule.go index 812cce77..c6b6ef69 100644 --- a/spotinst/ocean_right_sizing_rule/fields_spotinst_ocean_right_sizing_rule.go +++ b/spotinst/ocean_right_sizing_rule/fields_spotinst_ocean_right_sizing_rule.go @@ -629,7 +629,7 @@ func Setup(fieldsMap map[commons.FieldName]*commons.GenericField) { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - string(AllowHpaRecommendation): { + string(AllowHpaRecommendations): { Type: schema.TypeBool, Optional: true, }, @@ -1119,7 +1119,7 @@ func expandRecommendationApplicationHPA(data interface{}) (*right_sizing.Recomme item := list[0] m := item.(map[string]interface{}) - if v, ok := m[string(AllowHpaRecommendation)].(bool); ok { + if v, ok := m[string(AllowHpaRecommendations)].(bool); ok { recommendationApplicationHPA.SetAllowHPARecommendations(spotinst.Bool(v)) } @@ -1133,7 +1133,7 @@ func flattenRecommendationApplicationHPA(recommendationApplicationHPA *right_siz result := make(map[string]interface{}) if recommendationApplicationHPA.AllowHPARecommendations != nil { - result[string(AllowHpaRecommendation)] = spotinst.BoolValue(recommendationApplicationHPA.AllowHPARecommendations) + result[string(AllowHpaRecommendations)] = spotinst.BoolValue(recommendationApplicationHPA.AllowHPARecommendations) } return []interface{}{result} } From 01cdd1bb3d0d1d12b997ee038d5df4ce638a8330 Mon Sep 17 00:00:00 2001 From: Sharad Kesarwani <108344822+sharadkesarwani@users.noreply.github.com> Date: Wed, 13 Nov 2024 19:50:11 +0530 Subject: [PATCH 5/9] Updated SDK version and changelog. --- CHANGELOG.md | 5 +++++ go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e116658..409bf1a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## Unreleased +## 1.197.0 (Nov, 13 2024) +ENHANCEMENTS: +* resource/spotinst_ocean_right_sizing_rule: Fixed rightsizing rule attach, detach issue for duplicate workloads. +* resource/spotinst_ocean_right_sizing_rule: Added support for `exclude_preliminary_recommendations`, `recommendation_application_hpa`, and `restart_replicas` attributes. + ## 1.196.0 (October, 28 2024) ENHANCEMENTS: * resource/spotinst_elastigroup_azure_v3: Added support for `scheduling`, `health`, `load_balancer`, `secrets`, `security` blocks. diff --git a/go.mod b/go.mod index 965a64be..60cb055b 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/hashicorp/terraform-plugin-docs v0.5.1 github.com/hashicorp/terraform-plugin-sdk/v2 v2.5.0 github.com/sethvargo/go-password v0.3.1 - github.com/spotinst/spotinst-sdk-go v1.372.0 + github.com/spotinst/spotinst-sdk-go v1.373.0 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 ) diff --git a/go.sum b/go.sum index 0103aecf..4f95d47f 100644 --- a/go.sum +++ b/go.sum @@ -587,8 +587,8 @@ github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spotinst/spotinst-sdk-go v1.372.0 h1:B4/+HK3D2Fe0821DOmw5RO4Lrzo2gi7oa6QWWjr5/7A= -github.com/spotinst/spotinst-sdk-go v1.372.0/go.mod h1:Tn4/eb0SFY6IXmxz71CClujvbD/PuT+EO6Ta8v6AML4= +github.com/spotinst/spotinst-sdk-go v1.373.0 h1:7swkwpX3JiLCQx6ZbXbgA36zcmUVgo2ZWUogTLp7aYw= +github.com/spotinst/spotinst-sdk-go v1.373.0/go.mod h1:Tn4/eb0SFY6IXmxz71CClujvbD/PuT+EO6Ta8v6AML4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= From 4397135ecf2e366f6ad34b6d39d04228ee89f29f Mon Sep 17 00:00:00 2001 From: Sharad Kesarwani <108344822+sharadkesarwani@users.noreply.github.com> Date: Wed, 13 Nov 2024 21:42:23 +0530 Subject: [PATCH 6/9] Fixed review comments --- CHANGELOG.md | 2 +- docs/resources/ocean_automatic_rightsizing.md | 23 +++++++++---------- ...source_spotinst_ocean_right_sizing_rule.go | 14 +++++------ 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 409bf1a5..8f6071f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 1.197.0 (Nov, 13 2024) ENHANCEMENTS: -* resource/spotinst_ocean_right_sizing_rule: Fixed rightsizing rule attach, detach issue for duplicate workloads. +* resource/spotinst_ocean_right_sizing_rule: Fixed rightsizing rule attach and detach workloads in subsequent updates. * resource/spotinst_ocean_right_sizing_rule: Added support for `exclude_preliminary_recommendations`, `recommendation_application_hpa`, and `restart_replicas` attributes. ## 1.196.0 (October, 28 2024) diff --git a/docs/resources/ocean_automatic_rightsizing.md b/docs/resources/ocean_automatic_rightsizing.md index 3e073013..70ddd078 100644 --- a/docs/resources/ocean_automatic_rightsizing.md +++ b/docs/resources/ocean_automatic_rightsizing.md @@ -20,7 +20,7 @@ resource "spotinst_ocean_right_sizing_rule" "example" { restart_replicas="ALL_MANIFEST" recommendation_application_hpa{ - allow_hpa_recommendation= true + allow_hpa_recommendations= true } recommendation_application_intervals { @@ -74,9 +74,8 @@ The following arguments are supported: * `ocean_id` - (Required) Identifier of the Ocean cluster. * `rule_name` - (Required) The unique name of the rule. * `exclude_preliminary_recommendations` - (Optional) Exclude preliminary recommendations (recommendations based on less than 4 full days of data). -* `restart_replicas` - (Optional) Enum: "MORE_THAN_ONE_REPLICA" "ALL_MANIFEST" "NO_RESTART" - Enable to sequentially restart pod batches according to recommendations, for all pods, only more than 1 replica, or not any pod. -* `recommendation_application_boundaries` - (Optional) Describes the Ocean Kubernetes Auto Scaler.Determines the Ocean Rightsizing rule recommendation application boundaries. +* `restart_replicas` - (Optional) Valid values: "MORE_THAN_ONE_REPLICA" "ALL_MANIFEST" "NO_RESTART". Enable to sequentially restart pod batches according to recommendations, for all pods, only more than 1 replica, or not any pod. +* `recommendation_application_boundaries` - (Optional) Determines the Ocean Rightsizing rule recommendation application boundaries. * `cpu_min` - (Optional) the minimal value of cpu in vCpu. * `cpu_max` - (Optional) the maximal value of cpu in vCpu. * `memory_min` - (Optional) the minimal value of memory in Gib. @@ -85,16 +84,16 @@ The following arguments are supported: * `allow_hpa_recommendations` - Determines by the rule if recommendation application is allowed for workloads with HPA definition. * `recommendation_application_intervals` - (Required) Determines the Ocean Rightsizing rule recommendation application intervals. - * `repetition_basis` - (Optional) Enum: "WEEKLY" "MONTHLY". The repetition basis. - * `weekly_repetition_basis` - (Optional) the maximal value of cpu in vCpu. - * `interval_days` - (Optional) Enum: "SUNDAY" "MONDAY" "TUESDAY" "WEDNESDAY" "THURSDAY" "FRIDAY" "SATURDAY". Array of the days of the week, when we want to trigger the apply recommendations. + * `repetition_basis` - (Optional) Valid values: "WEEKLY" "MONTHLY". The repetition basis. + * `weekly_repetition_basis` - (Optional) Determines the Ocean Rightsizing rule weekly repetition basis. + * `interval_days` - (Optional) Valid values: "SUNDAY" "MONDAY" "TUESDAY" "WEDNESDAY" "THURSDAY" "FRIDAY" "SATURDAY". Array of the days of the week, when we want to trigger the apply recommendations. * `interval_hours_start_time` - (Optional) Start time. * `interval_hours_end_time` - (Optional) End time. - * `monthly_repetition_basis` - (Optional) the minimal value of memory in Gib. + * `monthly_repetition_basis` - (Optional) Determines the Ocean Rightsizing rule monthly repetition basis. * `interval_months` - (Optional) Array of the months (in number), when we want to trigger the apply recommendations. - * `week_of_the_month` - (Optional) Enum: "FIRST" "SECOND" "THIRD" "FOURTH" "LAST". Array of the weeks in the month, when we want to trigger the apply recommendations. + * `week_of_the_month` - (Optional) Valid values: "FIRST" "SECOND" "THIRD" "FOURTH" "LAST". Array of the weeks in the month, when we want to trigger the apply recommendations. * `weekly_repetition_basis` - (Optional) Determines the Ocean Rightsizing rule weekly repetition basis. - * `interval_days` - (Optional) Enum: "SUNDAY" "MONDAY" "TUESDAY" "WEDNESDAY" "THURSDAY" "FRIDAY" "SATURDAY". Array of the days of the week, when we want to trigger the apply recommendations. + * `interval_days` - (Optional) Valid values: "SUNDAY" "MONDAY" "TUESDAY" "WEDNESDAY" "THURSDAY" "FRIDAY" "SATURDAY". Array of the days of the week, when we want to trigger the apply recommendations. * `interval_hours_start_time` - (Optional) Start time. * `interval_hours_end_time` - (Optional) End time. @@ -115,7 +114,7 @@ The following arguments are supported: * `workloads` - (Optional) List of workloads. * `workload_type` - (Optional). The type of the workload. * `workload_name` - (Optional). The name of the workload. - * `regex_name` - (Optional). The regex for the workload name. + * `regex_name` - (Optional). The regex for the workload name. Not allowed when using workload_name. * `labels` - (Optional). * `key` - (Optional). * `value` - (Optional) . @@ -159,7 +158,7 @@ attach_workloads { * `workloads` - (Optional) List of workloads. * `workload_type` - (Optional). The type of the workload. * `workload_name` - (Optional). The name of the workload. - * `regex_name` - (Optional). The regex for the workload name. + * `regex_name` - (Optional). The regex for the workload name.Not allowed when using workload_name. * `labels` - (Optional). * `key` - (Optional). * `value` - (Optional) . diff --git a/spotinst/resource_spotinst_ocean_right_sizing_rule.go b/spotinst/resource_spotinst_ocean_right_sizing_rule.go index 34f23d1d..51d6ff15 100644 --- a/spotinst/resource_spotinst_ocean_right_sizing_rule.go +++ b/spotinst/resource_spotinst_ocean_right_sizing_rule.go @@ -401,8 +401,7 @@ func expandWorkloads(data interface{}, namespaceName *string, response *right_si workload.RegexName = spotinst.String(v) } - //During first workload API call responseDataWorkload, responseDataRegex and responseDataLabel will be empty list, hence not validating user's workload again GET attachedWorkload response in IF block. - //In case of responseDataWorkload, responseDataRegex empty list not going to validate user's workload again GET attachedWorkload response in IF block. + //Since responseDataWorkload, responseDataRegex and responseDataLabel will be empty list in GET attachedWorkload API response, so validation of user's workload against GET attachedWorkload response in IF block is not required in case of first attach workload API call. if len(responseDataWorkload) == 0 && len(responseDataRegex) == 0 { workloads = append(workloads, workload) } else { @@ -437,7 +436,7 @@ func expandWorkloads(data interface{}, namespaceName *string, response *right_si } } if v, ok := attr[string(ocean_right_sizing_rule.RegexName)].(string); ok && v != "" { - // Creating list[map] so that we can check whether workload requested by user is already attached on not. + // Creating list[map] so that we can check whether `workload with regex` requested by user is already attached on not. var list []map[string]interface{} for _, obj := range responseDataRegex { item := map[string]interface{}{ @@ -490,7 +489,7 @@ func expandLabels(data interface{}, namespaceName *string, response *right_sizin label.Value = spotinst.String(v) } - //In case of responseDataLabel empty list not going to validate user's workload again GET attachedWorkload response in IF block. + //In case of responseDataLabel returned as empty list then not validating user's workload against GET attachedWorkload response. if len(responseDataLabels) == 0 { labels = append(labels, label) } else { @@ -607,7 +606,6 @@ func expandNamespacesForDetach(data interface{}, ruleName string, oceanId *strin } else { namespaces = append(namespaces, namespace) } - //namespaces = append(namespaces, namespace) } return namespaces, nil } @@ -637,7 +635,6 @@ func expandWorkloadsForDetach(data interface{}, namespaceName *string, response workload.RegexName = spotinst.String(v) } if len(responseDataWorkload) == 0 && len(responseDataRegex) == 0 { - //workloads = append(workloads, workload) continue } else { if v, ok := attr[string(ocean_right_sizing_rule.WorkloadName)].(string); ok && v != "" { @@ -671,7 +668,7 @@ func expandWorkloadsForDetach(data interface{}, namespaceName *string, response } } if v, ok := attr[string(ocean_right_sizing_rule.RegexName)].(string); ok && v != "" { - // Creating list[map] so that we can check whether workload requested by user is already attached on not. + // Creating list[map] so that we can check whether `workload with regex` requested by user is already attached on not. var list []map[string]interface{} for _, obj := range responseDataRegex { item := map[string]interface{}{ @@ -723,7 +720,8 @@ func expandLabelsForDetach(data interface{}, namespaceName *string, response *ri if v, ok := attr[string(ocean_right_sizing_rule.ValueForDetach)].(string); ok && v != "" { label.Value = spotinst.String(v) } - //In case of responseDataLabel empty list not going to validate user's workload again GET attachedWorkload response in IF block. + //In case of responseDataLabel returned as empty list then not validating user's workload against GET attachedWorkload response. + if len(responseDataLabels) == 0 { continue } else { From 842ea54fcaeb674a9ac81951e3f50a094a6ce129 Mon Sep 17 00:00:00 2001 From: Sharad Kesarwani <108344822+sharadkesarwani@users.noreply.github.com> Date: Wed, 13 Nov 2024 21:52:55 +0530 Subject: [PATCH 7/9] removed print statements. --- spotinst/resource_spotinst_ocean_right_sizing_rule.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spotinst/resource_spotinst_ocean_right_sizing_rule.go b/spotinst/resource_spotinst_ocean_right_sizing_rule.go index 51d6ff15..5f22841e 100644 --- a/spotinst/resource_spotinst_ocean_right_sizing_rule.go +++ b/spotinst/resource_spotinst_ocean_right_sizing_rule.go @@ -329,8 +329,9 @@ func expandNamespaces(data interface{}, ruleName string, oceanId *string, meta i OceanId: oceanId, } resp, err := meta.(*Client).ocean.RightSizing().ReadRightsizingRuleAttachedWorkloads(context.Background(), input) - log.Print(resp) - log.Print(err) + if err != nil { + return namespaces, err + } for _, item := range list { attr := item.(map[string]interface{}) @@ -563,8 +564,9 @@ func expandNamespacesForDetach(data interface{}, ruleName string, oceanId *strin OceanId: oceanId, } resp, err := meta.(*Client).ocean.RightSizing().ReadRightsizingRuleAttachedWorkloads(context.Background(), input) - log.Print(resp) - log.Print(err) + if err != nil { + return namespaces, err + } for _, item := range list { attr := item.(map[string]interface{}) From 17c440ee09f7125b35be195fa6e5ea1b6a5733c2 Mon Sep 17 00:00:00 2001 From: Sharad Kesarwani <108344822+sharadkesarwani@users.noreply.github.com> Date: Wed, 13 Nov 2024 21:55:55 +0530 Subject: [PATCH 8/9] updated go version in go.mod file --- go.mod | 2 +- .../fields_spotinst_elastigroup_azure_extensions.go | 1 + .../fields_spotinst_elastigroup_azure_health.go | 1 + .../fields_spotinst_elastigroup_azure_launchspecification.go | 1 + .../fields_spotinst_elastigroup_azure_load_balancer.go | 1 + .../fields_spotinst_elastigroup_azure_scaling_policies.go | 1 + .../fields_spotinst_elastigroup_azure_scheduling.go | 3 ++- .../fields_spotinst_elastigroup_azure_secret.go | 1 + .../fields_spotinst_elastigroup_azure_strategy.go | 1 + spotinst/resource_spotinst_elastigroup_azure_v3.go | 5 +++-- 10 files changed, 13 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 60cb055b..f6ba06ac 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/spotinst/terraform-provider-spotinst -go 1.21 +go 1.21.0 require ( github.com/bflad/tfproviderlint v0.29.0 diff --git a/spotinst/azure_v3/elastigroup_azure_extension/fields_spotinst_elastigroup_azure_extensions.go b/spotinst/azure_v3/elastigroup_azure_extension/fields_spotinst_elastigroup_azure_extensions.go index 0619527d..d375b163 100644 --- a/spotinst/azure_v3/elastigroup_azure_extension/fields_spotinst_elastigroup_azure_extensions.go +++ b/spotinst/azure_v3/elastigroup_azure_extension/fields_spotinst_elastigroup_azure_extensions.go @@ -2,6 +2,7 @@ package elastigroup_azure_extension import ( "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" azurev3 "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3" "github.com/spotinst/spotinst-sdk-go/spotinst" diff --git a/spotinst/azure_v3/elastigroup_azure_health/fields_spotinst_elastigroup_azure_health.go b/spotinst/azure_v3/elastigroup_azure_health/fields_spotinst_elastigroup_azure_health.go index 3018b6ab..0f5ac8b2 100644 --- a/spotinst/azure_v3/elastigroup_azure_health/fields_spotinst_elastigroup_azure_health.go +++ b/spotinst/azure_v3/elastigroup_azure_health/fields_spotinst_elastigroup_azure_health.go @@ -2,6 +2,7 @@ package elastigroup_azure_health import ( "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" azurev3 "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3" "github.com/spotinst/spotinst-sdk-go/spotinst" diff --git a/spotinst/azure_v3/elastigroup_azure_launchspecification/fields_spotinst_elastigroup_azure_launchspecification.go b/spotinst/azure_v3/elastigroup_azure_launchspecification/fields_spotinst_elastigroup_azure_launchspecification.go index ce8419b7..b6b507f6 100644 --- a/spotinst/azure_v3/elastigroup_azure_launchspecification/fields_spotinst_elastigroup_azure_launchspecification.go +++ b/spotinst/azure_v3/elastigroup_azure_launchspecification/fields_spotinst_elastigroup_azure_launchspecification.go @@ -3,6 +3,7 @@ package elastigroup_azure_launchspecification import ( "errors" "fmt" + azurev3 "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" diff --git a/spotinst/azure_v3/elastigroup_azure_load_balancer/fields_spotinst_elastigroup_azure_load_balancer.go b/spotinst/azure_v3/elastigroup_azure_load_balancer/fields_spotinst_elastigroup_azure_load_balancer.go index b21d2a95..08e8a23a 100644 --- a/spotinst/azure_v3/elastigroup_azure_load_balancer/fields_spotinst_elastigroup_azure_load_balancer.go +++ b/spotinst/azure_v3/elastigroup_azure_load_balancer/fields_spotinst_elastigroup_azure_load_balancer.go @@ -2,6 +2,7 @@ package elastigroup_azure_load_balancer import ( "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" azurev3 "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3" "github.com/spotinst/spotinst-sdk-go/spotinst" diff --git a/spotinst/azure_v3/elastigroup_azure_scaling_policies/fields_spotinst_elastigroup_azure_scaling_policies.go b/spotinst/azure_v3/elastigroup_azure_scaling_policies/fields_spotinst_elastigroup_azure_scaling_policies.go index 1e4ff323..c21443af 100644 --- a/spotinst/azure_v3/elastigroup_azure_scaling_policies/fields_spotinst_elastigroup_azure_scaling_policies.go +++ b/spotinst/azure_v3/elastigroup_azure_scaling_policies/fields_spotinst_elastigroup_azure_scaling_policies.go @@ -2,6 +2,7 @@ package elastigroup_azure_scaling_policies import ( "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" azurev3 "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3" "github.com/spotinst/spotinst-sdk-go/spotinst" diff --git a/spotinst/azure_v3/elastigroup_azure_scheduling/fields_spotinst_elastigroup_azure_scheduling.go b/spotinst/azure_v3/elastigroup_azure_scheduling/fields_spotinst_elastigroup_azure_scheduling.go index 1f6a9f41..147cbe19 100644 --- a/spotinst/azure_v3/elastigroup_azure_scheduling/fields_spotinst_elastigroup_azure_scheduling.go +++ b/spotinst/azure_v3/elastigroup_azure_scheduling/fields_spotinst_elastigroup_azure_scheduling.go @@ -2,11 +2,12 @@ package elastigroup_azure_scheduling import ( "fmt" + "strconv" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" azurev3 "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3" "github.com/spotinst/spotinst-sdk-go/spotinst" "github.com/spotinst/terraform-provider-spotinst/spotinst/commons" - "strconv" ) func Setup(fieldsMap map[commons.FieldName]*commons.GenericField) { diff --git a/spotinst/azure_v3/elastigroup_azure_secrets/fields_spotinst_elastigroup_azure_secret.go b/spotinst/azure_v3/elastigroup_azure_secrets/fields_spotinst_elastigroup_azure_secret.go index df1b44e6..c6d57f43 100644 --- a/spotinst/azure_v3/elastigroup_azure_secrets/fields_spotinst_elastigroup_azure_secret.go +++ b/spotinst/azure_v3/elastigroup_azure_secrets/fields_spotinst_elastigroup_azure_secret.go @@ -2,6 +2,7 @@ package elastigroup_azure_secrets import ( "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" azurev3 "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3" "github.com/spotinst/spotinst-sdk-go/spotinst" diff --git a/spotinst/azure_v3/elastigroup_azure_strategy/fields_spotinst_elastigroup_azure_strategy.go b/spotinst/azure_v3/elastigroup_azure_strategy/fields_spotinst_elastigroup_azure_strategy.go index 898b0cbd..2c2aa33c 100644 --- a/spotinst/azure_v3/elastigroup_azure_strategy/fields_spotinst_elastigroup_azure_strategy.go +++ b/spotinst/azure_v3/elastigroup_azure_strategy/fields_spotinst_elastigroup_azure_strategy.go @@ -2,6 +2,7 @@ package elastigroup_azure_strategy import ( "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" azurev3 "github.com/spotinst/spotinst-sdk-go/service/elastigroup/providers/azure/v3" diff --git a/spotinst/resource_spotinst_elastigroup_azure_v3.go b/spotinst/resource_spotinst_elastigroup_azure_v3.go index 47f90709..15f9a150 100644 --- a/spotinst/resource_spotinst_elastigroup_azure_v3.go +++ b/spotinst/resource_spotinst_elastigroup_azure_v3.go @@ -3,14 +3,15 @@ package spotinst import ( "context" "fmt" + "log" + "time" + "github.com/spotinst/terraform-provider-spotinst/spotinst/azure_v3/elastigroup_azure_extension" "github.com/spotinst/terraform-provider-spotinst/spotinst/azure_v3/elastigroup_azure_health" "github.com/spotinst/terraform-provider-spotinst/spotinst/azure_v3/elastigroup_azure_load_balancer" "github.com/spotinst/terraform-provider-spotinst/spotinst/azure_v3/elastigroup_azure_scaling_policies" "github.com/spotinst/terraform-provider-spotinst/spotinst/azure_v3/elastigroup_azure_scheduling" "github.com/spotinst/terraform-provider-spotinst/spotinst/azure_v3/elastigroup_azure_secrets" - "log" - "time" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" From 045ffc71916763fe95cb5c074579d60c8036bd67 Mon Sep 17 00:00:00 2001 From: Sharad Kesarwani <108344822+sharadkesarwani@users.noreply.github.com> Date: Wed, 13 Nov 2024 22:05:01 +0530 Subject: [PATCH 9/9] updated go version --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index f6ba06ac..60cb055b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/spotinst/terraform-provider-spotinst -go 1.21.0 +go 1.21 require ( github.com/bflad/tfproviderlint v0.29.0