pkg/tfplan2cai/converters/services/compute/compute_instance.go (380 lines of code) (raw):

package compute import ( "errors" "fmt" "strings" "google.golang.org/api/googleapi" compute "google.golang.org/api/compute/v0.beta" "github.com/GoogleCloudPlatform/terraform-google-conversion/v6/pkg/caiasset" "github.com/GoogleCloudPlatform/terraform-google-conversion/v6/pkg/tfplan2cai/converters/cai" "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport" ) const ComputeInstanceAssetType string = "compute.googleapis.com/Instance" const ComputeDiskAssetType string = "compute.googleapis.com/Disk" func ResourceConverterComputeInstance() cai.ResourceConverter { return cai.ResourceConverter{ Convert: GetComputeInstanceAndDisksCaiObjects, } } func GetComputeInstanceAndDisksCaiObjects(d tpgresource.TerraformResourceData, config *transport_tpg.Config) ([]caiasset.Asset, error) { if instanceAsset, err := GetComputeInstanceCaiObject(d, config); err == nil { assets := []caiasset.Asset{instanceAsset} if diskAsset, err := GetComputeDiskCaiObject(d, config); err == nil { assets = append(assets, diskAsset) return assets, nil } else { return []caiasset.Asset{}, err } } else { return []caiasset.Asset{}, err } } func GetComputeInstanceCaiObject(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (caiasset.Asset, error) { name, err := cai.AssetName(d, config, "//compute.googleapis.com/projects/{{project}}/zones/{{zone}}/instances/{{name}}") if err != nil { return caiasset.Asset{}, err } if data, err := GetComputeInstanceData(d, config); err == nil { return caiasset.Asset{ Name: name, Type: ComputeInstanceAssetType, Resource: &caiasset.AssetResource{ Version: "v1", DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest", DiscoveryName: "Instance", Data: data, }, }, nil } else { return caiasset.Asset{}, err } } func GetComputeInstanceData(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]interface{}, error) { project, err := tpgresource.GetProject(d, config) if err != nil { return nil, err } instance, err := expandComputeInstance(project, d, config) if err != nil { return nil, err } return cai.JsonMap(instance) } func expandComputeInstance(project string, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (*compute.Instance, error) { // Get the machine type var machineTypeUrl string if mt, ok := d.GetOk("machine_type"); ok { machineType, err := tpgresource.ParseMachineTypesFieldValue(mt.(string), d, config) if err != nil { return nil, fmt.Errorf( "Error loading machine type: %s", err) } machineTypeUrl = machineType.RelativeLink() } // Build up the list of disks disks := []*compute.AttachedDisk{} if _, hasBootDisk := d.GetOk("boot_disk"); hasBootDisk { bootDisk, err := expandBootDisk(d, config, project) if err != nil { return nil, err } disks = append(disks, bootDisk) } if _, hasScratchDisk := d.GetOk("scratch_disk"); hasScratchDisk { scratchDisks, err := expandScratchDisks(d, config, project) if err != nil { return nil, err } disks = append(disks, scratchDisks...) } attachedDisksCount := d.Get("attached_disk.#").(int) for i := 0; i < attachedDisksCount; i++ { diskConfig := d.Get(fmt.Sprintf("attached_disk.%d", i)).(map[string]interface{}) disk, err := expandAttachedDisk(diskConfig, d, config) if err != nil { return nil, err } disks = append(disks, disk) } sch := d.Get("scheduling").([]interface{}) var scheduling *compute.Scheduling if len(sch) == 0 { // TF doesn't do anything about defaults inside of nested objects, so if // scheduling hasn't been set, then send it with its default values. scheduling = &compute.Scheduling{ AutomaticRestart: googleapi.Bool(true), } } else { prefix := "scheduling.0" scheduling = &compute.Scheduling{ AutomaticRestart: googleapi.Bool(d.Get(prefix + ".automatic_restart").(bool)), Preemptible: d.Get(prefix + ".preemptible").(bool), OnHostMaintenance: d.Get(prefix + ".on_host_maintenance").(string), ProvisioningModel: d.Get(prefix + ".provisioning_model").(string), ForceSendFields: []string{"AutomaticRestart", "Preemptible"}, } } params, err := expandParams(d) if err != nil { return nil, fmt.Errorf("Error creating params: %s", err) } metadata, err := resourceInstanceMetadata(d) if err != nil { return nil, fmt.Errorf("Error creating metadata: %s", err) } PartnerMetadata, err := resourceInstancePartnerMetadata(d) if err != nil { return nil, fmt.Errorf("Error creating partner metadata: %s", err) } networkInterfaces, err := expandNetworkInterfaces(d, config) if err != nil { return nil, fmt.Errorf("Error creating network interfaces: %s", err) } networkPerformanceConfig, err := expandNetworkPerformanceConfig(d, config) if err != nil { return nil, fmt.Errorf("Error creating network performance config: %s", err) } accels, err := expandInstanceGuestAccelerators(d, config) if err != nil { return nil, fmt.Errorf("Error creating guest accelerators: %s", err) } reservationAffinity, err := expandReservationAffinity(d) if err != nil { return nil, fmt.Errorf("Error creating reservation affinity: %s", err) } // Create the instance information return &compute.Instance{ CanIpForward: d.Get("can_ip_forward").(bool), Description: d.Get("description").(string), Disks: disks, MachineType: machineTypeUrl, Metadata: metadata, PartnerMetadata: PartnerMetadata, Name: d.Get("name").(string), Zone: d.Get("zone").(string), NetworkInterfaces: networkInterfaces, NetworkPerformanceConfig: networkPerformanceConfig, Tags: resourceInstanceTags(d), Params: params, Labels: tpgresource.ExpandLabels(d), ServiceAccounts: expandServiceAccounts(d.Get("service_account").([]interface{})), GuestAccelerators: accels, MinCpuPlatform: d.Get("min_cpu_platform").(string), Scheduling: scheduling, DeletionProtection: d.Get("deletion_protection").(bool), Hostname: d.Get("hostname").(string), ForceSendFields: []string{"CanIpForward", "DeletionProtection"}, ConfidentialInstanceConfig: expandConfidentialInstanceConfig(d), AdvancedMachineFeatures: expandAdvancedMachineFeatures(d), ShieldedInstanceConfig: expandShieldedVmConfigs(d), DisplayDevice: expandDisplayDevice(d), ResourcePolicies: tpgresource.ConvertStringArr(d.Get("resource_policies").([]interface{})), ReservationAffinity: reservationAffinity, KeyRevocationActionType: d.Get("key_revocation_action_type").(string), }, nil } func expandAttachedDisk(diskConfig map[string]interface{}, d tpgresource.TerraformResourceData, meta interface{}) (*compute.AttachedDisk, error) { config := meta.(*transport_tpg.Config) s := diskConfig["source"].(string) var sourceLink string if strings.Contains(s, "regions/") { source, err := tpgresource.ParseRegionDiskFieldValue(s, d, config) if err != nil { return nil, err } sourceLink = source.RelativeLink() } else { source, err := tpgresource.ParseDiskFieldValue(s, d, config) if err != nil { return nil, err } sourceLink = source.RelativeLink() } disk := &compute.AttachedDisk{ Source: sourceLink, } if v, ok := diskConfig["mode"]; ok { disk.Mode = v.(string) } if v, ok := diskConfig["device_name"]; ok { disk.DeviceName = v.(string) } keyValue, keyOk := diskConfig["disk_encryption_key_raw"] if keyOk { if keyValue != "" { disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{ RawKey: keyValue.(string), } } } kmsValue, kmsOk := diskConfig["kms_key_self_link"] if kmsOk { if keyOk && keyValue != "" && kmsValue != "" { return nil, errors.New("Only one of kms_key_self_link and disk_encryption_key_raw can be set") } if kmsValue != "" { disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{ KmsKeyName: kmsValue.(string), } } } return disk, nil } // See comment on expandInstanceTemplateGuestAccelerators regarding why this // code is duplicated. func expandInstanceGuestAccelerators(d tpgresource.TerraformResourceData, config *transport_tpg.Config) ([]*compute.AcceleratorConfig, error) { configs, ok := d.GetOk("guest_accelerator") if !ok { return nil, nil } accels := configs.([]interface{}) guestAccelerators := make([]*compute.AcceleratorConfig, 0, len(accels)) for _, raw := range accels { data := raw.(map[string]interface{}) if data["count"].(int) == 0 { continue } at, err := tpgresource.ParseAcceleratorFieldValue(data["type"].(string), d, config) if err != nil { return nil, fmt.Errorf("cannot parse accelerator type: %v", err) } guestAccelerators = append(guestAccelerators, &compute.AcceleratorConfig{ AcceleratorCount: int64(data["count"].(int)), AcceleratorType: at.RelativeLink(), }) } return guestAccelerators, nil } func expandParams(d tpgresource.TerraformResourceData) (*compute.InstanceParams, error) { if _, ok := d.GetOk("params.0.resource_manager_tags"); ok { params := &compute.InstanceParams{ ResourceManagerTags: tpgresource.ExpandStringMap(d, "params.0.resource_manager_tags"), } return params, nil } return nil, nil } func expandBootDisk(d tpgresource.TerraformResourceData, config *transport_tpg.Config, project string) (*compute.AttachedDisk, error) { disk := &compute.AttachedDisk{ AutoDelete: d.Get("boot_disk.0.auto_delete").(bool), Boot: true, } if v, ok := d.GetOk("boot_disk.0.device_name"); ok { disk.DeviceName = v.(string) } if v, ok := d.GetOk("boot_disk.0.disk_encryption_key_raw"); ok { if v != "" { disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{ RawKey: v.(string), } } } if v, ok := d.GetOk("boot_disk.0.kms_key_self_link"); ok { if v != "" { disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{ KmsKeyName: v.(string), } } } if v, ok := d.GetOk("boot_disk.0.source"); ok { source, err := tpgresource.ParseDiskFieldValue(v.(string), d, config) if err != nil { return nil, err } disk.Source = source.RelativeLink() } if _, ok := d.GetOk("boot_disk.0.initialize_params"); ok { if v, ok := d.GetOk("boot_disk.0.initialize_params.0.size"); ok { disk.DiskSizeGb = int64(v.(int)) } } if v, ok := d.GetOk("boot_disk.0.mode"); ok { disk.Mode = v.(string) } return disk, nil } func expandScratchDisks(d tpgresource.TerraformResourceData, config *transport_tpg.Config, project string) ([]*compute.AttachedDisk, error) { diskType, err := readDiskType(config, d, "local-ssd") if err != nil { return nil, fmt.Errorf("Error loading disk type 'local-ssd': %s", err) } n := d.Get("scratch_disk.#").(int) scratchDisks := make([]*compute.AttachedDisk, 0, n) for i := 0; i < n; i++ { scratchDisks = append(scratchDisks, &compute.AttachedDisk{ AutoDelete: true, Type: "SCRATCH", Interface: d.Get(fmt.Sprintf("scratch_disk.%d.interface", i)).(string), InitializeParams: &compute.AttachedDiskInitializeParams{ DiskType: diskType.RelativeLink(), }, }) } return scratchDisks, nil } func expandStoragePool(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { // ExpandStoragePoolUrl is generated by MMv1 // return ExpandStoragePoolUrl(v, d, config) return nil, nil } func GetComputeDiskCaiObject(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (caiasset.Asset, error) { name, err := cai.AssetName(d, config, "//compute.googleapis.com/projects/{{project}}/zones/{{zone}}/disks/{{name}}") if err != nil { return caiasset.Asset{}, err } if data, err := GetComputeDiskData(d, config); err == nil { return caiasset.Asset{ Name: name, Type: ComputeDiskAssetType, Resource: &caiasset.AssetResource{ Version: "v1", DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest", DiscoveryName: "Disk", Data: data, }, }, nil } else { return caiasset.Asset{}, err } } func GetComputeDiskData(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]interface{}, error) { project, err := tpgresource.GetProject(d, config) if err != nil { return nil, err } diskApiObj, err := expandBootDisk(d, config, project) if err != nil { return nil, err } diskDetails, err := cai.JsonMap(diskApiObj) if err != nil { return nil, err } if v, ok := d.GetOk("boot_disk.0.initialize_params.0.type"); ok { diskTypeName := v.(string) diskType, err := readDiskType(config, d, diskTypeName) if err != nil { return nil, fmt.Errorf("Error loading disk type '%s': %s", diskTypeName, err) } diskDetails["DiskType"] = diskType.RelativeLink() } if v, ok := d.GetOk("boot_disk.0.initialize_params.0.image"); ok { diskDetails["SourceImage"] = v.(string) } if _, ok := d.GetOk("boot_disk.0.initialize_params.0.labels"); ok { diskDetails["Labels"] = tpgresource.ExpandStringMap(d, "boot_disk.0.initialize_params.0.labels") } if _, ok := d.GetOk("boot_disk.0.initialize_params.0.resource_policies"); ok { diskDetails["ResourcePolicies"] = tpgresource.ConvertStringArr(d.Get("boot_disk.0.initialize_params.0.resource_policies").([]interface{})) } if v, ok := d.GetOk("boot_disk.0.initialize_params.0.provisioned_iops"); ok { diskDetails["ProvisionedIops"] = int64(v.(int)) } if v, ok := d.GetOk("boot_disk.0.initialize_params.0.provisioned_throughput"); ok { diskDetails["ProvisionedThroughput"] = int64(v.(int)) } if v, ok := d.GetOk("boot_disk.0.initialize_params.0.enable_confidential_compute"); ok { diskDetails["EnableConfidentialCompute"] = v.(bool) } if v, ok := d.GetOk("boot_disk.0.initialize_params.0.storage_pool"); ok { storagePoolUrl, err := expandStoragePool(v, d, config) if err != nil { return nil, fmt.Errorf("Error resolving storage pool name '%s': '%s'", v.(string), err) } diskDetails["StoragePool"] = storagePoolUrl.(string) } return diskDetails, nil }