From 06e49804bc643ce7ef861172b6b23cc35cf1487c Mon Sep 17 00:00:00 2001 From: Jared Burns Date: Thu, 22 Aug 2024 19:24:45 -0400 Subject: [PATCH] fix: missing vds info with r/cluster import Updating Cluster import to solve issues where vds is blank on data source. Signed-off-by: Jared Burns --- go.mod | 5 +- internal/cluster/cluster_operations.go | 100 ++++++++++++------ internal/cluster/host_spec_subresource.go | 39 +++++-- .../nioc_bandwidth_allocation_subresource.go | 10 +- internal/network/vds_subresource.go | 31 +++--- internal/provider/data_source_cluster_test.go | 1 + internal/provider/resource_cluster_test.go | 4 + 7 files changed, 136 insertions(+), 54 deletions(-) diff --git a/go.mod b/go.mod index 477780d..54e40ff 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,8 @@ module github.com/vmware/terraform-provider-vcf -go 1.22.6 -toolchain go1.22.9 +go 1.22.7 + +toolchain go1.23.2 require ( github.com/hashicorp/terraform-plugin-docs v0.20.0 diff --git a/internal/cluster/cluster_operations.go b/internal/cluster/cluster_operations.go index cce8743..2688b3f 100644 --- a/internal/cluster/cluster_operations.go +++ b/internal/cluster/cluster_operations.go @@ -12,9 +12,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/vmware/terraform-provider-vcf/internal/api_client" "github.com/vmware/vcf-sdk-go/vcf" + "github.com/vmware/terraform-provider-vcf/internal/api_client" "github.com/vmware/terraform-provider-vcf/internal/datastores" "github.com/vmware/terraform-provider-vcf/internal/network" "github.com/vmware/terraform-provider-vcf/internal/resource_utils" @@ -370,19 +370,19 @@ func FlattenCluster(ctx context.Context, clusterObj *vcf.Cluster, apiClient *vcf result["is_default"] = clusterObj.IsDefault result["is_stretched"] = clusterObj.IsStretched - if clusterObj.VdsSpecs != nil { - flattenedVdsSpecs := getFlattenedVdsSpecsForRefs(*clusterObj.VdsSpecs) - result["vds"] = flattenedVdsSpecs + flattenedVdsSpecs, err := getFlattenedVdsSpecsForRefs(ctx, *clusterObj.Id, apiClient) + if err != nil { + return nil, fmt.Errorf("failed to get flattened VDS specs: %w", err) } + result["vds"] = flattenedVdsSpecs - if clusterObj.Hosts != nil { - flattenedHostSpecs, err := getFlattenedHostSpecsForRefs(ctx, *clusterObj.Hosts, apiClient) - if err != nil { - return nil, err - } - result["host"] = flattenedHostSpecs + flattenedHostSpecs, err := getFlattenedHostSpecsForRefs(ctx, *clusterObj.Hosts, apiClient) + if err != nil { + return nil, err } + result["host"] = flattenedHostSpecs + return &result, nil } @@ -404,18 +404,17 @@ func ImportCluster(ctx context.Context, data *schema.ResourceData, apiClient *vc _ = data.Set("primary_datastore_type", clusterObj.PrimaryDatastoreType) _ = data.Set("is_default", clusterObj.IsDefault) _ = data.Set("is_stretched", clusterObj.IsStretched) - if clusterObj.VdsSpecs != nil { - flattenedVdsSpecs := getFlattenedVdsSpecsForRefs(*clusterObj.VdsSpecs) - _ = data.Set("vds", flattenedVdsSpecs) + flattenedVdsSpecs, err := getFlattenedVdsSpecsForRefs(ctx, *clusterObj.Id, apiClient) + if err != nil { + return nil, err } + _ = data.Set("vds", flattenedVdsSpecs) - if clusterObj.Hosts != nil { - flattenedHostSpecs, err := getFlattenedHostSpecsForRefs(ctx, *clusterObj.Hosts, apiClient) - if err != nil { - return nil, err - } - _ = data.Set("host", flattenedHostSpecs) + flattenedHostSpecs, err := getFlattenedHostSpecsForRefs(ctx, *clusterObj.Hosts, apiClient) + if err != nil { + return nil, err } + _ = data.Set("host", flattenedHostSpecs) // get all domains and find our cluster to set the "domain_id" attribute, because // cluster API doesn't provide parent domain ID. @@ -462,20 +461,61 @@ func getFlattenedHostSpecsForRefs(ctx context.Context, hostRefs []vcf.HostRefere api_client.LogError(vcfErr) return nil, errors.New(*vcfErr.Message) } - flattenedHostSpecs = append(flattenedHostSpecs, *FlattenHost(*hostObj)) + flattenedHostSpecs = append(flattenedHostSpecs, FlattenHost(*hostObj)) } return flattenedHostSpecs, nil } -func getFlattenedVdsSpecsForRefs(vdsSpecs []vcf.VdsSpec) []map[string]interface{} { - flattenedVdsSpecs := *new([]map[string]interface{}) - // Since backend API returns objects in random order sort VDSSpec list to ensure - // import is reproducible - sort.SliceStable(vdsSpecs, func(i, j int) bool { - return vdsSpecs[i].Name < vdsSpecs[j].Name - }) - for _, vdsSpec := range vdsSpecs { - flattenedVdsSpecs = append(flattenedVdsSpecs, network.FlattenVdsSpec(vdsSpec)) +func getFlattenedVdsSpecsForRefs(ctx context.Context, clusterId string, apiClient *vcf.ClientWithResponses) ([]map[string]interface{}, error) { + vdsResponse, err := apiClient.GetVdsesWithResponse(ctx, clusterId) + if err != nil { + return nil, err } - return flattenedVdsSpecs + + if vdsResponse.JSON200 == nil { + return nil, fmt.Errorf("vdsResponse.JSON200 is nil") + } + + flattenedVdsSpecs := make([]map[string]interface{}, len(*vdsResponse.JSON200)) + for i, vds := range *vdsResponse.JSON200 { + portGroups := vds.PortGroups + portGroupSpecs := make([]vcf.PortgroupSpec, len(*portGroups)) + for j, pg := range *portGroups { + portGroupSpecs[j] = vcf.PortgroupSpec{ + Name: pg.Name, + TransportType: pg.TransportType, + ActiveUplinks: pg.ActiveUplinks, + } + } + + niocBandwidthAllocations := vds.NiocBandwidthAllocations + niocSpecs := make([]vcf.NiocBandwidthAllocationSpec, len(*niocBandwidthAllocations)) + for k, nioc := range *niocBandwidthAllocations { + niocType := "" + if nioc.Type != nil { + niocType = *nioc.Type + } + + niocSpecs[k] = vcf.NiocBandwidthAllocationSpec{ + Type: niocType, + NiocTrafficResourceAllocation: vcf.NiocTrafficResourceAllocation{ + SharesInfo: &vcf.SharesInfo{ + Shares: nioc.NiocTrafficResourceAllocation.SharesInfo.Shares, + Level: nioc.NiocTrafficResourceAllocation.SharesInfo.Level, + }, + }, + } + } + + vdsSpec := vcf.VdsSpec{ + Name: vds.Name, + IsUsedByNsxt: vds.IsUsedByNsxt, + PortGroupSpecs: &portGroupSpecs, + NiocBandwidthAllocationSpecs: &niocSpecs, + } + + flattenedVdsSpecs[i] = network.FlattenVdsSpec(&vdsSpec) + } + + return flattenedVdsSpecs, nil } diff --git a/internal/cluster/host_spec_subresource.go b/internal/cluster/host_spec_subresource.go index f49d9c9..14757df 100644 --- a/internal/cluster/host_spec_subresource.go +++ b/internal/cluster/host_spec_subresource.go @@ -82,22 +82,49 @@ func HostSpecSchema() *schema.Resource { Type: schema.TypeList, Optional: true, Description: "vmnic configuration for the ESXi host", - Elem: network.VMNicSchema(), + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the physical NIC", + }, + "mac_address": { + Type: schema.TypeString, + Required: true, + Description: "MAC address of the physical NIC", + }, + }, + }, }, }, } } -func FlattenHost(host vcf.Host) *map[string]interface{} { +func FlattenHost(host vcf.Host) map[string]interface{} { result := make(map[string]interface{}) result["id"] = host.Id result["host_name"] = host.Fqdn - ipAddresses := *host.IpAddresses - if len(ipAddresses) > 0 { - result["ip_address"] = ipAddresses[0].IpAddress + + if host.IpAddresses != nil { + ipAddresses := *host.IpAddresses + if len(ipAddresses) > 0 { + result["ip_address"] = ipAddresses[0].IpAddress + } + } + + if host.PhysicalNics != nil { + var physicalNics []map[string]interface{} + for _, nic := range *host.PhysicalNics { + nicMap := make(map[string]interface{}) + nicMap["name"] = nic.DeviceName + nicMap["mac_address"] = nic.MacAddress + physicalNics = append(physicalNics, nicMap) + } + result["vmnic"] = physicalNics } - return &result + return result } func TryConvertToHostSpec(object map[string]interface{}) (*vcf.HostSpec, error) { diff --git a/internal/network/nioc_bandwidth_allocation_subresource.go b/internal/network/nioc_bandwidth_allocation_subresource.go index 70c92b1..450871a 100644 --- a/internal/network/nioc_bandwidth_allocation_subresource.go +++ b/internal/network/nioc_bandwidth_allocation_subresource.go @@ -109,11 +109,13 @@ func tryConvertToNiocBandwidthAllocationSpec(object map[string]interface{}) (*vc func flattenNiocBandwidthAllocationSpec(spec vcf.NiocBandwidthAllocationSpec) map[string]interface{} { result := make(map[string]interface{}) + result["type"] = spec.Type - result["limit"] = *spec.NiocTrafficResourceAllocation.Limit - result["reservation"] = *spec.NiocTrafficResourceAllocation.Reservation - result["shares"] = spec.NiocTrafficResourceAllocation.SharesInfo.Shares - result["shares_level"] = spec.NiocTrafficResourceAllocation.SharesInfo.Level + + if spec.NiocTrafficResourceAllocation.SharesInfo != nil { + result["shares"] = spec.NiocTrafficResourceAllocation.SharesInfo.Shares + result["shares_level"] = spec.NiocTrafficResourceAllocation.SharesInfo.Level + } return result } diff --git a/internal/network/vds_subresource.go b/internal/network/vds_subresource.go index b231a15..1dee113 100644 --- a/internal/network/vds_subresource.go +++ b/internal/network/vds_subresource.go @@ -9,9 +9,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - utils "github.com/vmware/terraform-provider-vcf/internal/resource_utils" "github.com/vmware/vcf-sdk-go/vcf" + utils "github.com/vmware/terraform-provider-vcf/internal/resource_utils" validationutils "github.com/vmware/terraform-provider-vcf/internal/validation" ) @@ -98,23 +98,30 @@ func TryConvertToVdsSpec(object map[string]interface{}) (*vcf.VdsSpec, error) { return result, nil } -func FlattenVdsSpec(vdsSpec vcf.VdsSpec) map[string]interface{} { +func FlattenVdsSpec(vdsSpec *vcf.VdsSpec) map[string]interface{} { result := make(map[string]interface{}) + if vdsSpec == nil { + return result + } result["name"] = vdsSpec.Name result["is_used_by_nsx"] = vdsSpec.IsUsedByNsxt - flattenedNiocBandwidthAllocationSpecs := *new([]map[string]interface{}) - for _, niocBandwidthAllocationSpec := range *vdsSpec.NiocBandwidthAllocationSpecs { - flattenedNiocBandwidthAllocationSpecs = append(flattenedNiocBandwidthAllocationSpecs, - flattenNiocBandwidthAllocationSpec(niocBandwidthAllocationSpec)) + + if vdsSpec.NiocBandwidthAllocationSpecs != nil { + flattenedNiocBandwidthAllocationSpecs := make([]map[string]interface{}, 0, len(*vdsSpec.NiocBandwidthAllocationSpecs)) + for _, niocBandwidthAllocationSpec := range *vdsSpec.NiocBandwidthAllocationSpecs { + flattenedSpec := flattenNiocBandwidthAllocationSpec(niocBandwidthAllocationSpec) + flattenedNiocBandwidthAllocationSpecs = append(flattenedNiocBandwidthAllocationSpecs, flattenedSpec) + } + result["nioc_bandwidth_allocations"] = flattenedNiocBandwidthAllocationSpecs } - result["nioc_bandwidth_allocations"] = flattenedNiocBandwidthAllocationSpecs - flattenedPortgroupSpecs := *new([]map[string]interface{}) - for _, portgroupSpec := range *vdsSpec.PortGroupSpecs { - flattenedPortgroupSpecs = append(flattenedPortgroupSpecs, - flattenPortgroupSpec(portgroupSpec)) + if vdsSpec.PortGroupSpecs != nil { + flattenedPortgroupSpecs := make([]map[string]interface{}, 0, len(*vdsSpec.PortGroupSpecs)) + for _, portgroupSpec := range *vdsSpec.PortGroupSpecs { + flattenedPortgroupSpecs = append(flattenedPortgroupSpecs, flattenPortgroupSpec(portgroupSpec)) + } + result["portgroup"] = flattenedPortgroupSpecs } - result["portgroup"] = flattenedPortgroupSpecs return result } diff --git a/internal/provider/data_source_cluster_test.go b/internal/provider/data_source_cluster_test.go index 4a764e4..731f0a4 100644 --- a/internal/provider/data_source_cluster_test.go +++ b/internal/provider/data_source_cluster_test.go @@ -29,6 +29,7 @@ func TestAccDataSourceVcfCluster(t *testing.T) { resource.TestCheckResourceAttrSet("data.vcf_cluster.cluster1", "primary_datastore_type"), resource.TestCheckResourceAttrSet("data.vcf_cluster.cluster1", "is_default"), resource.TestCheckResourceAttrSet("data.vcf_cluster.cluster1", "is_stretched"), + resource.TestCheckResourceAttrSet("data.vcf_cluster.cluster1", "vds"), resource.TestCheckResourceAttrSet("data.vcf_cluster.cluster1", "host.0.id"), resource.TestCheckResourceAttrSet("data.vcf_cluster.cluster1", "host.0.host_name"), resource.TestCheckResourceAttrSet("data.vcf_cluster.cluster1", "host.0.ip_address"), diff --git a/internal/provider/resource_cluster_test.go b/internal/provider/resource_cluster_test.go index 34681e6..959fd9b 100644 --- a/internal/provider/resource_cluster_test.go +++ b/internal/provider/resource_cluster_test.go @@ -45,6 +45,7 @@ func TestAccResourceVcfClusterCreate(t *testing.T) { resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "primary_datastore_type"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "is_default"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "is_stretched"), + resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "vds"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "host.0.id"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "host.1.id"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "host.2.id"), @@ -116,6 +117,7 @@ func TestAccResourceVcfClusterFull(t *testing.T) { resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "primary_datastore_type"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "is_default"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "is_stretched"), + resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "vds"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "host.0.id"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "host.1.id"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "host.2.id"), @@ -151,6 +153,7 @@ func TestAccResourceVcfClusterFull(t *testing.T) { resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "primary_datastore_type"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "is_default"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "is_stretched"), + resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "vds"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "host.0.id"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "host.1.id"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "host.2.id"), @@ -180,6 +183,7 @@ func TestAccResourceVcfClusterFull(t *testing.T) { resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "primary_datastore_type"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "is_default"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "is_stretched"), + resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "vds"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "host.0.id"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "host.1.id"), resource.TestCheckResourceAttrSet("vcf_cluster.cluster1", "host.2.id"),