Skip to content

Commit

Permalink
add external ipv6 support (GoogleCloudPlatform#5241)
Browse files Browse the repository at this point in the history
* add external ipv6 support for subnetworks

* add support for ipv6 to instance network interfaces

* add documentation

* update from review comments

* make stack_type O+C

* make stack_type O+C on subnetwork too. oops.

* update per review comments
  • Loading branch information
megan07 authored and khajduczenia committed Oct 12, 2021
1 parent c7d34e4 commit e9cc9ce
Show file tree
Hide file tree
Showing 10 changed files with 388 additions and 13 deletions.
29 changes: 29 additions & 0 deletions mmv1/products/compute/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14728,6 +14728,35 @@ objects:
https://cloud.google.com/vpc/docs/flow-logs#filtering for details on how to format this field.
The default value is 'true', which evaluates to include everything.
default_value: "true"
- !ruby/object:Api::Type::Enum
name: 'stackType'
update_verb: :PATCH
update_url: projects/{{project}}/regions/{{region}}/subnetworks/{{name}}
fingerprint_name: 'fingerprint'
values:
- :IPV4_ONLY
- :IPV4_IPV6
description: |
The stack type for this subnet to identify whether the IPv6 feature is enabled or not.
If not specified IPV4_ONLY will be used.
- !ruby/object:Api::Type::Enum
name: 'ipv6AccessType'
values:
- :EXTERNAL
description: |
The access type of IPv6 address this subnet holds. It's immutable and can only be specified during creation
or the first time the subnet is updated into IPV4_IPV6 dual stack. If the ipv6_type is EXTERNAL then this subnet
cannot enable direct path.
- !ruby/object:Api::Type::String
name: 'ipv6CidrRange'
output: true
description: |
The range of internal IPv6 addresses that are owned by this subnetwork.
- !ruby/object:Api::Type::String
name: 'externalIpv6Prefix'
output: true
description: |
The range of external IPv6 addresses that are owned by this subnetwork.
references: !ruby/object:Api::Resource::ReferenceLinks
guides:
'Private Google Access':
Expand Down
8 changes: 8 additions & 0 deletions mmv1/products/compute/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2685,6 +2685,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides
exclude: true
logConfig.metadataFields: !ruby/object:Overrides::Terraform::PropertyOverride
is_set: true
stackType: !ruby/object:Overrides::Terraform::PropertyOverride
default_from_api: true
ipCidrRange: !ruby/object:Overrides::Terraform::PropertyOverride
validation: !ruby/object:Provider::Terraform::Validation
function: 'validateIpCidrRange'
Expand Down Expand Up @@ -2721,6 +2723,12 @@ overrides: !ruby/object:Overrides::ResourceOverrides
vars:
subnetwork_name: "l7lb-test-subnetwork"
network_name: "l7lb-test-network"
- !ruby/object:Provider::Terraform::Examples
name: "subnetwork_ipv6"
primary_resource_id: "subnetwork-ipv6"
vars:
subnetwork_name: "ipv6-test-subnetwork"
network_name: "ipv6-test-network"
TargetHttpProxy: !ruby/object:Overrides::Terraform::ResourceOverride
examples:
- !ruby/object:Provider::Terraform::Examples
Expand Down
16 changes: 16 additions & 0 deletions mmv1/templates/terraform/examples/subnetwork_ipv6.tf.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
resource "google_compute_subnetwork" "subnetwork-ipv6" {
name = "<%= ctx[:vars]['subnetwork_name'] %>"

ip_cidr_range = "10.0.0.0/22"
region = "us-west2"

stack_type = "IPV4_IPV6"
ipv6_access_type = "EXTERNAL"

network = google_compute_network.custom-test.id
}

resource "google_compute_network" "custom-test" {
name = "<%= ctx[:vars]['network_name'] %>"
auto_create_subnetworks = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,51 @@ func resourceComputeInstance() *schema.Resource {
},
},
},

"stack_type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice([]string{"IPV4_ONLY", "IPV4_IPV6", ""}, false),
Description: `The stack type for this network interface to identify whether the IPv6 feature is enabled or not. If not specified, IPV4_ONLY will be used.`,
},

"ipv6_access_type": {
Type: schema.TypeString,
Computed: true,
Description: `One of EXTERNAL, INTERNAL to indicate whether the IP can be accessed from the Internet. This field is always inherited from its subnetwork.`,
},

"ipv6_access_config": {
Type: schema.TypeList,
Optional: true,
Description: `An array of IPv6 access configurations for this interface. Currently, only one IPv6 access config, DIRECT_IPV6, is supported. If there is no ipv6AccessConfig specified, then this instance will have no external IPv6 Internet access.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"network_tier": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"PREMIUM"}, false),
Description: `The service-level to be provided for IPv6 traffic when the subnet has an external subnet. Only PREMIUM tier is valid for IPv6`,
},
"public_ptr_domain_name": {
Type: schema.TypeString,
Optional: true,
Description: `The domain name to be used when creating DNSv6 records for the external IPv6 ranges.`,
},
"external_ipv6": {
Type: schema.TypeString,
Computed: true,
Description: `The first IPv6 address of the external IPv6 range associated with this instance, prefix length is stored in externalIpv6PrefixLength in ipv6AccessConfig. The field is output only, an IPv6 address from a subnetwork associated with the instance will be allocated dynamically.`,
},
"external_ipv6_prefix_length": {
Type: schema.TypeString,
Computed: true,
Description: `The prefix length of the external IPv6 range.`,
},
},
},
},
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,53 @@ func resourceComputeInstanceTemplate() *schema.Resource {
},
},
},

"stack_type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice([]string{"IPV4_ONLY", "IPV4_IPV6", ""}, false),
Description: `The stack type for this network interface to identify whether the IPv6 feature is enabled or not. If not specified, IPV4_ONLY will be used.`,
},

"ipv6_access_type": {
Type: schema.TypeString,
Computed: true,
Description: `One of EXTERNAL, INTERNAL to indicate whether the IP can be accessed from the Internet. This field is always inherited from its subnetwork.`,
},

"ipv6_access_config": {
Type: schema.TypeList,
Optional: true,
Description: `An array of IPv6 access configurations for this interface. Currently, only one IPv6 access config, DIRECT_IPV6, is supported. If there is no ipv6AccessConfig specified, then this instance will have no external IPv6 Internet access.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"network_tier": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"PREMIUM"}, false),
Description: `The service-level to be provided for IPv6 traffic when the subnet has an external subnet. Only PREMIUM tier is valid for IPv6`,
},
// Possibly configurable- this was added so we don't break if it's inadvertently set
// (assuming the same ass access config)
"public_ptr_domain_name": {
Type: schema.TypeString,
Computed: true,
Description: `The domain name to be used when creating DNSv6 records for the external IPv6 ranges.`,
},
"external_ipv6": {
Type: schema.TypeString,
Computed: true,
Description: `The first IPv6 address of the external IPv6 range associated with this instance, prefix length is stored in externalIpv6PrefixLength in ipv6AccessConfig. The field is output only, an IPv6 address from a subnetwork associated with the instance will be allocated dynamically.`,
},
"external_ipv6_prefix_length": {
Type: schema.TypeString,
Computed: true,
Description: `The prefix length of the external IPv6 range.`,
},
},
},
},
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,32 @@ func TestAccComputeInstanceTemplate_IP(t *testing.T) {
})
}

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

var instanceTemplate compute.InstanceTemplate

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeInstanceTemplateDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccComputeInstanceTemplate_ipv6(randString(t, 10)),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceTemplateExists(
t, "google_compute_instance_template.foobar", &instanceTemplate),
),
},
{
ResourceName: "google_compute_instance_template.foobar",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

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

Expand Down Expand Up @@ -1586,6 +1612,59 @@ resource "google_compute_instance_template" "foobar" {
`, suffix, suffix)
}

func testAccComputeInstanceTemplate_ipv6(suffix string) string {
return fmt.Sprintf(`
resource "google_compute_address" "foo" {
name = "tf-test-instance-template-%s"
}

data "google_compute_image" "my_image" {
family = "debian-9"
project = "debian-cloud"
}

resource "google_compute_network" "foo" {
name = "tf-test-network-%s"
auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "subnetwork-ipv6" {
name = "tf-test-subnetwork-%s"

ip_cidr_range = "10.0.0.0/22"
region = "us-west2"

stack_type = "IPV4_IPV6"
ipv6_access_type = "EXTERNAL"

network = google_compute_network.foo.id
}

resource "google_compute_instance_template" "foobar" {
name = "tf-test-instance-template-%s"
machine_type = "e2-medium"
region = "us-west2"
tags = ["foo", "bar"]

disk {
source_image = data.google_compute_image.my_image.self_link
}

network_interface {
subnetwork = google_compute_subnetwork.subnetwork-ipv6.name
stack_type = "IPV4_IPV6"
ipv6_access_config {
network_tier = "PREMIUM"
}
}

metadata = {
foo = "bar"
}
}
`, suffix, suffix, suffix, suffix)
}

func testAccComputeInstanceTemplate_networkTier(suffix string) string {
return fmt.Sprintf(`
data "google_compute_image" "my_image" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,35 @@ func TestAccComputeInstance_IP(t *testing.T) {
})
}

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

var instance compute.Instance
var ipName = fmt.Sprintf("tf-test-%s", randString(t, 10))
var instanceName = fmt.Sprintf("tf-test-%s", randString(t, 10))
var ptrName = 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: testAccComputeInstance_ipv6(ipName, instanceName, ptrName),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeInstanceExists(
t, "google_compute_instance.foobar", &instance),
),
},
{
ResourceName: "google_compute_instance.foobar",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

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

Expand Down Expand Up @@ -3326,6 +3355,62 @@ resource "google_compute_instance" "foobar" {
`, ip, instance)
}

func testAccComputeInstance_ipv6(ip, instance, record string) string {
return fmt.Sprintf(`
data "google_compute_image" "my_image" {
family = "debian-9"
project = "debian-cloud"
}

resource "google_compute_subnetwork" "subnetwork-ipv6" {
name = "%s-subnetwork"

ip_cidr_range = "10.0.0.0/22"
region = "us-west2"

stack_type = "IPV4_IPV6"
ipv6_access_type = "EXTERNAL"

network = google_compute_network.custom-test.id
}

resource "google_compute_network" "custom-test" {
name = "%s-network"
auto_create_subnetworks = false
}

resource "google_compute_address" "foo" {
name = "%s"
}

resource "google_compute_instance" "foobar" {
name = "%s"
machine_type = "e2-medium"
zone = "us-west2-a"
tags = ["foo", "bar"]

boot_disk {
initialize_params {
image = data.google_compute_image.my_image.self_link
}
}

network_interface {
subnetwork = google_compute_subnetwork.subnetwork-ipv6.name
stack_type = "IPV4_IPV6"
ipv6_access_config {
network_tier = "PREMIUM"
public_ptr_domain_name = "%s.gcp.tfacc.hashicorptest.com."
}
}

metadata = {
foo = "bar"
}
}
`, instance, instance, ip, instance, record)
}

func testAccComputeInstance_PTRRecord(record, instance string) string {
return fmt.Sprintf(`
data "google_compute_image" "my_image" {
Expand Down
Loading

0 comments on commit e9cc9ce

Please sign in to comment.