Skip to content

Commit 321a31e

Browse files
modular-magicianLuca Prete
and
Luca Prete
authored
Add internal IPV6 support for GoogleComputeAddress and ComputeInstance resources (#8432) (#15780)
* Address added ipVersion parameter for reserving both ipv4 and ipv6 addresses Instance added ipv6Address for assigning static ipv6 address to compute instance resource ComputeInstance added test for internal ipv6 new field * Fix yaml syntax issues * Fixes * Add ipv6PrefixLength and fix tests * added ipv6PrefixLength and ipv6Address to compute_instance_template * added ipv6PrefixLength and ipv6Address to region_compute_instance_template --------- Signed-off-by: Modular Magician <[email protected]> Co-authored-by: Luca Prete <[email protected]>
1 parent 31e6cd1 commit 321a31e

7 files changed

+235
-0
lines changed

.changelog/8432.txt

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
```release-note:enhancement
2+
compute: added `ipVersion` field to `google_compute_address` resource
3+
compute: added `ipv6Address` field to `google_compute_instance` resource
4+
```

google/services/compute/compute_instance_helpers.go

+2
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ func flattenNetworkInterfaces(d *schema.ResourceData, config *transport_tpg.Conf
271271
"nic_type": iface.NicType,
272272
"stack_type": iface.StackType,
273273
"ipv6_access_config": flattenIpv6AccessConfigs(iface.Ipv6AccessConfigs),
274+
"ipv6_address": iface.Ipv6Address,
274275
"queue_count": iface.QueueCount,
275276
}
276277
// Instance template interfaces never have names, so they're absent
@@ -368,6 +369,7 @@ func expandNetworkInterfaces(d tpgresource.TerraformResourceData, config *transp
368369
StackType: data["stack_type"].(string),
369370
QueueCount: int64(data["queue_count"].(int)),
370371
Ipv6AccessConfigs: expandIpv6AccessConfigs(data["ipv6_access_config"].([]interface{})),
372+
Ipv6Address: data["ipv6_address"].(string),
371373
}
372374
}
373375
return ifaces, nil

google/services/compute/resource_compute_address_test.go

+50
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,53 @@ resource "google_compute_address" "foobar" {
112112
}
113113
`, i)
114114
}
115+
116+
func TestAccComputeAddress_internalIpv6(t *testing.T) {
117+
acctest.VcrTest(t, resource.TestCase{
118+
PreCheck: func() { acctest.AccTestPreCheck(t) },
119+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
120+
CheckDestroy: testAccCheckComputeAddressDestroyProducer(t),
121+
Steps: []resource.TestStep{
122+
{
123+
Config: testAccComputeAddress_internalIpv6(acctest.RandString(t, 10)),
124+
},
125+
{
126+
ResourceName: "google_compute_address.ipv6",
127+
ImportState: true,
128+
ImportStateVerify: true,
129+
},
130+
},
131+
})
132+
}
133+
134+
func testAccComputeAddress_internalIpv6(i string) string {
135+
return fmt.Sprintf(`
136+
resource "google_compute_network" "default" {
137+
name = "tf-test-network-test-%s"
138+
enable_ula_internal_ipv6 = true
139+
auto_create_subnetworks = false
140+
}
141+
142+
resource "google_compute_subnetwork" "foo" {
143+
name = "subnetwork-test-%s"
144+
ip_cidr_range = "10.0.0.0/16"
145+
region = "us-east1"
146+
network = google_compute_network.default.self_link
147+
stack_type = "IPV4_IPV6"
148+
ipv6_access_type = "INTERNAL"
149+
}
150+
151+
resource "google_compute_address" "ipv6" {
152+
name = "tf-test-address-internal-ipv6-%s"
153+
subnetwork = google_compute_subnetwork.foo.self_link
154+
region = "us-east1"
155+
address_type = "INTERNAL"
156+
purpose = "GCE_ENDPOINT"
157+
ip_version = "IPV6"
158+
}
159+
`,
160+
i, // google_compute_network.default name
161+
i, // google_compute_subnetwork.foo name
162+
i, // google_compute_address.ipv6
163+
)
164+
}

google/services/compute/resource_compute_instance.go

+57
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,21 @@ func ResourceComputeInstance() *schema.Resource {
430430
},
431431
},
432432

433+
"internal_ipv6_prefix_length": {
434+
Type: schema.TypeInt,
435+
Optional: true,
436+
Computed: true,
437+
Description: `The prefix length of the primary internal IPv6 range.`,
438+
},
439+
440+
"ipv6_address": {
441+
Type: schema.TypeString,
442+
Optional: true,
443+
Computed: true,
444+
DiffSuppressFunc: ipv6RepresentationDiffSuppress,
445+
Description: `An IPv6 internal network address for this network interface. If not specified, Google Cloud will automatically assign an internal IPv6 address from the instance's subnetwork.`,
446+
},
447+
433448
"queue_count": {
434449
Type: schema.TypeInt,
435450
Optional: true,
@@ -1788,6 +1803,40 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
17881803
}
17891804
}
17901805

1806+
if !updateDuringStop && d.HasChange(prefix+".ipv6_address") {
1807+
1808+
networkInterfacePatchObj := &compute.NetworkInterface{
1809+
Ipv6Address: d.Get(prefix + ".ipv6_address").(string),
1810+
Fingerprint: instNetworkInterface.Fingerprint,
1811+
}
1812+
updateCall := config.NewComputeClient(userAgent).Instances.UpdateNetworkInterface(project, zone, instance.Name, networkName, networkInterfacePatchObj).Do
1813+
op, err := updateCall()
1814+
if err != nil {
1815+
return errwrap.Wrapf("Error updating network interface: {{err}}", err)
1816+
}
1817+
opErr := ComputeOperationWaitTime(config, op, project, "network interface to update", userAgent, d.Timeout(schema.TimeoutUpdate))
1818+
if opErr != nil {
1819+
return opErr
1820+
}
1821+
}
1822+
1823+
if !updateDuringStop && d.HasChange(prefix+".internal_ipv6_prefix_length") {
1824+
1825+
networkInterfacePatchObj := &compute.NetworkInterface{
1826+
InternalIpv6PrefixLength: d.Get(prefix + ".internal_ipv6_prefix_length").(int64),
1827+
Fingerprint: instNetworkInterface.Fingerprint,
1828+
}
1829+
updateCall := config.NewComputeClient(userAgent).Instances.UpdateNetworkInterface(project, zone, instance.Name, networkName, networkInterfacePatchObj).Do
1830+
op, err := updateCall()
1831+
if err != nil {
1832+
return errwrap.Wrapf("Error updating network interface: {{err}}", err)
1833+
}
1834+
opErr := ComputeOperationWaitTime(config, op, project, "network interface to update", userAgent, d.Timeout(schema.TimeoutUpdate))
1835+
if opErr != nil {
1836+
return opErr
1837+
}
1838+
}
1839+
17911840
if updateDuringStop {
17921841
// Lets be explicit about what we are changing in the patch call
17931842
networkInterfacePatchObj := &compute.NetworkInterface{
@@ -1802,6 +1851,14 @@ func resourceComputeInstanceUpdate(d *schema.ResourceData, meta interface{}) err
18021851
networkInterfacePatchObj.NetworkIP = networkInterface.NetworkIP
18031852
}
18041853

1854+
if d.HasChange(prefix + ".internal_ipv6_prefix_length") {
1855+
networkInterfacePatchObj.Ipv6Address = networkInterface.Ipv6Address
1856+
}
1857+
1858+
if d.HasChange(prefix + ".ipv6_address") {
1859+
networkInterfacePatchObj.Ipv6Address = networkInterface.Ipv6Address
1860+
}
1861+
18051862
// Access config can run into some issues since we can't tell the difference between
18061863
// the users declared intent (config within their hcl file) and what we have inferred from the
18071864
// server (terraform state). Access configs contain an ip subproperty that can be incompatible

google/services/compute/resource_compute_instance_template.go

+13
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,19 @@ Google Cloud KMS.`,
533533
},
534534
},
535535
},
536+
"internal_ipv6_prefix_length": {
537+
Type: schema.TypeInt,
538+
Optional: true,
539+
Computed: true,
540+
Description: `The prefix length of the primary internal IPv6 range.`,
541+
},
542+
"ipv6_address": {
543+
Type: schema.TypeString,
544+
Optional: true,
545+
Computed: true,
546+
DiffSuppressFunc: ipv6RepresentationDiffSuppress,
547+
Description: `An IPv6 internal network address for this network interface. If not specified, Google Cloud will automatically assign an internal IPv6 address from the instance's subnetwork.`,
548+
},
536549
"queue_count": {
537550
Type: schema.TypeInt,
538551
Optional: true,

google/services/compute/resource_compute_instance_test.go

+96
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,31 @@ func TestAccComputeInstance_ipv6ExternalReservation(t *testing.T) {
344344
})
345345
}
346346

347+
func TestAccComputeInstance_internalIPv6(t *testing.T) {
348+
t.Parallel()
349+
350+
var instance compute.Instance
351+
var ipName = fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
352+
var instanceName = fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
353+
354+
acctest.VcrTest(t, resource.TestCase{
355+
PreCheck: func() { acctest.AccTestPreCheck(t) },
356+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
357+
CheckDestroy: testAccCheckComputeInstanceDestroyProducer(t),
358+
Steps: []resource.TestStep{
359+
{
360+
Config: testAccComputeInstance_internalIpv6(ipName, instanceName),
361+
Check: resource.ComposeTestCheckFunc(
362+
testAccCheckComputeInstanceExists(
363+
t, "google_compute_instance.foobar", &instance),
364+
testAccCheckComputeInstanceIpv6AccessConfigHasInternalIPv6(&instance),
365+
),
366+
},
367+
computeInstanceImportStep("us-west2-a", instanceName, []string{}),
368+
},
369+
})
370+
}
371+
347372
func TestAccComputeInstance_PTRRecord(t *testing.T) {
348373
t.Parallel()
349374

@@ -2650,6 +2675,18 @@ func testAccCheckComputeInstanceIpv6AccessConfigHasExternalIPv6(instance *comput
26502675
}
26512676
}
26522677

2678+
func testAccCheckComputeInstanceIpv6AccessConfigHasInternalIPv6(instance *compute.Instance) resource.TestCheckFunc {
2679+
return func(s *terraform.State) error {
2680+
for _, i := range instance.NetworkInterfaces {
2681+
if i.Ipv6Address == "" {
2682+
return fmt.Errorf("no internal IPv6 address")
2683+
}
2684+
}
2685+
2686+
return nil
2687+
}
2688+
}
2689+
26532690
func testAccCheckComputeInstanceAccessConfigHasPTR(instance *compute.Instance) resource.TestCheckFunc {
26542691
return func(s *terraform.State) error {
26552692
for _, i := range instance.NetworkInterfaces {
@@ -3639,6 +3676,65 @@ resource "google_compute_instance" "foobar" {
36393676
`, instance, instance, ip, instance, record)
36403677
}
36413678

3679+
func testAccComputeInstance_internalIpv6(ip, instance string) string {
3680+
return fmt.Sprintf(`
3681+
data "google_compute_image" "my_image" {
3682+
family = "debian-11"
3683+
project = "debian-cloud"
3684+
}
3685+
3686+
resource "google_compute_subnetwork" "subnetwork_ipv6" {
3687+
name = "%s-subnetwork"
3688+
3689+
ip_cidr_range = "10.0.0.0/22"
3690+
region = "us-west2"
3691+
3692+
stack_type = "IPV4_IPV6"
3693+
ipv6_access_type = "INTERNAL"
3694+
3695+
network = google_compute_network.custom-test.id
3696+
}
3697+
3698+
resource "google_compute_network" "custom-test" {
3699+
name = "%s-network"
3700+
enable_ula_internal_ipv6 = true
3701+
auto_create_subnetworks = false
3702+
}
3703+
3704+
resource "google_compute_address" "ipv6" {
3705+
name = "%s"
3706+
region = "us-west2"
3707+
address_type = "INTERNAL"
3708+
purpose = "GCE_ENDPOINT"
3709+
subnetwork = google_compute_subnetwork.subnetwork_ipv6.id
3710+
ip_version = "IPV6"
3711+
}
3712+
3713+
resource "google_compute_instance" "foobar" {
3714+
name = "%s"
3715+
machine_type = "e2-medium"
3716+
zone = "us-west2-a"
3717+
tags = ["foo", "bar"]
3718+
3719+
boot_disk {
3720+
initialize_params {
3721+
image = data.google_compute_image.my_image.self_link
3722+
}
3723+
}
3724+
3725+
network_interface {
3726+
subnetwork = google_compute_subnetwork.subnetwork_ipv6.name
3727+
stack_type = "IPV4_IPV6"
3728+
ipv6_address = google_compute_address.ipv6.address
3729+
}
3730+
3731+
metadata = {
3732+
foo = "bar"
3733+
}
3734+
}
3735+
`, instance, instance, ip, instance)
3736+
}
3737+
36423738
func testAccComputeInstance_ipv6ExternalReservation(instance string) string {
36433739
return fmt.Sprintf(`
36443740
resource "google_compute_address" "ipv6-address" {

google/services/compute/resource_compute_region_instance_template.go

+13
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,19 @@ Google Cloud KMS.`,
517517
},
518518
},
519519
},
520+
"internal_ipv6_prefix_length": {
521+
Type: schema.TypeInt,
522+
Optional: true,
523+
Computed: true,
524+
Description: `The prefix length of the primary internal IPv6 range.`,
525+
},
526+
"ipv6_address": {
527+
Type: schema.TypeString,
528+
Optional: true,
529+
Computed: true,
530+
DiffSuppressFunc: ipv6RepresentationDiffSuppress,
531+
Description: `An IPv6 internal network address for this network interface. If not specified, Google Cloud will automatically assign an internal IPv6 address from the instance's subnetwork.`,
532+
},
520533
"queue_count": {
521534
Type: schema.TypeInt,
522535
Optional: true,

0 commit comments

Comments
 (0)