Skip to content

Commit b705681

Browse files
authored
feat: aks public ip (#471)
* feat: add aks public ip * tests * validation * fix tests * code review remarks
1 parent df771de commit b705681

File tree

6 files changed

+370
-23
lines changed

6 files changed

+370
-23
lines changed

castai/resource_node_configuration.go

Lines changed: 162 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,29 +21,32 @@ import (
2121
)
2222

2323
const (
24-
FieldNodeConfigurationName = "name"
25-
FieldNodeConfigurationDiskCpuRatio = "disk_cpu_ratio"
26-
FieldNodeConfigurationMinDiskSize = "min_disk_size"
27-
FieldNodeConfigurationDrainTimeoutSec = "drain_timeout_sec"
28-
FieldNodeConfigurationSubnets = "subnets"
29-
FieldNodeConfigurationSSHPublicKey = "ssh_public_key"
30-
FieldNodeConfigurationImage = "image"
31-
FieldNodeConfigurationTags = "tags"
32-
FieldNodeConfigurationInitScript = "init_script"
33-
FieldNodeConfigurationContainerRuntime = "container_runtime"
34-
FieldNodeConfigurationDockerConfig = "docker_config"
35-
FieldNodeConfigurationKubeletConfig = "kubelet_config"
36-
FieldNodeConfigurationAKS = "aks"
37-
FieldNodeConfigurationEKS = "eks"
38-
FieldNodeConfigurationKOPS = "kops"
39-
FieldNodeConfigurationGKE = "gke"
40-
FieldNodeConfigurationEKSTargetGroup = "target_group"
41-
FieldNodeConfigurationAKSImageFamily = "aks_image_family"
42-
FieldNodeConfigurationAKSEphemeralOSDisk = "ephemeral_os_disk"
43-
FieldNodeConfigurationEKSImageFamily = "eks_image_family"
44-
FieldNodeConfigurationLoadbalancers = "loadbalancers"
45-
FieldNodeConfigurationAKSLoadbalancerIPPools = "ip_based_backend_pools"
46-
FieldNodeConfigurationAKSLoadbalancerNICPools = "nic_based_backend_pools"
24+
FieldNodeConfigurationName = "name"
25+
FieldNodeConfigurationDiskCpuRatio = "disk_cpu_ratio"
26+
FieldNodeConfigurationMinDiskSize = "min_disk_size"
27+
FieldNodeConfigurationDrainTimeoutSec = "drain_timeout_sec"
28+
FieldNodeConfigurationSubnets = "subnets"
29+
FieldNodeConfigurationSSHPublicKey = "ssh_public_key"
30+
FieldNodeConfigurationImage = "image"
31+
FieldNodeConfigurationTags = "tags"
32+
FieldNodeConfigurationInitScript = "init_script"
33+
FieldNodeConfigurationContainerRuntime = "container_runtime"
34+
FieldNodeConfigurationDockerConfig = "docker_config"
35+
FieldNodeConfigurationKubeletConfig = "kubelet_config"
36+
FieldNodeConfigurationAKS = "aks"
37+
FieldNodeConfigurationEKS = "eks"
38+
FieldNodeConfigurationKOPS = "kops"
39+
FieldNodeConfigurationGKE = "gke"
40+
FieldNodeConfigurationEKSTargetGroup = "target_group"
41+
FieldNodeConfigurationAKSImageFamily = "aks_image_family"
42+
FieldNodeConfigurationAKSEphemeralOSDisk = "ephemeral_os_disk"
43+
FieldNodeConfigurationEKSImageFamily = "eks_image_family"
44+
FieldNodeConfigurationLoadbalancers = "loadbalancers"
45+
FieldNodeConfigurationAKSLoadbalancerIPPools = "ip_based_backend_pools"
46+
FieldNodeConfigurationAKSLoadbalancerNICPools = "nic_based_backend_pools"
47+
FieldNodeConfigurationAKSNetworkSecurityGroup = "network_security_group"
48+
FieldNodeConfigurationAKSApplicationSecurityGroups = "application_security_groups"
49+
FieldNodeConfigurationAKSPublicIP = "public_ip"
4750
)
4851

4952
const (
@@ -367,6 +370,61 @@ func resourceNodeConfiguration() *schema.Resource {
367370
},
368371
},
369372
},
373+
FieldNodeConfigurationAKSNetworkSecurityGroup: {
374+
Type: schema.TypeString,
375+
Optional: true,
376+
Description: "Network security group to be used for provisioned nodes, if not provided default security group from `castpool` will be used",
377+
},
378+
FieldNodeConfigurationAKSApplicationSecurityGroups: {
379+
Type: schema.TypeList,
380+
Optional: true,
381+
Description: "Application security groups to be used for provisioned nodes",
382+
Elem: &schema.Schema{
383+
Type: schema.TypeString,
384+
},
385+
},
386+
FieldNodeConfigurationAKSPublicIP: {
387+
Type: schema.TypeList,
388+
Optional: true,
389+
Description: "Public IP configuration for CAST AI provisioned nodes",
390+
MaxItems: 1,
391+
Elem: &schema.Resource{
392+
Schema: map[string]*schema.Schema{
393+
"public_ip_prefix": {
394+
Type: schema.TypeString,
395+
Optional: true,
396+
Description: "Public IP prefix to be used for provisioned nodes",
397+
},
398+
"tags": {
399+
Type: schema.TypeMap,
400+
Optional: true,
401+
Elem: &schema.Schema{
402+
Type: schema.TypeString,
403+
},
404+
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
405+
allowedKeys := []string{
406+
"FirstPartyUsage",
407+
"NetworkDomain",
408+
"RoutingPreference",
409+
}
410+
411+
tags := v.(map[string]interface{})
412+
for key := range tags {
413+
if !lo.Contains(allowedKeys, key) {
414+
errors = append(errors, fmt.Errorf("invalid key %q in %q, allowed keys: %v", key, k, allowedKeys))
415+
}
416+
}
417+
return
418+
},
419+
},
420+
"idle_timeout_in_minutes": {
421+
Type: schema.TypeInt,
422+
Optional: true,
423+
Description: "Idle timeout in minutes for public IP",
424+
},
425+
},
426+
},
427+
},
370428
FieldNodeConfigurationLoadbalancers: {
371429
Type: schema.TypeList,
372430
Optional: true,
@@ -1046,9 +1104,52 @@ func toAKSSConfig(obj map[string]interface{}) *sdk.NodeconfigV1AKSConfig {
10461104
out.LoadBalancers = toAksLoadBalancers(v)
10471105
}
10481106

1107+
if v, ok := obj[FieldNodeConfigurationAKSPublicIP].([]interface{}); ok && len(v) > 0 {
1108+
out.PublicIp = toAKSNodePublicIP(v[0])
1109+
}
1110+
1111+
if v, ok := obj[FieldNodeConfigurationAKSNetworkSecurityGroup].(string); ok && v != "" {
1112+
out.NetworkSecurityGroupId = toPtr(v)
1113+
}
1114+
1115+
if v, ok := obj[FieldNodeConfigurationAKSApplicationSecurityGroups].([]interface{}); ok && len(v) > 0 {
1116+
out.ApplicationSecurityGroupIds = toPtr(toStringList(v))
1117+
}
1118+
10491119
return out
10501120
}
10511121

1122+
func toAKSNodePublicIP(obj any) *sdk.NodeconfigV1AKSConfigPublicIP {
1123+
if obj == nil {
1124+
return nil
1125+
}
1126+
1127+
publicIP := &sdk.NodeconfigV1AKSConfigPublicIP{}
1128+
1129+
if v, ok := obj.(map[string]any)["public_ip_prefix"].(string); ok && v != "" {
1130+
publicIP.IpPrefix = lo.ToPtr(v)
1131+
}
1132+
1133+
if v, ok := obj.(map[string]any)["tags"].(map[string]any); ok && len(v) > 0 {
1134+
tagList := []sdk.NodeconfigV1AKSConfigPublicIPAKSPublicIPTags{}
1135+
1136+
for k, vv := range v {
1137+
tagList = append(tagList, sdk.NodeconfigV1AKSConfigPublicIPAKSPublicIPTags{
1138+
TagValue: lo.ToPtr(vv.(string)),
1139+
IpTagType: lo.ToPtr(k),
1140+
})
1141+
}
1142+
publicIP.Tags = &tagList
1143+
}
1144+
1145+
if v, ok := obj.(map[string]any)["idle_timeout_in_minutes"].(int); ok && v > 0 {
1146+
publicIP.IdleTimeoutInMinutes = lo.ToPtr(int32(v))
1147+
}
1148+
1149+
return publicIP
1150+
1151+
}
1152+
10521153
func toAKSEphemeralOSDisk(obj any) *sdk.NodeconfigV1AKSConfigOsDiskEphemeral {
10531154
if obj == nil {
10541155
return nil
@@ -1197,9 +1298,47 @@ func flattenAKSConfig(config *sdk.NodeconfigV1AKSConfig) []map[string]interface{
11971298
m[FieldNodeConfigurationAKSEphemeralOSDisk] = fromAKSEphemeralOSDisk(v)
11981299
}
11991300

1301+
if v := config.PublicIp; v != nil {
1302+
m[FieldNodeConfigurationAKSPublicIP] = fromAKSNodePublicIP(v)
1303+
}
1304+
1305+
if v := config.NetworkSecurityGroupId; v != nil {
1306+
m[FieldNodeConfigurationAKSNetworkSecurityGroup] = *config.NetworkSecurityGroupId
1307+
}
1308+
1309+
if v := config.ApplicationSecurityGroupIds; v != nil {
1310+
m[FieldNodeConfigurationAKSApplicationSecurityGroups] = *config.ApplicationSecurityGroupIds
1311+
}
1312+
12001313
return []map[string]interface{}{m}
12011314
}
12021315

1316+
func fromAKSNodePublicIP(sdkPublicIp *sdk.NodeconfigV1AKSConfigPublicIP) []map[string]any {
1317+
if sdkPublicIp == nil {
1318+
return nil
1319+
}
1320+
1321+
m := map[string]interface{}{}
1322+
if sdkPublicIp.IpPrefix != nil {
1323+
m["public_ip_prefix"] = *sdkPublicIp.IpPrefix
1324+
}
1325+
1326+
if sdkPublicIp.Tags != nil {
1327+
tags := make(map[string]interface{})
1328+
for _, tag := range *sdkPublicIp.Tags {
1329+
tags[lo.FromPtr(tag.IpTagType)] = lo.FromPtr(tag.TagValue)
1330+
}
1331+
m["tags"] = tags
1332+
}
1333+
1334+
if sdkPublicIp.IdleTimeoutInMinutes != nil {
1335+
m["idle_timeout_in_minutes"] = *sdkPublicIp.IdleTimeoutInMinutes
1336+
}
1337+
1338+
return []map[string]any{m}
1339+
1340+
}
1341+
12031342
func fromAKSEphemeralOSDisk(sdkEph *sdk.NodeconfigV1AKSConfigOsDiskEphemeral) []map[string]interface{} {
12041343
if sdkEph == nil {
12051344
return nil

castai/resource_node_configuration_aks_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ func TestAccResourceNodeConfiguration_aks(t *testing.T) {
4747
resource.TestCheckResourceAttr(resourceName, "aks.0.ephemeral_os_disk.0.cache", "ReadOnly"),
4848
resource.TestCheckResourceAttr(resourceName, "aks.0.loadbalancers.0.name", "test-lb"),
4949
resource.TestCheckResourceAttr(resourceName, "aks.0.loadbalancers.0.ip_based_backend_pools.0.name", "test"),
50+
resource.TestCheckResourceAttr(resourceName, "aks.0.network_security_group", "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg/providers/Microsoft.Network/networkSecurityGroups/test-nsg"),
51+
resource.TestCheckResourceAttr(resourceName, "aks.0.application_security_groups.0", "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg/providers/Microsoft.Network/applicationSecurityGroups/test-asg"),
52+
resource.TestCheckResourceAttr(resourceName, "aks.0.public_ip.0.public_ip_prefix", "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/test-ip"),
53+
resource.TestCheckResourceAttr(resourceName, "aks.0.public_ip.0.tags.FirstPartyUsage", "something"),
54+
resource.TestCheckResourceAttr(resourceName, "aks.0.public_ip.0.idle_timeout_in_minutes", "10"),
5055
resource.TestCheckResourceAttr(resourceName, "eks.#", "0"),
5156
resource.TestCheckResourceAttr(resourceName, "kops.#", "0"),
5257
resource.TestCheckResourceAttr(resourceName, "gke.#", "0"),
@@ -113,6 +118,15 @@ resource "castai_node_configuration" "test" {
113118
ip_based_backend_pools {
114119
name = "test"
115120
}
121+
}
122+
network_security_group = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg/providers/Microsoft.Network/networkSecurityGroups/test-nsg"
123+
application_security_groups = ["/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg/providers/Microsoft.Network/applicationSecurityGroups/test-asg"]
124+
public_ip {
125+
public_ip_prefix = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/test-ip"
126+
tags = {
127+
FirstPartyUsage = "something"
128+
}
129+
idle_timeout_in_minutes = 10
116130
}
117131
}
118132
}

castai/sdk/api.gen.go

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)