pkg/cai2hcl/converters/services/compute/compute_instance.go (158 lines of code) (raw):

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) { 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.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 }