diff --git a/.changelog/4655.txt b/.changelog/4655.txt new file mode 100644 index 00000000000..b3d3bd24d6b --- /dev/null +++ b/.changelog/4655.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +compute: added gVNIC support for `google_compute_instance_template` +``` diff --git a/google/resource_compute_instance_template.go b/google/resource_compute_instance_template.go index b7e067d8c9c..af5133b7659 100644 --- a/google/resource_compute_instance_template.go +++ b/google/resource_compute_instance_template.go @@ -325,6 +325,13 @@ func resourceComputeInstanceTemplate() *schema.Resource { Computed: true, Description: `The name of the network_interface.`, }, + "nic_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"GVNIC", "VIRTIO_NET"}, false), + Description: `The type of vNIC to be used on this interface. Possible values:GVNIC, VIRTIO_NET`, + }, "access_config": { Type: schema.TypeList, Optional: true, @@ -847,7 +854,7 @@ func resourceComputeInstanceTemplateCreate(d *schema.ResourceData, meta interfac return err } - networks, err := expandComputeInstanceTemplateNetworkInterfaces(d, config) + networks, err := expandNetworkInterfaces(d, config) if err != nil { return err } @@ -1211,7 +1218,7 @@ func resourceComputeInstanceTemplateRead(d *schema.ResourceData, meta interface{ return fmt.Errorf("Error setting project: %s", err) } if instanceTemplate.Properties.NetworkInterfaces != nil { - networkInterfaces, region, _, _, err := flattenComputeInstanceTemplateNetworkInterfaces(d, config, instanceTemplate.Properties.NetworkInterfaces) + networkInterfaces, region, _, _, err := flattenNetworkInterfaces(d, config, instanceTemplate.Properties.NetworkInterfaces) if err != nil { return err } @@ -1336,75 +1343,3 @@ func resourceComputeInstanceTemplateImportState(d *schema.ResourceData, meta int return []*schema.ResourceData{d}, nil } - -// this func could be replaced by flattenNetworkInterfaces once NicType is supported -func flattenComputeInstanceTemplateNetworkInterfaces(d *schema.ResourceData, config *Config, networkInterfaces []*computeBeta.NetworkInterface) ([]map[string]interface{}, string, string, string, error) { - flattened := make([]map[string]interface{}, len(networkInterfaces)) - var region, internalIP, externalIP string - - for i, iface := range networkInterfaces { - var ac []map[string]interface{} - ac, externalIP = flattenAccessConfigs(iface.AccessConfigs) - - subnet, err := ParseSubnetworkFieldValue(iface.Subnetwork, d, config) - if err != nil { - return nil, "", "", "", err - } - region = subnet.Region - - flattened[i] = map[string]interface{}{ - "network_ip": iface.NetworkIP, - "network": ConvertSelfLinkToV1(iface.Network), - "subnetwork": ConvertSelfLinkToV1(iface.Subnetwork), - "subnetwork_project": subnet.Project, - "access_config": ac, - "alias_ip_range": flattenAliasIpRange(iface.AliasIpRanges), - } - // Instance template interfaces never have names, so they're absent - // in the instance template network_interface schema. We want to use the - // same flattening code for both resource types, so we avoid trying to - // set the name field when it's not set at the GCE end. - if iface.Name != "" { - flattened[i]["name"] = iface.Name - } - if internalIP == "" { - internalIP = iface.NetworkIP - } - } - return flattened, region, internalIP, externalIP, nil -} - -// this func could be replaced by expandNetworkInterfaces once NicType is supported -func expandComputeInstanceTemplateNetworkInterfaces(d TerraformResourceData, config *Config) ([]*computeBeta.NetworkInterface, error) { - configs := d.Get("network_interface").([]interface{}) - ifaces := make([]*computeBeta.NetworkInterface, len(configs)) - for i, raw := range configs { - data := raw.(map[string]interface{}) - - network := data["network"].(string) - subnetwork := data["subnetwork"].(string) - if network == "" && subnetwork == "" { - return nil, fmt.Errorf("exactly one of network or subnetwork must be provided") - } - - nf, err := 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 := ParseSubnetworkFieldValueWithProjectField(subnetwork, subnetProjectField, d, config) - if err != nil { - return nil, fmt.Errorf("cannot determine self_link for subnetwork %q: %s", subnetwork, err) - } - - ifaces[i] = &computeBeta.NetworkInterface{ - NetworkIP: data["network_ip"].(string), - Network: nf.RelativeLink(), - Subnetwork: sf.RelativeLink(), - AccessConfigs: expandAccessConfigs(data["access_config"].([]interface{})), - AliasIpRanges: expandAliasIpRanges(data["alias_ip_range"].([]interface{})), - } - } - return ifaces, nil -} diff --git a/google/resource_compute_instance_template_test.go b/google/resource_compute_instance_template_test.go index d75beb67edf..5d641f590cb 100644 --- a/google/resource_compute_instance_template_test.go +++ b/google/resource_compute_instance_template_test.go @@ -925,6 +925,35 @@ func TestAccComputeInstanceTemplate_resourcePolicies(t *testing.T) { }) } +func TestAccComputeInstanceTemplate_nictype_update(t *testing.T) { + t.Parallel() + + var instanceTemplate computeBeta.InstanceTemplate + var instanceTemplateName = fmt.Sprintf("tf-test-%s", randString(t, 10)) + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeInstanceTemplate_nictype(instanceTemplateName, instanceTemplateName, "GVNIC"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists( + t, "google_compute_instance_template.foobar", &instanceTemplate), + ), + }, + { + Config: testAccComputeInstanceTemplate_nictype(instanceTemplateName, instanceTemplateName, "VIRTIO_NET"), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceTemplateExists( + t, "google_compute_instance_template.foobar", &instanceTemplate), + ), + }, + }, + }) +} + func testAccCheckComputeInstanceTemplateDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { config := googleProviderConfig(t) @@ -2301,3 +2330,60 @@ resource "google_compute_resource_policy" "foo" { } `, suffix, policyName) } + +func testAccComputeInstanceTemplate_nictype(image, instance, nictype string) string { + return fmt.Sprintf(` +resource "google_compute_image" "example" { + name = "%s" + raw_disk { + source = "https://storage.googleapis.com/bosh-gce-raw-stemcells/bosh-stemcell-97.98-google-kvm-ubuntu-xenial-go_agent-raw-1557960142.tar.gz" + } + + guest_os_features { + type = "SECURE_BOOT" + } + + guest_os_features { + type = "MULTI_IP_SUBNET" + } + + guest_os_features { + type = "GVNIC" + } +} +resource "google_compute_instance_template" "foobar" { + name = "instancet-test-%s" + machine_type = "e2-medium" + can_ip_forward = false + tags = ["foo", "bar"] + + disk { + source_image = google_compute_image.example.name + auto_delete = true + boot = true + } + + network_interface { + network = "default" + nic_type = "%s" + } + + scheduling { + preemptible = false + automatic_restart = true + } + + metadata = { + foo = "bar" + } + + service_account { + scopes = ["userinfo-email", "compute-ro", "storage-ro"] + } + + labels = { + my_label = "foobar" + } +} +`, image, instance, nictype) +} diff --git a/website/docs/r/compute_instance_template.html.markdown b/website/docs/r/compute_instance_template.html.markdown index f7436a7b66b..7d147b7b33a 100644 --- a/website/docs/r/compute_instance_template.html.markdown +++ b/website/docs/r/compute_instance_template.html.markdown @@ -359,6 +359,8 @@ The `network_interface` block supports: array of alias IP ranges for this network interface. Can only be specified for network interfaces on subnet-mode networks. Structure documented below. +* `nic_type` - (Optional) The type of vNIC to be used on this interface. Possible values: GVNIC, VIRTIO_NET. + The `access_config` block supports: * `nat_ip` - (Optional) The IP address that will be 1:1 mapped to the instance's