From 636581c401d8a2ee028ce656e6d0176653bea9fc Mon Sep 17 00:00:00 2001 From: Sam Levenick Date: Thu, 13 Apr 2023 10:38:39 -0700 Subject: [PATCH] Compute public advertised prefixes (#7682) --- .../compute/PublicAdvertisedPrefix.yaml | 75 +++++++ .../compute/PublicDelegatedPrefix.yaml | 85 ++++++++ mmv1/provider/terraform/examples.rb | 3 +- .../terraform/env_var_context.go.erb | 2 + .../public_advertised_prefixes_basic.tf.erb | 6 + .../public_delegated_prefixes_basic.tf.erb | 14 ++ ...e_compute_public_advertised_prefix_test.go | 196 ++++++++++++++++++ .../terraform/utils/provider_test.go.erb | 2 +- .../utils/provider_test_utils.go.erb | 11 + 9 files changed, 392 insertions(+), 2 deletions(-) create mode 100644 mmv1/products/compute/PublicAdvertisedPrefix.yaml create mode 100644 mmv1/products/compute/PublicDelegatedPrefix.yaml create mode 100644 mmv1/templates/terraform/examples/public_advertised_prefixes_basic.tf.erb create mode 100644 mmv1/templates/terraform/examples/public_delegated_prefixes_basic.tf.erb create mode 100644 mmv1/third_party/terraform/tests/resource_compute_public_advertised_prefix_test.go diff --git a/mmv1/products/compute/PublicAdvertisedPrefix.yaml b/mmv1/products/compute/PublicAdvertisedPrefix.yaml new file mode 100644 index 000000000000..0347cffd7771 --- /dev/null +++ b/mmv1/products/compute/PublicAdvertisedPrefix.yaml @@ -0,0 +1,75 @@ +# Copyright 2023 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- !ruby/object:Api::Resource +name: 'PublicAdvertisedPrefix' +base_url: projects/{{project}}/global/publicAdvertisedPrefixes +has_self_link: true +immutable: true +description: | + Represents a PublicAdvertisedPrefix for use with bring your own IP addresses (BYOIP). +references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Using bring your own IP': 'https://cloud.google.com/vpc/docs/using-bring-your-own-ip' + api: 'https://cloud.google.com/compute/docs/reference/rest/v1/publicAdvertisedPrefixes' +async: !ruby/object:Api::OpAsync + operation: !ruby/object:Api::OpAsync::Operation + kind: 'compute#operation' + path: 'name' + base_url: 'projects/{{project}}/global/operations/{{op_id}}' + wait_ms: 1000 + result: !ruby/object:Api::OpAsync::Result + path: 'targetLink' + status: !ruby/object:Api::OpAsync::Status + path: 'status' + complete: 'DONE' + allowed: + - 'PENDING' + - 'RUNNING' + - 'DONE' + error: !ruby/object:Api::OpAsync::Error + path: 'error/errors' + message: 'message' +examples: + - !ruby/object:Provider::Terraform::Examples + name: "public_advertised_prefixes_basic" + primary_resource_id: "prefixes" + # PAPs have very low quota limits and a shared testing range so serialized tests exist in: + # resource_compute_public_advertised_prefix_test.go + skip_test: true + vars: + prefixes_name: "my-prefix" + test_env_vars: + description: :PAP_DESCRIPTION +properties: + - !ruby/object:Api::Type::String + name: 'description' + description: An optional description of this resource. + - !ruby/object:Api::Type::String + name: 'name' + description: | + Name of the resource. The name must be 1-63 characters long, and + comply with RFC1035. Specifically, the name must be 1-63 characters + long and match the regular expression `[a-z]([-a-z0-9]*[a-z0-9])?` + which means the first character must be a lowercase letter, and all + following characters must be a dash, lowercase letter, or digit, + except the last character, which cannot be a dash. + required: true + - !ruby/object:Api::Type::String + name: 'dnsVerificationIp' + description: The IPv4 address to be used for reverse DNS verification. + required: true + - !ruby/object:Api::Type::String + name: 'ipCidrRange' + description: The IPv4 address range, in CIDR format, represented by this public advertised prefix. + required: true diff --git a/mmv1/products/compute/PublicDelegatedPrefix.yaml b/mmv1/products/compute/PublicDelegatedPrefix.yaml new file mode 100644 index 000000000000..22a61b2eb3d6 --- /dev/null +++ b/mmv1/products/compute/PublicDelegatedPrefix.yaml @@ -0,0 +1,85 @@ +# Copyright 2023 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- !ruby/object:Api::Resource +name: 'PublicDelegatedPrefix' +base_url: projects/{{project}}/regions/{{region}}/publicDelegatedPrefixes +has_self_link: true +immutable: true +description: | + Represents a PublicDelegatedPrefix for use with bring your own IP addresses (BYOIP). +references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Using bring your own IP': 'https://cloud.google.com/vpc/docs/using-bring-your-own-ip' + api: 'https://cloud.google.com/compute/docs/reference/rest/v1/publicDelegatedPrefixes' +async: !ruby/object:Api::OpAsync + operation: !ruby/object:Api::OpAsync::Operation + kind: 'compute#operation' + path: 'name' + base_url: 'projects/{{project}}/regions/{{region}}/operations/{{op_id}}' + wait_ms: 1000 + result: !ruby/object:Api::OpAsync::Result + path: 'targetLink' + status: !ruby/object:Api::OpAsync::Status + path: 'status' + complete: 'DONE' + allowed: + - 'PENDING' + - 'RUNNING' + - 'DONE' + error: !ruby/object:Api::OpAsync::Error + path: 'error/errors' + message: 'message' +examples: + - !ruby/object:Provider::Terraform::Examples + name: "public_delegated_prefixes_basic" + primary_resource_id: "prefixes" + # PAPs have very low quota limits and a shared testing range so serialized tests exist in: + # resource_compute_public_advertised_prefix_test.go + skip_test: true + vars: + prefixes_name: "my-prefix" + test_env_vars: + description: :PAP_DESCRIPTION +properties: + - !ruby/object:Api::Type::String + name: 'region' + description: 'A region where the prefix will reside.' + url_param_only: true + required: true + - !ruby/object:Api::Type::String + name: 'description' + description: An optional description of this resource. + - !ruby/object:Api::Type::Boolean + name: 'isLiveMigration' + description: If true, the prefix will be live migrated. + - !ruby/object:Api::Type::String + name: 'name' + description: | + Name of the resource. The name must be 1-63 characters long, and + comply with RFC1035. Specifically, the name must be 1-63 characters + long and match the regular expression `[a-z]([-a-z0-9]*[a-z0-9])?` + which means the first character must be a lowercase letter, and all + following characters must be a dash, lowercase letter, or digit, + except the last character, which cannot be a dash. + required: true + - !ruby/object:Api::Type::String + name: 'parentPrefix' + description: The URL of parent prefix. Either PublicAdvertisedPrefix or PublicDelegatedPrefix. + required: true + diff_suppress_func: 'compareSelfLinkOrResourceName' + - !ruby/object:Api::Type::String + name: 'ipCidrRange' + description: The IPv4 address range, in CIDR format, represented by this public advertised prefix. + required: true + diff --git a/mmv1/provider/terraform/examples.rb b/mmv1/provider/terraform/examples.rb index bbcf964db3a5..1aa8e268e2ed 100644 --- a/mmv1/provider/terraform/examples.rb +++ b/mmv1/provider/terraform/examples.rb @@ -162,7 +162,8 @@ def config_documentation(pwd) MASTER_BILLING_ACCT: '000000-0000000-0000000-000000', SERVICE_ACCT: 'my@service-account.com', CUST_ID: 'A01b123xz', - IDENTITY_USER: 'cloud_identity_user' + IDENTITY_USER: 'cloud_identity_user', + PAP_DESCRIPTION: 'description' } @vars ||= {} @test_env_vars ||= {} diff --git a/mmv1/templates/terraform/env_var_context.go.erb b/mmv1/templates/terraform/env_var_context.go.erb index c4872d9994b7..c38132b72dc4 100644 --- a/mmv1/templates/terraform/env_var_context.go.erb +++ b/mmv1/templates/terraform/env_var_context.go.erb @@ -23,5 +23,7 @@ "<%= var_name -%>": GetTestCustIdFromEnv(t), <% elsif var_type == :IDENTITY_USER -%> "<%= var_name -%>": GetTestIdentityUserFromEnv(t), + <% elsif var_type == :PAP_DESCRIPTION -%> + "<%= var_name -%>": GetTestPublicAdvertisedPrefixDescriptionFromEnv(t), <% end -%> <% end -%> diff --git a/mmv1/templates/terraform/examples/public_advertised_prefixes_basic.tf.erb b/mmv1/templates/terraform/examples/public_advertised_prefixes_basic.tf.erb new file mode 100644 index 000000000000..79a7c65f34e0 --- /dev/null +++ b/mmv1/templates/terraform/examples/public_advertised_prefixes_basic.tf.erb @@ -0,0 +1,6 @@ +resource "google_compute_public_advertised_prefix" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['prefixes_name'] %>" + description = "<%= ctx[:test_env_vars]['description'] %>" + dns_verification_ip = "127.127.0.0" + ip_cidr_range = "127.127.0.0/16" +} diff --git a/mmv1/templates/terraform/examples/public_delegated_prefixes_basic.tf.erb b/mmv1/templates/terraform/examples/public_delegated_prefixes_basic.tf.erb new file mode 100644 index 000000000000..feb036bd752d --- /dev/null +++ b/mmv1/templates/terraform/examples/public_delegated_prefixes_basic.tf.erb @@ -0,0 +1,14 @@ +resource "google_compute_public_advertised_prefix" "advertised" { + name = "<%= ctx[:vars]['prefixes_name'] %>" + description = "<%= ctx[:test_env_vars]['description'] %>" + dns_verification_ip = "127.127.0.0" + ip_cidr_range = "127.127.0.0/16" +} + +resource "google_compute_public_delegated_prefix" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['prefixes_name'] %>" + region = "us-central1" + description = "my description" + ip_cidr_range = "127.127.0.0/24" + parent_prefix = google_compute_public_advertised_prefix.advertised.id +} diff --git a/mmv1/third_party/terraform/tests/resource_compute_public_advertised_prefix_test.go b/mmv1/third_party/terraform/tests/resource_compute_public_advertised_prefix_test.go new file mode 100644 index 000000000000..8b4340cc0d5b --- /dev/null +++ b/mmv1/third_party/terraform/tests/resource_compute_public_advertised_prefix_test.go @@ -0,0 +1,196 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +// Since we only have access to one test prefix range we cannot run tests in parallel +func TestAccComputePublicPrefixes(t *testing.T) { + testCases := map[string]func(t *testing.T){ + "delegated_prefix": testAccComputePublicDelegatedPrefix_publicDelegatedPrefixesBasicTest, + "advertised_prefix": testAccComputePublicAdvertisedPrefix_publicAdvertisedPrefixesBasicTest, + } + + for name, tc := range testCases { + // shadow the tc variable into scope so that when + // the loop continues, if t.Run hasn't executed tc(t) + // yet, we don't have a race condition + // see https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } +} + +func testAccComputePublicAdvertisedPrefix_publicAdvertisedPrefixesBasicTest(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "description": GetTestPublicAdvertisedPrefixDescriptionFromEnv(t), + "random_suffix": RandString(t, 10), + } + + VcrTest(t, resource.TestCase{ + PreCheck: func() { AccTestPreCheck(t) }, + ProtoV5ProviderFactories: ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputePublicAdvertisedPrefixDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputePublicAdvertisedPrefix_publicAdvertisedPrefixesBasicExample(context), + }, + { + ResourceName: "google_compute_public_advertised_prefix.prefix", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputePublicAdvertisedPrefix_publicAdvertisedPrefixesBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_public_advertised_prefix" "prefix" { + name = "tf-test-my-prefix%{random_suffix}" + description = "%{description}" + dns_verification_ip = "127.127.0.0" + ip_cidr_range = "127.127.0.0/16" +} +`, context) +} + +func testAccComputePublicDelegatedPrefix_publicDelegatedPrefixesBasicTest(t *testing.T) { + context := map[string]interface{}{ + "description": GetTestPublicAdvertisedPrefixDescriptionFromEnv(t), + "random_suffix": RandString(t, 10), + } + + VcrTest(t, resource.TestCase{ + PreCheck: func() { AccTestPreCheck(t) }, + ProtoV5ProviderFactories: ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputePublicDelegatedPrefixDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputePublicDelegatedPrefix_publicDelegatedPrefixesBasicExample(context), + }, + { + ResourceName: "google_compute_public_delegated_prefix.prefix", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region"}, + }, + }, + }) +} + +func testAccComputePublicDelegatedPrefix_publicDelegatedPrefixesBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_public_advertised_prefix" "advertised" { + name = "tf-test-my-prefix%{random_suffix}" + description = "%{description}" + dns_verification_ip = "127.127.0.0" + ip_cidr_range = "127.127.0.0/16" +} + +resource "google_compute_public_delegated_prefix" "prefix" { + name = "tf-test-my-prefix%{random_suffix}" + description = "my description" + region = "us-central1" + ip_cidr_range = "127.127.0.0/24" + parent_prefix = google_compute_public_advertised_prefix.advertised.id +} + +resource "google_compute_public_delegated_prefix" "subprefix" { + name = "tf-test-my-subprefix%{random_suffix}" + description = "my description" + region = "us-central1" + ip_cidr_range = "127.127.0.0/26" + parent_prefix = google_compute_public_delegated_prefix.prefix.id +} +`, context) +} + +func testAccCheckComputePublicDelegatedPrefixDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_public_delegated_prefix" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := GoogleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/publicDelegatedPrefixes/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = SendRequest(config, "GET", billingProject, url, config.UserAgent, nil) + if err == nil { + return fmt.Errorf("ComputePublicDelegatedPrefix still exists at %s", url) + } + } + + return nil + } +} + +func testAccCheckComputePublicAdvertisedPrefixDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_public_advertised_prefix" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := GoogleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{ComputeBasePath}}projects/{{project}}/global/publicAdvertisedPrefixes/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = SendRequest(config, "GET", billingProject, url, config.UserAgent, nil) + if err == nil { + return fmt.Errorf("ComputePublicAdvertisedPrefix still exists at %s", url) + } + } + + return nil + } +} diff --git a/mmv1/third_party/terraform/utils/provider_test.go.erb b/mmv1/third_party/terraform/utils/provider_test.go.erb index a69ed6113702..67c530c39573 100644 --- a/mmv1/third_party/terraform/utils/provider_test.go.erb +++ b/mmv1/third_party/terraform/utils/provider_test.go.erb @@ -335,4 +335,4 @@ provider "google" { user_project_override = %v } `, accessToken, override) -} +} \ No newline at end of file diff --git a/mmv1/third_party/terraform/utils/provider_test_utils.go.erb b/mmv1/third_party/terraform/utils/provider_test_utils.go.erb index 3d0a1ada2ba7..3f755b56ca88 100644 --- a/mmv1/third_party/terraform/utils/provider_test_utils.go.erb +++ b/mmv1/third_party/terraform/utils/provider_test_utils.go.erb @@ -100,6 +100,12 @@ var masterBillingAccountEnvVars = []string{ "GOOGLE_MASTER_BILLING_ACCOUNT", } +// This value is the description used for test PublicAdvertisedPrefix setup to avoid required DNS +// setup. This is only used during integration tests and would be invalid to surface to users +var papDescriptionEnvVars = []string{ + "GOOGLE_PUBLIC_AVERTISED_PREFIX_DESCRIPTION", +} + func init() { configs = make(map[string]*Config) fwProviders = make(map[string]*frameworkTestProvider) @@ -259,6 +265,11 @@ func GetTestServiceAccountFromEnv(t *testing.T) string { return MultiEnvSearch(serviceAccountEnvVars) } +func GetTestPublicAdvertisedPrefixDescriptionFromEnv(t *testing.T) string { + SkipIfEnvNotSet(t, papDescriptionEnvVars...) + return MultiEnvSearch(papDescriptionEnvVars) +} + // Some tests fail during VCR. One common case is race conditions when creating resources. // If a test config adds two fine-grained resources with the same parent it is undefined // which will be created first, causing VCR to fail ~50% of the time