diff --git a/castai/const.go b/castai/const.go index 6e6bd773..2f7e9054 100644 --- a/castai/const.go +++ b/castai/const.go @@ -6,4 +6,7 @@ const ( FieldKey = "key" FieldValue = "value" FieldEffect = "effect" + + Enabled = "enabled" + Disabled = "disabled" ) diff --git a/castai/resource_node_template.go b/castai/resource_node_template.go index b4bdab92..bf951c07 100644 --- a/castai/resource_node_template.go +++ b/castai/resource_node_template.go @@ -8,6 +8,7 @@ import ( "time" "github.com/google/uuid" + "github.com/hashicorp/go-cty/cty" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -20,6 +21,7 @@ import ( const ( FieldNodeTemplateArchitectures = "architectures" FieldNodeTemplateComputeOptimized = "compute_optimized" + FieldNodeTemplateComputeOptimizedStatus = "compute_optimized_status" FieldNodeTemplateConfigurationId = "configuration_id" FieldNodeTemplateConstraints = "constraints" FieldNodeTemplateCustomInstancesEnabled = "custom_instances_enabled" @@ -54,6 +56,7 @@ const ( FieldNodeTemplateSpotInterruptionPredictionsEnabled = "spot_interruption_predictions_enabled" FieldNodeTemplateSpotInterruptionPredictionsType = "spot_interruption_predictions_type" FieldNodeTemplateStorageOptimized = "storage_optimized" + FieldNodeTemplateStorageOptimizedStatus = "storage_optimized_status" FieldNodeTemplateUseSpotFallbacks = "use_spot_fallbacks" FieldNodeTemplateCustomPriority = "custom_priority" FieldNodeTemplateDedicatedNodeAffinity = "dedicated_node_affinity" @@ -219,10 +222,29 @@ func resourceNodeTemplate() *schema.Resource { Description: "Max Memory (Mib) per node.", }, FieldNodeTemplateStorageOptimized: { - Type: schema.TypeBool, - Optional: true, - Default: false, - Description: "Storage optimized instance constraint - will only pick storage optimized nodes if true", + Type: schema.TypeBool, + Optional: true, + Default: false, + Deprecated: "This attribute will be removed in the next major release. Use `storage_optimized_status` instead.", + Description: "Storage optimized instance constraint - will only pick storage optimized nodes if true", + ConflictsWith: []string{FieldNodeTemplateConstraints + ".0." + FieldNodeTemplateStorageOptimizedStatus}, + ValidateDiagFunc: func(i interface{}, path cty.Path) diag.Diagnostics { + return diag.Diagnostics{ + { + Severity: diag.Error, + Summary: "Deprecated field `storage_optimized`", + Detail: "Please use `storage_optimized_status` instead.", + AttributePath: path, + }, + } + }, + }, + FieldNodeTemplateStorageOptimizedStatus: { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "Storage optimized instance constraint - will only pick storage optimized nodes if enabled. Supported values include: `enabled`, `disabled` or empty string.", + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{"", Enabled, Disabled}, false)), }, FieldNodeTemplateIsGpuOnly: { Type: schema.TypeBool, @@ -231,10 +253,29 @@ func resourceNodeTemplate() *schema.Resource { Description: "GPU instance constraint - will only pick nodes with GPU if true", }, FieldNodeTemplateComputeOptimized: { - Type: schema.TypeBool, - Optional: true, - Default: false, - Description: "Compute optimized instance constraint - will only pick compute optimized nodes if true.", + Type: schema.TypeBool, + Optional: true, + Default: false, + Deprecated: "This attribute will be removed in the next major release. Use `compute_optimized_status` instead.", + Description: "Compute optimized instance constraint - will only pick compute optimized nodes if true.", + ConflictsWith: []string{FieldNodeTemplateConstraints + ".0." + FieldNodeTemplateComputeOptimizedStatus}, + ValidateDiagFunc: func(i interface{}, path cty.Path) diag.Diagnostics { + return diag.Diagnostics{ + { + Severity: diag.Error, + Summary: "Deprecated field `compute_optimized`", + Detail: "Please use `compute_optimized_status` instead.", + AttributePath: path, + }, + } + }, + }, + FieldNodeTemplateComputeOptimizedStatus: { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "Compute optimized instance constraint - will only pick compute optimized nodes if enabled. Supported values include: `enabled`, `disabled` or empty string.", + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{"", Enabled, Disabled}, false)), }, FieldNodeTemplateInstanceFamilies: { Type: schema.TypeList, @@ -567,10 +608,18 @@ func flattenConstraints(c *sdk.NodetemplatesV1TemplateConstraints) ([]map[string out[FieldNodeTemplateInstanceFamilies] = flattenInstanceFamilies(c.InstanceFamilies) } if c.ComputeOptimized != nil { - out[FieldNodeTemplateComputeOptimized] = c.ComputeOptimized + if lo.FromPtr(c.ComputeOptimized) { + out[FieldNodeTemplateComputeOptimizedStatus] = Enabled + } else { + out[FieldNodeTemplateComputeOptimizedStatus] = Disabled + } } if c.StorageOptimized != nil { - out[FieldNodeTemplateStorageOptimized] = c.StorageOptimized + if lo.FromPtr(c.StorageOptimized) { + out[FieldNodeTemplateStorageOptimizedStatus] = Enabled + } else { + out[FieldNodeTemplateStorageOptimizedStatus] = Disabled + } } if c.Spot != nil { out[FieldNodeTemplateSpot] = c.Spot @@ -1035,9 +1084,18 @@ func toTemplateConstraints(obj map[string]any) *sdk.NodetemplatesV1TemplateConst } out := &sdk.NodetemplatesV1TemplateConstraints{} - if v, ok := obj[FieldNodeTemplateComputeOptimized].(bool); ok { - out.ComputeOptimized = toPtr(v) + + if v, ok := obj[FieldNodeTemplateComputeOptimizedStatus].(string); ok { + switch v { + case Enabled: + out.ComputeOptimized = toPtr(true) + case Disabled: + out.ComputeOptimized = toPtr(false) + default: + out.ComputeOptimized = nil + } } + if v, ok := obj[FieldNodeTemplateFallbackRestoreRateSeconds].(int); ok { out.FallbackRestoreRateSeconds = toPtr(int32(v)) } @@ -1075,9 +1133,18 @@ func toTemplateConstraints(obj map[string]any) *sdk.NodetemplatesV1TemplateConst out.Spot = toPtr(!v) } } - if v, ok := obj[FieldNodeTemplateStorageOptimized].(bool); ok { - out.StorageOptimized = toPtr(v) + + if v, ok := obj[FieldNodeTemplateStorageOptimizedStatus].(string); ok { + switch v { + case Enabled: + out.StorageOptimized = toPtr(true) + case Disabled: + out.StorageOptimized = toPtr(false) + default: + out.StorageOptimized = nil + } } + if v, ok := obj[FieldNodeTemplateUseSpotFallbacks].(bool); ok { out.UseSpotFallbacks = toPtr(v) } diff --git a/castai/resource_node_template_test.go b/castai/resource_node_template_test.go index ce970169..88a8daa4 100644 --- a/castai/resource_node_template_test.go +++ b/castai/resource_node_template_test.go @@ -56,7 +56,7 @@ func TestNodeTemplateResourceReadContext(t *testing.T) { "spotDiversityPriceIncreaseLimitPercent": 20, "spotInterruptionPredictionsEnabled": true, "spotInterruptionPredictionsType": "aws-rebalance-recommendations", - "storageOptimized": false, + "storageOptimized": true, "computeOptimized": false, "minCpu": 10, "maxCpu": 10000, @@ -155,6 +155,7 @@ constraints.0.architectures.# = 2 constraints.0.architectures.0 = amd64 constraints.0.architectures.1 = arm64 constraints.0.compute_optimized = false +constraints.0.compute_optimized_status = disabled constraints.0.custom_priority.# = 1 constraints.0.custom_priority.0.instance_families.# = 2 constraints.0.custom_priority.0.instance_families.0 = a @@ -203,6 +204,7 @@ constraints.0.spot_diversity_price_increase_limit_percent = 20 constraints.0.spot_interruption_predictions_enabled = true constraints.0.spot_interruption_predictions_type = aws-rebalance-recommendations constraints.0.storage_optimized = false +constraints.0.storage_optimized_status = enabled constraints.0.use_spot_fallbacks = false custom_instances_enabled = true custom_instances_with_extended_memory_enabled = true @@ -459,6 +461,8 @@ func TestAccResourceNodeTemplate_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "constraints.0.custom_priority.0.spot", "true"), resource.TestCheckResourceAttr(resourceName, "constraints.0.custom_priority.0.on_demand", "true"), resource.TestCheckResourceAttr(resourceName, "constraints.0.dedicated_node_affinity.#", "0"), + resource.TestCheckResourceAttr(resourceName, "constraints.0.storage_optimized_status", "disabled"), + resource.TestCheckResourceAttr(resourceName, "constraints.0.compute_optimized_status", ""), ), }, { @@ -517,6 +521,8 @@ func TestAccResourceNodeTemplate_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "constraints.0.custom_priority.1.spot", "true"), resource.TestCheckResourceAttr(resourceName, "constraints.0.custom_priority.1.on_demand", "true"), resource.TestCheckResourceAttr(resourceName, "constraints.0.dedicated_node_affinity.#", "0"), + resource.TestCheckResourceAttr(resourceName, "constraints.0.storage_optimized_status", "enabled"), + resource.TestCheckResourceAttr(resourceName, "constraints.0.compute_optimized_status", "disabled"), ), }, }, @@ -571,6 +577,7 @@ func testAccNodeTemplateConfig(rName, clusterName string) string { spot_interruption_predictions_enabled = true spot_interruption_predictions_type = "interruption-predictions" use_spot_fallbacks = true + storage_optimized_status = "disabled" min_cpu = 4 max_cpu = 100 instance_families { @@ -580,9 +587,7 @@ func testAccNodeTemplateConfig(rName, clusterName string) string { include_names = [] exclude_names = [] manufacturers = ["NVIDIA"] - } - compute_optimized = false - storage_optimized = false + } custom_priority { instance_families = ["c", "d"] @@ -627,8 +632,8 @@ func testNodeTemplateUpdated(rName, clusterName string) string { spot_interruption_predictions_enabled = true spot_interruption_predictions_type = "interruption-predictions" fallback_restore_rate_seconds = 1800 - storage_optimized = false - compute_optimized = false + storage_optimized_status = "enabled" + compute_optimized_status = "disabled" architectures = ["arm64"] custom_priority { diff --git a/docs/resources/node_template.md b/docs/resources/node_template.md index f8a72911..5e8a9aa3 100644 --- a/docs/resources/node_template.md +++ b/docs/resources/node_template.md @@ -44,7 +44,8 @@ CAST AI node template resource to manage node templates Optional: - `architectures` (List of String) List of acceptable instance CPU architectures, the default is amd64. Allowed values: amd64, arm64. -- `compute_optimized` (Boolean) Compute optimized instance constraint - will only pick compute optimized nodes if true. +- `compute_optimized` (Boolean, Deprecated) Compute optimized instance constraint - will only pick compute optimized nodes if true. +- `compute_optimized_status` (String) Compute optimized instance constraint - will only pick compute optimized nodes if enabled. Supported values include: `enabled`, `disabled` or empty string. - `custom_priority` (Block List) (see [below for nested schema](#nestedblock--constraints--custom_priority)) - `dedicated_node_affinity` (Block List) Dedicated node affinity - creates preference for instances to be created on sole tenancy or dedicated nodes. This feature is only available for GCP clusters and sole tenancy nodes with local @@ -67,7 +68,8 @@ Optional: - `spot_diversity_price_increase_limit_percent` (Number) Allowed node configuration price increase when diversifying instance types. E.g. if the value is 10%, then the overall price of diversified instance types can be 10% higher than the price of the optimal configuration. - `spot_interruption_predictions_enabled` (Boolean) Enable/disable spot interruption predictions. - `spot_interruption_predictions_type` (String) Spot interruption predictions type. Can be either "aws-rebalance-recommendations" or "interruption-predictions". -- `storage_optimized` (Boolean) Storage optimized instance constraint - will only pick storage optimized nodes if true +- `storage_optimized` (Boolean, Deprecated) Storage optimized instance constraint - will only pick storage optimized nodes if true +- `storage_optimized_status` (String) Storage optimized instance constraint - will only pick storage optimized nodes if enabled. Supported values include: `enabled`, `disabled` or empty string. - `use_spot_fallbacks` (Boolean) Spot instance fallback constraint - when true, on-demand instances will be created, when spots are unavailable.