From 02c93f04c90d6dbc27cb89f2f3b1c8cd846b71ab Mon Sep 17 00:00:00 2001 From: Mike Norgate Date: Thu, 11 Apr 2024 09:30:17 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20add=20support=20for=20setting=20node=20?= =?UTF-8?q?affinity=20selectors=20for=20dedicated=20n=E2=80=A6=20(#291)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- castai/resource_node_template.go | 71 +++++++++++++++++++++++++++ castai/resource_node_template_test.go | 14 +++++- castai/sdk/api.gen.go | 37 +++++++++----- docs/resources/node_template.md | 14 ++++++ 4 files changed, 122 insertions(+), 14 deletions(-) diff --git a/castai/resource_node_template.go b/castai/resource_node_template.go index 20fc1749..b4bdab92 100644 --- a/castai/resource_node_template.go +++ b/castai/resource_node_template.go @@ -59,6 +59,10 @@ const ( FieldNodeTemplateDedicatedNodeAffinity = "dedicated_node_affinity" FieldNodeTemplateAzName = "az_name" FieldNodeTemplateInstanceTypes = "instance_types" + FieldNodeTemplateAffinityName = "affinity" + FieldNodeTemplateAffinityKeyName = "key" + FieldNodeTemplateAffinityOperatorName = "operator" + FieldNodeTemplateAffinityValuesName = "values" ) const ( @@ -73,9 +77,19 @@ const ( OsWindows = "windows" ) +const ( + NodeSelectorOperationIn = "In" + NodeSelectorOperationNotIn = "NotIn" + NodeSelectorOperationExists = "Exists" + NodeSelectorOperationDoesNot = "DoesNotExist" + NodeSelectorOperationGt = "Gt" + NodeSelectorOperationLt = "Lt" +) + func resourceNodeTemplate() *schema.Resource { supportedArchitectures := []string{ArchAMD64, ArchARM64} supportedOs := []string{OsLinux, OsWindows} + supportedSelectorOperations := []string{NodeSelectorOperationIn, NodeSelectorOperationNotIn, NodeSelectorOperationExists, NodeSelectorOperationDoesNot, NodeSelectorOperationGt, NodeSelectorOperationLt} return &schema.Resource{ CreateContext: resourceNodeTemplateCreate, @@ -372,6 +386,33 @@ func resourceNodeTemplate() *schema.Resource { Type: schema.TypeString, Description: "Name of node group.", }, + FieldNodeTemplateAffinityName: { + Optional: true, + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + FieldNodeTemplateAffinityKeyName: { + Required: true, + Type: schema.TypeString, + Description: "Key of the node affinity selector.", + }, + FieldNodeTemplateAffinityOperatorName: { + Required: true, + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(supportedSelectorOperations, false)), + Description: fmt.Sprintf("Operator of the node affinity selector. Allowed values: %s.", strings.Join(supportedSelectorOperations, ", ")), + }, + FieldNodeTemplateAffinityValuesName: { + Required: true, + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "Values of the node affinity selector.", + }, + }, + }, + }, }, }, }, @@ -638,6 +679,17 @@ func flattenNodeAffinity(affinities []sdk.NodetemplatesV1TemplateConstraintsDedi result[FieldNodeTemplateName] = lo.FromPtr(item.Name) result[FieldNodeTemplateAzName] = lo.FromPtr(item.AzName) + + if item.Affinity != nil && len(*item.Affinity) > 0 { + result[FieldNodeTemplateAffinityName] = lo.Map(*item.Affinity, func(affinity sdk.K8sSelectorV1KubernetesNodeAffinity, index int) map[string]any { + return map[string]any{ + FieldNodeTemplateAffinityKeyName: affinity.Key, + FieldNodeTemplateAffinityOperatorName: affinity.Operator, + FieldNodeTemplateAffinityValuesName: affinity.Values, + } + }) + } + return result }) } @@ -1160,6 +1212,25 @@ func toTemplateConstraintsNodeAffinity(o map[string]any) *sdk.NodetemplatesV1Tem if v, ok := o[FieldNodeTemplateInstanceTypes].([]any); ok { out.InstanceTypes = toPtr(toStringList(v)) } + if v, ok := o[FieldNodeTemplateAffinityName].([]any); ok { + out.Affinity = toPtr(lo.Map(v, func(item any, _ int) sdk.K8sSelectorV1KubernetesNodeAffinity { + val, ok := item.(map[string]any) + if !ok { + return sdk.K8sSelectorV1KubernetesNodeAffinity{} + } + out := sdk.K8sSelectorV1KubernetesNodeAffinity{} + if v, ok := val[FieldNodeTemplateAffinityKeyName].(string); ok { + out.Key = v + } + if v, ok := val[FieldNodeTemplateAffinityOperatorName].(string); ok { + out.Operator = sdk.K8sSelectorV1Operator(v) + } + if v, ok := val[FieldNodeTemplateAffinityValuesName].([]any); ok { + out.Values = toStringList(v) + } + return out + })) + } return &out } diff --git a/castai/resource_node_template_test.go b/castai/resource_node_template_test.go index 1dc79907..ce970169 100644 --- a/castai/resource_node_template_test.go +++ b/castai/resource_node_template_test.go @@ -92,7 +92,14 @@ func TestNodeTemplateResourceReadContext(t *testing.T) { { "name": "foo", "azName": "eu-central-1a", - "instanceTypes": ["m5.24xlarge"] + "instanceTypes": ["m5.24xlarge"], + "affinity": [ + { + "key": "gke.io/gcp-nodepool", + "operator": "In", + "values": ["foo"] + } + ] } ] }, @@ -179,6 +186,11 @@ constraints.0.max_memory = 0 constraints.0.min_cpu = 10 constraints.0.min_memory = 0 constraints.0.dedicated_node_affinity.# = 1 +constraints.0.dedicated_node_affinity.0.affinity.# = 1 +constraints.0.dedicated_node_affinity.0.affinity.0.key = gke.io/gcp-nodepool +constraints.0.dedicated_node_affinity.0.affinity.0.operator = In +constraints.0.dedicated_node_affinity.0.affinity.0.values.# = 1 +constraints.0.dedicated_node_affinity.0.affinity.0.values.0 = foo constraints.0.dedicated_node_affinity.0.az_name = eu-central-1a constraints.0.dedicated_node_affinity.0.instance_types.# = 1 constraints.0.dedicated_node_affinity.0.instance_types.0 = m5.24xlarge diff --git a/castai/sdk/api.gen.go b/castai/sdk/api.gen.go index 36f7d3a1..513b17db 100644 --- a/castai/sdk/api.gen.go +++ b/castai/sdk/api.gen.go @@ -87,8 +87,8 @@ const ( In K8sSelectorV1Operator = "in" Lt K8sSelectorV1Operator = "Lt" Lt1 K8sSelectorV1Operator = "lt" - NotIn K8sSelectorV1Operator = "notIn" - NotInt K8sSelectorV1Operator = "NotInt" + NotIn K8sSelectorV1Operator = "NotIn" + NotIn1 K8sSelectorV1Operator = "notIn" ) // Defines values for NodeconfigV1AKSConfigOsDiskType. @@ -1577,7 +1577,7 @@ type K8sSelectorV1KubernetesNodeAffinity struct { Key string `json:"key"` // - IN: In values - // - NotInt: Not int values + // - NotIn: Not in values // - Exists: Just exist // - DoesNotExist: Values does not exist // - Gt: Greater then @@ -1587,7 +1587,7 @@ type K8sSelectorV1KubernetesNodeAffinity struct { } // - IN: In values -// - NotInt: Not int values +// - NotIn: Not in values // - Exists: Just exist // - DoesNotExist: Values does not exist // - Gt: Greater then @@ -1685,8 +1685,11 @@ type NodeconfigV1NewNodeConfiguration struct { // Optional docker daemon configuration properties. Provide only properties that you want to override. Available values https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file DockerConfig *map[string]interface{} `json:"dockerConfig,omitempty"` - Eks *NodeconfigV1EKSConfig `json:"eks,omitempty"` - Gke *NodeconfigV1GKEConfig `json:"gke,omitempty"` + + // Drain timeout in seconds. Defaults to 0. + DrainTimeoutSec *int32 `json:"drainTimeoutSec"` + Eks *NodeconfigV1EKSConfig `json:"eks,omitempty"` + Gke *NodeconfigV1GKEConfig `json:"gke,omitempty"` // Image to be used while provisioning the node. If nothing is provided will be resolved to latest available image based on Kubernetes version if possible. Image *string `json:"image"` @@ -1737,8 +1740,11 @@ type NodeconfigV1NodeConfiguration struct { // Optional docker daemon configuration properties. Applicable for EKS only. DockerConfig *map[string]interface{} `json:"dockerConfig"` - Eks *NodeconfigV1EKSConfig `json:"eks,omitempty"` - Gke *NodeconfigV1GKEConfig `json:"gke,omitempty"` + + // Drain timeout in seconds. Defaults to 0. + DrainTimeoutSec *int32 `json:"drainTimeoutSec"` + Eks *NodeconfigV1EKSConfig `json:"eks,omitempty"` + Gke *NodeconfigV1GKEConfig `json:"gke,omitempty"` // The node configuration ID. Id *string `json:"id,omitempty"` @@ -1792,8 +1798,11 @@ type NodeconfigV1NodeConfigurationUpdate struct { // Optional docker daemon configuration properties. Provide only properties that you want to override. Available values https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file DockerConfig *map[string]interface{} `json:"dockerConfig,omitempty"` - Eks *NodeconfigV1EKSConfig `json:"eks,omitempty"` - Gke *NodeconfigV1GKEConfig `json:"gke,omitempty"` + + // Drain timeout in seconds. Defaults to 0. + DrainTimeoutSec *int32 `json:"drainTimeoutSec"` + Eks *NodeconfigV1EKSConfig `json:"eks,omitempty"` + Gke *NodeconfigV1GKEConfig `json:"gke,omitempty"` // Image to be used while provisioning the node. If nothing is provided will be resolved to latest available image based on Kubernetes version if possible. Image *string `json:"image"` @@ -2117,9 +2126,11 @@ type NodetemplatesV1TemplateConstraintsCustomPriority struct { // NodetemplatesV1TemplateConstraintsDedicatedNodeAffinity defines model for nodetemplates.v1.TemplateConstraints.DedicatedNodeAffinity. type NodetemplatesV1TemplateConstraintsDedicatedNodeAffinity struct { - AzName *string `json:"azName,omitempty"` - InstanceTypes *[]string `json:"instanceTypes,omitempty"` - Name *string `json:"name,omitempty"` + // The affinity rules required for choosing the node. + Affinity *[]K8sSelectorV1KubernetesNodeAffinity `json:"affinity,omitempty"` + AzName *string `json:"azName,omitempty"` + InstanceTypes *[]string `json:"instanceTypes,omitempty"` + Name *string `json:"name,omitempty"` } // NodetemplatesV1TemplateConstraintsGPUConstraints defines model for nodetemplates.v1.TemplateConstraints.GPUConstraints. diff --git a/docs/resources/node_template.md b/docs/resources/node_template.md index 7936f15a..f8a72911 100644 --- a/docs/resources/node_template.md +++ b/docs/resources/node_template.md @@ -89,6 +89,20 @@ Required: - `instance_types` (List of String) Instance/node types in this node group. - `name` (String) Name of node group. +Optional: + +- `affinity` (Block List) (see [below for nested schema](#nestedblock--constraints--dedicated_node_affinity--affinity)) + + +### Nested Schema for `constraints.dedicated_node_affinity.affinity` + +Required: + +- `key` (String) Key of the node affinity selector. +- `operator` (String) Operator of the node affinity selector. Allowed values: In, NotIn, Exists, DoesNotExist, Gt, Lt. +- `values` (List of String) Values of the node affinity selector. + + ### Nested Schema for `constraints.gpu`