From 018cfa2a8507e094e529fe26e938520cb811c153 Mon Sep 17 00:00:00 2001 From: nikchern Date: Fri, 7 Jul 2023 09:17:41 -0700 Subject: [PATCH] PLT-552: aws and vmware cluster context support. (#311) * PLT-497: Draft for additional security groups. * PLT-532:Fixed placement change detection issue (#300) * PLT-532:Fixed placement change detection issue * fix diff * Docs-0.14.2 (#297) * Generate docs. * Generate docs. * PLT-527: validate placements irrespective of the order. (#302) * PLT-527:Fixed Vsphere control plane placement validation * added comments * placement validation * PLT-527: Added sorting and correct error message. * Update spectrocloud/resource_cluster_vsphere.go * PLT-536:fix for additional_label delete --------- Co-authored-by: Sivaanand Murugesan * PLT-537: Adding autoscaling options for MAAS. (#304) * PLT-542:Added Validation for resource_pool attribute for control_place placements (#307) * Docs refresh for 0.14.2. (#305) * Docs refresh for 0.14.2. * Min max description add for 0.14.2. * PLT-546: Workaround empty element in Set framework issue. (#309) * PLT-525: AWS US GOV cloud account support. (#299) * PLT-525: AWS US GOV cloud account support. * PLT-525: Correcting unit test. * PLT-525: Unit test stubs for resources. * PLT-543:Added support for OCI registry for profile creation (#308) * Fixing unit-tests and refresh docs. (#310) * PLT-536:Added fix for machine pool type set empty item issue (#306) * PLT-536:Added fix for machine pool type set empty item issue * Update spectrocloud/resource_cluster_aks.go Co-authored-by: nikchern * Update spectrocloud/resource_cluster_aws.go * Update spectrocloud/resource_cluster_azure.go * Update spectrocloud/resource_cluster_coxedge.go * Update spectrocloud/resource_cluster_edge_native.go * Update spectrocloud/resource_cluster_edge_vsphere.go * Update spectrocloud/resource_cluster_eks.go * Update spectrocloud/resource_cluster_gcp.go * Update spectrocloud/resource_cluster_libvirt.go * Update spectrocloud/resource_cluster_openstack.go * Update spectrocloud/resource_cluster_tke.go * Update spectrocloud/resource_cluster_virtual.go --------- Co-authored-by: nikchern * PLT-497: Additional security groups support. * PLT-497: Split schema for AWS support. * PLT-552: context fix for VMware and AWS. * PLT-552: docs refresh. --------- Co-authored-by: Sivaanand Murugesan --- docs/resources/cluster_aws.md | 3 + docs/resources/cluster_eks.md | 1 + go.mod | 4 +- go.sum | 8 +- spectrocloud/cluster_common_fields.go | 8 +- spectrocloud/cluster_common_hash.go | 50 ++++++++++ spectrocloud/data_source_cluster.go | 2 +- spectrocloud/data_volume_schema_test.go | 2 - spectrocloud/resource_cluster_aks.go | 2 +- spectrocloud/resource_cluster_aws.go | 91 ++++++++++++++++++- .../resource_cluster_aws_expand_test.go | 79 ++++++++++++++++ .../resource_cluster_aws_flatten_test.go | 75 +++++++++++++++ spectrocloud/resource_cluster_azure.go | 2 +- spectrocloud/resource_cluster_coxedge.go | 2 +- spectrocloud/resource_cluster_edge_native.go | 2 +- spectrocloud/resource_cluster_edge_vsphere.go | 2 +- spectrocloud/resource_cluster_eks.go | 80 +++++++++++----- .../resource_cluster_eks_expand_test.go | 2 +- .../resource_cluster_eks_flatten_test.go | 9 ++ spectrocloud/resource_cluster_gcp.go | 2 +- spectrocloud/resource_cluster_libvirt.go | 2 +- spectrocloud/resource_cluster_openstack.go | 2 +- spectrocloud/resource_cluster_tke.go | 2 +- spectrocloud/resource_cluster_virtual.go | 2 +- spectrocloud/resource_cluster_vsphere.go | 4 +- spectrocloud/resource_cluster_vsphere_test.go | 1 - spectrocloud/schemas/eks_template.go | 11 ++- 27 files changed, 398 insertions(+), 52 deletions(-) create mode 100644 spectrocloud/resource_cluster_aws_expand_test.go create mode 100644 spectrocloud/resource_cluster_aws_flatten_test.go diff --git a/docs/resources/cluster_aws.md b/docs/resources/cluster_aws.md index 806cbdee..5cd6222a 100644 --- a/docs/resources/cluster_aws.md +++ b/docs/resources/cluster_aws.md @@ -179,13 +179,16 @@ Required: Optional: - `additional_labels` (Map of String) +- `additional_security_groups` (Set of String) Additional security groups to attach to the instance. - `az_subnets` (Map of String) Mutually exclusive with `azs`. Use `az_subnets` for Static provisioning. - `azs` (Set of String) Mutually exclusive with `az_subnets`. Use `azs` for Dynamic provisioning. - `capacity_type` (String) Capacity type is an instance type, can be 'on-demand' or 'spot'. Defaults to 'on-demand'. - `control_plane` (Boolean) Whether this machine pool is a control plane. Defaults to `false`. - `control_plane_as_worker` (Boolean) Whether this machine pool is a control plane and a worker. Defaults to `false`. - `disk_size_gb` (Number) +- `max` (Number) Maximum number of nodes in the machine pool. This is used for autoscaling the machine pool. - `max_price` (String) +- `min` (Number) Minimum number of nodes in the machine pool. This is used for autoscaling the machine pool. - `taints` (Block List) (see [below for nested schema](#nestedblock--machine_pool--taints)) - `update_strategy` (String) Update strategy for the machine pool. Valid values are `RollingUpdateScaleOut` and `RollingUpdateScaleIn`. diff --git a/docs/resources/cluster_eks.md b/docs/resources/cluster_eks.md index d9099049..b8be7890 100644 --- a/docs/resources/cluster_eks.md +++ b/docs/resources/cluster_eks.md @@ -168,6 +168,7 @@ Optional: Optional: +- `additional_security_groups` (Set of String) Additional security groups to attach to the instance. - `ami_id` (String) The ID of the custom Amazon Machine Image (AMI). - `root_volume_iops` (Number) The number of input/output operations per second (IOPS) for the root volume. - `root_volume_throughput` (Number) The throughput of the root volume in MiB/s. diff --git a/go.mod b/go.mod index af7f661a..9944a1ae 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,8 @@ require ( github.com/hashicorp/terraform-plugin-docs v0.13.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1 github.com/robfig/cron v1.2.0 - github.com/spectrocloud/hapi v1.14.1-0.20230619173315-64e3e574c13d - github.com/spectrocloud/palette-sdk-go v0.0.0-20230629160306-b732211f7547 + github.com/spectrocloud/hapi v1.14.1-0.20230703141843-3da1ad40c0a6 + github.com/spectrocloud/palette-sdk-go v0.0.0-20230704181323-336d6bd76b91 github.com/stretchr/testify v1.8.0 gotest.tools v2.2.0+incompatible k8s.io/api v0.23.5 diff --git a/go.sum b/go.sum index 5812b08c..424af633 100644 --- a/go.sum +++ b/go.sum @@ -715,10 +715,10 @@ github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spectrocloud/gomi v1.14.1-0.20230412095143-b0595c6c6f08 h1:AnOC0U+ExlKBeT5yF2Pg8PPfVOfxwOUBS/5deOl1Q4Y= github.com/spectrocloud/gomi v1.14.1-0.20230412095143-b0595c6c6f08/go.mod h1:UnhUDpFEvtYh6m384r3xzj8/+Z6/hMp2O8whEMYVHec= -github.com/spectrocloud/hapi v1.14.1-0.20230619173315-64e3e574c13d h1:4R0CarVmnTRT0b4SlkFWZcwiNRtavjWAcwnSMYmGhGg= -github.com/spectrocloud/hapi v1.14.1-0.20230619173315-64e3e574c13d/go.mod h1:9lX5c6bShSkAg24223A7XBCyJj4/Kr9w0YFv6Mf5ZlE= -github.com/spectrocloud/palette-sdk-go v0.0.0-20230629160306-b732211f7547 h1:WuZD24RZC3mcYPWuBfzVZI35DE+eieJwChf/jOhH3jw= -github.com/spectrocloud/palette-sdk-go v0.0.0-20230629160306-b732211f7547/go.mod h1:KiG053DAd5vV/nWv1KgVOI6YkHWuMGb5wpD6j2zzttk= +github.com/spectrocloud/hapi v1.14.1-0.20230703141843-3da1ad40c0a6 h1:gnTkGdGus+IcEsqJRDvZ2gpGwmx6kbei2wB9LRLwxZc= +github.com/spectrocloud/hapi v1.14.1-0.20230703141843-3da1ad40c0a6/go.mod h1:O/Bkbw92QPSGPNQPqKt7Qlkn+9BKK/a22KTUlk76KHI= +github.com/spectrocloud/palette-sdk-go v0.0.0-20230704181323-336d6bd76b91 h1:BGRy3b/xP4JaZqhAy+Ik2CcqX39jbkkYtYeyCkbnlWs= +github.com/spectrocloud/palette-sdk-go v0.0.0-20230704181323-336d6bd76b91/go.mod h1:KiG053DAd5vV/nWv1KgVOI6YkHWuMGb5wpD6j2zzttk= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= diff --git a/spectrocloud/cluster_common_fields.go b/spectrocloud/cluster_common_fields.go index 745d17b8..8c25f3f4 100644 --- a/spectrocloud/cluster_common_fields.go +++ b/spectrocloud/cluster_common_fields.go @@ -9,7 +9,11 @@ import ( // read common fields like kubeconfig, tags, backup policy, scan policy, cluster_rbac_binding, namespaces func readCommonFields(c *client.V1Client, d *schema.ResourceData, cluster *models.V1SpectroCluster) (diag.Diagnostics, bool) { - kubecfg, err := c.GetClusterKubeConfig(d.Id()) + ClusterContext := "project" + if cluster.Metadata.Annotations["scope"] != "" { + ClusterContext = cluster.Metadata.Annotations["scope"] + } + kubecfg, err := c.GetClusterKubeConfig(d.Id(), ClusterContext) if err != nil { return diag.FromErr(err), true } @@ -21,7 +25,7 @@ func readCommonFields(c *client.V1Client, d *schema.ResourceData, cluster *model return diag.FromErr(err), true } - if policy, err := c.GetClusterBackupConfig(d.Id()); err != nil { + if policy, err := c.GetClusterBackupConfig(d.Id(), ClusterContext); err != nil { return diag.FromErr(err), true } else if policy != nil && policy.Spec.Config != nil { if err := d.Set("backup_policy", flattenBackupPolicy(policy.Spec.Config)); err != nil { diff --git a/spectrocloud/cluster_common_hash.go b/spectrocloud/cluster_common_hash.go index 87202176..d4ab6f9d 100644 --- a/spectrocloud/cluster_common_hash.go +++ b/spectrocloud/cluster_common_hash.go @@ -5,6 +5,7 @@ import ( "fmt" "hash/fnv" "sort" + "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -96,11 +97,29 @@ func resourceMachinePoolAwsHash(v interface{}) int { buf.WriteString(fmt.Sprintf("%t-", m["control_plane_as_worker"].(bool))) buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) buf.WriteString(fmt.Sprintf("%d-", m["count"].(int))) + + if m["min"] != nil { + buf.WriteString(fmt.Sprintf("%d-", m["min"].(int))) + } + if m["max"] != nil { + buf.WriteString(fmt.Sprintf("%d-", m["max"].(int))) + } buf.WriteString(fmt.Sprintf("%s-", m["update_strategy"].(string))) buf.WriteString(fmt.Sprintf("%s-", m["instance_type"].(string))) buf.WriteString(fmt.Sprintf("%s-", m["capacity_type"].(string))) buf.WriteString(fmt.Sprintf("%s-", m["max_price"].(string))) + if m["azs"] != nil { + azsSet := m["azs"].(*schema.Set) + azsList := azsSet.List() + azsListStr := make([]string, len(azsList)) + for i, v := range azsList { + azsListStr[i] = v.(string) + } + sort.Strings(azsListStr) + azsStr := strings.Join(azsListStr, "-") + buf.WriteString(fmt.Sprintf("%s-", azsStr)) + } buf.WriteString(fmt.Sprintf("%s-", m["azs"].(*schema.Set).GoString())) buf.WriteString(HashStringMap(m["az_subnets"])) @@ -133,9 +152,40 @@ func resourceMachinePoolEksHash(v interface{}) int { buf.WriteString(fmt.Sprintf("%s-%s", i, j.(string))) } + if m["eks_launch_template"] != nil { + buf.WriteString(eksLaunchTemplate(m["eks_launch_template"])) + } + return int(hash(buf.String())) } +func eksLaunchTemplate(v interface{}) string { + var buf bytes.Buffer + if len(v.([]interface{})) > 0 { + m := v.([]interface{})[0].(map[string]interface{}) + + if m["ami_id"] != nil { + buf.WriteString(fmt.Sprintf("%s-", m["ami_id"].(string))) + } + if m["root_volume_type"] != nil { + buf.WriteString(fmt.Sprintf("%s-", m["root_volume_type"].(string))) + } + if m["root_volume_iops"] != nil { + buf.WriteString(fmt.Sprintf("%d-", m["root_volume_iops"].(int))) + } + if m["root_volume_throughput"] != nil { + buf.WriteString(fmt.Sprintf("%d-", m["root_volume_throughput"].(int))) + } + if m["additional_security_groups"] != nil { + for _, sg := range m["additional_security_groups"].(*schema.Set).List() { + buf.WriteString(fmt.Sprintf("%s-", sg.(string))) + } + } + } + + return buf.String() +} + func resourceMachinePoolCoxEdgeHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) diff --git a/spectrocloud/data_source_cluster.go b/spectrocloud/data_source_cluster.go index 52d21d93..f400a556 100644 --- a/spectrocloud/data_source_cluster.go +++ b/spectrocloud/data_source_cluster.go @@ -46,7 +46,7 @@ func dataSourceClusterRead(_ context.Context, d *schema.ResourceData, m interfac } if cluster != nil { d.SetId(cluster.Metadata.UID) - kubeConfig, _ := c.GetClusterKubeConfig(cluster.Metadata.UID) + kubeConfig, _ := c.GetClusterKubeConfig(cluster.Metadata.UID, ClusterContext) if err := d.Set("kube_config", kubeConfig); err != nil { return diag.FromErr(err) } diff --git a/spectrocloud/data_volume_schema_test.go b/spectrocloud/data_volume_schema_test.go index efbb5dec..f72ee6b7 100644 --- a/spectrocloud/data_volume_schema_test.go +++ b/spectrocloud/data_volume_schema_test.go @@ -123,7 +123,6 @@ func TestCreateDataVolumePositive(t *testing.T) { ClusterRbac: nil, ClusterResources: nil, ControlPlaneHealthCheckTimeout: "", - Fips: nil, HostClusterConfig: &models.V1HostClusterConfig{ ClusterEndpoint: &models.V1HostClusterEndpoint{ Config: nil, @@ -223,7 +222,6 @@ func TestCreateDataVolume(t *testing.T) { ClusterRbac: nil, ClusterResources: nil, ControlPlaneHealthCheckTimeout: "", - Fips: nil, HostClusterConfig: &models.V1HostClusterConfig{ ClusterEndpoint: &models.V1HostClusterEndpoint{ Config: nil, diff --git a/spectrocloud/resource_cluster_aks.go b/spectrocloud/resource_cluster_aks.go index 3bbdc4dc..daba7270 100644 --- a/spectrocloud/resource_cluster_aks.go +++ b/spectrocloud/resource_cluster_aks.go @@ -335,7 +335,7 @@ func resourceClusterAksUpdate(ctx context.Context, d *schema.ResourceData, m int for _, mp := range ns { machinePoolResource := mp.(map[string]interface{}) - // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 + // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 if machinePoolResource["name"].(string) != "" { name := machinePoolResource["name"].(string) hash := resourceMachinePoolAksHash(machinePoolResource) diff --git a/spectrocloud/resource_cluster_aws.go b/spectrocloud/resource_cluster_aws.go index c7a5f773..ba854157 100644 --- a/spectrocloud/resource_cluster_aws.go +++ b/spectrocloud/resource_cluster_aws.go @@ -3,6 +3,7 @@ package spectrocloud import ( "context" "log" + "sort" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -156,6 +157,16 @@ func resourceClusterAws() *schema.Resource { Type: schema.TypeString, Required: true, }, + "min": { + Type: schema.TypeInt, + Optional: true, + Description: "Minimum number of nodes in the machine pool. This is used for autoscaling the machine pool.", + }, + "max": { + Type: schema.TypeInt, + Optional: true, + Description: "Maximum number of nodes in the machine pool. This is used for autoscaling the machine pool.", + }, "capacity_type": { Type: schema.TypeString, Default: "on-demand", @@ -198,6 +209,15 @@ func resourceClusterAws() *schema.Resource { Required: true, }, }, + "additional_security_groups": { + Type: schema.TypeSet, + Set: schema.HashString, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "Additional security groups to attach to the instance.", + }, }, }, }, @@ -225,12 +245,12 @@ func resourceClusterAwsCreate(ctx context.Context, d *schema.ResourceData, m int cluster := toAwsCluster(c, d) - uid, err := c.CreateClusterAws(cluster) + ClusterContext := d.Get("context").(string) + uid, err := c.CreateClusterAws(cluster, ClusterContext) if err != nil { return diag.FromErr(err) } - ClusterContext := d.Get("context").(string) diagnostics, isError := waitForClusterCreation(ctx, d, ClusterContext, uid, diags, c, true) if isError { return diagnostics @@ -293,12 +313,16 @@ func flattenMachinePoolConfigsAws(machinePools []*models.V1AwsMachinePoolConfig) SetAdditionalLabelsAndTaints(machinePool.AdditionalLabels, machinePool.Taints, oi) - oi["control_plane"] = machinePool.IsControlPlane + if machinePool.IsControlPlane != nil { + oi["control_plane"] = *machinePool.IsControlPlane + } oi["control_plane_as_worker"] = machinePool.UseControlPlaneAsWorker oi["name"] = machinePool.Name oi["count"] = int(machinePool.Size) flattenUpdateStrategy(machinePool.UpdateStrategy, oi) + oi["min"] = int(machinePool.MinSize) + oi["max"] = int(machinePool.MaxSize) oi["instance_type"] = machinePool.InstanceType if machinePool.CapacityType != nil { oi["capacity_type"] = machinePool.CapacityType @@ -312,9 +336,36 @@ func flattenMachinePoolConfigsAws(machinePools []*models.V1AwsMachinePoolConfig) } else { oi["azs"] = machinePool.Azs } + + if machinePool.AdditionalSecurityGroups != nil && len(machinePool.AdditionalSecurityGroups) > 0 { + additionalSecuritygroup := make([]string, 0) + for _, sg := range machinePool.AdditionalSecurityGroups { + additionalSecuritygroup = append(additionalSecuritygroup, sg.ID) + } + oi["additional_security_groups"] = additionalSecuritygroup + } + ois[i] = oi } + sort.SliceStable(ois, func(i, j int) bool { + var controlPlaneI, controlPlaneJ bool + if ois[i].(map[string]interface{})["control_plane"] != nil { + controlPlaneI = ois[i].(map[string]interface{})["control_plane"].(bool) + } + if ois[j].(map[string]interface{})["control_plane"] != nil { + controlPlaneJ = ois[j].(map[string]interface{})["control_plane"].(bool) + } + + // If both are control planes or both are not, sort by name + if controlPlaneI == controlPlaneJ { + return ois[i].(map[string]interface{})["name"].(string) < ois[j].(map[string]interface{})["name"].(string) + } + + // Otherwise, control planes come first + return controlPlaneI && !controlPlaneJ + }) + return ois } @@ -346,7 +397,7 @@ func resourceClusterAwsUpdate(ctx context.Context, d *schema.ResourceData, m int for _, mp := range ns.List() { machinePoolResource := mp.(map[string]interface{}) - // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 + // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 if machinePoolResource["name"].(string) != "" { name := machinePoolResource["name"].(string) if name != "" { @@ -420,13 +471,25 @@ func toAwsCluster(c *client.V1Client, d *schema.ResourceData) *models.V1SpectroA }, } - //for _, machinePool := range d.Get("machine_pool").([]interface{}) { machinePoolConfigs := make([]*models.V1AwsMachinePoolConfigEntity, 0) for _, machinePool := range d.Get("machine_pool").(*schema.Set).List() { mp := toMachinePoolAws(machinePool, cluster.Spec.CloudConfig.VpcID) machinePoolConfigs = append(machinePoolConfigs, mp) } + sort.SliceStable(machinePoolConfigs, func(i, j int) bool { + controlPlaneI := machinePoolConfigs[i].PoolConfig.IsControlPlane + controlPlaneJ := machinePoolConfigs[j].PoolConfig.IsControlPlane + + // If both are control planes or both are not, sort by name + if controlPlaneI == controlPlaneJ { + return *machinePoolConfigs[i].PoolConfig.Name < *machinePoolConfigs[j].PoolConfig.Name + } + + // Otherwise, control planes come first + return controlPlaneI && !controlPlaneJ + }) + cluster.Spec.Machinepoolconfig = machinePoolConfigs cluster.Spec.ClusterConfig = toClusterConfig(d) @@ -463,6 +526,17 @@ func toMachinePoolAws(machinePool interface{}, vpcId string) *models.V1AwsMachin azs = append(azs, az.(string)) } } + min := int32(m["count"].(int)) + max := int32(m["count"].(int)) + + if m["min"] != nil { + min = int32(m["min"].(int)) + } + + if m["max"] != nil { + max = int32(m["max"].(int)) + } + mp := &models.V1AwsMachinePoolConfigEntity{ CloudConfig: &models.V1AwsMachinePoolCloudConfigEntity{ Azs: azs, @@ -481,6 +555,8 @@ func toMachinePoolAws(machinePool interface{}, vpcId string) *models.V1AwsMachin UpdateStrategy: &models.V1UpdateStrategy{ Type: getUpdateStrategy(m), }, + MinSize: min, + MaxSize: max, UseControlPlaneAsWorker: controlPlaneAsWorker, }, } @@ -495,5 +571,10 @@ func toMachinePoolAws(machinePool interface{}, vpcId string) *models.V1AwsMachin MaxPrice: maxPrice, } } + + if m["additional_security_groups"] != nil { + mp.CloudConfig.AdditionalSecurityGroups = setAdditionalSecurityGroups(m) + } + return mp } diff --git a/spectrocloud/resource_cluster_aws_expand_test.go b/spectrocloud/resource_cluster_aws_expand_test.go new file mode 100644 index 00000000..ea460f14 --- /dev/null +++ b/spectrocloud/resource_cluster_aws_expand_test.go @@ -0,0 +1,79 @@ +package spectrocloud + +import ( + "reflect" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/spectrocloud/hapi/models" + + "github.com/spectrocloud/terraform-provider-spectrocloud/types" +) + +func TestToMachinePoolAws(t *testing.T) { + testCases := []struct { + name string + input map[string]interface{} + vpcId string + expected *models.V1AwsMachinePoolConfigEntity + }{ + { + name: "Test 1: Basic test case", + input: map[string]interface{}{ + "control_plane": false, + "control_plane_as_worker": false, + "name": "testPool", + "count": 3, + "instance_type": "t2.micro", + "min": 1, + "max": 5, + "capacity_type": "on-demand", + "update_strategy": "RollingUpdateScaleOut", + "disk_size_gb": 65, + "azs": schema.NewSet(schema.HashString, []interface{}{"us-west-1a", "us-west-1b"}), + "additional_security_groups": schema.NewSet(schema.HashString, []interface{}{"sg-12345", "sg-67890"}), + }, + vpcId: "vpc-12345", + expected: &models.V1AwsMachinePoolConfigEntity{ + CloudConfig: &models.V1AwsMachinePoolCloudConfigEntity{ + Azs: []string{"us-west-1a", "us-west-1b"}, + InstanceType: types.Ptr("t2.micro"), + CapacityType: types.Ptr("on-demand"), + RootDeviceSize: int64(65), + Subnets: []*models.V1AwsSubnetEntity{}, // assuming no az_subnets provided + AdditionalSecurityGroups: []*models.V1AwsResourceReference{ + { + ID: "sg-12345", + }, + { + ID: "sg-67890", + }, + }, + }, + PoolConfig: &models.V1MachinePoolConfigEntity{ + AdditionalLabels: map[string]string{}, + IsControlPlane: false, + Labels: []string{}, + Name: types.Ptr("testPool"), + Size: types.Ptr(int32(3)), + UpdateStrategy: &models.V1UpdateStrategy{ + Type: "RollingUpdateScaleOut", + }, + MinSize: int32(1), + MaxSize: int32(5), + UseControlPlaneAsWorker: false, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := toMachinePoolAws(tc.input, tc.vpcId) + if !reflect.DeepEqual(result, tc.expected) { + t.Errorf("Unexpected result (-want +got):\n%s", cmp.Diff(tc.expected, result)) + } + }) + } +} diff --git a/spectrocloud/resource_cluster_aws_flatten_test.go b/spectrocloud/resource_cluster_aws_flatten_test.go new file mode 100644 index 00000000..02e7ee4f --- /dev/null +++ b/spectrocloud/resource_cluster_aws_flatten_test.go @@ -0,0 +1,75 @@ +package spectrocloud + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/spectrocloud/hapi/models" + + "github.com/spectrocloud/terraform-provider-spectrocloud/types" +) + +func TestFlattenMachinePoolConfigsAws(t *testing.T) { + testCases := []struct { + name string + input []*models.V1AwsMachinePoolConfig + expected []interface{} + }{ + { + name: "nil input", + input: nil, + expected: []interface{}{}, + }, + { + name: "empty input", + input: []*models.V1AwsMachinePoolConfig{}, + expected: []interface{}{}, + }, + { + name: "non-empty input", + input: []*models.V1AwsMachinePoolConfig{ + { + Name: "pool1", + IsControlPlane: types.Ptr(true), + UseControlPlaneAsWorker: false, + Size: 3, + MinSize: 1, + MaxSize: 5, + InstanceType: "t2.micro", + RootDeviceSize: 8, + SubnetIds: map[string]string{"us-west-2d": "subnet-87654321"}, + AdditionalSecurityGroups: []*models.V1AwsResourceReference{ + { + ID: "sg-1234567890", + }, + }, + }, + }, + expected: []interface{}{ + map[string]interface{}{ + "name": "pool1", + "control_plane": true, + "control_plane_as_worker": false, + "additional_labels": map[string]any{}, + "count": 3, + "min": 1, + "max": 5, + "instance_type": "t2.micro", + "disk_size_gb": 8, + "az_subnets": map[string]string{"us-west-2d": "subnet-87654321"}, + "update_strategy": "RollingUpdateScaleOut", + "additional_security_groups": []string{"sg-1234567890"}, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := flattenMachinePoolConfigsAws(tc.input) + if !cmp.Equal(result, tc.expected) { + t.Errorf("Unexpected result (-want +got):\n%s", cmp.Diff(tc.expected, result)) + } + }) + } +} diff --git a/spectrocloud/resource_cluster_azure.go b/spectrocloud/resource_cluster_azure.go index 2dc7da21..3769fa02 100644 --- a/spectrocloud/resource_cluster_azure.go +++ b/spectrocloud/resource_cluster_azure.go @@ -388,7 +388,7 @@ func resourceClusterAzureUpdate(ctx context.Context, d *schema.ResourceData, m i for _, mp := range ns.List() { machinePoolResource := mp.(map[string]interface{}) - // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 + // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 if machinePoolResource["name"].(string) != "" { name := machinePoolResource["name"].(string) hash := resourceMachinePoolAzureHash(machinePoolResource) diff --git a/spectrocloud/resource_cluster_coxedge.go b/spectrocloud/resource_cluster_coxedge.go index c32eb6e1..3464caae 100644 --- a/spectrocloud/resource_cluster_coxedge.go +++ b/spectrocloud/resource_cluster_coxedge.go @@ -563,7 +563,7 @@ func resourceCoxEdgeClusterUpdate(ctx context.Context, d *schema.ResourceData, m for _, mp := range ns { machinePoolResource := mp.(map[string]interface{}) - // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 + // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 if machinePoolResource["name"].(string) != "" { name := machinePoolResource["name"].(string) hash := resourceMachinePoolCoxEdgeHash(machinePoolResource) diff --git a/spectrocloud/resource_cluster_edge_native.go b/spectrocloud/resource_cluster_edge_native.go index 9a8ddd94..7261fedb 100644 --- a/spectrocloud/resource_cluster_edge_native.go +++ b/spectrocloud/resource_cluster_edge_native.go @@ -344,7 +344,7 @@ func resourceClusterEdgeNativeUpdate(ctx context.Context, d *schema.ResourceData for _, mp := range ns.List() { machinePoolResource := mp.(map[string]interface{}) - // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 + // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 if machinePoolResource["name"].(string) != "" { name := machinePoolResource["name"].(string) if name == "" { diff --git a/spectrocloud/resource_cluster_edge_vsphere.go b/spectrocloud/resource_cluster_edge_vsphere.go index 4a04a878..05895c05 100644 --- a/spectrocloud/resource_cluster_edge_vsphere.go +++ b/spectrocloud/resource_cluster_edge_vsphere.go @@ -400,7 +400,7 @@ func resourceClusterEdgeVsphereUpdate(ctx context.Context, d *schema.ResourceDat for _, mp := range ns { machinePoolResource := mp.(map[string]interface{}) - // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 + // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 if machinePoolResource["name"].(string) != "" { name := machinePoolResource["name"].(string) hash := resourceMachinePoolVsphereHash(machinePoolResource) diff --git a/spectrocloud/resource_cluster_eks.go b/spectrocloud/resource_cluster_eks.go index 72dc43bf..fb288a47 100644 --- a/spectrocloud/resource_cluster_eks.go +++ b/spectrocloud/resource_cluster_eks.go @@ -235,7 +235,7 @@ func resourceClusterEks() *schema.Resource { Type: schema.TypeString, }, }, - "eks_launch_template": schemas.EksLaunchTemplate(), + "eks_launch_template": schemas.AwsLaunchTemplate(), }, }, }, @@ -476,6 +476,13 @@ func flattenEksLaunchTemplate(launchTemplate *models.V1AwsLaunchTemplate) []inte lt["root_volume_iops"] = launchTemplate.RootVolume.Iops lt["root_volume_throughput"] = launchTemplate.RootVolume.Throughput } + if launchTemplate.AdditionalSecurityGroups != nil && len(launchTemplate.AdditionalSecurityGroups) > 0 { + var additionalSecurityGroups []string + for _, sg := range launchTemplate.AdditionalSecurityGroups { + additionalSecurityGroups = append(additionalSecurityGroups, sg.ID) + } + lt["additional_security_groups"] = additionalSecurityGroups + } return []interface{}{lt} } @@ -558,7 +565,7 @@ func resourceClusterEksUpdate(ctx context.Context, d *schema.ResourceData, m int for _, mp := range ns { machinePoolResource := mp.(map[string]interface{}) - // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 + // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 if machinePoolResource["name"].(string) != "" { name := machinePoolResource["name"].(string) hash := resourceMachinePoolEksHash(machinePoolResource) @@ -822,12 +829,12 @@ func toMachinePoolEks(machinePool interface{}) *models.V1EksMachinePoolConfigEnt } } - mp.CloudConfig.AwsLaunchTemplate = setAwsLaunchTemplate(m) + mp.CloudConfig.AwsLaunchTemplate = setEksLaunchTemplate(m) return mp } -func setAwsLaunchTemplate(machinePool map[string]interface{}) *models.V1AwsLaunchTemplate { +func setEksLaunchTemplate(machinePool map[string]interface{}) *models.V1AwsLaunchTemplate { var launchTemplate *models.V1AwsLaunchTemplate if machinePool["eks_launch_template"] != nil { @@ -838,34 +845,65 @@ func setAwsLaunchTemplate(machinePool map[string]interface{}) *models.V1AwsLaunc eksLaunchTemplate := eksLaunchTemplateList[0].(map[string]interface{}) - if eksLaunchTemplate["ami_id"] != nil || eksLaunchTemplate["root_volume_type"] != nil || eksLaunchTemplate["root_volume_iops"] != nil || eksLaunchTemplate["root_volume_throughput"] != nil { - launchTemplate = &models.V1AwsLaunchTemplate{ - RootVolume: &models.V1AwsRootVolume{}, - } + keys := []string{"ami_id", "root_volume_type", "root_volume_iops", "root_volume_throughput", "additional_security_groups"} - if eksLaunchTemplate["ami_id"] != nil { - launchTemplate.Ami = &models.V1AwsAmiReference{ - ID: eksLaunchTemplate["ami_id"].(string), - } - } + // if at least one key is present continue function body, otherwise return launchTemplate + if hasNoneOfKeys(eksLaunchTemplate, keys) { + return launchTemplate + } - if eksLaunchTemplate["root_volume_type"] != nil { - launchTemplate.RootVolume.Type = eksLaunchTemplate["root_volume_type"].(string) - } + launchTemplate = &models.V1AwsLaunchTemplate{ + RootVolume: &models.V1AwsRootVolume{}, + } - if eksLaunchTemplate["root_volume_iops"] != nil { - launchTemplate.RootVolume.Iops = int64(eksLaunchTemplate["root_volume_iops"].(int)) + if eksLaunchTemplate["ami_id"] != nil { + launchTemplate.Ami = &models.V1AwsAmiReference{ + ID: eksLaunchTemplate["ami_id"].(string), } + } - if eksLaunchTemplate["root_volume_throughput"] != nil { - launchTemplate.RootVolume.Throughput = int64(eksLaunchTemplate["root_volume_throughput"].(int)) - } + if eksLaunchTemplate["root_volume_type"] != nil { + launchTemplate.RootVolume.Type = eksLaunchTemplate["root_volume_type"].(string) } + + if eksLaunchTemplate["root_volume_iops"] != nil { + launchTemplate.RootVolume.Iops = int64(eksLaunchTemplate["root_volume_iops"].(int)) + } + + if eksLaunchTemplate["root_volume_throughput"] != nil { + launchTemplate.RootVolume.Throughput = int64(eksLaunchTemplate["root_volume_throughput"].(int)) + } + + launchTemplate.AdditionalSecurityGroups = setAdditionalSecurityGroups(eksLaunchTemplate) } return launchTemplate } +func setAdditionalSecurityGroups(eksLaunchTemplate map[string]interface{}) []*models.V1AwsResourceReference { + if eksLaunchTemplate["additional_security_groups"] != nil { + securityGroups := expandStringList(eksLaunchTemplate["additional_security_groups"].(*schema.Set).List()) + additionalSecurityGroups := make([]*models.V1AwsResourceReference, 0) + for _, securityGroup := range securityGroups { + additionalSecurityGroups = append(additionalSecurityGroups, &models.V1AwsResourceReference{ + ID: securityGroup, + }) + } + return additionalSecurityGroups + } + + return nil +} + +func hasNoneOfKeys(m map[string]interface{}, keys []string) bool { + for _, key := range keys { + if m[key] != nil { + return false + } + } + return true +} + func toFargateProfileEks(fargateProfile interface{}) *models.V1FargateProfile { m := fargateProfile.(map[string]interface{}) diff --git a/spectrocloud/resource_cluster_eks_expand_test.go b/spectrocloud/resource_cluster_eks_expand_test.go index e56850a0..f023b993 100644 --- a/spectrocloud/resource_cluster_eks_expand_test.go +++ b/spectrocloud/resource_cluster_eks_expand_test.go @@ -53,7 +53,7 @@ func TestSetAwsLaunchTemplate(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - result := setAwsLaunchTemplate(tc.input) + result := setEksLaunchTemplate(tc.input) if !reflect.DeepEqual(result, tc.expected) { t.Errorf("Unexpected result (-want +got):\n%s", cmp.Diff(tc.expected, result)) } diff --git a/spectrocloud/resource_cluster_eks_flatten_test.go b/spectrocloud/resource_cluster_eks_flatten_test.go index f0ad954d..90d0bea1 100644 --- a/spectrocloud/resource_cluster_eks_flatten_test.go +++ b/spectrocloud/resource_cluster_eks_flatten_test.go @@ -31,6 +31,12 @@ func TestFlattenEksLaunchTemplate(t *testing.T) { Iops: 100, Throughput: 125, }, + // add security group "sg-12345678" + AdditionalSecurityGroups: []*models.V1AwsResourceReference{ + { + ID: "sg-12345678", + }, + }, }, expected: []interface{}{ map[string]interface{}{ @@ -38,6 +44,9 @@ func TestFlattenEksLaunchTemplate(t *testing.T) { "root_volume_type": "gp2", "root_volume_iops": int64(100), "root_volume_throughput": int64(125), + "additional_security_groups": []string{ + "sg-12345678", + }, }, }, }, diff --git a/spectrocloud/resource_cluster_gcp.go b/spectrocloud/resource_cluster_gcp.go index 8ecf9b69..1a32dad3 100644 --- a/spectrocloud/resource_cluster_gcp.go +++ b/spectrocloud/resource_cluster_gcp.go @@ -317,7 +317,7 @@ func resourceClusterGcpUpdate(ctx context.Context, d *schema.ResourceData, m int for _, mp := range ns.List() { machinePoolResource := mp.(map[string]interface{}) - // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 + // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 if machinePoolResource["name"].(string) != "" { name := machinePoolResource["name"].(string) hash := resourceMachinePoolGcpHash(machinePoolResource) diff --git a/spectrocloud/resource_cluster_libvirt.go b/spectrocloud/resource_cluster_libvirt.go index 085cf623..a51ce065 100644 --- a/spectrocloud/resource_cluster_libvirt.go +++ b/spectrocloud/resource_cluster_libvirt.go @@ -514,7 +514,7 @@ func resourceClusterVirtUpdate(ctx context.Context, d *schema.ResourceData, m in for _, mp := range ns { machinePoolResource := mp.(map[string]interface{}) - // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 + // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 if machinePoolResource["name"].(string) != "" { name := machinePoolResource["name"].(string) if name == "" { diff --git a/spectrocloud/resource_cluster_openstack.go b/spectrocloud/resource_cluster_openstack.go index 656923ce..4fd25692 100644 --- a/spectrocloud/resource_cluster_openstack.go +++ b/spectrocloud/resource_cluster_openstack.go @@ -393,7 +393,7 @@ func resourceClusterOpenStackUpdate(ctx context.Context, d *schema.ResourceData, for _, mp := range ns { machinePoolResource := mp.(map[string]interface{}) - // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 + // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 if machinePoolResource["name"].(string) != "" { name := machinePoolResource["name"].(string) hash := resourceMachinePoolOpenStackHash(machinePoolResource) diff --git a/spectrocloud/resource_cluster_tke.go b/spectrocloud/resource_cluster_tke.go index 0eff7acd..5ff0228e 100644 --- a/spectrocloud/resource_cluster_tke.go +++ b/spectrocloud/resource_cluster_tke.go @@ -338,7 +338,7 @@ func resourceClusterTkeUpdate(ctx context.Context, d *schema.ResourceData, m int for _, mp := range ns { machinePoolResource := mp.(map[string]interface{}) - // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 + // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 if machinePoolResource["name"].(string) != "" { name := machinePoolResource["name"].(string) hash := resourceMachinePoolTkeHash(machinePoolResource) diff --git a/spectrocloud/resource_cluster_virtual.go b/spectrocloud/resource_cluster_virtual.go index 1996cbdc..49641f7c 100644 --- a/spectrocloud/resource_cluster_virtual.go +++ b/spectrocloud/resource_cluster_virtual.go @@ -257,7 +257,7 @@ func resourceClusterVirtualUpdate(ctx context.Context, d *schema.ResourceData, m for _, mp := range ns.List() { machinePoolResource := mp.(map[string]interface{}) - // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 + // since known issue in TF SDK: https://github.com/hashicorp/terraform-plugin-sdk/issues/588 if machinePoolResource["name"].(string) != "" { name := machinePoolResource["name"].(string) hash := resourceMachinePoolVirtualHash(machinePoolResource) diff --git a/spectrocloud/resource_cluster_vsphere.go b/spectrocloud/resource_cluster_vsphere.go index 885cd3f5..9dd932e1 100644 --- a/spectrocloud/resource_cluster_vsphere.go +++ b/spectrocloud/resource_cluster_vsphere.go @@ -294,12 +294,12 @@ func resourceClusterVsphereCreate(ctx context.Context, d *schema.ResourceData, m cluster := toVsphereCluster(c, d) - uid, err := c.CreateClusterVsphere(cluster) + ClusterContext := d.Get("context").(string) + uid, err := c.CreateClusterVsphere(cluster, ClusterContext) if err != nil { return diag.FromErr(err) } - ClusterContext := d.Get("context").(string) diagnostics, isError := waitForClusterCreation(ctx, d, ClusterContext, uid, diags, c, true) if isError { return diagnostics diff --git a/spectrocloud/resource_cluster_vsphere_test.go b/spectrocloud/resource_cluster_vsphere_test.go index 9ad5c915..f13a8663 100644 --- a/spectrocloud/resource_cluster_vsphere_test.go +++ b/spectrocloud/resource_cluster_vsphere_test.go @@ -286,7 +286,6 @@ func TestResourceClusterVsphereRead(t *testing.T) { ClusterRbac: nil, ClusterResources: nil, ControlPlaneHealthCheckTimeout: "", - Fips: nil, HostClusterConfig: &models.V1HostClusterConfig{ ClusterEndpoint: &models.V1HostClusterEndpoint{ Config: nil, diff --git a/spectrocloud/schemas/eks_template.go b/spectrocloud/schemas/eks_template.go index c495e95d..14d4fae6 100644 --- a/spectrocloud/schemas/eks_template.go +++ b/spectrocloud/schemas/eks_template.go @@ -2,7 +2,7 @@ package schemas import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -func EksLaunchTemplate() *schema.Schema { +func AwsLaunchTemplate() *schema.Schema { return &schema.Schema{ Type: schema.TypeList, Optional: true, @@ -29,6 +29,15 @@ func EksLaunchTemplate() *schema.Schema { Optional: true, Description: "The throughput of the root volume in MiB/s.", }, + "additional_security_groups": { + Type: schema.TypeSet, + Set: schema.HashString, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Optional: true, + Description: "Additional security groups to attach to the instance.", + }, }, }, }