diff --git a/mmv1/third_party/terraform/services/compute/compute_instance_helpers.go.tmpl b/mmv1/third_party/terraform/services/compute/compute_instance_helpers.go.tmpl index 3863236b923a..2a068b8eb37c 100644 --- a/mmv1/third_party/terraform/services/compute/compute_instance_helpers.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/compute_instance_helpers.go.tmpl @@ -1221,3 +1221,583 @@ func flattenComputeInstanceSourceEncryptionKey(v *compute.CustomerEncryptionKey) }, } } + +// _v2 expand functions return map[string]interface{} / []interface{} for direct +// HTTP JSON serialization via transport_tpg.SendRequest, replacing Apiary struct usage. + +func expandAliasIpRanges_v2(ranges []interface{}) []interface{} { + ipRanges := make([]interface{}, 0, len(ranges)) + for _, raw := range ranges { + data := raw.(map[string]interface{}) + entry := map[string]interface{}{ + "ipCidrRange": data["ip_cidr_range"].(string), + } + if v := data["subnetwork_range_name"].(string); v != "" { + entry["subnetworkRangeName"] = v + } + ipRanges = append(ipRanges, entry) + } + return ipRanges +} + +func expandAccessConfigs_v2(configs []interface{}) []interface{} { + acs := make([]interface{}, len(configs)) + for i, raw := range configs { + ac := map[string]interface{}{ + "type": "ONE_TO_ONE_NAT", + } + if raw != nil { + data := raw.(map[string]interface{}) + if v := data["nat_ip"].(string); v != "" { + ac["natIP"] = v + } + if v := data["network_tier"].(string); v != "" { + ac["networkTier"] = v + } + if ptr, ok := data["public_ptr_domain_name"]; ok && ptr != "" { + ac["setPublicPtr"] = true + ac["publicPtrDomainName"] = ptr.(string) + } + } + acs[i] = ac + } + return acs +} + +func expandIpv6AccessConfigs_v2(configs []interface{}) []interface{} { + iacs := make([]interface{}, len(configs)) + for i, raw := range configs { + iac := map[string]interface{}{ + "type": "DIRECT_IPV6", + } + if raw != nil { + data := raw.(map[string]interface{}) + if v := data["network_tier"].(string); v != "" { + iac["networkTier"] = v + } + if ptr, ok := data["public_ptr_domain_name"]; ok && ptr != "" { + iac["publicPtrDomainName"] = ptr.(string) + } + if eip, ok := data["external_ipv6"]; ok && eip != "" { + iac["externalIpv6"] = eip.(string) + } + if eipl, ok := data["external_ipv6_prefix_length"]; ok && eipl != "" { + if strVal, ok := eipl.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + iac["externalIpv6PrefixLength"] = intVal + } + } + } + if name, ok := data["name"]; ok && name != "" { + iac["name"] = name.(string) + } + } + iacs[i] = iac + } + return iacs +} + +func expandServiceAccounts_v2(configs []interface{}) []interface{} { + accounts := make([]interface{}, len(configs)) + for i, raw := range configs { + data := raw.(map[string]interface{}) + email := data["email"].(string) + if email == "" { + email = "default" + } + accounts[i] = map[string]interface{}{ + "email": email, + "scopes": tpgresource.CanonicalizeServiceScopes(tpgresource.ConvertStringSet(data["scopes"].(*schema.Set))), + } + } + return accounts +} + +func resourceInstanceTags_v2(d tpgresource.TerraformResourceData) map[string]interface{} { + if v := d.Get("tags"); v != nil { + vs := v.(*schema.Set) + items := make([]string, vs.Len()) + for i, v := range vs.List() { + items[i] = v.(string) + } + return map[string]interface{}{ + "items": items, + "fingerprint": d.Get("tags_fingerprint").(string), + } + } + return nil +} + +func expandShieldedVmConfigs_v2(d tpgresource.TerraformResourceData) map[string]interface{} { + if _, ok := d.GetOk("shielded_instance_config"); !ok { + return nil + } + prefix := "shielded_instance_config.0" + return map[string]interface{}{ + "enableSecureBoot": d.Get(prefix + ".enable_secure_boot").(bool), + "enableVtpm": d.Get(prefix + ".enable_vtpm").(bool), + "enableIntegrityMonitoring": d.Get(prefix + ".enable_integrity_monitoring").(bool), + } +} + +func expandConfidentialInstanceConfig_v2(d tpgresource.TerraformResourceData) map[string]interface{} { + if _, ok := d.GetOk("confidential_instance_config"); !ok { + return nil + } + prefix := "confidential_instance_config.0" + result := map[string]interface{}{ + "enableConfidentialCompute": d.Get(prefix + ".enable_confidential_compute").(bool), + } + if v := d.Get(prefix + ".confidential_instance_type").(string); v != "" { + result["confidentialInstanceType"] = v + } + return result +} + +func expandAdvancedMachineFeatures_v2(d tpgresource.TerraformResourceData) map[string]interface{} { + if _, ok := d.GetOk("advanced_machine_features"); !ok { + return nil + } + prefix := "advanced_machine_features.0" + result := map[string]interface{}{} + if v := d.Get(prefix + ".enable_nested_virtualization").(bool); v { + result["enableNestedVirtualization"] = v + } + if v := int64(d.Get(prefix + ".threads_per_core").(int)); v > 0 { + result["threadsPerCore"] = v + } + if v := d.Get(prefix + ".turbo_mode").(string); v != "" { + result["turboMode"] = v + } + if v := int64(d.Get(prefix + ".visible_core_count").(int)); v > 0 { + result["visibleCoreCount"] = v + } + if v := d.Get(prefix + ".performance_monitoring_unit").(string); v != "" { + result["performanceMonitoringUnit"] = v + } + if v := d.Get(prefix + ".enable_uefi_networking").(bool); v { + result["enableUefiNetworking"] = v + } + return result +} + +func expandDisplayDevice_v2(d tpgresource.TerraformResourceData) map[string]interface{} { + if _, ok := d.GetOk("enable_display"); !ok { + return nil + } + return map[string]interface{}{ + "enableDisplay": d.Get("enable_display").(bool), + } +} + +func expandNetworkPerformanceConfig_v2(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]interface{}, error) { + configs, ok := d.GetOk("network_performance_config") + if !ok { + return nil, nil + } + npcSlice := configs.([]interface{}) + if len(npcSlice) > 1 { + return nil, fmt.Errorf("cannot specify multiple network_performance_configs") + } + if len(npcSlice) == 0 || npcSlice[0] == nil { + return nil, nil + } + npc := npcSlice[0].(map[string]interface{}) + return map[string]interface{}{ + "totalEgressBandwidthTier": npc["total_egress_bandwidth_tier"].(string), + }, nil +} + +func expandComputeInstanceEncryptionKey_v2(d tpgresource.TerraformResourceData) map[string]interface{} { + iek, ok := d.GetOk("instance_encryption_key") + if !ok { + return nil + } + iekRes := iek.([]interface{})[0].(map[string]interface{}) + result := map[string]interface{}{} + if v := iekRes["kms_key_self_link"].(string); v != "" { + result["kmsKeyName"] = v + } + if v := iekRes["kms_key_service_account"].(string); v != "" { + result["kmsKeyServiceAccount"] = v + } + return result +} + +func expandComputeInstanceSourceEncryptionKey_v2(d tpgresource.TerraformResourceData, field string) map[string]interface{} { + cek, ok := d.GetOk(field) + if !ok { + return nil + } + cekRes := cek.([]interface{})[0].(map[string]interface{}) + result := map[string]interface{}{} + if v := cekRes["rsa_encrypted_key"].(string); v != "" { + result["rsaEncryptedKey"] = v + } + if v := cekRes["raw_key"].(string); v != "" { + result["rawKey"] = v + } + if v := cekRes["kms_key_self_link"].(string); v != "" { + result["kmsKeyName"] = v + } + if v := cekRes["kms_key_service_account"].(string); v != "" { + result["kmsKeyServiceAccount"] = v + } + return result +} + +func expandComputeInstanceGuestOsFeatures_v2(v interface{}) []interface{} { + if v == nil { + return nil + } + var result []interface{} + for _, feature := range v.([]interface{}) { + result = append(result, map[string]interface{}{ + "type": feature.(string), + }) + } + return result +} + +func expandReservationAffinity_v2(d tpgresource.TerraformResourceData) (map[string]interface{}, error) { + _, ok := d.GetOk("reservation_affinity") + if !ok { + return nil, nil + } + + prefix := "reservation_affinity.0" + reservationAffinityType := d.Get(prefix + ".type").(string) + + affinity := map[string]interface{}{ + "consumeReservationType": reservationAffinityType, + } + + _, hasSpecificReservation := d.GetOk(prefix + ".specific_reservation") + if (reservationAffinityType == "SPECIFIC_RESERVATION") != hasSpecificReservation { + return nil, fmt.Errorf("specific_reservation must be set when reservation_affinity is SPECIFIC_RESERVATION, and not set otherwise") + } + + if hasSpecificReservation { + specificPrefix := prefix + ".specific_reservation.0" + var values []string + for _, v := range d.Get(specificPrefix + ".values").([]interface{}) { + values = append(values, v.(string)) + } + affinity["key"] = d.Get(specificPrefix + ".key").(string) + affinity["values"] = values + } + + return affinity, nil +} + +func expandComputeMaxRunDuration_v2(v interface{}) (map[string]interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + original := l[0].(map[string]interface{}) + duration := map[string]interface{}{} + + if nanos := original["nanos"]; reflect.ValueOf(nanos).IsValid() && !tpgresource.IsEmptyValue(reflect.ValueOf(nanos)) { + duration["nanos"] = int64(nanos.(int)) + } + if seconds := original["seconds"]; reflect.ValueOf(seconds).IsValid() && !tpgresource.IsEmptyValue(reflect.ValueOf(seconds)) { + duration["seconds"] = int64(seconds.(int)) + } + return duration, nil +} + +func expandComputeOnInstanceStopAction_v2(v interface{}) (map[string]interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + original := l[0].(map[string]interface{}) + d, ok := original["discard_local_ssd"] + if !ok { + return nil, nil + } + return map[string]interface{}{ + "discardLocalSsd": d.(bool), + }, nil +} + +func expandComputeLocalSsdRecoveryTimeout_v2(v interface{}) (map[string]interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + original := l[0].(map[string]interface{}) + duration := map[string]interface{}{} + + if nanos := original["nanos"]; reflect.ValueOf(nanos).IsValid() && !tpgresource.IsEmptyValue(reflect.ValueOf(nanos)) { + duration["nanos"] = int64(nanos.(int)) + } + if seconds := original["seconds"]; reflect.ValueOf(seconds).IsValid() && !tpgresource.IsEmptyValue(reflect.ValueOf(seconds)) { + duration["seconds"] = int64(seconds.(int)) + } + return duration, nil +} + +{{ if ne $.TargetVersionName `ga` -}} +func expandGracefulShutdownMaxDuration_v2(v interface{}) (map[string]interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + maxDurationMap := l[0].(map[string]interface{}) + // Always include seconds to mirror ForceSendFields["Seconds"] in the Apiary version. + duration := map[string]interface{}{ + "seconds": int64(maxDurationMap["seconds"].(int)), + } + if nanos := maxDurationMap["nanos"]; reflect.ValueOf(nanos).IsValid() && !tpgresource.IsEmptyValue(reflect.ValueOf(nanos)) { + duration["nanos"] = int64(nanos.(int)) + } + return duration, nil +} + +func expandGracefulShutdown_v2(v interface{}) (map[string]interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + original := l[0].(map[string]interface{}) + gracefulShutdown := map[string]interface{}{ + "enabled": original["enabled"].(bool), + } + maxDuration, err := expandGracefulShutdownMaxDuration_v2(original["max_duration"].([]interface{})) + if err != nil { + return nil, err + } + if maxDuration != nil { + gracefulShutdown["maxDuration"] = maxDuration + } + return gracefulShutdown, nil +} + +func expandComputePreemptionNoticeDuration_v2(v interface{}) (map[string]interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + original := l[0].(map[string]interface{}) + duration := map[string]interface{}{} + if nanos, ok := original["nanos"]; ok && nanos != nil { + duration["nanos"] = int64(nanos.(int)) + } + if seconds, ok := original["seconds"]; ok && seconds != nil { + duration["seconds"] = int64(seconds.(int)) + } + return duration, nil +} +{{- end }} + +func expandScheduling_v2(v interface{}) (map[string]interface{}, error) { + if v == nil { + return map[string]interface{}{"automaticRestart": true}, nil + } + ls := v.([]interface{}) + if len(ls) == 0 { + return map[string]interface{}{"automaticRestart": true}, nil + } + if len(ls) > 1 || ls[0] == nil { + return nil, fmt.Errorf("expected exactly one scheduling block") + } + + original := ls[0].(map[string]interface{}) + scheduling := map[string]interface{}{} + + if v, ok := original["automatic_restart"]; ok { + scheduling["automaticRestart"] = v.(bool) + } + if v, ok := original["preemptible"]; ok { + scheduling["preemptible"] = v.(bool) + } + if v, ok := original["on_host_maintenance"]; ok { + if s := v.(string); s != "" { + scheduling["onHostMaintenance"] = s + } + } + if v, ok := original["node_affinities"]; ok && v != nil { + naSet := v.(*schema.Set).List() + nodeAffinities := make([]interface{}, 0, len(naSet)) + for _, nodeAffRaw := range naSet { + if nodeAffRaw == nil { + continue + } + nodeAff := nodeAffRaw.(map[string]interface{}) + nodeAffinities = append(nodeAffinities, map[string]interface{}{ + "key": nodeAff["key"].(string), + "operator": nodeAff["operator"].(string), + "values": tpgresource.ConvertStringArr(nodeAff["values"].(*schema.Set).List()), + }) + } + scheduling["nodeAffinities"] = nodeAffinities + } + if v, ok := original["min_node_cpus"]; ok { + if n := int64(v.(int)); n > 0 { + scheduling["minNodeCpus"] = n + } + } + if v, ok := original["provisioning_model"]; ok { + if s := v.(string); s != "" { + scheduling["provisioningModel"] = s + } + } + if v, ok := original["instance_termination_action"]; ok { + if s := v.(string); s != "" { + scheduling["instanceTerminationAction"] = s + } + } + if v, ok := original["availability_domain"]; ok && v != nil { + if n := int64(v.(int)); n > 0 { + scheduling["availabilityDomain"] = n + } + } + if v, ok := original["max_run_duration"]; ok { + transformed, err := expandComputeMaxRunDuration_v2(v) + if err != nil { + return nil, err + } + if transformed != nil { + scheduling["maxRunDuration"] = transformed + } + } + if v, ok := original["on_instance_stop_action"]; ok { + transformed, err := expandComputeOnInstanceStopAction_v2(v) + if err != nil { + return nil, err + } + if transformed != nil { + scheduling["onInstanceStopAction"] = transformed + } + } +{{- if ne $.TargetVersionName "ga" }} + if v, ok := original["host_error_timeout_seconds"]; ok { + // API bug: SetScheduling doesn't clear this field when set to 0; send null explicitly. + if v == 0 || v == nil { + scheduling["hostErrorTimeoutSeconds"] = nil + } else { + scheduling["hostErrorTimeoutSeconds"] = int64(v.(int)) + } + } + if v, ok := original["maintenance_interval"]; ok { + if s := v.(string); s != "" { + scheduling["maintenanceInterval"] = s + } + } + if v, ok := original["graceful_shutdown"]; ok { + transformed, err := expandGracefulShutdown_v2(v) + if err != nil { + return nil, err + } + if transformed != nil { + scheduling["gracefulShutdown"] = transformed + } + } + if v, ok := original["skip_guest_os_shutdown"]; ok { + scheduling["skipGuestOsShutdown"] = v.(bool) + } + if v, ok := original["preemption_notice_duration"]; ok { + transformed, err := expandComputePreemptionNoticeDuration_v2(v) + if err != nil { + return nil, err + } + if transformed != nil { + scheduling["preemptionNoticeDuration"] = transformed + } + } +{{- end }} + if v, ok := original["local_ssd_recovery_timeout"]; ok { + transformed, err := expandComputeLocalSsdRecoveryTimeout_v2(v) + if err != nil { + return nil, err + } + if transformed != nil { + scheduling["localSsdRecoveryTimeout"] = transformed + } + } + if v, ok := original["termination_time"]; ok { + if s := v.(string); s != "" { + scheduling["terminationTime"] = s + } + } + return scheduling, nil +} + +func expandNetworkInterfaces_v2(d tpgresource.TerraformResourceData, config *transport_tpg.Config) ([]interface{}, error) { + configs := d.Get("network_interface").([]interface{}) + ifaces := make([]interface{}, len(configs)) + for i, raw := range configs { + data := raw.(map[string]interface{}) + + var networkAttachment string + network := data["network"].(string) + subnetwork := data["subnetwork"].(string) + if networkAttachmentObj, ok := data["network_attachment"]; ok { + networkAttachment = networkAttachmentObj.(string) + } + if networkAttachment == "" && network == "" && subnetwork == "" { + return nil, fmt.Errorf("exactly one of network, subnetwork, or network_attachment must be provided") + } + + nf, err := tpgresource.ParseNetworkFieldValue(network, d, config) + if err != nil { + return nil, fmt.Errorf("cannot determine self_link for network %q: %s", network, err) + } + + subnetProjectField := fmt.Sprintf("network_interface.%d.subnetwork_project", i) + sf, err := tpgresource.ParseSubnetworkFieldValueWithProjectField(subnetwork, subnetProjectField, d, config) + if err != nil { + return nil, fmt.Errorf("cannot determine self_link for subnetwork %q: %s", subnetwork, err) + } + + iface := map[string]interface{}{ + "accessConfigs": expandAccessConfigs_v2(data["access_config"].([]interface{})), + "aliasIpRanges": expandAliasIpRanges_v2(data["alias_ip_range"].([]interface{})), + "ipv6AccessConfigs": expandIpv6AccessConfigs_v2(data["ipv6_access_config"].([]interface{})), + } + // Only include optional string/numeric fields when non-zero to match omitempty + // semantics of the Apiary struct — sending empty values can cause API validation errors. + if v := nf.RelativeLink(); v != "" { + iface["network"] = v + } + if v := sf.RelativeLink(); v != "" { + iface["subnetwork"] = v + } + if v := data["network_ip"].(string); v != "" { + iface["networkIP"] = v + } + if networkAttachment != "" { + iface["networkAttachment"] = networkAttachment + } + if v := int64(data["vlan"].(int)); v != 0 { + iface["vlan"] = v + } + if v := data["nic_type"].(string); v != "" { + iface["nicType"] = v + } + if v := data["stack_type"].(string); v != "" { + iface["stackType"] = v + } + if v := int64(data["queue_count"].(int)); v != 0 { + iface["queueCount"] = v + } + if v := data["ipv6_address"].(string); v != "" { + iface["ipv6Address"] = v + } + if v := int64(data["internal_ipv6_prefix_length"].(int)); v != 0 { + iface["internalIpv6PrefixLength"] = v + } + if v := data["igmp_query"].(string); v != "" { + iface["igmpQuery"] = v + } +{{- if ne $.TargetVersionName "ga" }} + if macAddress, ok := data["mac_address"]; ok { + iface["macAddress"] = macAddress.(string) + } +{{- end }} + ifaces[i] = iface + } + return ifaces, nil +} diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl index 9905532a2944..b51c172229c0 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl @@ -8,7 +8,6 @@ import ( "fmt" "log" "net/http" - "strconv" "strings" "time" @@ -1896,6 +1895,121 @@ func expandComputeInstance(project string, d *schema.ResourceData, config *trans }, nil } +// expandComputeInstance_v2 builds the instance request body as map[string]interface{} +// for direct serialization via transport_tpg.SendRequest. +// Disk-related fields (expandBootDisk, expandScratchDisks, expandAttachedDisk) are not yet +// migrated; they serialize correctly via encoding/json since none use ForceSendFields. +func expandComputeInstance_v2(project string, d *schema.ResourceData, config *transport_tpg.Config) (map[string]interface{}, error) { + 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() + } + + var disks []interface{} + 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 + } + for _, sd := range scratchDisks { + disks = append(disks, sd) + } + } + for i := 0; i < d.Get("attached_disk.#").(int); 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) + } + + scheduling, err := expandScheduling_v2(d.Get("scheduling")) + if err != nil { + return nil, fmt.Errorf("Error creating scheduling: %s", err) + } + 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) + } + {{ if ne $.TargetVersionName `ga` -}} + partnerMetadataMap, err := resourceInstancePartnerMetadata(d) + if err != nil { + return nil, fmt.Errorf("Error creating partner metadata: %s", err) + } + {{- end }} + networkInterfaces, err := expandNetworkInterfaces_v2(d, config) + if err != nil { + return nil, fmt.Errorf("Error creating network interfaces: %s", err) + } + networkPerformanceConfig, err := expandNetworkPerformanceConfig_v2(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_v2(d) + if err != nil { + return nil, fmt.Errorf("Error creating reservation affinity: %s", err) + } + + instance := map[string]interface{}{ + "canIpForward": d.Get("can_ip_forward").(bool), + "description": d.Get("description").(string), + "disks": disks, + "machineType": machineTypeUrl, + "metadata": metadata, + "name": d.Get("name").(string), + "networkInterfaces": networkInterfaces, + "networkPerformanceConfig": networkPerformanceConfig, + "tags": resourceInstanceTags_v2(d), + "params": params, + "labels": tpgresource.ExpandEffectiveLabels(d), + "serviceAccounts": expandServiceAccounts_v2(d.Get("service_account").([]interface{})), + "guestAccelerators": accels, + "scheduling": scheduling, + "deletionProtection": d.Get("deletion_protection").(bool), + "confidentialInstanceConfig": expandConfidentialInstanceConfig_v2(d), + "advancedMachineFeatures": expandAdvancedMachineFeatures_v2(d), + "shieldedInstanceConfig": expandShieldedVmConfigs_v2(d), + "displayDevice": expandDisplayDevice_v2(d), + "resourcePolicies": tpgresource.ConvertStringArr(d.Get("resource_policies").([]interface{})), + "reservationAffinity": reservationAffinity, + "instanceEncryptionKey": expandComputeInstanceEncryptionKey_v2(d), + } + if v := d.Get("min_cpu_platform").(string); v != "" { + instance["minCpuPlatform"] = v + } + if v := d.Get("hostname").(string); v != "" { + instance["hostname"] = v + } + if v := d.Get("key_revocation_action_type").(string); v != "" { + instance["keyRevocationActionType"] = v + } +{{- if ne $.TargetVersionName `ga` }} + instance["eraseWindowsVssSignature"] = d.Get("erase_windows_vss_signature").(bool) + instance["partnerMetadata"] = partnerMetadataMap +{{- end }} + return instance, nil +} + var computeInstanceStatus = []string{ "PROVISIONING", "REPAIRING", @@ -1919,12 +2033,25 @@ func getAllStatusBut(status string) []string { } func changeInstanceStatusOnCreation(config *transport_tpg.Config, d *schema.ResourceData, project, zone, status, userAgent string) error { - var op *compute.Operation + var op interface{} var err error + name := d.Get("name").(string) if status == "TERMINATED" { - op, err = NewClient(config, userAgent).Instances.Stop(project, zone, d.Get("name").(string)).Do() + op, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/stop", config.ComputeBasePath, project, zone, name), + UserAgent: userAgent, + }) } else if status == "SUSPENDED" { - op, err = NewClient(config, userAgent).Instances.Suspend(project, zone, d.Get("name").(string)).Do() + op, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/suspend", config.ComputeBasePath, project, zone, name), + UserAgent: userAgent, + }) } if err != nil { return fmt.Errorf("Error changing instance status after creation: %s", err) @@ -1997,7 +2124,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("Error loading zone '%s': %s", z, err) } - instance, err := expandComputeInstance(project, d, config) + instanceBody, err := expandComputeInstance_v2(project, d, config) if err != nil { return err } @@ -2010,13 +2137,20 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err {{- end }} log.Printf("[INFO] Requesting instance creation") - op, err := NewClient(config, userAgent).Instances.Insert(project, zone.Name, instance).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances", config.ComputeBasePath, project, zone.Name), + UserAgent: userAgent, + Body: instanceBody, + }) if err != nil { return fmt.Errorf("Error creating instance: %s", err) } // Store the ID now - d.SetId(fmt.Sprintf("projects/%s/zones/%s/instances/%s", project, z, instance.Name)) + d.SetId(fmt.Sprintf("projects/%s/zones/%s/instances/%s", project, z, d.Get("name").(string))) // Wait for the operation to complete waitErr := ComputeOperationWaitTime(config, op, project, "instance to create", userAgent, d.Timeout(schema.TimeoutCreate)) @@ -2027,7 +2161,7 @@ func resourceComputeInstanceCreate(d *schema.ResourceData, meta interface{}) err } {{ if ne $.TargetVersionName `ga` -}} - err = computeInstanceAddSecurityPolicy(d, config, securityPolicies, project, z, userAgent, instance.Name) + err = computeInstanceAddSecurityPolicy(d, config, securityPolicies, project, z, userAgent, d.Get("name").(string)) if err != nil { return fmt.Errorf("Error creating instance while setting the security policies: %s", err) } @@ -2413,10 +2547,19 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if err != nil { return fmt.Errorf("Error retrieving instance: %s", err) } - - instance.Description = d.Get("description").(string) - - op, err := NewClient(config, userAgent).Instances.Update(project, zone, instance.Name, instance).Do() + instanceMap, err := tpgresource.ConvertToMap(instance) + if err != nil { + return fmt.Errorf("Error converting instance: %s", err) + } + instanceMap["description"] = d.Get("description").(string) + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PUT", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: instanceMap, + }) if err != nil { return fmt.Errorf("Error updating instance: %s", err) } @@ -2440,10 +2583,9 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if err != nil { return fmt.Errorf("Error parsing metadata: %s", err) } - - metadataV1 := &compute.Metadata{} - if err := tpgresource.Convert(metadata, metadataV1); err != nil { - return err + metadataMap, err := tpgresource.ConvertToMap(metadata) + if err != nil { + return fmt.Errorf("Error converting metadata: %s", err) } // We're retrying for an error 412 where the metadata fingerprint is out of date @@ -2456,9 +2598,16 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err return fmt.Errorf("Error retrieving metadata: %s", err) } - metadataV1.Fingerprint = instance.Metadata.Fingerprint + metadataMap["fingerprint"] = instance.Metadata.Fingerprint - op, err := NewClient(config, userAgent).Instances.SetMetadata(project, zone, instance.Name, metadataV1).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/setMetadata", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: metadataMap, + }) if err != nil { return fmt.Errorf("Error updating metadata: %s", err) } @@ -2485,16 +2634,30 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if err != nil { return fmt.Errorf("Error retrieving partner_metadata: %s", err) } - instance.Fingerprint = instance.Fingerprint patchedPM := resourceInstancePatchPartnerMetadata(d, convertPartnerMetadataFromCompute(instance.PartnerMetadata)) - computePM, err := convertPartnerMetadataToCompute(patchedPM) + // Deleted namespaces have nil values; convert to {} so the API clears them. + partnerMetadataBody := make(map[string]interface{}) + for k, v := range patchedPM { + if v == nil { + partnerMetadataBody[k] = map[string]interface{}{} + } else { + partnerMetadataBody[k] = v + } + } + instanceMap, err := tpgresource.ConvertToMap(instance) if err != nil { - return fmt.Errorf("Error converting partner metadata: %s", err) + return fmt.Errorf("Error converting instance: %s", err) } - instance.PartnerMetadata = computePM - instance.NullFields = []string{"partnerMetadata"} - - op, err := NewClient(config, userAgent).Instances.Update(project, zone, instance.Name, instance).Do() + instanceMap["partnerMetadata"] = partnerMetadataBody + + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PUT", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: instanceMap, + }) if err != nil { return fmt.Errorf("Error updating partner_metadata: %s", err) } @@ -2514,13 +2677,14 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err {{ end }} if d.HasChange("tags") { - tags := resourceInstanceTags(d) - tagsV1 := &compute.Tags{} - if err := tpgresource.Convert(tags, tagsV1); err != nil { - return err - } - op, err := NewClient(config, userAgent).Instances.SetTags( - project, zone, d.Get("name").(string), tagsV1).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/setTags", config.ComputeBasePath, project, zone, d.Get("name").(string)), + UserAgent: userAgent, + Body: resourceInstanceTags_v2(d), + }) if err != nil { return fmt.Errorf("Error updating tags: %s", err) } @@ -2532,11 +2696,17 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } if d.HasChange("effective_labels") { - labels := tpgresource.ExpandEffectiveLabels(d) - labelFingerprint := d.Get("label_fingerprint").(string) - req := compute.InstancesSetLabelsRequest{Labels: labels, LabelFingerprint: labelFingerprint} - - op, err := NewClient(config, userAgent).Instances.SetLabels(project, zone, instance.Name, &req).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/setLabels", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: map[string]interface{}{ + "labels": tpgresource.ExpandEffectiveLabels(d), + "labelFingerprint": d.Get("label_fingerprint").(string), + }, + }) if err != nil { return fmt.Errorf("Error updating labels: %s", err) } @@ -2554,15 +2724,27 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if err != nil { return fmt.Errorf("Error retrieving instance: %s", err) } - params, err := expandParams(d) if err != nil { return fmt.Errorf("Error updating params: %s", err) } - - instance.Params = params - - op, err := NewClient(config, userAgent).Instances.Update(project, zone, instance.Name, instance).Do() + paramsMap, err := tpgresource.ConvertToMap(params) + if err != nil { + return fmt.Errorf("Error converting params: %s", err) + } + instanceMap, err := tpgresource.ConvertToMap(instance) + if err != nil { + return fmt.Errorf("Error converting instance: %s", err) + } + instanceMap["params"] = paramsMap + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PUT", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: instanceMap, + }) if err != nil { return fmt.Errorf("Error updating instance: %s", err) } @@ -2583,9 +2765,14 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if d.HasChange("resource_policies") { if len(instance.ResourcePolicies) > 0 { - req := compute.InstancesRemoveResourcePoliciesRequest{ResourcePolicies: instance.ResourcePolicies} - - op, err := NewClient(config, userAgent).Instances.RemoveResourcePolicies(project, zone, instance.Name, &req).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/removeResourcePolicies", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: map[string]interface{}{"resourcePolicies": instance.ResourcePolicies}, + }) if err != nil { return fmt.Errorf("Error removing existing resource policies: %s", err) } @@ -2598,9 +2785,14 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err resourcePolicies := tpgresource.ConvertStringArr(d.Get("resource_policies").([]interface{})) if len(resourcePolicies) > 0 { - req := compute.InstancesAddResourcePoliciesRequest{ResourcePolicies: resourcePolicies} - - op, err := NewClient(config, userAgent).Instances.AddResourcePolicies(project, zone, instance.Name, &req).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/addResourcePolicies", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: map[string]interface{}{"resourcePolicies": resourcePolicies}, + }) if err != nil { return fmt.Errorf("Error adding resource policies: %s", err) } @@ -2615,13 +2807,19 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err bootRequiredSchedulingChange := schedulingHasChangeRequiringReboot(d) bootNotRequiredSchedulingChange := schedulingHasChangeWithoutReboot(d) if bootNotRequiredSchedulingChange { - scheduling, err := expandScheduling(d.Get("scheduling")) + scheduling, err := expandScheduling_v2(d.Get("scheduling")) if err != nil { return fmt.Errorf("Error creating request data to update scheduling: %s", err) } - op, err := NewClient(config, userAgent).Instances.SetScheduling( - project, zone, instance.Name, scheduling).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/setScheduling", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: scheduling, + }) if err != nil { return fmt.Errorf("Error updating scheduling policy: %s", err) } @@ -2634,7 +2832,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } } - networkInterfaces, err := expandNetworkInterfaces(d, config) + networkInterfaces, err := expandNetworkInterfaces_v2(d, config) if err != nil { return fmt.Errorf("Error getting network interface from config: %s", err) } @@ -2671,11 +2869,11 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err var updatesToNIWhileStopped []func(inst map[string]interface{}) error for i := 0; i < len(networkInterfaces); i++ { prefix := fmt.Sprintf("network_interface.%d", i) - networkInterface := networkInterfaces[i] + networkInterface := networkInterfaces[i].(map[string]interface{}) instNetworkInterface := instance.NetworkInterfaces[i] networkName := d.Get(prefix + ".name").(string) - subnetwork := networkInterface.Subnetwork + subnetwork := networkInterface["subnetwork"].(string) updateDuringStop := d.HasChange(prefix+".subnetwork") || d.HasChange(prefix+".network") || d.HasChange(prefix+".subnetwork_project") // Sanity check @@ -2705,7 +2903,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if err != nil { return fmt.Errorf("Cannot determine self_link for network %q: %s", resp.Network, err) } - networkInterface.Network = nf.RelativeLink() + networkInterface["network"] = nf.RelativeLink() } } @@ -2727,14 +2925,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } // Create new ones - acMaps := make([]interface{}, len(networkInterface.AccessConfigs)) - for j, ac := range networkInterface.AccessConfigs { - acMap, err := tpgresource.ConvertToMap(ac) - if err != nil { - return fmt.Errorf("Error converting access config to map: %w", err) - } - acMaps[j] = acMap - } + acMaps := networkInterface["accessConfigs"].([]interface{}) err = computeInstanceAddAccessConfigs(d, config, instNiMap, acMaps, project, zone, userAgent, instance.Name) if err != nil { return err @@ -2752,14 +2943,19 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err // Alias IP ranges cannot be updated; they must be removed and then added // unless you are changing subnetwork/network if len(instNetworkInterface.AliasIpRanges) > 0 { - ni := &compute.NetworkInterface{ - Fingerprint: instNetworkInterface.Fingerprint, - ForceSendFields: []string{"AliasIpRanges"}, + newAliasIpRanges, _ := networkInterface["aliasIpRanges"].([]interface{}) + ni := map[string]interface{}{ + "fingerprint": instNetworkInterface.Fingerprint, + "aliasIpRanges": CheckForCommonAliasIp_v2(instNetworkInterface, newAliasIpRanges), } - if commonAliasIpRanges := CheckForCommonAliasIp(instNetworkInterface, networkInterface); len(commonAliasIpRanges) > 0 { - ni.AliasIpRanges = commonAliasIpRanges - } - op, err := NewClient(config, userAgent).Instances.UpdateNetworkInterface(project, zone, instance.Name, networkName, ni).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/updateNetworkInterface?networkInterface=%s", config.ComputeBasePath, project, zone, instance.Name, networkName), + UserAgent: userAgent, + Body: ni, + }) if err != nil { return errwrap.Wrapf("Error removing alias_ip_range: {{"{{"}}err{{"}}"}}", err) } @@ -2775,12 +2971,18 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err instNetworkInterface = instance.NetworkInterfaces[i] } - networkInterfacePatchObj := &compute.NetworkInterface{ - AliasIpRanges: networkInterface.AliasIpRanges, - Fingerprint: instNetworkInterface.Fingerprint, + networkInterfacePatchObj := map[string]interface{}{ + "aliasIpRanges": networkInterface["aliasIpRanges"].([]interface{}), + "fingerprint": instNetworkInterface.Fingerprint, } - updateCall := NewClient(config, userAgent).Instances.UpdateNetworkInterface(project, zone, instance.Name, networkName, networkInterfacePatchObj).Do - op, err := updateCall() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/updateNetworkInterface?networkInterface=%s", config.ComputeBasePath, project, zone, instance.Name, networkName), + UserAgent: userAgent, + Body: networkInterfacePatchObj, + }) if err != nil { return errwrap.Wrapf("Error updating network interface: {{"{{"}}err{{"}}"}}", err) } @@ -2791,13 +2993,18 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } if !updateDuringStop && d.HasChange(prefix+".stack_type") { - - networkInterfacePatchObj := &compute.NetworkInterface{ - StackType: d.Get(prefix+".stack_type").(string), - Fingerprint: instNetworkInterface.Fingerprint, + networkInterfacePatchObj := map[string]interface{}{ + "stackType": d.Get(prefix + ".stack_type").(string), + "fingerprint": instNetworkInterface.Fingerprint, } - updateCall := NewClient(config, userAgent).Instances.UpdateNetworkInterface(project, zone, instance.Name, networkName, networkInterfacePatchObj).Do - op, err := updateCall() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/updateNetworkInterface?networkInterface=%s", config.ComputeBasePath, project, zone, instance.Name, networkName), + UserAgent: userAgent, + Body: networkInterfacePatchObj, + }) if err != nil { return errwrap.Wrapf("Error updating network interface: {{"{{"}}err{{"}}"}}", err) } @@ -2808,13 +3015,18 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } if !updateDuringStop && d.HasChange(prefix+".igmp_query") { - - networkInterfacePatchObj := &compute.NetworkInterface{ - IgmpQuery: d.Get(prefix+".igmp_query").(string), - Fingerprint: instNetworkInterface.Fingerprint, + networkInterfacePatchObj := map[string]interface{}{ + "igmpQuery": d.Get(prefix + ".igmp_query").(string), + "fingerprint": instNetworkInterface.Fingerprint, } - updateCall := NewClient(config, userAgent).Instances.UpdateNetworkInterface(project, zone, instance.Name, networkName, networkInterfacePatchObj).Do - op, err := updateCall() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/updateNetworkInterface?networkInterface=%s", config.ComputeBasePath, project, zone, instance.Name, networkName), + UserAgent: userAgent, + Body: networkInterfacePatchObj, + }) if err != nil { return errwrap.Wrapf("Error updating network interface: {{"{{"}}err{{"}}"}}", err) } @@ -2825,13 +3037,18 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } if !updateDuringStop && d.HasChange(prefix+".ipv6_address") { - - networkInterfacePatchObj := &compute.NetworkInterface{ - Ipv6Address: d.Get(prefix+".ipv6_address").(string), - Fingerprint: instNetworkInterface.Fingerprint, + networkInterfacePatchObj := map[string]interface{}{ + "ipv6Address": d.Get(prefix + ".ipv6_address").(string), + "fingerprint": instNetworkInterface.Fingerprint, } - updateCall := NewClient(config, userAgent).Instances.UpdateNetworkInterface(project, zone, instance.Name, networkName, networkInterfacePatchObj).Do - op, err := updateCall() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/updateNetworkInterface?networkInterface=%s", config.ComputeBasePath, project, zone, instance.Name, networkName), + UserAgent: userAgent, + Body: networkInterfacePatchObj, + }) if err != nil { return errwrap.Wrapf("Error updating network interface: {{"{{"}}err{{"}}"}}", err) } @@ -2842,13 +3059,18 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } if !updateDuringStop && d.HasChange(prefix+".internal_ipv6_prefix_length") { - - networkInterfacePatchObj := &compute.NetworkInterface{ - InternalIpv6PrefixLength: d.Get(prefix+".internal_ipv6_prefix_length").(int64), - Fingerprint: instNetworkInterface.Fingerprint, + networkInterfacePatchObj := map[string]interface{}{ + "internalIpv6PrefixLength": int64(d.Get(prefix + ".internal_ipv6_prefix_length").(int)), + "fingerprint": instNetworkInterface.Fingerprint, } - updateCall := NewClient(config, userAgent).Instances.UpdateNetworkInterface(project, zone, instance.Name, networkName, networkInterfacePatchObj).Do - op, err := updateCall() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/updateNetworkInterface?networkInterface=%s", config.ComputeBasePath, project, zone, instance.Name, networkName), + UserAgent: userAgent, + Body: networkInterfacePatchObj, + }) if err != nil { return errwrap.Wrapf("Error updating network interface: {{"{{"}}err{{"}}"}}", err) } @@ -2860,24 +3082,26 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if updateDuringStop { // Lets be explicit about what we are changing in the patch call - networkInterfacePatchObj := &compute.NetworkInterface{ - Network: networkInterface.Network, - Subnetwork: networkInterface.Subnetwork, - AliasIpRanges: networkInterface.AliasIpRanges, + networkInterfacePatchObj := map[string]interface{}{ + "network": networkInterface["network"].(string), + "subnetwork": networkInterface["subnetwork"].(string), + } + if aliasIps, ok := networkInterface["aliasIpRanges"].([]interface{}); ok && len(aliasIps) > 0 { + networkInterfacePatchObj["aliasIpRanges"] = aliasIps } // network_ip can be inferred if not declared. Let's only patch if it's being changed by user // otherwise this could fail if the network ip is not compatible with the new Subnetwork/Network. if d.HasChange(prefix + ".network_ip") { - networkInterfacePatchObj.NetworkIP = networkInterface.NetworkIP + networkInterfacePatchObj["networkIP"] = networkInterface["networkIP"].(string) } if d.HasChange(prefix+".internal_ipv6_prefix_length") { - networkInterfacePatchObj.Ipv6Address = networkInterface.Ipv6Address + networkInterfacePatchObj["ipv6Address"] = networkInterface["ipv6Address"].(string) } if d.HasChange(prefix+".ipv6_address") { - networkInterfacePatchObj.Ipv6Address = networkInterface.Ipv6Address + networkInterfacePatchObj["ipv6Address"] = networkInterface["ipv6Address"].(string) } // Access config can run into some issues since we can't tell the difference between @@ -2886,21 +3110,8 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err // with the subnetwork/network we are transitioning to. Due to this we only change access // configs if we notice the configuration (user intent) changes. accessConfigsHaveChanged := d.HasChange(prefix + ".access_config") - - acMaps := make([]interface{}, len(networkInterface.AccessConfigs)) - for j, ac := range networkInterface.AccessConfigs { - acMap, err := tpgresource.ConvertToMap(ac) - if err != nil { - return fmt.Errorf("Error converting access config to map: %w", err) - } - acMaps[j] = acMap - } - niPatchMap, err := tpgresource.ConvertToMap(networkInterfacePatchObj) - if err != nil { - return fmt.Errorf("Error converting network interface patch object to map: %w", err) - } - - updateCall := computeInstanceCreateUpdateWhileStoppedCall(d, config, niPatchMap, acMaps, accessConfigsHaveChanged, i, project, zone, userAgent, instance.Name) + acMaps := networkInterface["accessConfigs"].([]interface{}) + updateCall := computeInstanceCreateUpdateWhileStoppedCall(d, config, networkInterfacePatchObj, acMaps, accessConfigsHaveChanged, i, project, zone, userAgent, instance.Name) updatesToNIWhileStopped = append(updatesToNIWhileStopped, updateCall) } } @@ -3008,7 +3219,20 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err // Detach the old disks. for hash, deviceName := range oDisks { if _, ok := nDisks[hash]; !ok { - op, err := NewClient(config, userAgent).Instances.DetachDisk(project, zone, instance.Name, deviceName).Do() + detachURL, err := transport_tpg.AddQueryParams( + fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/detachDisk", config.ComputeBasePath, project, zone, instance.Name), + map[string]string{"deviceName": deviceName}, + ) + if err != nil { + return fmt.Errorf("Error building detachDisk URL: %s", err) + } + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: detachURL, + UserAgent: userAgent, + }) if err != nil { return errwrap.Wrapf("Error detaching disk: %s", err) } @@ -3023,7 +3247,18 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err // Attach the new disks for _, disk := range attach { - op, err := NewClient(config, userAgent).Instances.AttachDisk(project, zone, instance.Name, disk).Do() + diskMap, err := tpgresource.ConvertToMap(disk) + if err != nil { + return fmt.Errorf("Error converting disk: %s", err) + } + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/attachDisk", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: diskMap, + }) if err != nil { return errwrap.Wrapf("Error attaching disk : {{"{{"}}err{{"}}"}}", err) } @@ -3038,7 +3273,23 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err // if any other boot_disk fields will be added here this should be wrapped in if d.HasChange("boot_disk") if d.HasChange("boot_disk.0.auto_delete") { - op, err := NewClient(config, userAgent).Instances.SetDiskAutoDelete(project, zone, instance.Name, d.Get("boot_disk.0.auto_delete").(bool), d.Get("boot_disk.0.device_name").(string)).Do() + autoDeleteURL, err := transport_tpg.AddQueryParams( + fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/setDiskAutoDelete", config.ComputeBasePath, project, zone, instance.Name), + map[string]string{ + "autoDelete": fmt.Sprintf("%v", d.Get("boot_disk.0.auto_delete").(bool)), + "deviceName": d.Get("boot_disk.0.device_name").(string), + }, + ) + if err != nil { + return fmt.Errorf("Error building setDiskAutoDelete URL: %s", err) + } + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: autoDeleteURL, + UserAgent: userAgent, + }) if err != nil { return fmt.Errorf("Error changing auto_delete: %s", err) } @@ -3067,7 +3318,20 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if d.HasChange("deletion_protection") { nDeletionProtection := d.Get("deletion_protection").(bool) - op, err := NewClient(config, userAgent).Instances.SetDeletionProtection(project, zone, d.Get("name").(string)).DeletionProtection(nDeletionProtection).Do() + deletionProtectionURL, err := transport_tpg.AddQueryParams( + fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/setDeletionProtection", config.ComputeBasePath, project, zone, d.Get("name").(string)), + map[string]string{"deletionProtection": fmt.Sprintf("%v", nDeletionProtection)}, + ) + if err != nil { + return fmt.Errorf("Error building setDeletionProtection URL: %s", err) + } + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: deletionProtectionURL, + UserAgent: userAgent, + }) if err != nil { return fmt.Errorf("Error updating deletion protection flag: %s", err) } @@ -3085,10 +3349,19 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if err != nil { return fmt.Errorf("Error retrieving instance: %s", err) } - - instance.CanIpForward = d.Get("can_ip_forward").(bool) - - op, err := NewClient(config, userAgent).Instances.Update(project, zone, instance.Name, instance).Do() + instanceMap, err := tpgresource.ConvertToMap(instance) + if err != nil { + return fmt.Errorf("Error converting instance: %s", err) + } + instanceMap["canIpForward"] = d.Get("can_ip_forward").(bool) + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PUT", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: instanceMap, + }) if err != nil { return fmt.Errorf("Error updating instance: %s", err) } @@ -3114,15 +3387,17 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err previousStatus, desiredStatus := d.GetChange("desired_status") if desiredStatus != "" { - var op *compute.Operation + var op interface{} if desiredStatus == "RUNNING" { if previousStatus == "SUSPENDED" { -{{- if ne $.TargetVersionName "ga" }} - op, err = NewClient(config, userAgent).Instances.Resume(project, zone, instance.Name, &compute.InstancesResumeRequest{}).Do() -{{- else }} - op, err = NewClient(config, userAgent).Instances.Resume(project, zone, instance.Name).Do() -{{- end }} + op, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/resume", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + }) if err != nil { return err } @@ -3133,12 +3408,24 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } } } else if desiredStatus == "TERMINATED" { - op, err = NewClient(config, userAgent).Instances.Stop(project, zone, instance.Name).Do() + op, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/stop", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + }) if err != nil { return err } } else if desiredStatus == "SUSPENDED" { - op, err = NewClient(config, userAgent).Instances.Suspend(project, zone, instance.Name).Do() + op, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/suspend", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + }) if err != nil { return err } @@ -3165,7 +3452,13 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } if statusBeforeUpdate != "TERMINATED" { - op, err := NewClient(config, userAgent).Instances.Stop(project, zone, instance.Name).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/stop", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + }) if err != nil { return errwrap.Wrapf("Error stopping instance: {{"{{"}}err{{"}}"}}", err) } @@ -3177,11 +3470,14 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } if d.HasChange("min_cpu_platform") { - minCpuPlatform := d.Get("min_cpu_platform") - req := &compute.InstancesSetMinCpuPlatformRequest{ - MinCpuPlatform: minCpuPlatform.(string), - } - op, err := NewClient(config, userAgent).Instances.SetMinCpuPlatform(project, zone, instance.Name, req).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/setMinCpuPlatform", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: map[string]interface{}{"minCpuPlatform": d.Get("min_cpu_platform").(string)}, + }) if err != nil { return err } @@ -3196,10 +3492,14 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if err != nil { return err } - req := &compute.InstancesSetMachineTypeRequest{ - MachineType: mt.RelativeLink(), - } - op, err := NewClient(config, userAgent).Instances.SetMachineType(project, zone, instance.Name, req).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/setMachineType", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: map[string]interface{}{"machineType": mt.RelativeLink()}, + }) if err != nil { return err } @@ -3211,13 +3511,20 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if d.HasChange("service_account.0.email") || scopesChange { sa := d.Get("service_account").([]interface{}) - req := &compute.InstancesSetServiceAccountRequest{ForceSendFields: []string{"email"}} + saBody := map[string]interface{}{"email": ""} if !isEmptyServiceAccountBlock(d) && len(sa) > 0 && sa[0] != nil { saMap := sa[0].(map[string]interface{}) - req.Email = saMap["email"].(string) - req.Scopes = tpgresource.CanonicalizeServiceScopes(tpgresource.ConvertStringSet(saMap["scopes"].(*schema.Set))) + saBody["email"] = saMap["email"].(string) + saBody["scopes"] = tpgresource.CanonicalizeServiceScopes(tpgresource.ConvertStringSet(saMap["scopes"].(*schema.Set))) } - op, err := NewClient(config, userAgent).Instances.SetServiceAccount(project, zone, instance.Name, req).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/setServiceAccount", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: saBody, + }) if err != nil { return err } @@ -3228,11 +3535,14 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } if d.HasChange("enable_display") { - req := &compute.DisplayDevice{ - EnableDisplay: d.Get("enable_display").(bool), - ForceSendFields: []string{"EnableDisplay"}, - } - op, err := NewClient(config, userAgent).Instances.UpdateDisplayDevice(project, zone, instance.Name, req).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/updateDisplayDevice", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: map[string]interface{}{"enableDisplay": d.Get("enable_display").(bool)}, + }) if err != nil { return fmt.Errorf("Error updating display device: %s", err) } @@ -3243,9 +3553,19 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } if d.HasChange("shielded_instance_config") { - shieldedVmConfig := expandShieldedVmConfigs(d) - - op, err := NewClient(config, userAgent).Instances.UpdateShieldedInstanceConfig(project, zone, instance.Name, shieldedVmConfig).Do() + prefix := "shielded_instance_config.0" + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/updateShieldedInstanceConfig", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: map[string]interface{}{ + "enableSecureBoot": d.Get(prefix + ".enable_secure_boot").(bool), + "enableVtpm": d.Get(prefix + ".enable_vtpm").(bool), + "enableIntegrityMonitoring": d.Get(prefix + ".enable_integrity_monitoring").(bool), + }, + }) if err != nil { return fmt.Errorf("Error updating shielded vm config: %s", err) } @@ -3258,13 +3578,19 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err } if bootRequiredSchedulingChange { - scheduling, err := expandScheduling(d.Get("scheduling")) + scheduling, err := expandScheduling_v2(d.Get("scheduling")) if err != nil { return fmt.Errorf("Error creating request data to update scheduling: %s", err) } - op, err := NewClient(config, userAgent).Instances.SetScheduling( - project, zone, instance.Name, scheduling).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/setScheduling", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: scheduling, + }) if err != nil { return fmt.Errorf("Error updating scheduling policy: %s", err) } @@ -3286,10 +3612,19 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err if err != nil { return fmt.Errorf("Error retrieving instance: %s", err) } - - instance.AdvancedMachineFeatures = expandAdvancedMachineFeatures(d) - - op, err := NewClient(config, userAgent).Instances.Update(project, zone, instance.Name, instance).Do() + instanceMap, err := tpgresource.ConvertToMap(instance) + if err != nil { + return fmt.Errorf("Error converting instance: %s", err) + } + instanceMap["advancedMachineFeatures"] = expandAdvancedMachineFeatures_v2(d) + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PUT", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: instanceMap, + }) if err != nil { return fmt.Errorf("Error updating instance: %s", err) } @@ -3356,7 +3691,7 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err return resourceComputeInstanceRead(d, meta) } -func startInstanceOperation(d *schema.ResourceData, config *transport_tpg.Config) (*compute.Operation, error) { +func startInstanceOperation(d *schema.ResourceData, config *transport_tpg.Config) (interface{}, error) { project, err := tpgresource.GetProject(d, config) if err != nil { return nil, err @@ -3367,7 +3702,7 @@ func startInstanceOperation(d *schema.ResourceData, config *transport_tpg.Config return nil, err } - userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) if err != nil { return nil, err } @@ -3385,25 +3720,40 @@ func startInstanceOperation(d *schema.ResourceData, config *transport_tpg.Config return nil, err } - var encrypted []*compute.CustomerEncryptionKeyProtectedDisk + var encryptedDisks []interface{} for _, disk := range instanceFromConfig.Disks { if disk.DiskEncryptionKey != nil { - key := compute.CustomerEncryptionKey{RawKey: disk.DiskEncryptionKey.RawKey, KmsKeyName: disk.DiskEncryptionKey.KmsKeyName} - eDisk := compute.CustomerEncryptionKeyProtectedDisk{Source: disk.Source, DiskEncryptionKey: &key} - encrypted = append(encrypted, &eDisk) + keyMap := map[string]interface{}{} + if v := disk.DiskEncryptionKey.RawKey; v != "" { + keyMap["rawKey"] = v + } + if v := disk.DiskEncryptionKey.KmsKeyName; v != "" { + keyMap["kmsKeyName"] = v + } + encryptedDisks = append(encryptedDisks, map[string]interface{}{ + "source": disk.Source, + "diskEncryptionKey": keyMap, + }) } } - var op *compute.Operation - - if len(encrypted) > 0 { - request := compute.InstancesStartWithEncryptionKeyRequest{Disks: encrypted} - op, err = NewClient(config, userAgent).Instances.StartWithEncryptionKey(project, zone, instance.Name, &request).Do() - } else { - op, err = NewClient(config, userAgent).Instances.Start(project, zone, instance.Name).Do() + if len(encryptedDisks) > 0 { + return transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/startWithEncryptionKey", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + Body: map[string]interface{}{"disks": encryptedDisks}, + }) } - - return op, err + return transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s/start", config.ComputeBasePath, project, zone, instance.Name), + UserAgent: userAgent, + }) } func expandAttachedDisk(diskConfig map[string]interface{}, d *schema.ResourceData, meta interface{}) (*compute.AttachedDisk, error) { @@ -3628,7 +3978,13 @@ func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) err if d.Get("deletion_protection").(bool) { return fmt.Errorf("Cannot delete instance %s: instance Deletion Protection is enabled. Set deletion_protection to false for this resource and run \"terraform apply\" before attempting to delete it.", d.Get("name").(string)) } else { - op, err := NewClient(config, userAgent).Instances.Delete(project, zone, d.Get("name").(string)).Do() + op, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/instances/%s", config.ComputeBasePath, project, zone, d.Get("name").(string)), + UserAgent: userAgent, + }) if err != nil { return fmt.Errorf("Error deleting instance: %s", err) } @@ -3637,9 +3993,16 @@ func resourceComputeInstanceDelete(d *schema.ResourceData, meta interface{}) err opErr := ComputeOperationWaitTime(config, op, project, "instance to delete", userAgent, d.Timeout(schema.TimeoutDelete)) if opErr != nil { // Refresh operation to check status - op, _ = NewClient(config, userAgent).ZoneOperations.Get(project, zone, strconv.FormatUint(op.Id, 10)).Do() + opName, _ := op["name"].(string) + refreshedOp, _ := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: project, + RawURL: fmt.Sprintf("%sprojects/%s/zones/%s/operations/%s", config.ComputeBasePath, project, zone, opName), + UserAgent: userAgent, + }) // Do not return an error if the operation actually completed - if op == nil || op.Status != "DONE" { + if status, ok := refreshedOp["status"].(string); !ok || status != "DONE" { return opErr } } @@ -4084,6 +4447,28 @@ func CheckForCommonAliasIp(old, new *compute.NetworkInterface) []*compute.AliasI return resultAliasIpRanges } +func CheckForCommonAliasIp_v2(old *compute.NetworkInterface, newAliasIpRanges []interface{}) []interface{} { + newAliasIpMap := make(map[string]bool) + for _, raw := range newAliasIpRanges { + if m, ok := raw.(map[string]interface{}); ok { + if cidr, ok := m["ipCidrRange"].(string); ok { + newAliasIpMap[cidr] = true + } + } + } + result := make([]interface{}, 0) + for _, val := range old.AliasIpRanges { + if newAliasIpMap[val.IpCidrRange] { + entry := map[string]interface{}{"ipCidrRange": val.IpCidrRange} + if val.SubnetworkRangeName != "" { + entry["subnetworkRangeName"] = val.SubnetworkRangeName + } + result = append(result, entry) + } + } + return result +} + func updateDisk(d *schema.ResourceData, config *transport_tpg.Config, userAgent, project, patchUrl string, obj map[string]interface{}) error { billingProject := project url, err := tpgresource.ReplaceVars(d, config, patchUrl)