Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion mmv1/products/compute/Subnetwork.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,10 @@ properties:
Partial URL, as in:
* `projects/{{projectId}}/regions/region/publicDelegatedPrefixes/{{sub-pdp-name}}`
* `regions/{{region}}/publicDelegatedPrefixes/{{sub-pdp-name}}`
ignore_read: true
update_verb: PATCH
update_url: projects/{{project}}/regions/{{region}}/subnetworks/{{name}}
diff_suppress_func: tpgresource.CompareSelfLinkOrResourceName
fingerprint_name: fingerprint
is_missing_in_cai: true
- name: ipv6GceEndpoint
type: Enum
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/hashicorp/terraform-provider-google/google/acctest"
"github.com/hashicorp/terraform-provider-google/google/envvar"
"github.com/hashicorp/terraform-provider-google/google/tpgresource"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
tpgcompute "github.com/hashicorp/terraform-provider-google/google/services/compute"

Expand Down Expand Up @@ -666,6 +667,71 @@ func TestAccComputeSubnetwork_resourceManagerTags(t *testing.T) {
})
}

func TestAccComputeSubnetwork_ipv6UpdateWithPdp(t *testing.T) {
t.Parallel()

randSuffix := acctest.RandString(t, 10)
networkName := fmt.Sprintf("tf-test-net-%s", randSuffix)
subnetName := fmt.Sprintf("tf-test-sub-%s", randSuffix)
papName := fmt.Sprintf("tf-test-pap-%s", randSuffix)
pdpName := fmt.Sprintf("tf-test-pdp-%s", randSuffix)
subPdpName := fmt.Sprintf("tf-test-spdp-%s", randSuffix)

subnetResName := "google_compute_subnetwork.test_subnet"
papResName := "google_compute_public_advertised_prefix.test_pap"
pdpResName := "google_compute_public_delegated_prefix.test_root_pdp"
subPdpResName := "google_compute_public_delegated_prefix.test_sub_pdp"

context := map[string]interface{}{
"description": envvar.GetTestPublicAdvertisedPrefixDescriptionFromEnv(t),
"network_name": networkName,
"subnet_name": subnetName,
"pap_name": papName,
"pdp_name": pdpName,
"sub_pdp_name": subPdpName,
"rand_suffix": randSuffix,
"region": "us-central1",
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: resource.ComposeTestCheckFunc(
testAccCheckComputeSubnetworkDestroyProducer(t),
),
Steps: []resource.TestStep{
// Step 1: Create PAP, root PDP, Sub PDP, IPv4 Network and Subnetwork
{
Config: testAccComputeSubnetwork_ipv6PdpSetup(context),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeSubnetworkExists(t, subnetResName, new(compute.Subnetwork)),
resource.TestCheckResourceAttr(subnetResName, "name", subnetName),
resource.TestCheckResourceAttr(subnetResName, "stack_type", "IPV4_ONLY"),
resource.TestCheckResourceAttrSet(papResName, "self_link"),
resource.TestCheckResourceAttrSet(pdpResName, "self_link"),
resource.TestCheckResourceAttrSet(subPdpResName, "self_link"),
),
},
// Step 2: Update Subnetwork to Dual Stack with IP Collection
{
Config: testAccComputeSubnetwork_ipv6PdpUpdate(context),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeSubnetworkExists(t, subnetResName, new(compute.Subnetwork)),
resource.TestCheckResourceAttr(subnetResName, "stack_type", "IPV4_IPV6"),
resource.TestCheckResourceAttr(subnetResName, "ipv6_access_type", "INTERNAL"),
testAccCheckSubnetIpCollectionMatchesPdp(t, subnetResName, subPdpResName),
),
},
// Import Check
{
ResourceName: subnetResName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccComputeSubnetwork_resourceManagerTags(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_compute_network" "custom-test" {
Expand Down Expand Up @@ -1264,3 +1330,117 @@ resource "google_compute_subnetwork" "subnetwork" {
}
`, cnName, subnetworkName)
}

func testAccCheckSubnetIpCollectionMatchesPdp(t *testing.T, subnetResourceName, pdpResourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
subnetRes, ok := s.RootModule().Resources[subnetResourceName]
if !ok {
return fmt.Errorf("Not found: %s", subnetResourceName)
}
pdpRes, ok := s.RootModule().Resources[pdpResourceName]
if !ok {
return fmt.Errorf("Not found: %s", pdpResourceName)
}

ipCollection := subnetRes.Primary.Attributes["ip_collection"]
expectedPdpSelfLink := pdpRes.Primary.Attributes["self_link"]

if ipCollection == "" {
return fmt.Errorf("ip_collection is empty, expected it to be set on %s", subnetResourceName)
}

normalizedIpCollection := tpgresource.GetResourceNameFromSelfLink(ipCollection)
normalizedPdpSelfLink := tpgresource.GetResourceNameFromSelfLink(expectedPdpSelfLink)

if normalizedIpCollection != normalizedPdpSelfLink {
return fmt.Errorf("mismatch: ip_collection (%s) and PDP self_link (%s) don't match after normalization. Expected %s, got %s",
ipCollection, expectedPdpSelfLink, normalizedPdpSelfLink, normalizedIpCollection)
}

return nil
}
}

func testAccComputeSubnetwork_ipv6PdpSetup(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_compute_network" "test_network" {
name = "%{network_name}"
auto_create_subnetworks = false
}

resource "google_compute_public_advertised_prefix" "test_pap" {
name = "%{pap_name}"
ip_cidr_range = "2001:db8::/32"
description = "%{description}"
pdp_scope = "REGIONAL"
ipv6_access_type = "INTERNAL"
}

resource "google_compute_public_delegated_prefix" "test_root_pdp" {
name = "%{pdp_name}"
region = "%{region}"
parent_prefix = google_compute_public_advertised_prefix.test_pap.id
ip_cidr_range = "2001:db8::/40"
mode = "DELEGATION"
}

resource "google_compute_public_delegated_prefix" "test_sub_pdp" {
name = "%{sub_pdp_name}"
region = "%{region}"
ip_cidr_range = "2001:db8::/48"
parent_prefix = google_compute_public_delegated_prefix.test_root_pdp.id
mode = "INTERNAL_IPV6_SUBNETWORK_CREATION"
}

resource "google_compute_subnetwork" "test_subnet" {
name = "%{subnet_name}"
ip_cidr_range = "10.2.0.0/16"
region = "%{region}"
network = google_compute_network.test_network.id
stack_type = "IPV4_ONLY"
}
`, context)
}

func testAccComputeSubnetwork_ipv6PdpUpdate(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_compute_network" "test_network" {
name = "%{network_name}"
auto_create_subnetworks = false
}

resource "google_compute_public_advertised_prefix" "test_pap" {
name = "%{pap_name}"
ip_cidr_range = "2001:db8::/32"
description = "%{description}"
pdp_scope = "REGIONAL"
ipv6_access_type = "INTERNAL"
}

resource "google_compute_public_delegated_prefix" "test_root_pdp" {
name = "%{pdp_name}"
region = "%{region}"
parent_prefix = google_compute_public_advertised_prefix.test_pap.id
ip_cidr_range = "2001:db8::/40"
mode = "DELEGATION"
}

resource "google_compute_public_delegated_prefix" "test_sub_pdp" {
name = "%{sub_pdp_name}"
region = "%{region}"
ip_cidr_range = "2001:db8::/48"
parent_prefix = google_compute_public_delegated_prefix.test_root_pdp.id
mode = "INTERNAL_IPV6_SUBNETWORK_CREATION"
}

resource "google_compute_subnetwork" "test_subnet" {
name = "%{subnet_name}"
ip_cidr_range = "10.2.0.0/16"
region = "%{region}"
network = google_compute_network.test_network.id
stack_type = "IPV4_IPV6"
ipv6_access_type = "INTERNAL"
ip_collection = google_compute_public_delegated_prefix.test_sub_pdp.id
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the config here and the debug log from the test, this update step isn't actually testing an update of the ip_collection field, since the value didn't change between steps. That is, in the create step it was projects/ci-test-project-188019/regions/us-central1/subnetworks/tf-test-sub-jx0ch37msg, and in the update step it was still projects/ci-test-project-188019/regions/us-central1/subnetworks/tf-test-sub-jx0ch37msg. To trigger an update of the field, you'll need to create a new google_compute_public_delegated_prefix and reference it here.

An update of the subnetwork is happening at this point in the test, but it's because the values of stack_type and ipv6_access_type changed.

Copy link
Copy Markdown
Member Author

@dhruv-23101998 dhruv-23101998 May 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please elaborate a little more? In the previous configuration, the subnetwork doesn't have the IP collection. Here we added IP Collection and it seems to be correctly updated. projects/ci-test-project-188019/regions/us-central1/subnetworks/tf-test-sub-jx0ch37msg is the subnetwork reference and not IP collection reference. Also, in this update test case, we want to test whether the Subnetwork is correctly associated with IP collection or not. As such IP collection itself won't get any update.

Are you asking to create the google_compute_public_delegated_prefix in the update step instead of the create step? If yes, how does this matter, as we only want to test the update in the subnetwork.

Also, just for context, the subnetwork cannot be updated just with the IP collection. ip_collection, stack_type and ipv6_access_type has to be updated together. In previous configuration, IP collection was absent which we added in the next update configuration along with different stack_type and ipv6_access_type.

If I'm misunderstanding something, please feel free to let me know.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I was looking at the wrong request/response pair in the log. LGTM

}
`, context)
}
Loading