diff --git a/mmv1/products/compute/Instance.yaml b/mmv1/products/compute/Instance.yaml index 4d8111c04eae..c8de201d2765 100644 --- a/mmv1/products/compute/Instance.yaml +++ b/mmv1/products/compute/Instance.yaml @@ -497,6 +497,14 @@ properties: should be specified. # networkInterfaces.kind is not necessary for convergence. custom_expand: 'templates/terraform/custom_expand/resourceref_with_validation.go.erb' + - !ruby/object:Api::Type::ResourceRef + name: 'networkAttachment' + resource: 'networkAttachment' + min_version: beta + imports: 'selfLink' + description: | + The URL of the network attachment that this interface should connect to in the following format: + projects/{projectNumber}/regions/{region_name}/networkAttachments/{network_attachment_name}. - !ruby/object:Api::Type::NestedObject name: 'scheduling' description: Sets the scheduling options for this instance. diff --git a/mmv1/third_party/terraform/services/compute/compute_instance_helpers.go.erb b/mmv1/third_party/terraform/services/compute/compute_instance_helpers.go.erb index 655c4341db21..4f577b14cc48 100644 --- a/mmv1/third_party/terraform/services/compute/compute_instance_helpers.go.erb +++ b/mmv1/third_party/terraform/services/compute/compute_instance_helpers.go.erb @@ -440,15 +440,6 @@ func expandNetworkInterfaces(d tpgresource.TerraformResourceData, config *transp return nil, fmt.Errorf("exactly one of network, subnetwork, or network_attachment must be provided") } - - if networkAttachment != "" { - if network != "" { - return nil, fmt.Errorf("Cannot have a network provided with networkAttachment given that networkAttachment is associated with a network already") - } - if subnetwork != "" { - return nil, fmt.Errorf("Cannot have a subnetwork provided with networkAttachment given that networkAttachment is associated with a subnetwork already") - } - } <% else -%> network := data["network"].(string) diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.erb index 9ade334d7099..799176f39943 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.erb @@ -305,6 +305,17 @@ func ResourceComputeInstance() *schema.Resource { Description: `The name or self_link of the subnetwork attached to this interface.`, }, + <% if version == "beta" -%> + "network_attachment": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName, + Description: `The URL of the network attachment that this interface should connect to in the following format: projects/{projectNumber}/regions/{region_name}/networkAttachments/{network_attachment_name}.`, + }, + <% end %> + "subnetwork_project": { Type: schema.TypeString, Optional: true, @@ -1509,7 +1520,7 @@ func resourceComputeInstanceRead(d *schema.ResourceData, meta interface{}) error } <% unless version == 'ga' -%> // Add extra check on Scheduling to prevent STOP instance setting MaxRunDuration. - // When Instance being stopped, GCE will wipe out the MaxRunDuration field. + // When Instance being stopped, GCE will wipe out the MaxRunDuration field. // And Terraform has no visiblity on this field after then. Given the infrastructure // constraint, MaxRunDuration will only be supported with instance has // DELETE InstanceTerminationAction diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance_test.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_instance_test.go.erb index bdd25e99ac0e..fa9c520d4629 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance_test.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance_test.go.erb @@ -2626,6 +2626,70 @@ func testAccCheckComputeInstanceUpdateMachineType(t *testing.T, n string) resour } } +<% if version == "beta"%> +func TestAccComputeInstance_NetworkAttachment(t *testing.T) { + t.Parallel() + suffix := fmt.Sprintf("%s", acctest.RandString(t, 10)) + var instance compute.Instance + + testNetworkAttachmentName := fmt.Sprintf("tf-test-network-attachment-%s", suffix) + + // Need to have the full network attachment name in the format project/{project_id}/regions/{region_id}/networkAttachments/{testNetworkAttachmentName} + fullFormNetworkAttachmentName := fmt.Sprintf("projects/%s/regions/%s/networkAttachments/%s", envvar.GetTestProjectFromEnv(), envvar.GetTestRegionFromEnv(), testNetworkAttachmentName) + + context := map[string]interface{}{ + "suffix": (acctest.RandString(t, 10)), + "network_attachment_name": testNetworkAttachmentName, + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_networkAttachment(context), + Check: resource.ComposeTestCheckFunc( + testAccCheckComputeInstanceExists( + t, "google_compute_instance.foobar", &instance), + testAccCheckComputeInstanceHasNetworkAttachment(&instance, fmt.Sprintf("https://www.googleapis.com/compute/beta/%s", fullFormNetworkAttachmentName)), + ), + }, + }, + }) +} + +func TestAccComputeInstance_NetworkAttachmentUpdate(t *testing.T) { + t.Parallel() + suffix := acctest.RandString(t, 10) + envRegion := envvar.GetTestRegionFromEnv() + instanceName := fmt.Sprintf("tf-test-compute-instance-%s", suffix) + + networkAttachmentSelflink1 := "google_compute_network_attachment.test_network_attachment_1.self_link" + networkAttachmentSelflink2 := "google_compute_network_attachment.test_network_attachment_2.self_link" + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeInstance_networkAttachmentUpdate(networkAttachmentSelflink1, envRegion, suffix), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"allow_stopping_for_update"}), + { + Config: testAccComputeInstance_networkAttachmentUpdate(networkAttachmentSelflink2, envRegion, suffix), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"allow_stopping_for_update"}), + { + Config: testAccComputeInstance_networkAttachmentUpdate(networkAttachmentSelflink1, envRegion, suffix), + }, + computeInstanceImportStep("us-central1-a", instanceName, []string{"allow_stopping_for_update"}), + }, + }) +} +<% end %> + func testAccCheckComputeInstanceDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { config := acctest.GoogleProviderConfig(t) @@ -3204,6 +3268,19 @@ func testAccCheckComputeInstanceHasMinCpuPlatform(instance *compute.Instance, mi } } +<% if version == "beta"%> +func testAccCheckComputeInstanceHasNetworkAttachment(instance *compute.Instance, networkAttachmentName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + for _, networkInterface := range instance.NetworkInterfaces { + if networkInterface.NetworkAttachment != "" && networkInterface.NetworkAttachment == networkAttachmentName { + return nil + } + } + return fmt.Errorf("Network Attachment %s, was not found in the instance template", networkAttachmentName) + } +} +<% end %> + func testAccCheckComputeInstanceHasMachineType(instance *compute.Instance, machineType string) resource.TestCheckFunc { return func(s *terraform.State) error { instanceMachineType := tpgresource.GetResourceNameFromSelfLink(instance.MachineType) @@ -6925,3 +7002,140 @@ resource "google_compute_disk" "debian" { } `, instance, diskName, suffix, suffix, suffix) } + +<% if version =="beta"%> +func testAccComputeInstance_networkAttachment(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_compute_image" "my_image" { + family = "debian-11" + project = "debian-cloud" +} + +resource "google_compute_network" "test-network"{ + name = "tf-test-network-%{suffix}" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "test-subnetwork" { + name = "tf-test-compute-subnet-%{suffix}" + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" + network = google_compute_network.test-network.id +} + +resource "google_compute_network_attachment" "test_network_attachment" { + name = "%{network_attachment_name}" + region = "us-central1" + description = "network attachment description" + connection_preference = "ACCEPT_AUTOMATIC" + + subnetworks = [ + google_compute_subnetwork.test-subnetwork.self_link + ] +} + +resource "google_compute_instance" "foobar" { + name = "tf-test-instance-%{suffix}" + machine_type = "e2-medium" + + boot_disk { + initialize_params { + image = data.google_compute_image.my_image.id + } + } + + network_interface { + network = "default" + } + + network_interface{ + network_attachment = google_compute_network_attachment.test_network_attachment.self_link + } + + metadata = { + foo = "bar" + } +} +`, context) +} + +func testAccComputeInstance_networkAttachmentUpdate(networkAttachment, region, suffix string) string { + return fmt.Sprintf(` +data "google_compute_image" "my_image" { + family = "debian-11" + project = "debian-cloud" +} + +resource "google_compute_network" "consumer_vpc_1" { + name = "tf-test-consumer-net-1-%s" + auto_create_subnetworks = false +} + +resource "google_compute_network" "consumer_vpc_2" { + name = "tf-test-consumer-net-2-%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "consumer_subnet_1" { + name = "tf-test-consumer-subnet-1-%s" + ip_cidr_range = "10.0.0.0/16" + region = "%s" + network = google_compute_network.consumer_vpc_1.id +} + +resource "google_compute_subnetwork" "consumer_subnet_2" { + name = "tf-test-consumer-subnet-2-%s" + ip_cidr_range = "10.3.0.0/16" + region = "%s" + network = google_compute_network.consumer_vpc_2.id +} + +resource "google_compute_network_attachment" "test_network_attachment_1" { + name = "tf-test-network-attachment-1-%s" + region = "%s" + description = "network attachment 1 description" + connection_preference = "ACCEPT_AUTOMATIC" + + subnetworks = [ + google_compute_subnetwork.consumer_subnet_1.self_link + ] +} + +resource "google_compute_network_attachment" "test_network_attachment_2" { + name = "tf-test-network-attachment-2-%s" + region = "%s" + description = "network attachment 2 description" + connection_preference = "ACCEPT_AUTOMATIC" + + subnetworks = [ + google_compute_subnetwork.consumer_subnet_2.self_link + ] +} + +resource "google_compute_instance" "foobar" { + name = "tf-test-compute-instance-%s" + machine_type = "e2-medium" + zone = "%s-a" + allow_stopping_for_update = true + + boot_disk { + initialize_params { + image = data.google_compute_image.my_image.self_link + } + } + + network_interface { + network = "default" + } + + network_interface{ + network_attachment = %s + } + + metadata = { + foo = "bar" + } +} +`, suffix, suffix, suffix, region, suffix, region, suffix, region, suffix, region, suffix, region, networkAttachment) +} +<% end %> diff --git a/mmv1/third_party/terraform/website/docs/r/compute_instance.html.markdown b/mmv1/third_party/terraform/website/docs/r/compute_instance.html.markdown index aa1c929d2db8..bbb897168f8d 100644 --- a/mmv1/third_party/terraform/website/docs/r/compute_instance.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/compute_instance.html.markdown @@ -236,7 +236,7 @@ is desired, you will need to modify your state file manually using For instance, the image `centos-6-v20180104` includes its family name `centos-6`. These images can be referred by family name here. -* `labels` - (Optional) A set of key/value label pairs assigned to the disk. This +* `labels` - (Optional) A set of key/value label pairs assigned to the disk. This field is only applicable for persistent disks. * `resource_manager_tags` - (Optional) A tag is a key-value pair that can be attached to a Google Cloud resource. You can use tags to conditionally allow or deny policies based on whether a resource has a specific tag. This value is not returned by the API. In Terraform, this value cannot be updated and changing it will recreate the resource. @@ -285,6 +285,7 @@ is desired, you will need to modify your state file manually using network is in auto subnet mode, specifying the subnetwork is optional. If the network is in custom subnet mode, specifying the subnetwork is required. + * `subnetwork_project` - (Optional) The project in which the subnetwork belongs. If the `subnetwork` is a self_link, this field is ignored in favor of the project defined in the subnetwork self_link. If the `subnetwork` is a name and this @@ -306,6 +307,8 @@ is desired, you will need to modify your state file manually using * `nic_type` - (Optional) The type of vNIC to be used on this interface. Possible values: GVNIC, VIRTIO_NET. +* `network_attachment` - (Optional) [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html) The URL of the network attachment that this interface should connect to in the following format: `projects/{projectNumber}/regions/{region_name}/networkAttachments/{network_attachment_name}`. + * `stack_type` - (Optional) The stack type for this network interface to identify whether the IPv6 feature is enabled or not. Values are IPV4_IPV6 or IPV4_ONLY. If not specified, IPV4_ONLY will be used. * `ipv6_access_config` - (Optional) An array of IPv6 access configurations for this interface. @@ -331,14 +334,14 @@ specified, then this instance will have no external IPv6 Internet access. Struct The `ipv6_access_config` block supports: -* `external_ipv6` - (Optional) The first IPv6 address of the external IPv6 range associated - with this instance, prefix length is stored in externalIpv6PrefixLength in ipv6AccessConfig. - To use a static external IP address, it must be unused and in the same region as the instance's zone. +* `external_ipv6` - (Optional) The first IPv6 address of the external IPv6 range associated + with this instance, prefix length is stored in externalIpv6PrefixLength in ipv6AccessConfig. + To use a static external IP address, it must be unused and in the same region as the instance's zone. If not specified, Google Cloud will automatically assign an external IPv6 address from the instance's subnetwork. * `external_ipv6_prefix_length` - (Optional) The prefix length of the external IPv6 range. -* `name` - (Optional) The name of this access configuration. In ipv6AccessConfigs, the recommended name +* `name` - (Optional) The name of this access configuration. In ipv6AccessConfigs, the recommended name is "External IPv6". * `network_tier` - (Optional) The service-level to be provided for IPv6 traffic when the @@ -390,12 +393,12 @@ specified, then this instance will have no external IPv6 Internet access. Struct * `min_node_cpus` - (Optional) The minimum number of virtual CPUs this instance will consume when running on a sole-tenant node. -* `provisioning_model` - (Optional) Describe the type of preemptible VM. This field accepts the value `STANDARD` or `SPOT`. If the value is `STANDARD`, there will be no discount. If this is set to `SPOT`, +* `provisioning_model` - (Optional) Describe the type of preemptible VM. This field accepts the value `STANDARD` or `SPOT`. If the value is `STANDARD`, there will be no discount. If this is set to `SPOT`, `preemptible` should be `true` and `automatic_restart` should be `false`. For more info about `SPOT`, read [here](https://cloud.google.com/compute/docs/instances/spot) - -* `instance_termination_action` - (Optional) Describe the type of termination action for VM. Can be `STOP` or `DELETE`. Read more on [here](https://cloud.google.com/compute/docs/instances/create-use-spot) + +* `instance_termination_action` - (Optional) Describe the type of termination action for VM. Can be `STOP` or `DELETE`. Read more on [here](https://cloud.google.com/compute/docs/instances/create-use-spot) * `max_run_duration` - (Optional) [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html) The duration of the instance. Instance will run and be terminated after then, the termination action could be defined in `instance_termination_action`. Only support `DELETE` `instance_termination_action` at this point. Structure is [documented below](#nested_max_run_duration). The `max_run_duration` block supports: