Skip to content

Commit

Permalink
Parse Terraform data and convert it to Domain Specs
Browse files Browse the repository at this point in the history
Preliminary implementation of Domain Create, Read, Destroy with Create Acceptance E2E test. Update code is not implemented (current code is just a placeholder).

Cluster resource is just a placeholder, no VCF APIs attached to the Terraform lifecycle.

TODO: E2E test featuring the Domain Update scenario.
TODO: extract more helper methods to reduce complexity in "TryConvertTo..." methods.

Testing done:
make lint
make build
make test

Signed-off-by: Dimitar Proynov <[email protected]>
  • Loading branch information
Dimitar Proynov committed Jun 23, 2023
1 parent a1c6439 commit fc831bb
Show file tree
Hide file tree
Showing 25 changed files with 1,656 additions and 265 deletions.
76 changes: 75 additions & 1 deletion internal/cluster/commissioned_host_subresource.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
package cluster

import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/vmware/terraform-provider-vcf/internal/network"
validation_utils "github.com/vmware/terraform-provider-vcf/internal/validation"
"github.com/vmware/vcf-sdk-go/models"
)

// CommissionedHostSchema this helper function extracts the Host
Expand Down Expand Up @@ -73,12 +75,84 @@ func CommissionedHostSchema() *schema.Resource {
Description: "SSH thumbprint(fingerprint) of the vSphere host. Note:This field will be mandatory in future releases.",
ValidateFunc: validation.NoZeroValues,
},
"vmnics": {
"vmnic": {
Type: schema.TypeList,
Optional: true,
Description: "Contains vmnic configurations for vSphere host",
Elem: network.VMNicSchema(),
},
"az_name": {
Type: schema.TypeString,
Computed: true,
Description: "Fault domain name of the host",
},
},
}
}

func FlattenHost(host *models.HostReference) *map[string]interface{} {
result := make(map[string]interface{})
if host == nil {
return &result
}
result["id"] = host.ID
result["host_name"] = host.Fqdn
result["ip_address"] = host.IPAddress
result["az_name"] = host.AzName

return &result
}

func TryConvertToHostSpec(object map[string]interface{}) (*models.HostSpec, error) {
result := &models.HostSpec{}
if object == nil {
return nil, fmt.Errorf("cannot conver to HostSpec, object is nil")
}
id := object["id"].(string)
if len(id) == 0 {
return nil, fmt.Errorf("cannot conver to HostSpec, id is required")
}
result.ID = &id
if hostName, ok := object["host_name"]; ok && !validation_utils.IsEmpty(hostName) {
result.HostName = hostName.(string)
}
if availabilityZoneName, ok := object["availability_zone_name"]; ok && !validation_utils.IsEmpty(availabilityZoneName) {
result.AzName = availabilityZoneName.(string)
}
if ipAddress, ok := object["ip_address"]; ok && !validation_utils.IsEmpty(ipAddress) {
result.IPAddress = ipAddress.(string)
}
if licenseKey, ok := object["license_key"]; ok && !validation_utils.IsEmpty(licenseKey) {
result.LicenseKey = licenseKey.(string)
}
if userName, ok := object["username"]; ok && !validation_utils.IsEmpty(userName) {
result.Username = userName.(string)
}
if password, ok := object["password"]; ok && !validation_utils.IsEmpty(password) {
result.Password = password.(string)
}
if serialNumber, ok := object["serial_number"]; ok && !validation_utils.IsEmpty(serialNumber) {
result.SerialNumber = serialNumber.(string)
}
if sshThumbprint, ok := object["ssh_thumbprint"]; ok && !validation_utils.IsEmpty(sshThumbprint) {
result.SSHThumbprint = sshThumbprint.(string)
}
if vmNicsRaw, ok := object["vmnic"]; ok && !validation_utils.IsEmpty(vmNicsRaw) {
vmNicsList := vmNicsRaw.([]interface{})
if len(vmNicsList) > 0 {
result.HostNetworkSpec = &models.HostNetworkSpec{}
result.HostNetworkSpec.VMNics = []*models.VMNic{}
for _, vmNicListEntry := range vmNicsList {
vmNic, err := network.TryConvertToVmNic(vmNicListEntry.(map[string]interface{}))
if err != nil {
return nil, err
}
result.HostNetworkSpec.VMNics = append(result.HostNetworkSpec.VMNics, vmNic)
}
} else {
return nil, fmt.Errorf("cannot convert to ClusterSpec, hosts list is empty")
}
}

return result, nil
}
60 changes: 48 additions & 12 deletions internal/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,57 @@ package constants

const (

// VCF_TEST_URL URL of a VCF instance, used for Acceptance tests.
VCF_TEST_URL = "VCF_TEST_URL"
// VCF_TEST_USERNAME username of SSO user, used for Acceptance tests.
VCF_TEST_USERNAME = "VCF_TEST_USERNAME"
// VCF_TEST_PASSWORD passowrd of SSO user, used for Acceptance tests.
VCF_TEST_PASSWORD = "VCF_TEST_PASSWORD"
// VcfTestUrl URL of a VCF instance, used for Acceptance tests.
VcfTestUrl = "VCF_TEST_URL"
// VcfTestUsername username of SSO user, used for Acceptance tests.
VcfTestUsername = "VCF_TEST_USERNAME"
// VcfTestPassword passowrd of SSO user, used for Acceptance tests.
VcfTestPassword = "VCF_TEST_PASSWORD"

// VCF_TEST_COMMISSIONED_HOST_FQDN the FQDN of an ESXi host, that has not been commissioned
// VcfTestHost1Fqdn the FQDN of the first ESXi host, that has not been commissioned
// with the SDDC Manager.
VCF_TEST_COMMISSIONED_HOST_FQDN = "VCF_TEST_COMMISSIONED_HOST_FQDN"
VcfTestHost1Fqdn = "VCF_TEST_HOST1_FQDN"

// VCF_TEST_COMMISSIONED_HOST_PASS the SSH pass of an ESXi host, that has not been commissioned
// VcfTestHost1Pass the SSH pass of the first ESXi host, that has not been commissioned
// with the SDDC Manager.
VCF_TEST_COMMISSIONED_HOST_PASS = "VCF_TEST_COMMISSIONED_HOST_PASS"
VcfTestHost1Pass = "VCF_TEST_HOST1_PASS"

// VCF_TEST_NETWORK_POOL_NAME used in vcf_network_pool Acceptance tests.
VCF_TEST_NETWORK_POOL_NAME = "terraform-test-pool"
// VcfTestHost2Fqdn the FQDN of the second ESXi host, that has not been commissioned
// with the SDDC Manager.
VcfTestHost2Fqdn = "VCF_TEST_HOST2_FQDN"

// VcfTestHost2Pass the SSH pass of the second ESXi host, that has not been commissioned
// with the SDDC Manager.
VcfTestHost2Pass = "VCF_TEST_HOST2_PASS"

// VcfTestHost3Fqdn the FQDN of the third ESXi host, that has not been commissioned
// with the SDDC Manager.
VcfTestHost3Fqdn = "VCF_TEST_HOST3_FQDN"

// VcfTestHost3Pass the SSH pass of the third ESXi host, that has not been commissioned
// with the SDDC Manager.
VcfTestHost3Pass = "VCF_TEST_HOST3_PASS"

// VcfTestHost4Fqdn the FQDN of the forth ESXi host, that has not been commissioned
// with the SDDC Manager.
VcfTestHost4Fqdn = "VCF_TEST_HOST4_FQDN"

// VcfTestHost4Pass the SSH pass of the forth ESXi host, that has not been commissioned
// with the SDDC Manager.
VcfTestHost4Pass = "VCF_TEST_HOST4_PASS"

// VcfTestNsxtLicenseKey license key for NSXT required for domain and cluster acceptance tests.
VcfTestNsxtLicenseKey = "VCF_TEST_NSXT_LICENSE_KEY"

// VcfTestEsxiLicenseKey license key for NSXT required for domain and cluster acceptance tests.
VcfTestEsxiLicenseKey = "VCF_TEST_ESXI_LICENSE_KEY"

// VcfTestVsanLicenseKey license key for VSAN required for domain and cluster acceptance tests.
VcfTestVsanLicenseKey = "VCF_TEST_VSAN_LICENSE_KEY"

// VcfTestGatewayIp the gateway IP required for domain and cluster acceptance tests.
VcfTestGatewayIp = "VCF_TEST_GATEWAY_IP"

// VcfTestNetworkPoolName used in vcf_network_pool Acceptance tests.
VcfTestNetworkPoolName = "terraform-test-pool"
)
44 changes: 44 additions & 0 deletions internal/datastores/nfs_datastore_subresource.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
package datastores

import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
validation_utils "github.com/vmware/terraform-provider-vcf/internal/validation"
"github.com/vmware/vcf-sdk-go/models"
)

// NfsDatastoreSchema this helper function extracts the NFS Datastore schema, so that
Expand Down Expand Up @@ -45,3 +48,44 @@ func NfsDatastoreSchema() *schema.Resource {
},
}
}

func TryConvertToNfsDatastoreSpec(object map[string]interface{}) (*models.NfsDatastoreSpec, error) {
if object == nil {
return nil, fmt.Errorf("cannot convert to NfsDatastoreSpec, object is nil")
}
datastoreName := object["datastore_name"].(string)
if len(datastoreName) == 0 {
return nil, fmt.Errorf("cannot convert to NfsDatastoreSpec, datastore_name is required")
}
path := object["path"].(string)
if len(path) == 0 {
return nil, fmt.Errorf("cannot convert to NfsDatastoreSpec, path is required")
}
result := &models.NfsDatastoreSpec{}
result.DatastoreName = &datastoreName
result.NasVolume = &models.NasVolumeSpec{}
result.NasVolume.Path = &path
if readOnly, ok := object["read_only"]; ok && !validation_utils.IsEmpty(readOnly) {
result.NasVolume.ReadOnly = toBoolPointer(readOnly)
} else {
return nil, fmt.Errorf("cannot convert to NfsDatastoreSpec, read_only is required")
}
if serverName, ok := object["server_name"]; ok && !validation_utils.IsEmpty(serverName) {
result.NasVolume.ServerName = []string{}
result.NasVolume.ServerName = append(result.NasVolume.ServerName, serverName.(string))
} else {
return nil, fmt.Errorf("cannot convert to NfsDatastoreSpec, server_name is required")
}
if userTag, ok := object["user_tag"]; ok && !validation_utils.IsEmpty(userTag) {
result.NasVolume.UserTag = userTag.(string)
}
return result, nil
}

func toBoolPointer(object interface{}) *bool {
if object == nil {
return nil
}
objectAsBool := object.(bool)
return &objectAsBool
}
19 changes: 19 additions & 0 deletions internal/datastores/vmfs_datastore_subresource.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package datastores

import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/vmware/vcf-sdk-go/models"
)

// VmfsDatastoreSchema this helper function extracts the VMFS Datastore schema, so that
Expand All @@ -21,3 +23,20 @@ func VmfsDatastoreSchema() *schema.Resource {
},
}
}

func TryConvertToVmfsDatastoreSpec(object map[string]interface{}) (*models.VmfsDatastoreSpec, error) {
if object == nil {
return nil, fmt.Errorf("cannot convert to VmfsDatastoreSpec, object is nil")
}
datastoreNames := object["datastore_names"].([]string)
if len(datastoreNames) == 0 {
return nil, fmt.Errorf("cannot convert to VmfsDatastoreSpec, datastore_names is required")
}
result := &models.VmfsDatastoreSpec{}
result.FcSpec = []*models.FcSpec{}
for _, datastoreName := range datastoreNames {
datastoreNameRef := &datastoreName
result.FcSpec = append(result.FcSpec, &models.FcSpec{DatastoreName: datastoreNameRef})
}
return result, nil
}
49 changes: 39 additions & 10 deletions internal/datastores/vsan_datastore_subresource.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
package datastores

import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
validation_utils "github.com/vmware/terraform-provider-vcf/internal/validation"
"github.com/vmware/vcf-sdk-go/models"
)

// VsanDatastoreSchema this helper function extracts the VSAN Datastore schema, so that
Expand All @@ -19,23 +22,49 @@ func VsanDatastoreSchema() *schema.Resource {
Description: "Datastore name used for cluster creation",
ValidateFunc: validation.NoZeroValues,
},
"dedup_and_compression_enabled": {
Type: schema.TypeBool,
Optional: true,
Description: "Enable vSAN deduplication and compression",
"license_key": {
Type: schema.TypeString,
Required: true,
Description: "License key for the vSAN data store to be applied in vCenter",
ValidateFunc: validation.NoZeroValues,
},
"failures_to_tolerate": {
Type: schema.TypeInt,
Optional: true,
Required: true,
Description: "Number of vSphere host failures to tolerate in the vSAN cluster (can be 0, 1, or 2)",
ValidateFunc: validation.IntBetween(0, 2),
},
"license_key": {
Type: schema.TypeString,
Optional: true,
Description: "License key for the vSAN data store to be applied in vCenter",
ValidateFunc: validation.NoZeroValues,
"dedup_and_compression_enabled": {
Type: schema.TypeBool,
Optional: true,
Description: "Enable vSAN deduplication and compression",
},
},
}
}

func TryConvertToVsanDatastoreSpec(object map[string]interface{}) (*models.VSANDatastoreSpec, error) {
if object == nil {
return nil, fmt.Errorf("cannot convert to VSANDatastoreSpec, object is nil")
}
datastoreName := object["datastore_name"].(string)
if len(datastoreName) == 0 {
return nil, fmt.Errorf("cannot convert to VSANDatastoreSpec, datastore_name is required")
}
result := &models.VSANDatastoreSpec{}
result.DatastoreName = &datastoreName
licenseKey := object["license_key"].(string)
if len(licenseKey) == 0 {
return nil, fmt.Errorf("cannot convert to VSANDatastoreSpec, license_key is required")
}
result.LicenseKey = licenseKey
if dedupAndCompressionEnabled, ok := object["dedup_and_compression_enabled"]; ok && !validation_utils.IsEmpty(dedupAndCompressionEnabled) {
result.DedupAndCompressionEnabled = dedupAndCompressionEnabled.(bool)
}
if failuresToTolerate, ok := object["failures_to_tolerate"]; ok {
failuresToTolerateInt := int32(failuresToTolerate.(int))
result.FailuresToTolerate = &failuresToTolerateInt
}

return result, nil
}
20 changes: 20 additions & 0 deletions internal/datastores/vsan_remote_datastore_cluster_subresource.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
package datastores

import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/vmware/vcf-sdk-go/models"
)

// VsanRemoteDatastoreClusterSchema this helper function extracts the VSAN Datastore Cluster
Expand All @@ -23,3 +25,21 @@ func VsanRemoteDatastoreClusterSchema() *schema.Resource {
},
}
}

func TryConvertToVSANRemoteDatastoreClusterSpec(object map[string]interface{}) (*models.VSANRemoteDatastoreClusterSpec, error) {
if object == nil {
return nil, fmt.Errorf("cannot convert to VSANRemoteDatastoreClusterSpec, object is nil")
}
datastoreUuids := object["datastore_uuids"].([]string)
if len(datastoreUuids) == 0 {
return nil, fmt.Errorf("cannot convert to VSANRemoteDatastoreClusterSpec, datastore_uuids is required")
}
result := &models.VSANRemoteDatastoreClusterSpec{}
result.VSANRemoteDatastoreSpec = []*models.VSANRemoteDatastoreSpec{}
for _, datastoreUuid := range datastoreUuids {
datastoreUuidRef := &datastoreUuid
result.VSANRemoteDatastoreSpec = append(result.VSANRemoteDatastoreSpec,
&models.VSANRemoteDatastoreSpec{DatastoreUUID: datastoreUuidRef})
}
return result, nil
}
Loading

0 comments on commit fc831bb

Please sign in to comment.