diff --git a/castai/resource_node_configuration.go b/castai/resource_node_configuration.go index e358e583..acb55695 100644 --- a/castai/resource_node_configuration.go +++ b/castai/resource_node_configuration.go @@ -40,6 +40,7 @@ const ( FieldNodeConfigurationEKSTargetGroup = "target_group" FieldNodeConfigurationAKSImageFamily = "aks_image_family" FieldNodeConfigurationEKSImageFamily = "eks_image_family" + FieldNodeConfigurationAKSLoadbalaners = "loadbalancers" ) const ( @@ -321,6 +322,34 @@ func resourceNodeConfiguration() *schema.Resource { return strings.EqualFold(oldValue, newValue) }, }, + FieldNodeConfigurationAKSLoadbalaners: { + Type: schema.TypeList, + Optional: true, + Description: "Loadboalancer configuration for CAST provisioned nodes", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Name of loadbalancer", + }, + "ip_based_backend_pools": { + Type: schema.TypeList, + Optional: true, + Description: "IP based backend pools configuration for CAST provisioned nodes", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the ip based backend pool", + }, + }, + }, + }, + }, + }, + }, }, }, }, @@ -880,9 +909,54 @@ func toAKSSConfig(obj map[string]interface{}) *sdk.NodeconfigV1AKSConfig { out.ImageFamily = toAKSImageFamily(v) } + if v, ok := obj[FieldNodeConfigurationAKSLoadbalaners].([]interface{}); ok && len(v) > 0 { + out.LoadBalancers = toAksLoadBalancers(v) + } + return out } +func toAksLoadBalancers(obj []interface{}) *[]sdk.NodeconfigV1AKSConfigLoadBalancers { + if obj == nil { + return nil + } + + out := make([]sdk.NodeconfigV1AKSConfigLoadBalancers, 0, len(obj)) + for _, lbRaw := range obj { + if lb, ok := lbRaw.(map[string]interface{}); ok { + sdkLB := sdk.NodeconfigV1AKSConfigLoadBalancers{} + if name, ok := lb["name"].(string); ok && name != "" { + sdkLB.Name = lo.ToPtr(name) + } + if ipBasedBackendPools, ok := lb["ip_based_backend_pools"].([]interface{}); ok && len(ipBasedBackendPools) > 0 { + sdkLB.IpBasedBackendPools = toAksIpBasedBackendPools(ipBasedBackendPools) + } + out = append(out, sdkLB) + } + } + + return &out +} + +func toAksIpBasedBackendPools(obj []interface{}) *[]sdk.NodeconfigV1AKSConfigLoadBalancersIPBasedBackendPool { + if obj == nil { + return nil + } + + out := make([]sdk.NodeconfigV1AKSConfigLoadBalancersIPBasedBackendPool, 0, len(obj)) + for _, poolRaw := range obj { + if pool, ok := poolRaw.(map[string]interface{}); ok { + sdkPool := sdk.NodeconfigV1AKSConfigLoadBalancersIPBasedBackendPool{} + if name, ok := pool["name"].(string); ok && name != "" { + sdkPool.Name = lo.ToPtr(name) + } + out = append(out, sdkPool) + } + } + + return &out +} + func toAKSOSDiskType(v string) *sdk.NodeconfigV1AKSConfigOsDiskType { if v == "" { return nil @@ -932,9 +1006,50 @@ func flattenAKSConfig(config *sdk.NodeconfigV1AKSConfig) []map[string]interface{ m[FieldNodeConfigurationAKSImageFamily] = fromAKSImageFamily(*v) } + if v := config.LoadBalancers; v != nil && len(*v) > 0 { + m[FieldNodeConfigurationAKSLoadbalaners] = fromAksLoadBalancers(*v) + } + return []map[string]interface{}{m} } +func fromAksLoadBalancers(lbs []sdk.NodeconfigV1AKSConfigLoadBalancers) []map[string]interface{} { + if lbs == nil { + return nil + } + + out := make([]map[string]interface{}, 0, len(lbs)) + for _, lb := range lbs { + m := map[string]interface{}{} + if lb.Name != nil { + m["name"] = *lb.Name + } + if lb.IpBasedBackendPools != nil && len(*lb.IpBasedBackendPools) > 0 { + m["ip_based_backend_pools"] = fromAksIpBasedBackendPools(*lb.IpBasedBackendPools) + } + out = append(out, m) + } + + return out +} + +func fromAksIpBasedBackendPools(pools []sdk.NodeconfigV1AKSConfigLoadBalancersIPBasedBackendPool) []map[string]interface{} { + if pools == nil { + return nil + } + + out := make([]map[string]interface{}, 0, len(pools)) + for _, pool := range pools { + m := map[string]interface{}{} + if pool.Name != nil { + m["name"] = *pool.Name + } + out = append(out, m) + } + + return out +} + func fromAKSDiskType(osDiskType *sdk.NodeconfigV1AKSConfigOsDiskType) string { if osDiskType == nil { return "" diff --git a/castai/resource_node_configuration_aks_test.go b/castai/resource_node_configuration_aks_test.go index f04ec441..09cf47fb 100644 --- a/castai/resource_node_configuration_aks_test.go +++ b/castai/resource_node_configuration_aks_test.go @@ -43,6 +43,8 @@ func TestAccResourceNodeConfiguration_aks(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "min_disk_size", "121"), resource.TestCheckResourceAttr(resourceName, "aks.0.max_pods_per_node", "32"), resource.TestCheckResourceAttr(resourceName, "aks.0.aks_image_family", "azure-linux"), + resource.TestCheckResourceAttr(resourceName, "aks.0.loadbalancers.0.name", "test-lb"), + resource.TestCheckResourceAttr(resourceName, "aks.0.loadbalancers.0.ip_based_backend_pools.0.name", "test"), resource.TestCheckResourceAttr(resourceName, "eks.#", "0"), resource.TestCheckResourceAttr(resourceName, "kops.#", "0"), resource.TestCheckResourceAttr(resourceName, "gke.#", "0"), @@ -100,6 +102,12 @@ resource "castai_node_configuration" "test" { aks { max_pods_per_node = 32 aks_image_family = "azure-linux" + loadbalancers { + name = "test-lb" + ip_based_backend_pools { + name = "test" + } + } } } `, rgName, rName)) diff --git a/docs/resources/node_configuration.md b/docs/resources/node_configuration.md index 8e670d88..582cab32 100644 --- a/docs/resources/node_configuration.md +++ b/docs/resources/node_configuration.md @@ -88,9 +88,30 @@ resource "castai_node_configuration" "default" { Optional: - `aks_image_family` (String) Image OS Family to use when provisioning node in AKS. If both image and family are provided, the system will use provided image and provisioning logic for given family. If only image family is provided, the system will attempt to resolve the latest image from that family based on kubernetes version and node architecture. If image family is omitted, a default family (based on cloud provider) will be used. See Cast.ai documentation for details. Possible values: (ubuntu,azure-linux) +- `loadbalancers` (Block List) Loadboalancer configuration for CAST provisioned nodes (see [below for nested schema](#nestedblock--aks--loadbalancers)) - `max_pods_per_node` (Number) Maximum number of pods that can be run on a node, which affects how many IP addresses you will need for each node. Defaults to 30 - `os_disk_type` (String) Type of managed os disk attached to the node. (See [disk types](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-types)). One of: standard, standard-ssd, premium-ssd (ultra and premium-ssd-v2 are not supported for os disk) + +### Nested Schema for `aks.loadbalancers` + +Required: + +- `name` (String) Name of loadbalancer + +Optional: + +- `ip_based_backend_pools` (Block List) IP based backend pools configuration for CAST provisioned nodes (see [below for nested schema](#nestedblock--aks--loadbalancers--ip_based_backend_pools)) + + +### Nested Schema for `aks.loadbalancers.ip_based_backend_pools` + +Required: + +- `name` (String) Name of the ip based backend pool + + + ### Nested Schema for `eks`