Skip to content

Commit

Permalink
Add google_compute_instance to v6 cai2hcl (#12723)
Browse files Browse the repository at this point in the history
  • Loading branch information
zli82016 authored Jan 15, 2025
1 parent 7b15af4 commit 8086596
Show file tree
Hide file tree
Showing 4 changed files with 561 additions and 4 deletions.
2 changes: 2 additions & 0 deletions mmv1/templates/tgc_v6/cai2hcl/resource_converters.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ package converters

import (
"github.com/GoogleCloudPlatform/terraform-google-conversion/v6/pkg/cai2hcl/models"
"github.com/GoogleCloudPlatform/terraform-google-conversion/v6/pkg/cai2hcl/converters/services/compute"
"github.com/GoogleCloudPlatform/terraform-google-conversion/v6/pkg/cai2hcl/converters/services/resourcemanager"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand All @@ -40,4 +41,5 @@ var provider *schema.Provider = tpg_provider.Provider()
// ConverterMap is a collection of converters instances, indexed by cai asset type.
var ConverterMap = map[string]models.Converter{
resourcemanager.ProjectAssetType: resourcemanager.NewProjectConverter(provider),
compute.ComputeInstanceAssetType: compute.NewComputeInstanceConverter(provider),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package compute

import (
"fmt"
"strings"

"github.com/GoogleCloudPlatform/terraform-google-conversion/v6/pkg/cai2hcl/converters/utils"
"github.com/GoogleCloudPlatform/terraform-google-conversion/v6/pkg/cai2hcl/models"
"github.com/GoogleCloudPlatform/terraform-google-conversion/v6/pkg/caiasset"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
compute "google.golang.org/api/compute/v0.beta"
)

// ComputeInstanceAssetType is the CAI asset type name for compute instance.
const ComputeInstanceAssetType string = "compute.googleapis.com/Instance"

// ComputeInstanceSchemaName is the TF resource schema name for compute instance.
const ComputeInstanceSchemaName string = "google_compute_instance"

// ComputeInstanceConverter for compute instance resource.
type ComputeInstanceConverter struct {
name string
schema map[string]*schema.Schema
}

// NewComputeInstanceConverter returns an HCL converter for compute instance.
func NewComputeInstanceConverter(provider *schema.Provider) models.Converter {
schema := provider.ResourcesMap[ComputeInstanceSchemaName].Schema

return &ComputeInstanceConverter{
name: ComputeInstanceSchemaName,
schema: schema,
}
}

// Convert converts asset to HCL resource blocks.
func (c *ComputeInstanceConverter) Convert(asset *caiasset.Asset) ([]*models.TerraformResourceBlock, error) {
if asset == nil || asset.Resource == nil && asset.Resource.Data == nil {
return nil, nil
}
var blocks []*models.TerraformResourceBlock
block, err := c.convertResourceData(asset)
if err != nil {
return nil, err
}
blocks = append(blocks, block)
return blocks, nil
}

func (c *ComputeInstanceConverter) convertResourceData(asset *caiasset.Asset) (*models.TerraformResourceBlock, error) {
if asset == nil || asset.Resource == nil || asset.Resource.Data == nil {
return nil, fmt.Errorf("asset resource data is nil")
}

project := utils.ParseFieldValue(asset.Name, "projects")

var instance *compute.Instance
if err := utils.DecodeJSON(asset.Resource.Data, &instance); err != nil {
return nil, err
}

hclData := make(map[string]interface{})

if instance.CanIpForward {
hclData["can_ip_forward"] = instance.CanIpForward
}
hclData["machine_type"] = tpgresource.GetResourceNameFromSelfLink(instance.MachineType)
hclData["network_performance_config"] = flattenNetworkPerformanceConfig(instance.NetworkPerformanceConfig)

// Set the networks
networkInterfaces, _, _, err := flattenNetworkInterfaces(instance.NetworkInterfaces, project)
if err != nil {
return nil, err
}
hclData["network_interface"] = networkInterfaces

if instance.Tags != nil {
hclData["tags"] = tpgresource.ConvertStringArrToInterface(instance.Tags.Items)
}

hclData["labels"] = utils.RemoveTerraformAttributionLabel(instance.Labels)
hclData["service_account"] = flattenServiceAccounts(instance.ServiceAccounts)
hclData["resource_policies"] = instance.ResourcePolicies

bootDisk, ads, scratchDisks := flattenDisks(instance.Disks, instance.Name)
hclData["boot_disk"] = bootDisk
hclData["attached_disk"] = ads
hclData["scratch_disk"] = scratchDisks

hclData["scheduling"] = flattenScheduling(instance.Scheduling)
hclData["guest_accelerator"] = flattenGuestAccelerators(instance.GuestAccelerators)
hclData["shielded_instance_config"] = flattenShieldedVmConfig(instance.ShieldedInstanceConfig)
hclData["enable_display"] = flattenEnableDisplay(instance.DisplayDevice)
hclData["min_cpu_platform"] = instance.MinCpuPlatform

// Only convert the field when its value is not default false
if instance.DeletionProtection {
hclData["deletion_protection"] = instance.DeletionProtection
}
hclData["zone"] = tpgresource.GetResourceNameFromSelfLink(instance.Zone)
hclData["name"] = instance.Name
hclData["description"] = instance.Description
hclData["hostname"] = instance.Hostname
hclData["confidential_instance_config"] = flattenConfidentialInstanceConfig(instance.ConfidentialInstanceConfig)
hclData["advanced_machine_features"] = flattenAdvancedMachineFeatures(instance.AdvancedMachineFeatures)
hclData["reservation_affinity"] = flattenReservationAffinity(instance.ReservationAffinity)
hclData["key_revocation_action_type"] = instance.KeyRevocationActionType

// TODO: convert details from the boot disk assets (separate disk assets) into initialize_params in cai2hcl?
// It needs to integrate the disk assets into instance assets with the resolver.

ctyVal, err := utils.MapToCtyValWithSchema(hclData, c.schema)
if err != nil {
return nil, err
}
return &models.TerraformResourceBlock{
Labels: []string{c.name, instance.Name},
Value: ctyVal,
}, nil

}

func flattenDisks(disks []*compute.AttachedDisk, instanceName string) ([]map[string]interface{}, []map[string]interface{}, []map[string]interface{}) {
attachedDisks := []map[string]interface{}{}
bootDisk := []map[string]interface{}{}
scratchDisks := []map[string]interface{}{}
for _, disk := range disks {
if disk.Boot {
bootDisk = flattenBootDisk(disk, instanceName)
} else if disk.Type == "SCRATCH" {
scratchDisks = append(scratchDisks, flattenScratchDisk(disk))
} else {
di := map[string]interface{}{
"source": tpgresource.ConvertSelfLinkToV1(disk.Source),
"device_name": disk.DeviceName,
"mode": disk.Mode,
}
if key := disk.DiskEncryptionKey; key != nil {
if key.KmsKeyName != "" {
// The response for crypto keys often includes the version of the key which needs to be removed
// format: projects/<project>/locations/<region>/keyRings/<keyring>/cryptoKeys/<key>/cryptoKeyVersions/1
di["kms_key_self_link"] = strings.Split(disk.DiskEncryptionKey.KmsKeyName, "/cryptoKeyVersions")[0]
}
}
attachedDisks = append(attachedDisks, di)
}
}

// Remove nils from map in case there were disks in the config that were not present on read;
// i.e. a disk was detached out of band
ads := []map[string]interface{}{}
for _, d := range attachedDisks {
if d != nil {
ads = append(ads, d)
}
}
return bootDisk, ads, scratchDisks
}

func flattenBootDisk(disk *compute.AttachedDisk, instanceName string) []map[string]interface{} {
result := map[string]interface{}{}

if !disk.AutoDelete {
result["auto_delete"] = false
}

if !strings.Contains(disk.DeviceName, "persistent-disk-") {
result["device_name"] = disk.DeviceName
}

if disk.Mode != "READ_WRITE" {
result["mode"] = disk.Mode
}

if disk.DiskEncryptionKey != nil {
if disk.DiskEncryptionKey.KmsKeyName != "" {
// The response for crypto keys often includes the version of the key which needs to be removed
// format: projects/<project>/locations/<region>/keyRings/<keyring>/cryptoKeys/<key>/cryptoKeyVersions/1
result["kms_key_self_link"] = strings.Split(disk.DiskEncryptionKey.KmsKeyName, "/cryptoKeyVersions")[0]
}
}

// Don't convert the field with the default value
if disk.Interface != "SCSI" {
result["interface"] = disk.Interface
}

if !strings.HasSuffix(disk.Source, instanceName) {
result["source"] = tpgresource.ConvertSelfLinkToV1(disk.Source)
}

if len(result) == 0 {
return nil
}

return []map[string]interface{}{result}
}

func flattenScratchDisk(disk *compute.AttachedDisk) map[string]interface{} {
result := map[string]interface{}{
"size": disk.DiskSizeGb,
}

if !strings.Contains(disk.DeviceName, "persistent-disk-") {
result["device_name"] = disk.DeviceName
}

// Don't convert the field with the default value
if disk.Interface != "SCSI" {
result["interface"] = disk.Interface
}

return result
}
Loading

0 comments on commit 8086596

Please sign in to comment.