diff --git a/.ci/magician/github/membership_data.go b/.ci/magician/github/membership_data.go index fb71bfed3987..5c1ac2d5d024 100644 --- a/.ci/magician/github/membership_data.go +++ b/.ci/magician/github/membership_data.go @@ -45,11 +45,6 @@ var ( startDate: newDate(2024, 4, 30, pdtLoc), endDate: newDate(2024, 7, 31, pdtLoc), }, - { - id: "SarahFrench", - startDate: newDate(2024, 8, 2, bstLoc), - endDate: newDate(2024, 8, 6, bstLoc), - }, { id: "shuyama1", startDate: newDate(2024, 5, 22, pdtLoc), @@ -95,5 +90,10 @@ var ( startDate: newDate(2024, 9, 13, pdtLoc), endDate: newDate(2024, 9, 20, pdtLoc), }, + { + id: "SarahFrench", + startDate: newDate(2024, 9, 20, bstLoc), + endDate: newDate(2024, 9, 23, bstLoc), + }, } ) diff --git a/mmv1/api/resource.go b/mmv1/api/resource.go index 7502a04da2f6..79be14b68c74 100644 --- a/mmv1/api/resource.go +++ b/mmv1/api/resource.go @@ -1192,8 +1192,7 @@ func (r Resource) ExtractIdentifiers(url string) []string { return result } -// For example, "projects/{{project}}/schemas/{{name}}", "{{project}}/{{name}}", "{{name}}" -func (r Resource) RawImportIdFormatsFromIam() []string { +func (r Resource) IamImportFormats() []string { var importFormat []string if r.IamPolicy != nil { @@ -1202,8 +1201,12 @@ func (r Resource) RawImportIdFormatsFromIam() []string { if len(importFormat) == 0 { importFormat = r.ImportFormat } + return importFormat +} - return ImportIdFormats(importFormat, r.Identity, r.BaseUrl) +// For example, "projects/{{project}}/schemas/{{name}}", "{{project}}/{{name}}", "{{name}}" +func (r Resource) RawImportIdFormatsFromIam() []string { + return ImportIdFormats(r.IamImportFormats(), r.Identity, r.BaseUrl) } // For example, projects/(?P[^/]+)/schemas/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+) @@ -1668,3 +1671,34 @@ func (r Resource) CaiApiVersion(productBackendName, caiProductBaseUrl string) st } return "" } + +// For example: the uri "projects/{{project}}/schemas/{{name}}" +// The paramerter is "schema" as "project" is not returned. +func (r Resource) CaiIamResourceParams() []string { + resourceUri := strings.ReplaceAll(r.IamResourceUri(), "{{name}}", fmt.Sprintf("{{%s}}", r.IamParentResourceName())) + + return google.Reject(r.ExtractIdentifiers(resourceUri), func(param string) bool { + return param == "project" + }) +} + +// Gets the Cai IAM asset name template +// For example: //monitoring.googleapis.com/v3/projects/{{project}}/services/{{service_id}} +func (r Resource) CaiIamAssetNameTemplate(productBackendName string) string { + iamImportFormat := r.IamImportFormats() + if len(iamImportFormat) > 0 { + name := strings.ReplaceAll(iamImportFormat[0], "{{name}}", fmt.Sprintf("{{%s}}", r.IamParentResourceName())) + name = strings.ReplaceAll(name, "%", "") + return fmt.Sprintf("//%s.googleapis.com/%s", productBackendName, name) + } + + caiBaseUrl := r.CaiBaseUrl + + if caiBaseUrl == "" { + caiBaseUrl = r.SelfLink + } + if caiBaseUrl == "" { + caiBaseUrl = r.BaseUrl + } + return fmt.Sprintf("//%s.googleapis.com/%s/{{%s}}", productBackendName, caiBaseUrl, r.IamParentResourceName()) +} diff --git a/mmv1/go.mod b/mmv1/go.mod index 6ba2aa672c36..79316b0a09ea 100644 --- a/mmv1/go.mod +++ b/mmv1/go.mod @@ -8,3 +8,7 @@ require ( ) require github.com/golang/glog v1.2.0 + +require github.com/otiai10/copy v1.9.0 + +require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect diff --git a/mmv1/go.sum b/mmv1/go.sum index 02e4ed2c5647..14b659976106 100644 --- a/mmv1/go.sum +++ b/mmv1/go.sum @@ -2,8 +2,16 @@ github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4= +github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.4.0/go.mod h1:gifjb2MYOoULtKLqUAEILUG/9KONW6f7YsJ6vQLTlFI= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/mmv1/google/slice_utils.go b/mmv1/google/slice_utils.go index 75ad22de3b7d..20219c7c85e1 100644 --- a/mmv1/google/slice_utils.go +++ b/mmv1/google/slice_utils.go @@ -39,3 +39,19 @@ func Reject[T any](S []T, test func(T) bool) (ret []T) { func Concat[T any](S1 []T, S2 []T) (ret []T) { return append(S1, S2...) } + +// difference returns the elements in `S1` that aren't in `S2`. +func Diff(S1, S2 []string) []string { + var ret []string + mb := make(map[string]bool, len(S2)) + for _, x := range S2 { + mb[x] = true + } + + for _, x := range S1 { + if _, found := mb[x]; !found { + ret = append(ret, x) + } + } + return ret +} diff --git a/mmv1/google/template_utils.go b/mmv1/google/template_utils.go index 78eb2dea26ac..9a8c5480cde9 100644 --- a/mmv1/google/template_utils.go +++ b/mmv1/google/template_utils.go @@ -65,6 +65,7 @@ var TemplateFunctions = template.FuncMap{ "join": strings.Join, "lower": strings.ToLower, "upper": strings.ToUpper, + "hasSuffix": strings.HasSuffix, "dict": wrapMultipleParams, "format2regex": Format2Regex, "hasPrefix": strings.HasPrefix, diff --git a/mmv1/main.go b/mmv1/main.go index b9d1bf429a35..eeb4de618c80 100644 --- a/mmv1/main.go +++ b/mmv1/main.go @@ -149,7 +149,6 @@ func main() { providerToGenerate = setProvider(*forceProvider, *version, productsForVersion[0], startTime) providerToGenerate.CopyCommonFiles(*outputPath, generateCode, generateDocs) - log.Printf("Compiling common files for terraform") if generateCode { providerToGenerate.CompileCommonFiles(*outputPath, productsForVersion, "") @@ -236,6 +235,8 @@ func setProvider(forceProvider, version string, productApi *api.Product, startTi switch forceProvider { case "tgc": return provider.NewTerraformGoogleConversion(productApi, version, startTime) + case "tgc_cai2hcl": + return provider.NewCaiToTerraformConversion(productApi, version, startTime) default: return provider.NewTerraform(productApi, version, startTime) } diff --git a/mmv1/products/bigquerydatapolicy/DataPolicy.yaml b/mmv1/products/bigquerydatapolicy/DataPolicy.yaml index 1463ecae2791..917a550830cd 100644 --- a/mmv1/products/bigquerydatapolicy/DataPolicy.yaml +++ b/mmv1/products/bigquerydatapolicy/DataPolicy.yaml @@ -82,6 +82,7 @@ properties: Policy tag resource name, in the format of projects/{project_number}/locations/{locationId}/taxonomies/{taxonomyId}/policyTags/{policyTag_id}. required: true diff_suppress_func: 'tpgresource.ProjectNumberDiffSuppress' + custom_flatten: 'templates/terraform/custom_flatten/bigquery_datapolicy_policy_tag_location_to_lower_case.go.erb' - !ruby/object:Api::Type::Enum name: dataPolicyType description: | diff --git a/mmv1/products/cloudrun/Service.yaml b/mmv1/products/cloudrun/Service.yaml index 2a277edcd829..6f5ab65a8770 100644 --- a/mmv1/products/cloudrun/Service.yaml +++ b/mmv1/products/cloudrun/Service.yaml @@ -53,6 +53,17 @@ examples: cloud_run_service_name: 'cloudrun-srv' test_env_vars: project: :PROJECT_NAME + - !ruby/object:Provider::Terraform::Examples + name: 'cloud_run_service_gpu' + min_version: 'beta' + primary_resource_id: 'default' + primary_resource_name: "fmt.Sprintf(\"tf-test-cloudrun-srv%s\", + context[\"random_suffix\"\ + ])" + vars: + cloud_run_service_name: 'cloudrun-srv' + test_env_vars: + project: :PROJECT_NAME - !ruby/object:Provider::Terraform::Examples name: 'cloud_run_service_sql' primary_resource_id: 'default' @@ -743,7 +754,13 @@ properties: The name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). If this is not specified, the default behavior is defined by gRPC. - + - !ruby/object:Api::Type::KeyValuePairs + name: nodeSelector + min_version: beta + description: |- + Node Selector describes the hardware requirements of the resources. + Use the following node selector keys to configure features on a Revision: + - `run.googleapis.com/accelerator` sets the [type of GPU](https://cloud.google.com/run/docs/configuring/services/gpu) required by the Revision to run. - !ruby/object:Api::Type::Integer name: containerConcurrency description: |- diff --git a/mmv1/products/cloudrun/go_Service.yaml b/mmv1/products/cloudrun/go_Service.yaml index cd4b9a634396..70d027954b61 100644 --- a/mmv1/products/cloudrun/go_Service.yaml +++ b/mmv1/products/cloudrun/go_Service.yaml @@ -69,6 +69,14 @@ examples: cloud_run_service_name: 'cloudrun-srv' test_env_vars: project: 'PROJECT_NAME' + - name: 'cloud_run_service_gpu' + primary_resource_id: 'default' + primary_resource_name: 'fmt.Sprintf("tf-test-cloudrun-srv%s", context["random_suffix"])' + min_version: 'beta' + vars: + cloud_run_service_name: 'cloudrun-srv' + test_env_vars: + project: 'PROJECT_NAME' - name: 'cloud_run_service_sql' primary_resource_id: 'default' primary_resource_name: 'fmt.Sprintf("tf-test-cloudrun-srv%s", context["random_suffix"])' @@ -737,7 +745,13 @@ properties: The name of the service to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). If this is not specified, the default behavior is defined by gRPC. - + - name: 'nodeSelector' + type: KeyValuePairs + description: |- + Node Selector describes the hardware requirements of the resources. + Use the following node selector keys to configure features on a Revision: + - `run.googleapis.com/accelerator` sets the [type of GPU](https://cloud.google.com/run/docs/configuring/services/gpu) required by the Revision to run. + min_version: 'beta' - name: 'containerConcurrency' type: Integer description: |- diff --git a/mmv1/products/cloudrunv2/Service.yaml b/mmv1/products/cloudrunv2/Service.yaml index d6c36d475bbe..f770698be82f 100644 --- a/mmv1/products/cloudrunv2/Service.yaml +++ b/mmv1/products/cloudrunv2/Service.yaml @@ -112,6 +112,15 @@ examples: cloud_run_service_name: 'cloudrun-service' ignore_read_extra: - 'deletion_protection' + - !ruby/object:Provider::Terraform::Examples + name: 'cloudrunv2_service_gpu' + min_version: 'beta' + primary_resource_id: 'default' + primary_resource_name: "fmt.Sprintf(\"tf-test-cloudrun-srv%s\", context[\"random_suffix\"])" + vars: + cloud_run_service_name: 'cloudrun-service' + ignore_read_extra: + - 'deletion_protection' - !ruby/object:Provider::Terraform::Examples name: 'cloudrunv2_service_probes' primary_resource_id: 'default' @@ -498,7 +507,7 @@ properties: - !ruby/object:Api::Type::KeyValuePairs name: 'limits' description: |- - Only memory and CPU are supported. Use key `cpu` for CPU limit and `memory` for memory limit. Note: The only supported values for CPU are '1', '2', '4', and '8'. Setting 4 CPU requires at least 2Gi of memory. The values of the map is string form of the 'quantity' k8s type: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go + Only memory, CPU, and nvidia.com/gpu are supported. Use key `cpu` for CPU limit, `memory` for memory limit, `nvidia.com/gpu` for gpu limit. Note: The only supported values for CPU are '1', '2', '4', and '8'. Setting 4 CPU requires at least 2Gi of memory. The values of the map is string form of the 'quantity' k8s type: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go default_from_api: true - !ruby/object:Api::Type::Boolean name: 'cpuIdle' @@ -899,6 +908,16 @@ properties: name: 'mesh' description: |- The Mesh resource name. For more information see https://cloud.google.com/service-mesh/docs/reference/network-services/rest/v1/projects.locations.meshes#resource:-mesh. + - !ruby/object:Api::Type::NestedObject + name: 'nodeSelector' + min_version: beta + description: Node Selector describes the hardware requirements of the resources. + properties: + - !ruby/object:Api::Type::String + name: 'accelerator' + required: true + description: + The GPU to attach to an instance. See https://cloud.google.com/run/docs/configuring/services/gpu for configuring GPU. - !ruby/object:Api::Type::Array name: 'traffic' description: |- diff --git a/mmv1/products/cloudrunv2/go_Service.yaml b/mmv1/products/cloudrunv2/go_Service.yaml index ff0bc7cb52a6..d640edf462fe 100644 --- a/mmv1/products/cloudrunv2/go_Service.yaml +++ b/mmv1/products/cloudrunv2/go_Service.yaml @@ -100,6 +100,14 @@ examples: cloud_run_service_name: 'cloudrun-service' ignore_read_extra: - 'deletion_protection' + - name: 'cloudrunv2_service_gpu' + primary_resource_id: 'default' + primary_resource_name: 'fmt.Sprintf("tf-test-cloudrun-srv%s", context["random_suffix"])' + min_version: 'beta' + vars: + cloud_run_service_name: 'cloudrun-service' + ignore_read_extra: + - 'deletion_protection' - name: 'cloudrunv2_service_probes' primary_resource_id: 'default' primary_resource_name: 'fmt.Sprintf("tf-test-cloudrun-srv%s", context["random_suffix"])' @@ -495,7 +503,7 @@ properties: - name: 'limits' type: KeyValuePairs description: |- - Only memory and CPU are supported. Use key `cpu` for CPU limit and `memory` for memory limit. Note: The only supported values for CPU are '1', '2', '4', and '8'. Setting 4 CPU requires at least 2Gi of memory. The values of the map is string form of the 'quantity' k8s type: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go + Only memory, CPU, and nvidia.com/gpu are supported. Use key `cpu` for CPU limit, `memory` for memory limit, `nvidia.com/gpu` for gpu limit. Note: The only supported values for CPU are '1', '2', '4', and '8'. Setting 4 CPU requires at least 2Gi of memory. The values of the map is string form of the 'quantity' k8s type: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go default_from_api: true - name: 'cpuIdle' type: Boolean @@ -904,6 +912,16 @@ properties: type: String description: |- The Mesh resource name. For more information see https://cloud.google.com/service-mesh/docs/reference/network-services/rest/v1/projects.locations.meshes#resource:-mesh. + - name: 'nodeSelector' + type: NestedObject + description: Node Selector describes the hardware requirements of the resources. + min_version: 'beta' + properties: + - name: 'accelerator' + type: String + description: + The GPU to attach to an instance. See https://cloud.google.com/run/docs/configuring/services/gpu for configuring GPU. + required: true - name: 'traffic' type: Array description: |- diff --git a/mmv1/products/compute/RegionNetworkFirewallPolicyWithRules.yaml b/mmv1/products/compute/RegionNetworkFirewallPolicyWithRules.yaml new file mode 100644 index 000000000000..248e22779385 --- /dev/null +++ b/mmv1/products/compute/RegionNetworkFirewallPolicyWithRules.yaml @@ -0,0 +1,574 @@ +# Copyright 2024 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: RegionNetworkFirewallPolicyWithRules +min_version: beta +base_url: projects/{{project}}/regions/{{region}}/firewallPolicies +create_url: projects/{{project}}/regions/{{region}}/firewallPolicies +self_link: projects/{{project}}/regions/{{region}}/firewallPolicies/{{name}} +update_verb: :PATCH +description: "The Compute NetworkFirewallPolicy with rules resource" +legacy_long_form_project: true +async: !ruby/object:Api::OpAsync + operation: !ruby/object:Api::OpAsync::Operation + kind: 'compute#operation' + path: 'name' + base_url: '{{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: 'compute_region_network_firewall_policy_with_rules_full' + primary_resource_id: 'region-network-firewall-policy-with-rules' + vars: + policy_name: 'tf-region-fw-policy-with-rules' + address_group_name: 'tf-address-group' + tag_key_name: 'tf-tag-key' + tag_value_name: 'tf-tag-value' + test_env_vars: + org_id: :ORG_ID +custom_code: !ruby/object:Provider::Terraform::CustomCode + constants: templates/terraform/constants/resource_compute_region_network_firewall_policy_with_rules.go.erb + encoder: templates/terraform/encoders/resource_compute_region_network_firewall_policy_with_rules.go.erb + decoder: templates/terraform/decoders/resource_compute_region_network_firewall_policy_with_rules.go.erb + update_encoder: templates/terraform/update_encoder/resource_compute_region_network_firewall_policy_with_rules.go.erb + post_create: templates/terraform/post_create/resource_compute_region_network_firewall_policy_with_rules.go.erb +parameters: + - !ruby/object:Api::Type::String + name: region + description: The region of this resource. + url_param_only: true + default_from_api: true + immutable: true +properties: + - !ruby/object:Api::Type::String + name: creationTimestamp + description: Creation timestamp in RFC3339 text format. + output: true + - !ruby/object:Api::Type::String + name: name + description: | + User-provided name of the Network firewall policy. + The name should be unique in the project in which the firewall policy is created. + 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 + immutable: true + - !ruby/object:Api::Type::String + name: networkFirewallPolicyId + description: The unique identifier for the resource. This identifier is defined by the server. + output: true + api_name: id + - !ruby/object:Api::Type::String + name: description + description: An optional description of this resource. + - !ruby/object:Api::Type::Array + name: 'rule' + api_name: 'rules' + description: A list of firewall policy rules. + required: true + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'description' + description: | + A description of the rule. + - !ruby/object:Api::Type::String + name: 'ruleName' + description: | + An optional name for the rule. This field is not a unique identifier + and can be updated. + - !ruby/object:Api::Type::Integer + name: 'priority' + description: | + An integer indicating the priority of a rule in the list. The priority must be a value + between 0 and 2147483647. Rules are evaluated from highest to lowest priority where 0 is the + highest priority and 2147483647 is the lowest priority. + required: true + - !ruby/object:Api::Type::NestedObject + name: 'match' + description: + A match condition that incoming traffic is evaluated against. If it + evaluates to true, the corresponding 'action' is enforced. + required: true + properties: + - !ruby/object:Api::Type::Array + name: 'srcIpRanges' + description: | + Source IP address range in CIDR format. Required for + INGRESS rules. + item_type: Api::Type::String + - !ruby/object:Api::Type::Array + name: 'destIpRanges' + description: | + Destination IP address range in CIDR format. Required for + EGRESS rules. + item_type: Api::Type::String + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'srcAddressGroups' + description: | + Address groups which should be matched against the traffic source. + Maximum number of source address groups is 10. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'destAddressGroups' + description: | + Address groups which should be matched against the traffic destination. + Maximum number of destination address groups is 10. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'srcFqdns' + description: | + Fully Qualified Domain Name (FQDN) which should be matched against + traffic source. Maximum number of source fqdn allowed is 100. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'destFqdns' + description: | + Fully Qualified Domain Name (FQDN) which should be matched against + traffic destination. Maximum number of destination fqdn allowed is 100. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'srcRegionCodes' + description: | + Region codes whose IP addresses will be used to match for source + of traffic. Should be specified as 2 letter country code defined as per + ISO 3166 alpha-2 country codes. ex."US" + Maximum number of source region codes allowed is 5000. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'destRegionCodes' + description: | + Region codes whose IP addresses will be used to match for destination + of traffic. Should be specified as 2 letter country code defined as per + ISO 3166 alpha-2 country codes. ex."US" + Maximum number of destination region codes allowed is 5000. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'srcThreatIntelligences' + description: | + Names of Network Threat Intelligence lists. + The IPs in these lists will be matched against traffic source. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'destThreatIntelligences' + description: | + Names of Network Threat Intelligence lists. + The IPs in these lists will be matched against traffic destination. + - !ruby/object:Api::Type::Array + name: 'layer4Config' + api_name: 'layer4Configs' + description: | + Pairs of IP protocols and ports that the rule should match. + required: true + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'ipProtocol' + description: | + The IP protocol to which this rule applies. The protocol + type is required when creating a firewall rule. + This value can either be one of the following well + known protocol strings (tcp, udp, icmp, esp, ah, ipip, sctp), + or the IP protocol number. + required: true + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'ports' + description: | + An optional list of ports to which this rule applies. This field + is only applicable for UDP or TCP protocol. Each entry must be + either an integer or a range. If not specified, this rule + applies to connections through any port. + Example inputs include: ["22"], ["80","443"], and + ["12345-12349"]. + - !ruby/object:Api::Type::Array + name: 'srcSecureTag' + api_name: 'srcSecureTags' + description: | + List of secure tag values, which should be matched at the source + of the traffic. + For INGRESS rule, if all the srcSecureTag are INEFFECTIVE, + and there is no srcIpRange, this rule will be ignored. + Maximum number of source tag values allowed is 256. + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'name' + description: | + Name of the secure tag, created with TagManager's TagValue API. + @pattern tagValues/[0-9]+ + - !ruby/object:Api::Type::Enum + name: 'state' + output: true + description: | + [Output Only] State of the secure tag, either `EFFECTIVE` or + `INEFFECTIVE`. A secure tag is `INEFFECTIVE` when it is deleted + or its network is deleted. + values: + - :EFFECTIVE + - :INEFFECTIVE + - !ruby/object:Api::Type::Array + name: 'targetSecureTag' + api_name: 'targetSecureTags' + description: | + A list of secure tags that controls which instances the firewall rule + applies to. If targetSecureTag are specified, then the + firewall rule applies only to instances in the VPC network that have one + of those EFFECTIVE secure tags, if all the target_secure_tag are in + INEFFECTIVE state, then this rule will be ignored. + targetSecureTag may not be set at the same time as + targetServiceAccounts. + If neither targetServiceAccounts nor + targetSecureTag are specified, the firewall rule applies + to all instances on the specified network. + Maximum number of target label tags allowed is 256. + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'name' + description: | + Name of the secure tag, created with TagManager's TagValue API. + @pattern tagValues/[0-9]+ + - !ruby/object:Api::Type::Enum + name: 'state' + output: true + description: | + [Output Only] State of the secure tag, either `EFFECTIVE` or + `INEFFECTIVE`. A secure tag is `INEFFECTIVE` when it is deleted + or its network is deleted. + values: + - :EFFECTIVE + - :INEFFECTIVE + - !ruby/object:Api::Type::String + name: 'action' + description: | + The Action to perform when the client connection triggers the rule. Can currently be either + "allow", "deny", "apply_security_profile_group" or "goto_next". + required: true + - !ruby/object:Api::Type::Enum + name: 'direction' + description: | + The direction in which this rule applies. If unspecified an INGRESS rule is created. + values: + - :INGRESS + - :EGRESS + - !ruby/object:Api::Type::Boolean + name: 'enableLogging' + description: | + Denotes whether to enable logging for a particular rule. + If logging is enabled, logs will be exported to the + configured export destination in Stackdriver. + send_empty_value: true + - !ruby/object:Api::Type::Array + name: 'targetServiceAccounts' + description: | + A list of service accounts indicating the sets of + instances that are applied with this rule. + item_type: Api::Type::String + - !ruby/object:Api::Type::String + name: 'securityProfileGroup' + description: | + A fully-qualified URL of a SecurityProfile resource instance. + Example: + https://networksecurity.googleapis.com/v1/projects/{project}/locations/{location}/securityProfileGroups/my-security-profile-group + Must be specified if action is 'apply_security_profile_group'. + - !ruby/object:Api::Type::Boolean + name: 'tlsInspect' + description: | + Boolean flag indicating if the traffic should be TLS decrypted. + It can be set only if action = 'apply_security_profile_group' and cannot be set for other actions. + - !ruby/object:Api::Type::Boolean + name: 'disabled' + description: | + Denotes whether the firewall policy rule is disabled. When set to true, + the firewall policy rule is not enforced and traffic behaves as if it did + not exist. If this is unspecified, the firewall policy rule will be + enabled. + - !ruby/object:Api::Type::Array + name: 'predefinedRules' + description: A list of firewall policy pre-defined rules. + output: true + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'description' + output: true + description: | + A description of the rule. + - !ruby/object:Api::Type::String + name: 'ruleName' + output: true + description: | + An optional name for the rule. This field is not a unique identifier + and can be updated. + - !ruby/object:Api::Type::Integer + name: 'priority' + output: true + description: | + An integer indicating the priority of a rule in the list. The priority must be a value + between 0 and 2147483647. Rules are evaluated from highest to lowest priority where 0 is the + highest priority and 2147483647 is the lowest priority. + - !ruby/object:Api::Type::NestedObject + name: 'match' + output: true + description: + A match condition that incoming traffic is evaluated against. If it + evaluates to true, the corresponding 'action' is enforced. + properties: + - !ruby/object:Api::Type::Array + name: 'srcIpRanges' + output: true + description: | + Source IP address range in CIDR format. Required for + INGRESS rules. + item_type: Api::Type::String + - !ruby/object:Api::Type::Array + name: 'destIpRanges' + output: true + description: | + Destination IP address range in CIDR format. Required for + EGRESS rules. + item_type: Api::Type::String + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + output: true + name: 'srcAddressGroups' + description: | + Address groups which should be matched against the traffic source. + Maximum number of source address groups is 10. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + output: true + name: 'destAddressGroups' + description: | + Address groups which should be matched against the traffic destination. + Maximum number of destination address groups is 10. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'srcFqdns' + output: true + description: | + Fully Qualified Domain Name (FQDN) which should be matched against + traffic source. Maximum number of source fqdn allowed is 100. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'destFqdns' + output: true + description: | + Fully Qualified Domain Name (FQDN) which should be matched against + traffic destination. Maximum number of destination fqdn allowed is 100. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'srcRegionCodes' + output: true + description: | + Region codes whose IP addresses will be used to match for source + of traffic. Should be specified as 2 letter country code defined as per + ISO 3166 alpha-2 country codes. ex."US" + Maximum number of source region codes allowed is 5000. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'destRegionCodes' + output: true + description: | + Region codes whose IP addresses will be used to match for destination + of traffic. Should be specified as 2 letter country code defined as per + ISO 3166 alpha-2 country codes. ex."US" + Maximum number of destination region codes allowed is 5000. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'srcThreatIntelligences' + output: true + description: | + Names of Network Threat Intelligence lists. + The IPs in these lists will be matched against traffic source. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'destThreatIntelligences' + output: true + description: | + Names of Network Threat Intelligence lists. + The IPs in these lists will be matched against traffic destination. + - !ruby/object:Api::Type::Array + name: 'layer4Config' + output: true + api_name: 'layer4Configs' + description: | + Pairs of IP protocols and ports that the rule should match. + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'ipProtocol' + output: true + description: | + The IP protocol to which this rule applies. The protocol + type is required when creating a firewall rule. + This value can either be one of the following well + known protocol strings (tcp, udp, icmp, esp, ah, ipip, sctp), + or the IP protocol number. + - !ruby/object:Api::Type::Array + item_type: Api::Type::String + name: 'ports' + output: true + description: | + An optional list of ports to which this rule applies. This field + is only applicable for UDP or TCP protocol. Each entry must be + either an integer or a range. If not specified, this rule + applies to connections through any port. + Example inputs include: ["22"], ["80","443"], and + ["12345-12349"]. + - !ruby/object:Api::Type::Array + name: 'srcSecureTag' + api_name: 'srcSecureTags' + output: true + description: | + List of secure tag values, which should be matched at the source + of the traffic. + For INGRESS rule, if all the srcSecureTag are INEFFECTIVE, + and there is no srcIpRange, this rule will be ignored. + Maximum number of source tag values allowed is 256. + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'name' + output: true + description: | + Name of the secure tag, created with TagManager's TagValue API. + @pattern tagValues/[0-9]+ + - !ruby/object:Api::Type::Enum + name: 'state' + output: true + description: | + [Output Only] State of the secure tag, either `EFFECTIVE` or + `INEFFECTIVE`. A secure tag is `INEFFECTIVE` when it is deleted + or its network is deleted. + values: + - :EFFECTIVE + - :INEFFECTIVE + - !ruby/object:Api::Type::Array + name: 'targetSecureTag' + api_name: 'targetSecureTags' + output: true + description: | + A list of secure tags that controls which instances the firewall rule + applies to. If targetSecureTag are specified, then the + firewall rule applies only to instances in the VPC network that have one + of those EFFECTIVE secure tags, if all the target_secure_tag are in + INEFFECTIVE state, then this rule will be ignored. + targetSecureTag may not be set at the same time as + targetServiceAccounts. + If neither targetServiceAccounts nor + targetSecureTag are specified, the firewall rule applies + to all instances on the specified network. + Maximum number of target label tags allowed is 256. + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'name' + output: true + description: | + Name of the secure tag, created with TagManager's TagValue API. + @pattern tagValues/[0-9]+ + - !ruby/object:Api::Type::Enum + name: 'state' + output: true + description: | + [Output Only] State of the secure tag, either `EFFECTIVE` or + `INEFFECTIVE`. A secure tag is `INEFFECTIVE` when it is deleted + or its network is deleted. + values: + - :EFFECTIVE + - :INEFFECTIVE + - !ruby/object:Api::Type::String + name: 'action' + output: true + description: | + The Action to perform when the client connection triggers the rule. Can currently be either + "allow", "deny", "apply_security_profile_group" or "goto_next". + - !ruby/object:Api::Type::Enum + name: 'direction' + output: true + description: | + The direction in which this rule applies. If unspecified an INGRESS rule is created. + values: + - :INGRESS + - :EGRESS + - !ruby/object:Api::Type::Boolean + name: 'enableLogging' + output: true + description: | + Denotes whether to enable logging for a particular rule. + If logging is enabled, logs will be exported to the + configured export destination in Stackdriver. + send_empty_value: true + - !ruby/object:Api::Type::Array + name: 'targetServiceAccounts' + output: true + description: | + A list of service accounts indicating the sets of + instances that are applied with this rule. + item_type: Api::Type::String + - !ruby/object:Api::Type::String + name: 'securityProfileGroup' + output: true + description: | + A fully-qualified URL of a SecurityProfile resource instance. + Example: + https://networksecurity.googleapis.com/v1/projects/{project}/locations/{location}/securityProfileGroups/my-security-profile-group + Must be specified if action is 'apply_security_profile_group'. + - !ruby/object:Api::Type::Boolean + name: 'tlsInspect' + output: true + description: | + Boolean flag indicating if the traffic should be TLS decrypted. + It can be set only if action = 'apply_security_profile_group' and cannot be set for other actions. + - !ruby/object:Api::Type::Boolean + name: 'disabled' + output: true + description: | + Denotes whether the firewall policy rule is disabled. When set to true, + the firewall policy rule is not enforced and traffic behaves as if it did + not exist. If this is unspecified, the firewall policy rule will be + enabled. + - !ruby/object:Api::Type::Fingerprint + name: fingerprint + description: Fingerprint of the resource. This field is used internally during updates of this resource. + output: true + - !ruby/object:Api::Type::String + name: selfLink + description: Server-defined URL for the resource. + output: true + - !ruby/object:Api::Type::String + name: selfLinkWithId + description: Server-defined URL for this resource with the resource id. + output: true + - !ruby/object:Api::Type::Integer + name: ruleTupleCount + description: Total count of all firewall policy rule tuples. A firewall policy can not exceed a set number of tuples. + output: true diff --git a/mmv1/products/compute/RouterNat.yaml b/mmv1/products/compute/RouterNat.yaml index 39a77175d0ce..dea5e11dac39 100644 --- a/mmv1/products/compute/RouterNat.yaml +++ b/mmv1/products/compute/RouterNat.yaml @@ -110,6 +110,7 @@ custom_code: !ruby/object:Provider::Terraform::CustomCode constants: 'templates/terraform/constants/router_nat.go.erb' pre_create: 'templates/terraform/constants/router_nat_validate_action_active_range.go.erb' pre_update: 'templates/terraform/constants/router_nat_validate_action_active_range.go.erb' + encoder: 'templates/terraform/encoders/router_nat_set_initial_nat_ips.go.erb' custom_diff: [ 'resourceComputeRouterNatDrainNatIpsCustomDiff', ] @@ -153,6 +154,25 @@ properties: values: - :MANUAL_ONLY - :AUTO_ONLY + - !ruby/object:Api::Type::Array + name: 'initialNatIps' + description: | + Self-links of NAT IPs to be used as initial value for creation alongside a RouterNatAddress resource. + Conflicts with natIps and drainNatIps. Only valid if natIpAllocateOption is set to MANUAL_ONLY. + immutable: true + ignore_read: true + conflicts: + - natIps + - drainNatIps + send_empty_value: true + is_set: true + set_hash_func: computeRouterNatIPsHash + item_type: !ruby/object:Api::Type::ResourceRef + name: 'address' + resource: 'Address' + imports: 'selfLink' + description: 'A reference to an address associated with this NAT' + custom_expand: 'templates/terraform/custom_expand/array_resourceref_with_validation.go.erb' - !ruby/object:Api::Type::Array name: 'natIps' description: | @@ -170,6 +190,7 @@ properties: imports: 'selfLink' description: 'A reference to an address associated with this NAT' custom_expand: 'templates/terraform/custom_expand/array_resourceref_with_validation.go.erb' + default_from_api: true - !ruby/object:Api::Type::Array name: 'drainNatIps' description: | @@ -183,6 +204,7 @@ properties: imports: 'selfLink' description: 'A reference to an address associated with this NAT' custom_expand: 'templates/terraform/custom_expand/array_resourceref_with_validation.go.erb' + default_from_api: true - !ruby/object:Api::Type::Enum name: 'sourceSubnetworkIpRangesToNat' required: true diff --git a/mmv1/products/compute/RouterNatAddress.yaml b/mmv1/products/compute/RouterNatAddress.yaml new file mode 100644 index 000000000000..43e53f23642b --- /dev/null +++ b/mmv1/products/compute/RouterNatAddress.yaml @@ -0,0 +1,140 @@ +# 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: 'RouterNatAddress' +base_url: projects/{{project}}/regions/{{region}}/routers/{{router}} +self_link: projects/{{project}}/regions/{{region}}/routers/{{router}} +create_url: projects/{{project}}/regions/{{region}}/routers/{{router}} +update_url: projects/{{project}}/regions/{{region}}/routers/{{router}} +delete_url: projects/{{project}}/regions/{{region}}/routers/{{router}} +create_verb: :PATCH +update_verb: :PATCH +delete_verb: :PATCH +identity: + - routerNat +collection_url_key: nats +nested_query: !ruby/object:Api::Resource::NestedQuery + modify_by_patch: true + keys: + - nats +description: | + A resource used to set the list of IP addresses to be used in a NAT service and manage the draining of destroyed IPs. + + ~> **Note:** This resource is to be used alongside a `google_compute_router_nat` resource, + the router nat resource must have no defined `nat_ips` or `drain_nat_ips` parameters, + instead using the `initial_nat_ips` parameter to set at least one IP for the creation of the resource. +references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Google Cloud Router': 'https://cloud.google.com/router/docs/' + api: 'https://cloud.google.com/compute/docs/reference/rest/v1/routers' +async: !ruby/object:Api::OpAsync + operation: !ruby/object:Api::OpAsync::Operation + kind: 'compute#operation' + path: 'routerNat' + base_url: 'projects/{{project}}/regions/{{regions}}/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' +exclude_tgc: true +id_format: 'projects/{{project}}/regions/{{region}}/routers/{{router}}/{{router_nat}}' +mutex: router/{{region}}/{{router}} +examples: + - !ruby/object:Provider::Terraform::Examples + name: 'router_nat_address_count' + primary_resource_id: 'nat_address' + skip_test: true + vars: + router_name: 'my-router' + nat_name: 'my-router-nat' + network_name: 'my-network' + subnet_name: 'my-subnetwork' + address_name: 'nat-manual-ip' +# ToDo: We use a custom code for CREATE since the generated code is erroneously not replacing the generated encoder with the custom one provided +custom_code: !ruby/object:Provider::Terraform::CustomCode + constants: 'templates/terraform/constants/router_nat_address.go.erb' + custom_create: templates/terraform/custom_create/router_nat_address_nested_query_create_encoder.go.erb + pre_delete: templates/terraform/pre_delete/compute_router_nat_address_delete_nat_ips_only.go.erb + encoder: 'templates/terraform/encoders/router_nat_address_patch_on_create.go.erb' + update_encoder: 'templates/terraform/encoders/router_nat_address_update_skip_encoder.go.erb' +custom_diff: [ + 'resourceComputeRouterNatAddressDrainNatIpsCustomDiff', +] +parameters: + - !ruby/object:Api::Type::ResourceRef + name: 'router' + resource: 'Router' + imports: 'name' + description: | + The name of the Cloud Router in which the referenced NAT service is configured. + required: true + immutable: true + url_param_only: true + - !ruby/object:Api::Type::ResourceRef + name: 'routerNat' + resource: 'RouterNat' + imports: 'name' + api_name: 'name' + description: | + The name of the Nat service in which this address will be configured. + required: true + immutable: true + - !ruby/object:Api::Type::ResourceRef + name: region + resource: Region + imports: name + description: Region where the NAT service reside. + immutable: true + required: false + url_param_only: true + default_from_api: true + custom_flatten: 'templates/terraform/custom_flatten/name_from_self_link.erb' +properties: + - !ruby/object:Api::Type::Array + name: 'natIps' + description: | + Self-links of NAT IPs to be used in a Nat service. Only valid if the referenced RouterNat + natIpAllocateOption is set to MANUAL_ONLY. + send_empty_value: true + required: true + is_set: true + set_hash_func: computeRouterNatIPsHash + item_type: !ruby/object:Api::Type::ResourceRef + name: 'address' + resource: 'Address' + imports: 'selfLink' + description: 'A reference to an address to be associated with this NAT' + custom_expand: 'templates/terraform/custom_expand/array_resourceref_with_validation.go.erb' + - !ruby/object:Api::Type::Array + name: 'drainNatIps' + description: | + A list of URLs of the IP resources to be drained. These IPs must be + valid static external IPs that have been assigned to the NAT. + send_empty_value: true + is_set: true + item_type: !ruby/object:Api::Type::ResourceRef + name: 'address' + resource: 'Address' + imports: 'selfLink' + description: 'A reference to an address associated with this NAT' + custom_expand: 'templates/terraform/custom_expand/array_resourceref_with_validation.go.erb' diff --git a/mmv1/products/compute/Subnetwork.yaml b/mmv1/products/compute/Subnetwork.yaml index 672dca4bcd14..1e34c6550096 100644 --- a/mmv1/products/compute/Subnetwork.yaml +++ b/mmv1/products/compute/Subnetwork.yaml @@ -214,13 +214,13 @@ properties: name: 'purpose' immutable: true description: | - The purpose of the resource. This field can be either `PRIVATE_RFC_1918`, `REGIONAL_MANAGED_PROXY`, `GLOBAL_MANAGED_PROXY`, `PRIVATE_SERVICE_CONNECT` or `PRIVATE_NAT`([Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)). + The purpose of the resource. This field can be either `PRIVATE`, `REGIONAL_MANAGED_PROXY`, `GLOBAL_MANAGED_PROXY`, `PRIVATE_SERVICE_CONNECT` or `PRIVATE_NAT`([Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)). A subnet with purpose set to `REGIONAL_MANAGED_PROXY` is a user-created subnetwork that is reserved for regional Envoy-based load balancers. A subnetwork in a given region with purpose set to `GLOBAL_MANAGED_PROXY` is a proxy-only subnet and is shared between all the cross-regional Envoy-based load balancers. A subnetwork with purpose set to `PRIVATE_SERVICE_CONNECT` reserves the subnet for hosting a Private Service Connect published service. A subnetwork with purpose set to `PRIVATE_NAT` is used as source range for Private NAT gateways. Note that `REGIONAL_MANAGED_PROXY` is the preferred setting for all regional Envoy load balancers. - If unspecified, the purpose defaults to `PRIVATE_RFC_1918`. + If unspecified, the purpose defaults to `PRIVATE`. default_from_api: true - !ruby/object:Api::Type::Enum name: 'role' diff --git a/mmv1/products/compute/go_BackendService.yaml b/mmv1/products/compute/go_BackendService.yaml index a224db0981b1..c5863819ad42 100644 --- a/mmv1/products/compute/go_BackendService.yaml +++ b/mmv1/products/compute/go_BackendService.yaml @@ -742,6 +742,7 @@ properties: - name: 'iap' type: NestedObject description: Settings for enabling Cloud Identity Aware Proxy + default_from_api: true send_empty_value: true properties: - name: 'enabled' diff --git a/mmv1/products/compute/go_Interconnect.yaml b/mmv1/products/compute/go_Interconnect.yaml index 2d6227970926..f01e7bf770e5 100644 --- a/mmv1/products/compute/go_Interconnect.yaml +++ b/mmv1/products/compute/go_Interconnect.yaml @@ -25,7 +25,7 @@ references: docs: base_url: 'projects/{{project}}/global/interconnects' self_link: 'projects/{{project}}/global/interconnects/{{name}}' -immutable: true +update_verb: 'PATCH' timeouts: insert_minutes: 20 update_minutes: 20 @@ -376,6 +376,7 @@ properties: description: | Indicates that this is a Cross-Cloud Interconnect. This field specifies the location outside of Google's network that the interconnect is connected to. + immutable: true - name: 'requestedFeatures' type: Array description: | @@ -384,6 +385,7 @@ properties: specified, the default value is false, which allocates non-MACsec capable ports first if available). Note that MACSEC is still technically allowed for compatibility reasons, but it does not work with the API, and will be removed in an upcoming major version. + immutable: true item_type: type: Enum description: | diff --git a/mmv1/products/compute/go_RegionBackendService.yaml b/mmv1/products/compute/go_RegionBackendService.yaml index fed219a2bdf3..89037c828208 100644 --- a/mmv1/products/compute/go_RegionBackendService.yaml +++ b/mmv1/products/compute/go_RegionBackendService.yaml @@ -747,6 +747,7 @@ properties: - name: 'iap' type: NestedObject description: Settings for enabling Cloud Identity Aware Proxy + default_from_api: true send_empty_value: true properties: - name: 'enabled' diff --git a/mmv1/products/compute/go_RegionNetworkFirewallPolicyWithRules.yaml b/mmv1/products/compute/go_RegionNetworkFirewallPolicyWithRules.yaml new file mode 100644 index 000000000000..9fc93b160f27 --- /dev/null +++ b/mmv1/products/compute/go_RegionNetworkFirewallPolicyWithRules.yaml @@ -0,0 +1,678 @@ +# Copyright 2024 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. + +# Warning: This is a temporary file, and should not be edited directly +--- +name: 'RegionNetworkFirewallPolicyWithRules' +description: "The Compute NetworkFirewallPolicy with rules resource" +min_version: 'beta' +docs: +base_url: 'projects/{{project}}/regions/{{region}}/firewallPolicies' +self_link: 'projects/{{project}}/regions/{{region}}/firewallPolicies/{{name}}' +create_url: 'projects/{{project}}/regions/{{region}}/firewallPolicies' +update_verb: 'PATCH' +timeouts: + insert_minutes: 20 + update_minutes: 20 + delete_minutes: 20 +async: + actions: ['create', 'delete', 'update'] + type: 'OpAsync' + operation: + base_url: '{{op_id}}' + kind: 'compute#operation' + path: 'name' + wait_ms: 1000 + result: + path: 'targetLink' + resource_inside_response: false + error: + path: 'error/errors' + message: 'message' +custom_code: + constants: 'templates/terraform/constants/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl' + encoder: 'templates/terraform/encoders/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl' + update_encoder: 'templates/terraform/update_encoder/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl' + decoder: 'templates/terraform/decoders/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl' + post_create: 'templates/terraform/post_create/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl' +legacy_long_form_project: true +examples: + - name: 'compute_region_network_firewall_policy_with_rules_full' + primary_resource_id: 'region-network-firewall-policy-with-rules' + vars: + policy_name: 'tf-region-fw-policy-with-rules' + address_group_name: 'tf-address-group' + tag_key_name: 'tf-tag-key' + tag_value_name: 'tf-tag-value' + test_env_vars: + org_id: 'ORG_ID' +parameters: + - name: 'region' + type: String + description: The region of this resource. + min_version: 'beta' + url_param_only: true + immutable: true + default_from_api: true +properties: + - name: 'creationTimestamp' + type: String + description: Creation timestamp in RFC3339 text format. + min_version: 'beta' + output: true + - name: 'name' + type: String + description: | + User-provided name of the Network firewall policy. + The name should be unique in the project in which the firewall policy is created. + 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. + min_version: 'beta' + required: true + immutable: true + - name: 'networkFirewallPolicyId' + type: String + description: The unique identifier for the resource. This identifier is defined by the server. + api_name: id + min_version: 'beta' + output: true + - name: 'description' + type: String + description: An optional description of this resource. + min_version: 'beta' + - name: 'rule' + type: Array + description: A list of firewall policy rules. + api_name: rules + min_version: 'beta' + required: true + item_type: + type: NestedObject + properties: + - name: 'description' + type: String + description: | + A description of the rule. + min_version: 'beta' + - name: 'ruleName' + type: String + description: | + An optional name for the rule. This field is not a unique identifier + and can be updated. + min_version: 'beta' + - name: 'priority' + type: Integer + description: | + An integer indicating the priority of a rule in the list. The priority must be a value + between 0 and 2147483647. Rules are evaluated from highest to lowest priority where 0 is the + highest priority and 2147483647 is the lowest priority. + min_version: 'beta' + required: true + - name: 'match' + type: NestedObject + description: + A match condition that incoming traffic is evaluated against. If it + evaluates to true, the corresponding 'action' is enforced. + min_version: 'beta' + required: true + properties: + - name: 'srcIpRanges' + type: Array + description: | + Source IP address range in CIDR format. Required for + INGRESS rules. + min_version: 'beta' + item_type: + type: String + - name: 'destIpRanges' + type: Array + description: | + Destination IP address range in CIDR format. Required for + EGRESS rules. + min_version: 'beta' + item_type: + type: String + - name: 'srcAddressGroups' + type: Array + description: | + Address groups which should be matched against the traffic source. + Maximum number of source address groups is 10. + min_version: 'beta' + item_type: + type: String + - name: 'destAddressGroups' + type: Array + description: | + Address groups which should be matched against the traffic destination. + Maximum number of destination address groups is 10. + min_version: 'beta' + item_type: + type: String + - name: 'srcFqdns' + type: Array + description: | + Fully Qualified Domain Name (FQDN) which should be matched against + traffic source. Maximum number of source fqdn allowed is 100. + min_version: 'beta' + item_type: + type: String + - name: 'destFqdns' + type: Array + description: | + Fully Qualified Domain Name (FQDN) which should be matched against + traffic destination. Maximum number of destination fqdn allowed is 100. + min_version: 'beta' + item_type: + type: String + - name: 'srcRegionCodes' + type: Array + description: | + Region codes whose IP addresses will be used to match for source + of traffic. Should be specified as 2 letter country code defined as per + ISO 3166 alpha-2 country codes. ex."US" + Maximum number of source region codes allowed is 5000. + min_version: 'beta' + item_type: + type: String + - name: 'destRegionCodes' + type: Array + description: | + Region codes whose IP addresses will be used to match for destination + of traffic. Should be specified as 2 letter country code defined as per + ISO 3166 alpha-2 country codes. ex."US" + Maximum number of destination region codes allowed is 5000. + min_version: 'beta' + item_type: + type: String + - name: 'srcThreatIntelligences' + type: Array + description: | + Names of Network Threat Intelligence lists. + The IPs in these lists will be matched against traffic source. + min_version: 'beta' + item_type: + type: String + - name: 'destThreatIntelligences' + type: Array + description: | + Names of Network Threat Intelligence lists. + The IPs in these lists will be matched against traffic destination. + min_version: 'beta' + item_type: + type: String + - name: 'layer4Config' + type: Array + description: | + Pairs of IP protocols and ports that the rule should match. + api_name: layer4Configs + min_version: 'beta' + required: true + item_type: + type: NestedObject + properties: + - name: 'ipProtocol' + type: String + description: | + The IP protocol to which this rule applies. The protocol + type is required when creating a firewall rule. + This value can either be one of the following well + known protocol strings (tcp, udp, icmp, esp, ah, ipip, sctp), + or the IP protocol number. + min_version: 'beta' + required: true + - name: 'ports' + type: Array + description: | + An optional list of ports to which this rule applies. This field + is only applicable for UDP or TCP protocol. Each entry must be + either an integer or a range. If not specified, this rule + applies to connections through any port. + Example inputs include: ["22"], ["80","443"], and + ["12345-12349"]. + min_version: 'beta' + item_type: + type: String + - name: 'srcSecureTag' + type: Array + description: | + List of secure tag values, which should be matched at the source + of the traffic. + For INGRESS rule, if all the srcSecureTag are INEFFECTIVE, + and there is no srcIpRange, this rule will be ignored. + Maximum number of source tag values allowed is 256. + api_name: srcSecureTags + min_version: 'beta' + item_type: + type: NestedObject + properties: + - name: 'name' + type: String + description: | + Name of the secure tag, created with TagManager's TagValue API. + @pattern tagValues/[0-9]+ + min_version: 'beta' + - name: 'state' + type: Enum + description: | + [Output Only] State of the secure tag, either `EFFECTIVE` or + `INEFFECTIVE`. A secure tag is `INEFFECTIVE` when it is deleted + or its network is deleted. + min_version: 'beta' + output: true + enum_values: + - 'EFFECTIVE' + - 'INEFFECTIVE' + - name: 'targetSecureTag' + type: Array + description: | + A list of secure tags that controls which instances the firewall rule + applies to. If targetSecureTag are specified, then the + firewall rule applies only to instances in the VPC network that have one + of those EFFECTIVE secure tags, if all the target_secure_tag are in + INEFFECTIVE state, then this rule will be ignored. + targetSecureTag may not be set at the same time as + targetServiceAccounts. + If neither targetServiceAccounts nor + targetSecureTag are specified, the firewall rule applies + to all instances on the specified network. + Maximum number of target label tags allowed is 256. + api_name: targetSecureTags + min_version: 'beta' + item_type: + type: NestedObject + properties: + - name: 'name' + type: String + description: | + Name of the secure tag, created with TagManager's TagValue API. + @pattern tagValues/[0-9]+ + min_version: 'beta' + - name: 'state' + type: Enum + description: | + [Output Only] State of the secure tag, either `EFFECTIVE` or + `INEFFECTIVE`. A secure tag is `INEFFECTIVE` when it is deleted + or its network is deleted. + min_version: 'beta' + output: true + enum_values: + - 'EFFECTIVE' + - 'INEFFECTIVE' + - name: 'action' + type: String + description: | + The Action to perform when the client connection triggers the rule. Can currently be either + "allow", "deny", "apply_security_profile_group" or "goto_next". + min_version: 'beta' + required: true + - name: 'direction' + type: Enum + description: | + The direction in which this rule applies. If unspecified an INGRESS rule is created. + min_version: 'beta' + enum_values: + - 'INGRESS' + - 'EGRESS' + - name: 'enableLogging' + type: Boolean + description: | + Denotes whether to enable logging for a particular rule. + If logging is enabled, logs will be exported to the + configured export destination in Stackdriver. + min_version: 'beta' + send_empty_value: true + - name: 'targetServiceAccounts' + type: Array + description: | + A list of service accounts indicating the sets of + instances that are applied with this rule. + min_version: 'beta' + item_type: + type: String + - name: 'securityProfileGroup' + type: String + description: | + A fully-qualified URL of a SecurityProfile resource instance. + Example: + https://networksecurity.googleapis.com/v1/projects/{project}/locations/{location}/securityProfileGroups/my-security-profile-group + Must be specified if action is 'apply_security_profile_group'. + min_version: 'beta' + - name: 'tlsInspect' + type: Boolean + description: | + Boolean flag indicating if the traffic should be TLS decrypted. + It can be set only if action = 'apply_security_profile_group' and cannot be set for other actions. + min_version: 'beta' + - name: 'disabled' + type: Boolean + description: | + Denotes whether the firewall policy rule is disabled. When set to true, + the firewall policy rule is not enforced and traffic behaves as if it did + not exist. If this is unspecified, the firewall policy rule will be + enabled. + min_version: 'beta' + - name: 'predefinedRules' + type: Array + description: A list of firewall policy pre-defined rules. + min_version: 'beta' + output: true + item_type: + type: NestedObject + properties: + - name: 'description' + type: String + description: | + A description of the rule. + min_version: 'beta' + output: true + - name: 'ruleName' + type: String + description: | + An optional name for the rule. This field is not a unique identifier + and can be updated. + min_version: 'beta' + output: true + - name: 'priority' + type: Integer + description: | + An integer indicating the priority of a rule in the list. The priority must be a value + between 0 and 2147483647. Rules are evaluated from highest to lowest priority where 0 is the + highest priority and 2147483647 is the lowest priority. + min_version: 'beta' + output: true + - name: 'match' + type: NestedObject + description: + A match condition that incoming traffic is evaluated against. If it + evaluates to true, the corresponding 'action' is enforced. + min_version: 'beta' + output: true + properties: + - name: 'srcIpRanges' + type: Array + description: | + Source IP address range in CIDR format. Required for + INGRESS rules. + min_version: 'beta' + output: true + item_type: + type: String + - name: 'destIpRanges' + type: Array + description: | + Destination IP address range in CIDR format. Required for + EGRESS rules. + min_version: 'beta' + output: true + item_type: + type: String + - name: 'srcAddressGroups' + type: Array + description: | + Address groups which should be matched against the traffic source. + Maximum number of source address groups is 10. + min_version: 'beta' + output: true + item_type: + type: String + - name: 'destAddressGroups' + type: Array + description: | + Address groups which should be matched against the traffic destination. + Maximum number of destination address groups is 10. + min_version: 'beta' + output: true + item_type: + type: String + - name: 'srcFqdns' + type: Array + description: | + Fully Qualified Domain Name (FQDN) which should be matched against + traffic source. Maximum number of source fqdn allowed is 100. + min_version: 'beta' + output: true + item_type: + type: String + - name: 'destFqdns' + type: Array + description: | + Fully Qualified Domain Name (FQDN) which should be matched against + traffic destination. Maximum number of destination fqdn allowed is 100. + min_version: 'beta' + output: true + item_type: + type: String + - name: 'srcRegionCodes' + type: Array + description: | + Region codes whose IP addresses will be used to match for source + of traffic. Should be specified as 2 letter country code defined as per + ISO 3166 alpha-2 country codes. ex."US" + Maximum number of source region codes allowed is 5000. + min_version: 'beta' + output: true + item_type: + type: String + - name: 'destRegionCodes' + type: Array + description: | + Region codes whose IP addresses will be used to match for destination + of traffic. Should be specified as 2 letter country code defined as per + ISO 3166 alpha-2 country codes. ex."US" + Maximum number of destination region codes allowed is 5000. + min_version: 'beta' + output: true + item_type: + type: String + - name: 'srcThreatIntelligences' + type: Array + description: | + Names of Network Threat Intelligence lists. + The IPs in these lists will be matched against traffic source. + min_version: 'beta' + output: true + item_type: + type: String + - name: 'destThreatIntelligences' + type: Array + description: | + Names of Network Threat Intelligence lists. + The IPs in these lists will be matched against traffic destination. + min_version: 'beta' + output: true + item_type: + type: String + - name: 'layer4Config' + type: Array + description: | + Pairs of IP protocols and ports that the rule should match. + api_name: layer4Configs + min_version: 'beta' + output: true + item_type: + type: NestedObject + properties: + - name: 'ipProtocol' + type: String + description: | + The IP protocol to which this rule applies. The protocol + type is required when creating a firewall rule. + This value can either be one of the following well + known protocol strings (tcp, udp, icmp, esp, ah, ipip, sctp), + or the IP protocol number. + min_version: 'beta' + output: true + - name: 'ports' + type: Array + description: | + An optional list of ports to which this rule applies. This field + is only applicable for UDP or TCP protocol. Each entry must be + either an integer or a range. If not specified, this rule + applies to connections through any port. + Example inputs include: ["22"], ["80","443"], and + ["12345-12349"]. + min_version: 'beta' + output: true + item_type: + type: String + - name: 'srcSecureTag' + type: Array + description: | + List of secure tag values, which should be matched at the source + of the traffic. + For INGRESS rule, if all the srcSecureTag are INEFFECTIVE, + and there is no srcIpRange, this rule will be ignored. + Maximum number of source tag values allowed is 256. + api_name: srcSecureTags + min_version: 'beta' + output: true + item_type: + type: NestedObject + properties: + - name: 'name' + type: String + description: | + Name of the secure tag, created with TagManager's TagValue API. + @pattern tagValues/[0-9]+ + min_version: 'beta' + output: true + - name: 'state' + type: Enum + description: | + [Output Only] State of the secure tag, either `EFFECTIVE` or + `INEFFECTIVE`. A secure tag is `INEFFECTIVE` when it is deleted + or its network is deleted. + min_version: 'beta' + output: true + enum_values: + - 'EFFECTIVE' + - 'INEFFECTIVE' + - name: 'targetSecureTag' + type: Array + description: | + A list of secure tags that controls which instances the firewall rule + applies to. If targetSecureTag are specified, then the + firewall rule applies only to instances in the VPC network that have one + of those EFFECTIVE secure tags, if all the target_secure_tag are in + INEFFECTIVE state, then this rule will be ignored. + targetSecureTag may not be set at the same time as + targetServiceAccounts. + If neither targetServiceAccounts nor + targetSecureTag are specified, the firewall rule applies + to all instances on the specified network. + Maximum number of target label tags allowed is 256. + api_name: targetSecureTags + min_version: 'beta' + output: true + item_type: + type: NestedObject + properties: + - name: 'name' + type: String + description: | + Name of the secure tag, created with TagManager's TagValue API. + @pattern tagValues/[0-9]+ + min_version: 'beta' + output: true + - name: 'state' + type: Enum + description: | + [Output Only] State of the secure tag, either `EFFECTIVE` or + `INEFFECTIVE`. A secure tag is `INEFFECTIVE` when it is deleted + or its network is deleted. + min_version: 'beta' + output: true + enum_values: + - 'EFFECTIVE' + - 'INEFFECTIVE' + - name: 'action' + type: String + description: | + The Action to perform when the client connection triggers the rule. Can currently be either + "allow", "deny", "apply_security_profile_group" or "goto_next". + min_version: 'beta' + output: true + - name: 'direction' + type: Enum + description: | + The direction in which this rule applies. If unspecified an INGRESS rule is created. + min_version: 'beta' + output: true + enum_values: + - 'INGRESS' + - 'EGRESS' + - name: 'enableLogging' + type: Boolean + description: | + Denotes whether to enable logging for a particular rule. + If logging is enabled, logs will be exported to the + configured export destination in Stackdriver. + min_version: 'beta' + output: true + send_empty_value: true + - name: 'targetServiceAccounts' + type: Array + description: | + A list of service accounts indicating the sets of + instances that are applied with this rule. + min_version: 'beta' + output: true + item_type: + type: String + - name: 'securityProfileGroup' + type: String + description: | + A fully-qualified URL of a SecurityProfile resource instance. + Example: + https://networksecurity.googleapis.com/v1/projects/{project}/locations/{location}/securityProfileGroups/my-security-profile-group + Must be specified if action is 'apply_security_profile_group'. + min_version: 'beta' + output: true + - name: 'tlsInspect' + type: Boolean + description: | + Boolean flag indicating if the traffic should be TLS decrypted. + It can be set only if action = 'apply_security_profile_group' and cannot be set for other actions. + min_version: 'beta' + output: true + - name: 'disabled' + type: Boolean + description: | + Denotes whether the firewall policy rule is disabled. When set to true, + the firewall policy rule is not enforced and traffic behaves as if it did + not exist. If this is unspecified, the firewall policy rule will be + enabled. + min_version: 'beta' + output: true + - name: 'fingerprint' + type: Fingerprint + description: Fingerprint of the resource. This field is used internally during updates of this resource. + min_version: 'beta' + output: true + - name: 'selfLink' + type: String + description: Server-defined URL for the resource. + min_version: 'beta' + output: true + - name: 'selfLinkWithId' + type: String + description: Server-defined URL for this resource with the resource id. + min_version: 'beta' + output: true + - name: 'ruleTupleCount' + type: Integer + description: Total count of all firewall policy rule tuples. A firewall policy can not exceed a set number of tuples. + min_version: 'beta' + output: true diff --git a/mmv1/products/healthcare/go_ConsentStore.yaml b/mmv1/products/healthcare/go_ConsentStore.yaml index c074ab71c852..8ba444b7b043 100644 --- a/mmv1/products/healthcare/go_ConsentStore.yaml +++ b/mmv1/products/healthcare/go_ConsentStore.yaml @@ -43,6 +43,7 @@ iam_policy: - '{{%dataset}}/consentStores/{{name}}' - '{{name}}' custom_code: +# Skipping the sweeper since this is a fine-grained resource under dataset exclude_sweeper: true examples: - name: 'healthcare_consent_store_basic' diff --git a/mmv1/products/healthcare/go_PipelineJob.yaml b/mmv1/products/healthcare/go_PipelineJob.yaml new file mode 100644 index 000000000000..bc3eeb5fa388 --- /dev/null +++ b/mmv1/products/healthcare/go_PipelineJob.yaml @@ -0,0 +1,266 @@ +# Copyright 2024 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. + +# Warning: This is a temporary file, and should not be edited directly +--- +name: 'PipelineJob' +kind: 'healthcare#pipelineJob' +description: | + PipelineJobs are Long Running Operations on Healthcare API to Map or Reconcile + incoming data into FHIR format +references: + guides: + 'Creating a PipelineJob': 'https://cloud.google.com/healthcare-api/private/healthcare-data-engine/docs/reference/rest/v1/projects.locations.datasets.pipelineJobs#PipelineJob' + api: 'https://cloud.google.com/healthcare-api/healthcare-data-engine/docs/reference/rest/v1/projects.locations.datasets.pipelineJobs' +docs: +id_format: '{{dataset}}/pipelineJobs/{{name}}' +base_url: '{{dataset}}/pipelineJobs?pipelineJobId={{name}}' +self_link: '{{dataset}}/pipelineJobs/{{name}}' +update_verb: 'PATCH' +update_mask: true +delete_url: '{{dataset}}/pipelineJobs/{{name}}' +import_format: + - '{{%dataset}}/pipelineJobs/{{name}}' + - '{{name}}' + - '{{dataset}}/pipelineJobs?pipelineJobId={{name}}' +timeouts: + insert_minutes: 20 + update_minutes: 20 + delete_minutes: 20 +custom_code: + decoder: 'templates/terraform/decoders/go/long_name_to_self_link.go.tmpl' +exclude_sweeper: true +examples: + - name: 'healthcare_pipeline_job_reconciliation' + primary_resource_id: 'example-pipeline' + vars: + pipeline_name: 'example_pipeline_job' + dataset_name: 'example_dataset' + fhir_store_name: 'fhir_store' + bucket_name: 'example_bucket_name' + - name: 'healthcare_pipeline_job_backfill' + primary_resource_id: 'example-pipeline' + vars: + backfill_pipeline_name: 'example_backfill_pipeline' + dataset_name: 'example_dataset' + mapping_pipeline_name: 'example_mapping_pipeline' + - name: 'healthcare_pipeline_job_whistle_mapping' + primary_resource_id: 'example-mapping-pipeline' + vars: + pipeline_name: 'example_mapping_pipeline_job' + dataset_name: 'example_dataset' + source_fhirstore_name: 'source_fhir_store' + dest_fhirstore_name: 'dest_fhir_store' + bucket_name: 'example_bucket_name' + - name: 'healthcare_pipeline_job_mapping_recon_dest' + primary_resource_id: 'example-mapping-pipeline' + vars: + pipeline_name: 'example_mapping_pipeline_job' + recon_pipeline_name: 'example_recon_pipeline_job' + dataset_name: 'example_dataset' + source_fhirstore_name: 'source_fhir_store' + dest_fhirstore_name: 'dest_fhir_store' + bucket_name: 'example_bucket_name' +parameters: + - name: 'location' + type: String + description: | + Location where the Pipeline Job is to run + url_param_only: true + required: true + immutable: true + - name: 'dataset' + type: String + description: | + Healthcare Dataset under which the Pipeline Job is to run + url_param_only: true + required: true + immutable: true +properties: + - name: 'name' + type: String + description: | + Specifies the name of the pipeline job. This field is user-assigned. + required: true + - name: 'disableLineage' + type: Boolean + description: | + If true, disables writing lineage for the pipeline. + required: false + default_value: false + - name: 'labels' + type: KeyValueLabels + description: | + User-supplied key-value pairs used to organize Pipeline Jobs. + Label keys must be between 1 and 63 characters long, have a UTF-8 encoding of + maximum 128 bytes, and must conform to the following PCRE regular expression: + [\p{Ll}\p{Lo}][\p{Ll}\p{Lo}\p{N}_-]{0,62} + Label values are optional, must be between 1 and 63 characters long, have a + UTF-8 encoding of maximum 128 bytes, and must conform to the following PCRE + regular expression: [\p{Ll}\p{Lo}\p{N}_-]{0,63} + No more than 64 labels can be associated with a given pipeline. + An object containing a list of "key": value pairs. + Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }. + required: false + - name: 'selfLink' + type: String + description: | + The fully qualified name of this dataset + ignore_read: true + output: true + - name: 'mappingPipelineJob' + type: NestedObject + description: | + Specifies mapping configuration. + required: false + conflicts: + - reconciliationPipelineJob + - backfillPipelineJob + properties: + - name: 'mappingConfig' + type: NestedObject + description: | + The location of the mapping configuration. + required: true + properties: + - name: 'description' + type: String + description: | + Describes the mapping configuration. + required: false + - name: 'whistleConfigSource' + type: NestedObject + description: | + Specifies the path to the mapping configuration for harmonization pipeline. + required: false + properties: + - name: 'uri' + type: String + description: | + Main configuration file which has the entrypoint or the root function. + Example: gs://{bucket-id}/{path/to/import-root/dir}/entrypoint-file-name.wstl. + required: true + - name: 'importUriPrefix' + type: String + description: | + Directory path where all the Whistle files are located. + Example: gs://{bucket-id}/{path/to/import-root/dir} + required: true + - name: 'fhirStreamingSource' + type: NestedObject + description: | + A streaming FHIR data source. + required: false + properties: + - name: 'fhirStore' + type: String + description: | + The path to the FHIR store in the format projects/{projectId}/locations/{locationId}/datasets/{datasetId}/fhirStores/{fhirStoreId}. + required: true + - name: 'description' + type: String + description: | + Describes the streaming FHIR data source. + required: false + - name: 'fhirStoreDestination' + type: String + description: | + If set, the mapping pipeline will write snapshots to this + FHIR store without assigning stable IDs. You must + grant your pipeline project's Cloud Healthcare Service + Agent serviceaccount healthcare.fhirResources.executeBundle + and healthcare.fhirResources.create permissions on the + destination store. The destination store must set + [disableReferentialIntegrity][FhirStore.disable_referential_integrity] + to true. The destination store must use FHIR version R4. + Format: project/{projectID}/locations/{locationID}/datasets/{datasetName}/fhirStores/{fhirStoreID}. + required: false + conflicts: + - reconciliationDestination + - name: 'reconciliationDestination' + type: Boolean + description: | + If set to true, a mapping pipeline will send output snapshots + to the reconciliation pipeline in its dataset. A reconciliation + pipeline must exist in this dataset before a mapping pipeline + with a reconciliation destination can be created. + required: false + conflicts: + - fhirStoreDestination + - name: 'reconciliationPipelineJob' + type: NestedObject + description: | + Specifies reconciliation configuration. + required: false + conflicts: + - mappingPipelineJob + - backfillPipelineJob + properties: + - name: 'mergeConfig' + type: NestedObject + description: | + Specifies the location of the reconciliation configuration. + required: true + properties: + - name: 'description' + type: String + description: | + Describes the mapping configuration. + required: false + - name: 'whistleConfigSource' + type: NestedObject + description: | + Specifies the path to the mapping configuration for harmonization pipeline. + required: true + properties: + - name: 'uri' + type: String + description: | + Main configuration file which has the entrypoint or the root function. + Example: gs://{bucket-id}/{path/to/import-root/dir}/entrypoint-file-name.wstl. + required: true + - name: 'importUriPrefix' + type: String + description: | + Directory path where all the Whistle files are located. + Example: gs://{bucket-id}/{path/to/import-root/dir} + required: true + - name: 'matchingUriPrefix' + type: String + description: | + Specifies the top level directory of the matching configs used + in all mapping pipelines, which extract properties for resources + to be matched on. + Example: gs://{bucket-id}/{path/to/matching/configs} + required: true + - name: 'fhirStoreDestination' + type: String + description: | + The harmonized FHIR store to write harmonized FHIR resources to, + in the format of: project/{projectID}/locations/{locationID}/datasets/{datasetName}/fhirStores/{id} + required: false + - name: 'backfillPipelineJob' + type: NestedObject + description: | + Specifies the backfill configuration. + required: false + conflicts: + - mappingPipelineJob + - reconciliationPipelineJob + properties: + - name: 'mappingPipelineJob' + type: String + description: | + Specifies the mapping pipeline job to backfill, the name format + should follow: projects/{projectId}/locations/{locationId}/datasets/{datasetId}/pipelineJobs/{pipelineJobId}. + required: false diff --git a/mmv1/products/logging/LogScope.yaml b/mmv1/products/logging/LogScope.yaml new file mode 100644 index 000000000000..394513c74001 --- /dev/null +++ b/mmv1/products/logging/LogScope.yaml @@ -0,0 +1,85 @@ +# Copyright 2024 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: 'LogScope' +base_url: '{{parent}}/locations/{{location}}/logScopes' +create_url: '{{parent}}/locations/{{location}}/logScopes?logScopeId={{name}}' +self_link: '{{parent}}/locations/{{location}}/logScopes/{{name}}' +import_format: ['{{%parent}}/locations/{{location}}/logScopes/{{name}}'] +update_verb: :PATCH +update_mask: true +references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Official Documentation': 'https://cloud.google.com/logging/docs/apis' + api: 'https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.locations.logScopes' +description: 'Describes a group of resources to read log entries from' +examples: + - !ruby/object:Provider::Terraform::Examples + name: 'logging_log_scope_basic' + primary_resource_id: 'logging_log_scope' + vars: + log_scope_name: 'my-log-scope' + log_view_name_1: 'view1' + log_view_name_2: 'view2' + test_env_vars: + project: :PROJECT_NAME +custom_code: !ruby/object:Provider::Terraform::CustomCode + encoder: templates/terraform/encoders/logging_log_scope.go.erb +parameters: + - !ruby/object:Api::Type::String + name: parent + description: The parent of the resource. + url_param_only: true + immutable: true + default_from_api: true + diff_suppress_func: 'tpgresource.CompareSelfLinkOrResourceName' + - !ruby/object:Api::Type::String + name: location + description: + 'The location of the resource. The supported locations are: global, + us-central1, us-east1, us-west1, asia-east1, europe-west1.' + url_param_only: true + immutable: true + default_from_api: true +properties: + - !ruby/object:Api::Type::String + name: name + description: + 'The resource name of the log scope. For example: + \`projects/my-project/locations/global/logScopes/my-log-scope\`' + required: true + immutable: true + ignore_read: true + diff_suppress_func: 'tpgresource.CompareResourceNames' + - !ruby/object:Api::Type::Array + # This has to be camelCase, even though it's snakeCase in proto definiation. + name: resourceNames + item_type: Api::Type::String + required: true + description: + 'Names of one or more parent resources : * \`projects/[PROJECT_ID]\` + May alternatively be one or more views : + * \`projects/[PROJECT_ID]/locations/[LOCATION_ID]/buckets/[BUCKET_ID]/views/[VIEW_ID]\` + A log scope can include a maximum of 50 projects and a maximum of 100 resources in total.' + - !ruby/object:Api::Type::String + name: description + description: Describes this log scopes. + - !ruby/object:Api::Type::String + name: createTime + description: Output only. The creation timestamp of the log scopes. + output: true + - !ruby/object:Api::Type::String + name: updateTime + description: Output only. The last update timestamp of the log scopes. + output: true diff --git a/mmv1/products/looker/Instance.yaml b/mmv1/products/looker/Instance.yaml index 0685a88c6b35..b30f2d234a8e 100644 --- a/mmv1/products/looker/Instance.yaml +++ b/mmv1/products/looker/Instance.yaml @@ -89,6 +89,13 @@ examples: client_id: 'my-client-id' client_secret: 'my-client-secret' custom_domain: 'my-custom-domain' + - !ruby/object:Provider::Terraform::Examples + name: 'looker_instance_psc' + primary_resource_id: 'looker-instance' + vars: + instance_name: 'my-instance' + client_id: 'my-client-id' + client_secret: 'my-client-secret' parameters: - !ruby/object:Api::Type::String name: 'region' @@ -392,6 +399,55 @@ properties: description: | Whether private IP is enabled on the Looker instance. default_value: false + # PscConfig Object + - !ruby/object:Api::Type::NestedObject + name: pscConfig + description: | + Information for Private Service Connect (PSC) setup for a Looker instance. + update_mask_fields: + - 'psc_config.allowed_vpcs' + - 'psc_config.service_attachments' + properties: + - !ruby/object:Api::Type::Array + name: 'allowedVpcs' + item_type: Api::Type::String + description: | + List of VPCs that are allowed ingress into the Looker instance. + - !ruby/object:Api::Type::String + name: 'lookerServiceAttachmentUri' + description: | + URI of the Looker service attachment. + output: true + - !ruby/object:Api::Type::Array + name: 'serviceAttachments' + description: | + List of egress service attachment configurations. + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::Enum + name: connectionStatus + description: | + Status of the service attachment connection. + output: true + values: + - :ACCEPTED + - :PENDING + - :REJECTED + - :NEEDS_ATTENTION + - :CLOSED + - !ruby/object:Api::Type::String + name: localFqdn + description: | + Fully qualified domain name that will be used in the private DNS record created for the service attachment. + - !ruby/object:Api::Type::String + name: targetServiceAttachmentUri + description: | + URI of the service attachment to connect to. + # PscConfig Object - End + - !ruby/object:Api::Type::Boolean + name: pscEnabled + description: | + Whether Public Service Connect (PSC) is enabled on the Looker instance - !ruby/object:Api::Type::Boolean name: publicIpEnabled description: | diff --git a/mmv1/products/looker/go_Instance.yaml b/mmv1/products/looker/go_Instance.yaml index 510fbf172aca..5f2cf1ae012e 100644 --- a/mmv1/products/looker/go_Instance.yaml +++ b/mmv1/products/looker/go_Instance.yaml @@ -62,6 +62,12 @@ examples: instance_name: 'my-instance' client_id: 'my-client-id' client_secret: 'my-client-secret' + - name: 'looker_instance_fips' + primary_resource_id: 'looker-instance' + vars: + instance_name: 'my-instance-fips' + client_id: 'my-client-id' + client_secret: 'my-client-secret' - name: 'looker_instance_enterprise_full' primary_resource_id: 'looker-instance' vars: @@ -95,6 +101,12 @@ examples: client_id: 'my-client-id' client_secret: 'my-client-secret' custom_domain: 'my-custom-domain' + - name: 'looker_instance_psc' + primary_resource_id: 'looker-instance' + vars: + instance_name: 'my-instance' + client_id: 'my-client-id' + client_secret: 'my-client-secret' parameters: - name: 'region' type: String @@ -260,6 +272,10 @@ properties: Full name and version of the CMEK key currently in use to encrypt Looker data. output: true # Encryption Config Object - End + - name: 'fipsEnabled' + type: Boolean + description: | + FIPS 140-2 Encryption enablement for Looker (Google Cloud Core). - name: 'ingressPrivateIp' type: String description: | @@ -391,6 +407,57 @@ properties: description: | Whether private IP is enabled on the Looker instance. default_value: false + # PscConfig Object + - name: 'pscConfig' + type: NestedObject + description: | + Information for Private Service Connect (PSC) setup for a Looker instance. + update_mask_fields: + - 'psc_config.allowed_vpcs' + - 'psc_config.service_attachments' + properties: + - name: 'allowedVpcs' + type: Array + description: | + List of VPCs that are allowed ingress into the Looker instance. + item_type: + type: String + - name: 'lookerServiceAttachmentUri' + type: String + description: | + URI of the Looker service attachment. + output: true + - name: 'serviceAttachments' + type: Array + description: | + List of egress service attachment configurations. + item_type: + type: NestedObject + properties: + - name: 'connectionStatus' + type: Enum + description: | + Status of the service attachment connection. + output: true + enum_values: + - 'ACCEPTED' + - 'PENDING' + - 'REJECTED' + - 'NEEDS_ATTENTION' + - 'CLOSED' + - name: 'localFqdn' + type: String + description: | + Fully qualified domain name that will be used in the private DNS record created for the service attachment. + - name: 'targetServiceAttachmentUri' + type: String + description: | + URI of the service attachment to connect to. + # PscConfig Object - End + - name: 'pscEnabled' + type: Boolean + description: | + Whether Public Service Connect (PSC) is enabled on the Looker instance - name: 'publicIpEnabled' type: Boolean description: | diff --git a/mmv1/products/metastore/go_Service.yaml b/mmv1/products/metastore/go_Service.yaml index 01873b30387d..8d8bc07142bb 100644 --- a/mmv1/products/metastore/go_Service.yaml +++ b/mmv1/products/metastore/go_Service.yaml @@ -64,6 +64,14 @@ examples: primary_resource_name: 'fmt.Sprintf("tf-test-metastore-srv%s", context["random_suffix"])' vars: metastore_service_name: 'metastore-srv' + - name: 'dataproc_metastore_service_deletion_protection' + primary_resource_id: 'default' + primary_resource_name: 'fmt.Sprintf("tf-test-metastore-srv%s", context["random_suffix"])' + vars: + metastore_service_name: 'metastore-srv' + deletion_protection: 'true' + test_vars_overrides: + 'deletion_protection': 'false' - name: 'dataproc_metastore_service_cmek_test' primary_resource_id: 'default' vars: @@ -297,6 +305,10 @@ properties: description: | A Cloud Storage URI of a folder, in the format gs:///. A sub-folder containing backup files will be stored below it. required: true + - name: 'deletionProtection' + type: Boolean + description: | + Indicates if the dataproc metastore should be protected against accidental deletions. - name: 'maintenanceWindow' type: NestedObject description: | diff --git a/mmv1/products/networkconnectivity/Spoke.yaml b/mmv1/products/networkconnectivity/Spoke.yaml index f6c1378783e5..a7031fdb4197 100644 --- a/mmv1/products/networkconnectivity/Spoke.yaml +++ b/mmv1/products/networkconnectivity/Spoke.yaml @@ -56,6 +56,33 @@ examples: instance_name: "basic-instance" hub_name: "basic-hub1" spoke_name: "basic-spoke" + - !ruby/object:Provider::Terraform::Examples + name: 'network_connectivity_spoke_vpn_tunnel_basic' + primary_resource_id: 'tunnel1' + vars: + network_name: "basic-network" + subnetwork_name: "basic-subnetwork" + gateway_name: "vpn-gateway" + external_gateway_name: "external-vpn-gateway" + router_name: "external-vpn-gateway" + vpn_tunnel_1_name: "tunnel1" + vpn_tunnel_2_name: "tunnel2" + router_interface_1_name: "router-interface1" + router_peer_1_name: "router-peer1" + router_interface_2_name: "router-interface2" + router_peer_2_name: "router-peer2" + hub_name: "basic-hub1" + vpn_tunnel_1_spoke_name: "vpn-tunnel-1-spoke" + vpn_tunnel_2_spoke_name: "vpn-tunnel-2-spoke" + - !ruby/object:Provider::Terraform::Examples + name: 'network_connectivity_spoke_interconnect_attachment_basic' + primary_resource_id: 'primary' + vars: + hub_name: "basic-hub1" + network_name: "basic-network" + router_name: "external-vpn-gateway" + interconnect_attachment_name: "partner-interconnect1" + interconnect_attachment_spoke_name: "interconnect-attachment-spoke" description: "The NetworkConnectivity Spoke resource" parameters: - !ruby/object:Api::Type::String @@ -114,6 +141,12 @@ properties: min_version: ga required: true immutable: true + - !ruby/object:Api::Type::Array + name: includeImportRanges + description: | + IP ranges allowed to be included during import from hub (does not control transit connectivity). + The only allowed value for now is "ALL_IPV4_RANGES". + item_type: Api::Type::String - !ruby/object:Api::Type::NestedObject name: linkedInterconnectAttachments description: A collection of VLAN attachment resources. These resources should be redundant attachments that all advertise the same prefixes to Google Cloud. Alternatively, in active/passive configurations, all attachments should be capable of advertising the same prefixes. @@ -136,6 +169,12 @@ properties: min_version: ga required: true immutable: true + - !ruby/object:Api::Type::Array + name: includeImportRanges + description: | + IP ranges allowed to be included during import from hub (does not control transit connectivity). + The only allowed value for now is "ALL_IPV4_RANGES". + item_type: Api::Type::String - !ruby/object:Api::Type::NestedObject name: linkedRouterApplianceInstances description: The URIs of linked Router appliance resources @@ -172,6 +211,12 @@ properties: min_version: ga required: true immutable: true + - !ruby/object:Api::Type::Array + name: includeImportRanges + description: | + IP ranges allowed to be included during import from hub (does not control transit connectivity). + The only allowed value for now is "ALL_IPV4_RANGES". + item_type: Api::Type::String - !ruby/object:Api::Type::NestedObject name: linkedVpcNetwork description: VPC network that is associated with the spoke. diff --git a/mmv1/products/networkconnectivity/go_Spoke.yaml b/mmv1/products/networkconnectivity/go_Spoke.yaml index 6913716512cb..b9eef6be0637 100644 --- a/mmv1/products/networkconnectivity/go_Spoke.yaml +++ b/mmv1/products/networkconnectivity/go_Spoke.yaml @@ -59,6 +59,31 @@ examples: instance_name: 'basic-instance' hub_name: 'basic-hub1' spoke_name: 'basic-spoke' + - name: 'network_connectivity_spoke_vpn_tunnel_basic' + primary_resource_id: 'tunnel1' + vars: + network_name: 'basic-network' + subnetwork_name: 'basic-subnetwork' + gateway_name: 'vpn-gateway' + external_gateway_name: 'external-vpn-gateway' + router_name: 'external-vpn-gateway' + vpn_tunnel_1_name: 'tunnel1' + vpn_tunnel_2_name: 'tunnel2' + router_interface_1_name: 'router-interface1' + router_peer_1_name: 'router-peer1' + router_interface_2_name: 'router-interface2' + router_peer_2_name: 'router-peer2' + hub_name: 'basic-hub1' + vpn_tunnel_1_spoke_name: 'vpn-tunnel-1-spoke' + vpn_tunnel_2_spoke_name: 'vpn-tunnel-2-spoke' + - name: 'network_connectivity_spoke_interconnect_attachment_basic' + primary_resource_id: 'primary' + vars: + hub_name: 'basic-hub1' + network_name: 'basic-network' + router_name: 'external-vpn-gateway' + interconnect_attachment_name: 'partner-interconnect1' + interconnect_attachment_spoke_name: 'interconnect-attachment-spoke' parameters: - name: 'location' type: String @@ -115,6 +140,13 @@ properties: description: A value that controls whether site-to-site data transfer is enabled for these resources. Note that data transfer is available only in supported locations. required: true immutable: true + - name: 'includeImportRanges' + type: Array + description: | + IP ranges allowed to be included during import from hub (does not control transit connectivity). + The only allowed value for now is "ALL_IPV4_RANGES". + item_type: + type: String - name: 'linkedInterconnectAttachments' type: NestedObject description: A collection of VLAN attachment resources. These resources should be redundant attachments that all advertise the same prefixes to Google Cloud. Alternatively, in active/passive configurations, all attachments should be capable of advertising the same prefixes. @@ -136,6 +168,13 @@ properties: description: A value that controls whether site-to-site data transfer is enabled for these resources. Note that data transfer is available only in supported locations. required: true immutable: true + - name: 'includeImportRanges' + type: Array + description: | + IP ranges allowed to be included during import from hub (does not control transit connectivity). + The only allowed value for now is "ALL_IPV4_RANGES". + item_type: + type: String - name: 'linkedRouterApplianceInstances' type: NestedObject description: The URIs of linked Router appliance resources @@ -168,6 +207,13 @@ properties: description: A value that controls whether site-to-site data transfer is enabled for these resources. Note that data transfer is available only in supported locations. required: true immutable: true + - name: 'includeImportRanges' + type: Array + description: | + IP ranges allowed to be included during import from hub (does not control transit connectivity). + The only allowed value for now is "ALL_IPV4_RANGES". + item_type: + type: String - name: 'linkedVpcNetwork' type: NestedObject description: VPC network that is associated with the spoke. diff --git a/mmv1/products/networkservices/ServiceLBPolicies.yaml b/mmv1/products/networkservices/ServiceLbPolicies.yaml similarity index 100% rename from mmv1/products/networkservices/ServiceLBPolicies.yaml rename to mmv1/products/networkservices/ServiceLbPolicies.yaml diff --git a/mmv1/products/secretmanagerregional/go_RegionalSecret.yaml b/mmv1/products/secretmanagerregional/go_RegionalSecret.yaml index 5668c9249878..0f25724aa831 100644 --- a/mmv1/products/secretmanagerregional/go_RegionalSecret.yaml +++ b/mmv1/products/secretmanagerregional/go_RegionalSecret.yaml @@ -138,19 +138,18 @@ properties: An object containing a list of "key": value pairs. Example: { "name": "wrench", "mass": "1.3kg", "count": "3" }. - # TODO : Add versionAliases field support once google_secret_manager_regional_secret_version is added - # - !ruby/object:Api::Type::KeyValuePairs - # name: versionAliases - # description: | - # Mapping from version alias to version name. + - name: 'versionAliases' + type: KeyValuePairs + description: | + Mapping from version alias to version name. - # A version alias is a string with a maximum length of 63 characters and can contain - # uppercase and lowercase letters, numerals, and the hyphen (-) and underscore ('_') - # characters. An alias string must start with a letter and cannot be the string - # 'latest' or 'NEW'. No more than 50 aliases can be assigned to a given secret. + A version alias is a string with a maximum length of 63 characters and can contain + uppercase and lowercase letters, numerals, and the hyphen (-) and underscore ('_') + characters. An alias string must start with a letter and cannot be the string + 'latest' or 'NEW'. No more than 50 aliases can be assigned to a given secret. - # An object containing a list of "key": value pairs. Example: - # { "name": "wrench", "mass": "1.3kg", "count": "3" }. + An object containing a list of "key": value pairs. Example: + { "name": "wrench", "mass": "1.3kg", "count": "3" }. - name: 'customerManagedEncryption' type: NestedObject description: | diff --git a/mmv1/products/secretmanagerregional/go_RegionalSecretVersion.yaml b/mmv1/products/secretmanagerregional/go_RegionalSecretVersion.yaml new file mode 100644 index 000000000000..1c8bd798c1f9 --- /dev/null +++ b/mmv1/products/secretmanagerregional/go_RegionalSecretVersion.yaml @@ -0,0 +1,161 @@ +# Copyright 2024 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. + +# Warning: This is a temporary file, and should not be edited directly +--- +name: 'RegionalSecretVersion' +description: | + A regional secret version resource. +docs: + optional_properties: | + * `is_secret_data_base64` - (Optional) If set to 'true', the secret data is expected to be base64-encoded string and would be sent as is. +base_url: '{{name}}' +self_link: '{{name}}' +create_url: '{{secret}}:addVersion' +delete_url: '{{name}}:destroy' +delete_verb: 'POST' +import_format: + - 'projects/{{%project}}/locations/{{%location}}/secrets/{{%secret_id}}/versions/{{%version}}' +timeouts: + insert_minutes: 20 + update_minutes: 20 + delete_minutes: 20 +custom_code: + extra_schema_entry: 'templates/terraform/extra_schema_entry/go/secret_version_is_secret_data_base64.go.tmpl' + decoder: 'templates/terraform/decoders/go/treat_destroyed_state_as_gone.tmpl' + pre_create: 'templates/terraform/pre_create/go/secret_manager_regional_secret_version.go.tmpl' + post_create: 'templates/terraform/post_create/go/regional_secret_version.go.tmpl' + pre_read: 'templates/terraform/pre_read/go/secret_manager_regional_secret_version.go.tmpl' + custom_update: 'templates/terraform/custom_update/go/regional_secret_version.go.tmpl' + pre_delete: 'templates/terraform/pre_delete/go/regional_secret_version_deletion_policy.go.tmpl' + custom_import: 'templates/terraform/custom_import/go/regional_secret_version.go.tmpl' +# Sweeper skipped as this resource has customized deletion. +exclude_sweeper: true +examples: + - name: 'regional_secret_version_basic' + primary_resource_id: 'regional_secret_version_basic' + vars: + secret_id: 'secret-version' + data: 'secret-data' + - name: 'regional_secret_version_with_base64_data' + primary_resource_id: 'regional_secret_version_base64' + vars: + secret_id: 'secret-version' + data: 'secret-data.pfx' + test_vars_overrides: + 'data': '"./test-fixtures/binary-file.pfx"' + ignore_read_extra: + - 'is_secret_data_base64' + - name: 'regional_secret_version_disabled' + primary_resource_id: 'regional_secret_version_disabled' + vars: + secret_id: 'secret-version' + data: 'secret-data' + - name: 'regional_secret_version_deletion_policy_abandon' + primary_resource_id: 'regional_secret_version_deletion_policy' + vars: + secret_id: 'secret-version' + data: 'secret-data' + ignore_read_extra: + - 'deletion_policy' + - name: 'regional_secret_version_deletion_policy_disable' + primary_resource_id: 'regional_secret_version_deletion_policy' + vars: + secret_id: 'secret-version' + data: 'secret-data' + ignore_read_extra: + - 'deletion_policy' +virtual_fields: + - name: 'deletion_policy' + description: | + The deletion policy for the regional secret version. Setting `ABANDON` allows the resource + to be abandoned rather than deleted. Setting `DISABLE` allows the resource to be + disabled rather than deleted. Default is `DELETE`. Possible values are: + * DELETE + * DISABLE + * ABANDON + type: String + default_value: "DELETE" +parameters: + - name: 'secret' + type: ResourceRef + description: | + Secret Manager regional secret resource. + url_param_only: true + required: true + immutable: true + resource: 'RegionalSecret' + imports: 'name' + - name: 'location' + type: String + description: | + Location of Secret Manager regional secret resource. + url_param_only: true + output: true +properties: + - name: 'name' + type: String + description: | + The resource name of the regional secret version. Format: + `projects/{{project}}/locations/{{location}}/secrets/{{secret_id}}/versions/{{version}}` + output: true + - name: 'createTime' + type: String + description: | + The time at which the regional secret version was created. + output: true + - name: 'destroyTime' + type: String + description: | + The time at which the regional secret version was destroyed. Only present if state is DESTROYED. + output: true + - name: 'customerManagedEncryption' + type: NestedObject + description: | + The customer-managed encryption configuration of the regional secret. + output: true + properties: + - name: 'kmsKeyVersionName' + type: String + description: | + The resource name of the Cloud KMS CryptoKey used to encrypt secret payloads. + output: true + - name: 'version' + type: String + description: | + The version of the Regional Secret. + output: true + custom_flatten: 'templates/terraform/custom_flatten/go/regional_secret_version_version.go.tmpl' + - name: 'enabled' + type: Boolean + description: | + The current state of the regional secret version. + api_name: state + custom_flatten: 'templates/terraform/custom_flatten/go/secret_version_enable.go.tmpl' + custom_expand: 'templates/terraform/custom_expand/go/regional_secret_version_enable.go.tmpl' + default_value: true + - name: 'payload' + type: NestedObject + description: The secret payload of the Regional SecretVersion. + required: true + custom_flatten: 'templates/terraform/custom_flatten/go/regional_secret_version_access.go.tmpl' + flatten_object: true + properties: + - name: 'secret_data' + type: String + description: The secret data. Must be no larger than 64KiB. + api_name: data + required: true + immutable: true + sensitive: true + custom_expand: 'templates/terraform/custom_expand/go/secret_version_secret_data.go.tmpl' diff --git a/mmv1/products/workbench/Instance.yaml b/mmv1/products/workbench/Instance.yaml index 580cf2c1532a..823a36577668 100644 --- a/mmv1/products/workbench/Instance.yaml +++ b/mmv1/products/workbench/Instance.yaml @@ -98,7 +98,7 @@ examples: - 'gce_setup.0.boot_disk.0.disk_type' - 'gce_setup.0.data_disks.0.disk_type' timeouts: !ruby/object:Api::Timeouts - insert_minutes: 10 + insert_minutes: 20 update_minutes: 20 custom_code: !ruby/object:Provider::Terraform::CustomCode constants: templates/terraform/constants/workbench_instance.go diff --git a/mmv1/provider/provider.go b/mmv1/provider/provider.go index 947c79c80d42..fab8e0a52161 100644 --- a/mmv1/provider/provider.go +++ b/mmv1/provider/provider.go @@ -28,7 +28,7 @@ func ProviderName(t Provider) string { return reflect.TypeOf(t).Name() } -func ImportPathFromVersion(t Provider, v string) string { +func ImportPathFromVersion(v string) string { var tpg, dir string switch v { case "ga": diff --git a/mmv1/provider/template_data.go b/mmv1/provider/template_data.go index c8e8fa5b599b..a578201d9895 100644 --- a/mmv1/provider/template_data.go +++ b/mmv1/provider/template_data.go @@ -177,6 +177,14 @@ func (td *TemplateData) GenerateTGCResourceFile(filePath string, resource api.Re td.GenerateFile(filePath, templatePath, resource, true, templates...) } +func (td *TemplateData) GenerateTGCIamResourceFile(filePath string, resource api.Resource) { + templatePath := "templates/tgc/resource_converter_iam.go.tmpl" + templates := []string{ + templatePath, + } + td.GenerateFile(filePath, templatePath, resource, true, templates...) +} + func (td *TemplateData) GenerateFile(filePath, templatePath string, input any, goFormat bool, templates ...string) { // log.Printf("Generating %s", filePath) diff --git a/mmv1/provider/terraform.go b/mmv1/provider/terraform.go index 75bd3697920f..9d333dabf9dc 100644 --- a/mmv1/provider/terraform.go +++ b/mmv1/provider/terraform.go @@ -63,7 +63,7 @@ func NewTerraform(product *api.Product, versionName string, startTime time.Time) t.Product.SetPropertiesBasedOnVersion(&t.Version) for _, r := range t.Product.Objects { r.SetCompiler(ProviderName(t)) - r.ImportPath = ImportPathFromVersion(t, versionName) + r.ImportPath = ImportPathFromVersion(versionName) } return t @@ -557,8 +557,8 @@ func (t Terraform) replaceImportPath(outputFolder, target string) { data := string(sourceByte) - gaImportPath := ImportPathFromVersion(t, "ga") - betaImportPath := ImportPathFromVersion(t, "beta") + gaImportPath := ImportPathFromVersion("ga") + betaImportPath := ImportPathFromVersion("beta") if strings.Contains(data, betaImportPath) { log.Fatalf("Importing a package from module %s is not allowed in file %s. Please import a package from module %s.", betaImportPath, filepath.Base(target), gaImportPath) diff --git a/mmv1/provider/terraform.rb b/mmv1/provider/terraform.rb index b3070a23cdc5..ce267c40736a 100644 --- a/mmv1/provider/terraform.rb +++ b/mmv1/provider/terraform.rb @@ -416,7 +416,8 @@ def generate_object_modified(object, output_folder, version_name) return if (output_folder.include? 'healthcare') || (output_folder.include? 'memorystore') generate_product = false - if @go_yaml_files + + unless @go_yaml_files.empty? found = false @go_yaml_files.each do |f| no_ext = Pathname.new(f).sub_ext '' @@ -433,11 +434,13 @@ def generate_object_modified(object, output_folder, version_name) data = build_object_data(pwd, object, output_folder, version_name) Dir.chdir output_folder Google::LOGGER.info "Generating #{object.name} rewrite yaml" - if @go_yaml_files + # rubocop:disable Style/UnlessElse + unless @go_yaml_files.empty? generate_newyaml_temp(pwd, data.clone, generate_product) else generate_newyaml(pwd, data.clone) end + # rubocop:enable Style/UnlessElse Dir.chdir pwd end diff --git a/mmv1/provider/terraform_tgc.go b/mmv1/provider/terraform_tgc.go index c1f9fe325b18..7a993ddd8fb1 100644 --- a/mmv1/provider/terraform_tgc.go +++ b/mmv1/provider/terraform_tgc.go @@ -16,19 +16,31 @@ package provider import ( + "bytes" + "errors" "fmt" + "io/ioutil" "log" "os" "path" + "path/filepath" + "regexp" + "strings" "time" + "golang.org/x/exp/slices" + "github.com/GoogleCloudPlatform/magic-modules/mmv1/api" "github.com/GoogleCloudPlatform/magic-modules/mmv1/api/product" "github.com/GoogleCloudPlatform/magic-modules/mmv1/google" ) type TerraformGoogleConversion struct { - ResourcesForVersion []map[string]string + IamResources []map[string]string + + NonDefinedTests []string + + Tests []string TargetVersionName string @@ -50,17 +62,12 @@ func NewTerraformGoogleConversion(product *api.Product, versionName string, star t.Product.SetPropertiesBasedOnVersion(&t.Version) for _, r := range t.Product.Objects { r.SetCompiler(ProviderName(t)) - r.ImportPath = ImportPathFromVersion(t, versionName) + r.ImportPath = ImportPathFromVersion(versionName) } return t } -func (tgc TerraformGoogleConversion) generatingHashicorpRepo() bool { - // This code is not used when generating TPG/TPGB - return false -} - func (tgc TerraformGoogleConversion) Generate(outputFolder, productPath, resourceToGenerate string, generateCode, generateDocs bool) { // Temporary shim to generate the missing resources directory. Can be removed // once the folder exists downstream. @@ -106,7 +113,7 @@ func (tgc TerraformGoogleConversion) GenerateObject(object api.Resource, outputF return } - // tgc.GenerateIamPolicy(object, *templateData, outputFolder, generateCode, generateDocs) + tgc.GenerateIamPolicy(object, *templateData, outputFolder, generateCode, generateDocs) } func (tgc TerraformGoogleConversion) GenerateResource(object api.Resource, templateData TemplateData, outputFolder string, generateCode, generateDocs bool) { @@ -120,10 +127,359 @@ func (tgc TerraformGoogleConversion) GenerateResource(object api.Resource, templ templateData.GenerateTGCResourceFile(targetFilePath, object) } +// Generate the IAM policy for this object. This is used to query and test +// IAM policies separately from the resource itself +// Docs are generated for the terraform provider, not here. +func (tgc TerraformGoogleConversion) GenerateIamPolicy(object api.Resource, templateData TemplateData, outputFolder string, generateCode, generateDocs bool) { + if !generateCode || object.IamPolicy.ExcludeTgc { + return + } + + productName := tgc.Product.ApiName + targetFolder := path.Join(outputFolder, "converters/google/resources/services", productName) + if err := os.MkdirAll(targetFolder, os.ModePerm); err != nil { + log.Println(fmt.Errorf("error creating parent directory %v: %v", targetFolder, err)) + } + + name := object.FilenameOverride + if name == "" { + name = google.Underscore(object.Name) + } + + targetFilePath := path.Join(targetFolder, fmt.Sprintf("%s_%s_iam.go", productName, name)) + templateData.GenerateTGCIamResourceFile(targetFilePath, object) + + targetFilePath = path.Join(targetFolder, fmt.Sprintf("iam_%s_%s.go", productName, name)) + templateData.GenerateIamPolicyFile(targetFilePath, object) + + // Don't generate tests - we can rely on the terraform provider + // to test these. +} + +// Generates the list of resources +func (tgc *TerraformGoogleConversion) generateCaiIamResources(products []*api.Product) { + for _, productDefinition := range products { + service := strings.ToLower(productDefinition.Name) + for _, object := range productDefinition.Objects { + if object.MinVersionObj().Name != "ga" || object.Exclude || object.ExcludeTgc { + continue + } + + var iamClassName string + iamPolicy := object.IamPolicy + if iamPolicy != nil && !iamPolicy.Exclude && !iamPolicy.ExcludeTgc { + + iamClassName = fmt.Sprintf("%s.ResourceConverter%s", service, object.ResourceName()) + + tgc.IamResources = append(tgc.IamResources, map[string]string{ + "TerraformName": object.TerraformName(), + "IamClassName": iamClassName, + }) + } + } + } +} + func (tgc TerraformGoogleConversion) CompileCommonFiles(outputFolder string, products []*api.Product, overridePath string) { + log.Printf("Compiling common files for tgc.") + + tgc.generateCaiIamResources(products) + tgc.NonDefinedTests = retrieveFullManifestOfNonDefinedTests() + + files := retrieveFullListOfTestFiles() + for _, file := range files { + tgc.Tests = append(tgc.Tests, strings.Split(file, ".")[0]) + } + tgc.Tests = slices.Compact(tgc.Tests) + + testSource := make(map[string]string) + for target, source := range retrieveTestSourceCodeWithLocation(".tmpl") { + target := strings.Replace(target, "go.tmpl", "go", 1) + testSource[target] = source + } + + templateData := NewTemplateData(outputFolder, tgc.TargetVersionName) + tgc.CompileFileList(outputFolder, testSource, *templateData, products) + + resourceConverters := map[string]string{ + "converters/google/resources/services/compute/compute_instance_helpers.go": "third_party/terraform/services/compute/go/compute_instance_helpers.go.tmpl", + "converters/google/resources/resource_converters.go": "third_party/tgc/resource_converters.go.tmpl", + "converters/google/resources/services/kms/iam_kms_key_ring.go": "third_party/terraform/services/kms/go/iam_kms_key_ring.go.tmpl", + "converters/google/resources/services/kms/iam_kms_crypto_key.go": "third_party/terraform/services/kms/go/iam_kms_crypto_key.go.tmpl", + "converters/google/resources/services/compute/metadata.go": "third_party/terraform/services/compute/go/metadata.go.tmpl", + "converters/google/resources/services/compute/compute_instance.go": "third_party/tgc/services/compute/compute_instance.go.tmpl", + } + tgc.CompileFileList(outputFolder, resourceConverters, *templateData, products) +} + +func (tgc TerraformGoogleConversion) CompileFileList(outputFolder string, files map[string]string, fileTemplate TemplateData, products []*api.Product) { + if err := os.MkdirAll(outputFolder, os.ModePerm); err != nil { + log.Println(fmt.Errorf("error creating output directory %v: %v", outputFolder, err)) + } + + for target, source := range files { + targetFile := filepath.Join(outputFolder, target) + targetDir := filepath.Dir(targetFile) + if err := os.MkdirAll(targetDir, os.ModePerm); err != nil { + log.Println(fmt.Errorf("error creating output directory %v: %v", targetDir, err)) + } + + templates := []string{ + source, + } + + formatFile := filepath.Ext(targetFile) == ".go" + + fileTemplate.GenerateFile(targetFile, source, tgc, formatFile, templates...) + tgc.replaceImportPath(outputFolder, target) + } +} + +func retrieveFullManifestOfNonDefinedTests() []string { + var tests []string + fileMap := make(map[string]bool) + + files := retrieveFullListOfTestFiles() + for _, file := range files { + tests = append(tests, strings.Split(file, ".")[0]) + fileMap[file] = true + } + tests = slices.Compact(tests) + + nonDefinedTests := google.Diff(tests, retrieveListOfManuallyDefinedTests()) + nonDefinedTests = google.Reject(nonDefinedTests, func(file string) bool { + return strings.HasSuffix(file, "_without_default_project") + }) + + for _, test := range nonDefinedTests { + _, ok := fileMap[fmt.Sprintf("%s.json", test)] + if !ok { + log.Fatalf("test file named %s.json expected but found none", test) + } + + _, ok = fileMap[fmt.Sprintf("%s.tf", test)] + if !ok { + log.Fatalf("test file named %s.tf expected but found none", test) + } + } + + return nonDefinedTests +} + +// Gets all of the test files in the folder third_party/tgc/tests/data +func retrieveFullListOfTestFiles() []string { + var testFiles []string + + files, err := ioutil.ReadDir("third_party/tgc/tests/data") + if err != nil { + log.Fatal(err) + } + for _, file := range files { + testFiles = append(testFiles, file.Name()) + } + slices.Sort(testFiles) + + return testFiles +} + +// Gets all of files in the folder third_party/tgc/tests/data +func retrieveFullListOfTestTilesWithLocation() map[string]string { + testFiles := make(map[string]string) + files := retrieveFullListOfTestFiles() + for _, file := range files { + target := fmt.Sprintf("testdata/templates/%s", file) + source := fmt.Sprintf("third_party/tgc/tests/data/%s", file) + testFiles[target] = source + } + return testFiles +} + +func retrieveTestSourceCodeWithLocation(suffix string) map[string]string { + var fileNames []string + var path string + if suffix == ".tmpl" { + path = "third_party/tgc/tests/source/go" + } else { + path = "third_party/tgc/tests/source" + } + files, err := ioutil.ReadDir(path) + if err != nil { + log.Fatal(err) + } + + for _, file := range files { + if filepath.Ext(file.Name()) == suffix { + fileNames = append(fileNames, file.Name()) + } + } + + slices.Sort(fileNames) + + testSource := make(map[string]string) + for _, file := range fileNames { + target := fmt.Sprintf("test/%s", file) + source := fmt.Sprintf("%s/%s", path, file) + testSource[target] = source + } + return testSource +} + +func retrieveListOfManuallyDefinedTests() []string { + m1 := retrieveListOfManuallyDefinedTestsFromFile("third_party/tgc/tests/source/go/cli_test.go.tmpl") + m2 := retrieveListOfManuallyDefinedTestsFromFile("third_party/tgc/tests/source/go/read_test.go.tmpl") + return google.Concat(m1, m2) +} + +// Reads the content of the file and then finds all of the tests in the contents +func retrieveListOfManuallyDefinedTestsFromFile(file string) []string { + data, err := os.ReadFile(file) + if err != nil { + log.Fatalf("Cannot open the file: %v", file) + } + var tests []string + testsReg := regexp.MustCompile(`\s*name\s*:\s*"([^,]+)"`) + matches := testsReg.FindAllStringSubmatch(string(data), -1) + for _, testWithName := range matches { + tests = append(tests, testWithName[1]) + } + return tests } func (tgc TerraformGoogleConversion) CopyCommonFiles(outputFolder string, generateCode, generateDocs bool) { + log.Printf("Copying common files.") + + if !generateCode { + return + } + + tgc.CopyFileList(outputFolder, retrieveFullListOfTestTilesWithLocation()) + tgc.CopyFileList(outputFolder, retrieveTestSourceCodeWithLocation(".go")) + + resourceConverters := map[string]string{ + "converters/google/resources/cai/constants.go": "third_party/tgc/cai/constants.go", + "converters/google/resources/constants.go": "third_party/tgc/constants.go", + "converters/google/resources/cai.go": "third_party/tgc/cai.go", + "converters/google/resources/cai/cai.go": "third_party/tgc/cai/cai.go", + "converters/google/resources/cai/cai_test.go": "third_party/tgc/cai/cai_test.go", + "converters/google/resources/org_policy_policy.go": "third_party/tgc/org_policy_policy.go", + "converters/google/resources/getconfig.go": "third_party/tgc/getconfig.go", + "converters/google/resources/folder.go": "third_party/tgc/folder.go", + "converters/google/resources/getconfig_test.go": "third_party/tgc/getconfig_test.go", + "converters/google/resources/cai/json_map.go": "third_party/tgc/cai/json_map.go", + "converters/google/resources/project.go": "third_party/tgc/project.go", + "converters/google/resources/sql_database_instance.go": "third_party/tgc/sql_database_instance.go", + "converters/google/resources/storage_bucket.go": "third_party/tgc/storage_bucket.go", + "converters/google/resources/cloudfunctions_function.go": "third_party/tgc/cloudfunctions_function.go", + "converters/google/resources/cloudfunctions_cloud_function.go": "third_party/tgc/cloudfunctions_cloud_function.go", + "converters/google/resources/bigquery_table.go": "third_party/tgc/bigquery_table.go", + "converters/google/resources/bigtable_cluster.go": "third_party/tgc/bigtable_cluster.go", + "converters/google/resources/bigtable_instance.go": "third_party/tgc/bigtable_instance.go", + "converters/google/resources/cai/iam_helpers.go": "third_party/tgc/cai/iam_helpers.go", + "converters/google/resources/cai/iam_helpers_test.go": "third_party/tgc/cai/iam_helpers_test.go", + "converters/google/resources/services/resourcemanager/organization_iam.go": "third_party/tgc/organization_iam.go", + "converters/google/resources/services/resourcemanager/project_iam.go": "third_party/tgc/project_iam.go", + "converters/google/resources/project_organization_policy.go": "third_party/tgc/project_organization_policy.go", + "converters/google/resources/folder_organization_policy.go": "third_party/tgc/folder_organization_policy.go", + "converters/google/resources/services/resourcemanager/folder_iam.go": "third_party/tgc/folder_iam.go", + "converters/google/resources/container.go": "third_party/tgc/container.go", + "converters/google/resources/project_service.go": "third_party/tgc/project_service.go", + "converters/google/resources/services/monitoring/monitoring_slo_helper.go": "third_party/tgc/monitoring_slo_helper.go", + "converters/google/resources/service_account.go": "third_party/tgc/service_account.go", + "converters/google/resources/services/compute/image.go": "third_party/terraform/services/compute/image.go", + "converters/google/resources/services/compute/disk_type.go": "third_party/terraform/services/compute/disk_type.go", + "converters/google/resources/services/kms/kms_utils.go": "third_party/terraform/services/kms/kms_utils.go", + "converters/google/resources/services/sourcerepo/source_repo_utils.go": "third_party/terraform/services/sourcerepo/source_repo_utils.go", + "converters/google/resources/services/pubsub/pubsub_utils.go": "third_party/terraform/services/pubsub/pubsub_utils.go", + "converters/google/resources/services/resourcemanager/iam_organization.go": "third_party/terraform/services/resourcemanager/iam_organization.go", + "converters/google/resources/services/resourcemanager/iam_folder.go": "third_party/terraform/services/resourcemanager/iam_folder.go", + "converters/google/resources/services/resourcemanager/iam_project.go": "third_party/terraform/services/resourcemanager/iam_project.go", + "converters/google/resources/services/privateca/privateca_utils.go": "third_party/terraform/services/privateca/privateca_utils.go", + "converters/google/resources/services/bigquery/iam_bigquery_dataset.go": "third_party/terraform/services/bigquery/iam_bigquery_dataset.go", + "converters/google/resources/services/bigquery/bigquery_dataset_iam.go": "third_party/tgc/bigquery_dataset_iam.go", + "converters/google/resources/compute_security_policy.go": "third_party/tgc/compute_security_policy.go", + "converters/google/resources/kms_key_ring_iam.go": "third_party/tgc/kms_key_ring_iam.go", + "converters/google/resources/kms_crypto_key_iam.go": "third_party/tgc/kms_crypto_key_iam.go", + "converters/google/resources/project_iam_custom_role.go": "third_party/tgc/project_iam_custom_role.go", + "converters/google/resources/organization_iam_custom_role.go": "third_party/tgc/organization_iam_custom_role.go", + "converters/google/resources/services/pubsub/iam_pubsub_subscription.go": "third_party/terraform/services/pubsub/iam_pubsub_subscription.go", + "converters/google/resources/services/pubsub/pubsub_subscription_iam.go": "third_party/tgc/pubsub_subscription_iam.go", + "converters/google/resources/services/spanner/iam_spanner_database.go": "third_party/terraform/services/spanner/iam_spanner_database.go", + "converters/google/resources/services/spanner/spanner_database_iam.go": "third_party/tgc/spanner_database_iam.go", + "converters/google/resources/services/spanner/iam_spanner_instance.go": "third_party/terraform/services/spanner/iam_spanner_instance.go", + "converters/google/resources/services/spanner/spanner_instance_iam.go": "third_party/tgc/spanner_instance_iam.go", + "converters/google/resources/storage_bucket_iam.go": "third_party/tgc/storage_bucket_iam.go", + "converters/google/resources/organization_policy.go": "third_party/tgc/organization_policy.go", + "converters/google/resources/iam_storage_bucket.go": "third_party/tgc/iam_storage_bucket.go", + "ancestrymanager/ancestrymanager.go": "third_party/tgc/ancestrymanager/ancestrymanager.go", + "ancestrymanager/ancestrymanager_test.go": "third_party/tgc/ancestrymanager/ancestrymanager_test.go", + "ancestrymanager/ancestryutil.go": "third_party/tgc/ancestrymanager/ancestryutil.go", + "ancestrymanager/ancestryutil_test.go": "third_party/tgc/ancestrymanager/ancestryutil_test.go", + "converters/google/convert.go": "third_party/tgc/convert.go", + "converters/google/convert_test.go": "third_party/tgc/convert_test.go", + "converters/google/resources/compute_instance_group.go": "third_party/tgc/compute_instance_group.go", + "converters/google/resources/job.go": "third_party/tgc/job.go", + "converters/google/resources/service_account_key.go": "third_party/tgc/service_account_key.go", + "converters/google/resources/compute_target_pool.go": "third_party/tgc/compute_target_pool.go", + "converters/google/resources/dataproc_cluster.go": "third_party/tgc/dataproc_cluster.go", + "converters/google/resources/composer_environment.go": "third_party/tgc/composer_environment.go", + "converters/google/resources/commitment.go": "third_party/tgc/commitment.go", + "converters/google/resources/firebase_project.go": "third_party/tgc/firebase_project.go", + "converters/google/resources/appengine_application.go": "third_party/tgc/appengine_application.go", + "converters/google/resources/apikeys_key.go": "third_party/tgc/apikeys_key.go", + "converters/google/resources/logging_folder_bucket_config.go": "third_party/tgc/logging_folder_bucket_config.go", + "converters/google/resources/logging_organization_bucket_config.go": "third_party/tgc/logging_organization_bucket_config.go", + "converters/google/resources/logging_project_bucket_config.go": "third_party/tgc/logging_project_bucket_config.go", + "converters/google/resources/logging_billing_account_bucket_config.go": "third_party/tgc/logging_billing_account_bucket_config.go", + "converters/google/resources/appengine_standard_version.go": "third_party/tgc/appengine_standard_version.go", + } + tgc.CopyFileList(outputFolder, resourceConverters) +} + +func (tgc TerraformGoogleConversion) CopyFileList(outputFolder string, files map[string]string) { + for target, source := range files { + targetFile := filepath.Join(outputFolder, target) + targetDir := filepath.Dir(targetFile) + + if err := os.MkdirAll(targetDir, os.ModePerm); err != nil { + log.Println(fmt.Errorf("error creating output directory %v: %v", targetDir, err)) + } + // If we've modified a file since starting an MM run, it's a reasonable + // assumption that it was this run that modified it. + if info, err := os.Stat(targetFile); !errors.Is(err, os.ErrNotExist) && tgc.StartTime.Before(info.ModTime()) { + log.Fatalf("%s was already modified during this run at %s", targetFile, info.ModTime().String()) + } + + sourceByte, err := os.ReadFile(source) + if err != nil { + log.Fatalf("Cannot read source file %s while copying: %s", source, err) + } + err = os.WriteFile(targetFile, sourceByte, 0644) + if err != nil { + log.Fatalf("Cannot write target file %s while copying: %s", target, err) + } + + // Replace import path based on version (beta/alpha) + if filepath.Ext(target) == ".go" || filepath.Ext(target) == ".mod" { + tgc.replaceImportPath(outputFolder, target) + } + } +} + +func (tgc TerraformGoogleConversion) replaceImportPath(outputFolder, target string) { + // Replace import paths to reference the resources dir instead of the google provider + targetFile := filepath.Join(outputFolder, target) + sourceByte, err := os.ReadFile(targetFile) + if err != nil { + log.Fatalf("Cannot read file %s to replace import path: %s", targetFile, err) + } + + // replace google to google-beta + gaImportPath := ImportPathFromVersion("ga") + sourceByte = bytes.Replace(sourceByte, []byte(gaImportPath), []byte(TERRAFORM_PROVIDER_BETA+"/"+RESOURCE_DIRECTORY_BETA), -1) + err = os.WriteFile(targetFile, sourceByte, 0644) + if err != nil { + log.Fatalf("Cannot write file %s to replace import path: %s", target, err) + } } diff --git a/mmv1/provider/terraform_tgc_cai2hcl.go b/mmv1/provider/terraform_tgc_cai2hcl.go new file mode 100644 index 000000000000..8e8ef16f4de1 --- /dev/null +++ b/mmv1/provider/terraform_tgc_cai2hcl.go @@ -0,0 +1,74 @@ +// Copyright 2024 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. + +package provider + +import ( + "fmt" + "log" + "os" + "time" + + "github.com/GoogleCloudPlatform/magic-modules/mmv1/api" + "github.com/GoogleCloudPlatform/magic-modules/mmv1/api/product" + "github.com/otiai10/copy" +) + +// Code generator for a library converting GCP CAI objects to Terraform state. +type CaiToTerraformConversion struct { + TargetVersionName string + + Version product.Version + + Product *api.Product + + StartTime time.Time +} + +func NewCaiToTerraformConversion(product *api.Product, versionName string, startTime time.Time) CaiToTerraformConversion { + t := CaiToTerraformConversion{ + Product: product, + TargetVersionName: versionName, + Version: *product.VersionObjOrClosest(versionName), + StartTime: startTime, + } + + t.Product.SetPropertiesBasedOnVersion(&t.Version) + for _, r := range t.Product.Objects { + r.SetCompiler(ProviderName(t)) + r.ImportPath = ImportPathFromVersion(t, versionName) + } + + return t +} + +func (cai2hcl CaiToTerraformConversion) Generate(outputFolder, productPath, resourceToGenerate string, generateCode, generateDocs bool) { +} + +func (cai2hcl CaiToTerraformConversion) CompileCommonFiles(outputFolder string, products []*api.Product, overridePath string) { +} + +func (cai2hcl CaiToTerraformConversion) CopyCommonFiles(outputFolder string, generateCode, generateDocs bool) { + if !generateCode { + return + } + log.Printf("Coping cai2hcl common files") + + if err := os.MkdirAll(outputFolder, os.ModePerm); err != nil { + log.Println(fmt.Errorf("error creating output directory %v: %v", outputFolder, err)) + } + + if err := copy.Copy("third_party/cai2hcl", outputFolder); err != nil { + log.Println(fmt.Errorf("error copying directory %v: %v", outputFolder, err)) + } +} diff --git a/mmv1/templates/terraform/constants/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl b/mmv1/templates/terraform/constants/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl new file mode 100644 index 000000000000..e3f13f8bdc40 --- /dev/null +++ b/mmv1/templates/terraform/constants/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl @@ -0,0 +1,51 @@ +func regionNetworkFirewallPolicyWithRulesConvertPriorityToInt(v interface {}) (int64, error) { + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal, nil + } + } + + if intVal, ok := v.(int64); ok { + return intVal, nil + } + + if floatVal, ok := v.(float64); ok { + intVal := int64(floatVal) + return intVal, nil + } + return 0, fmt.Errorf("Incorrect rule priority: %s. Priority must be a number", v) +} + +func regionNetworkFirewallPolicyWithRulesIsPredefinedRule(rule map[string]interface{}) (bool, error) { + // Priorities from 2147483548 to 2147483647 are reserved and cannot be modified by the user. + const ReservedPriorityStart = 2147483548 + + priority := rule["priority"] + priorityInt, err := regionNetworkFirewallPolicyWithRulesConvertPriorityToInt(priority) + + if err != nil { + return false, err + } + + return priorityInt >= ReservedPriorityStart, nil +} + +func regionNetworkFirewallPolicyWithRulesSplitPredefinedRules(allRules []interface{}) ([]interface{}, []interface{}, error) { + predefinedRules := make([]interface{}, 0) + rules := make([]interface{}, 0) + + for _, rule := range allRules { + isPredefined, err := regionNetworkFirewallPolicyWithRulesIsPredefinedRule(rule.(map[string]interface{})) + if err != nil { + return nil, nil, err + } + + if isPredefined { + predefinedRules = append(predefinedRules, rule) + } else { + rules = append(rules, rule) + } + } + + return rules, predefinedRules, nil +} diff --git a/mmv1/templates/terraform/constants/resource_compute_region_network_firewall_policy_with_rules.go.erb b/mmv1/templates/terraform/constants/resource_compute_region_network_firewall_policy_with_rules.go.erb new file mode 100644 index 000000000000..e3f13f8bdc40 --- /dev/null +++ b/mmv1/templates/terraform/constants/resource_compute_region_network_firewall_policy_with_rules.go.erb @@ -0,0 +1,51 @@ +func regionNetworkFirewallPolicyWithRulesConvertPriorityToInt(v interface {}) (int64, error) { + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal, nil + } + } + + if intVal, ok := v.(int64); ok { + return intVal, nil + } + + if floatVal, ok := v.(float64); ok { + intVal := int64(floatVal) + return intVal, nil + } + return 0, fmt.Errorf("Incorrect rule priority: %s. Priority must be a number", v) +} + +func regionNetworkFirewallPolicyWithRulesIsPredefinedRule(rule map[string]interface{}) (bool, error) { + // Priorities from 2147483548 to 2147483647 are reserved and cannot be modified by the user. + const ReservedPriorityStart = 2147483548 + + priority := rule["priority"] + priorityInt, err := regionNetworkFirewallPolicyWithRulesConvertPriorityToInt(priority) + + if err != nil { + return false, err + } + + return priorityInt >= ReservedPriorityStart, nil +} + +func regionNetworkFirewallPolicyWithRulesSplitPredefinedRules(allRules []interface{}) ([]interface{}, []interface{}, error) { + predefinedRules := make([]interface{}, 0) + rules := make([]interface{}, 0) + + for _, rule := range allRules { + isPredefined, err := regionNetworkFirewallPolicyWithRulesIsPredefinedRule(rule.(map[string]interface{})) + if err != nil { + return nil, nil, err + } + + if isPredefined { + predefinedRules = append(predefinedRules, rule) + } else { + rules = append(rules, rule) + } + } + + return rules, predefinedRules, nil +} diff --git a/mmv1/templates/terraform/constants/router_nat.go.erb b/mmv1/templates/terraform/constants/router_nat.go.erb index 4c7997d556e9..40c84c0fcc41 100644 --- a/mmv1/templates/terraform/constants/router_nat.go.erb +++ b/mmv1/templates/terraform/constants/router_nat.go.erb @@ -174,4 +174,4 @@ func computeRouterNatRulesHash(v interface{}) int { routerNatRulesHash += sourceNatActiveRangeHash + sourceNatDrainRangeHash <% end -%> return routerNatRulesHash -} \ No newline at end of file +} diff --git a/mmv1/templates/terraform/constants/router_nat_address.go.erb b/mmv1/templates/terraform/constants/router_nat_address.go.erb new file mode 100644 index 000000000000..34c7584650d3 --- /dev/null +++ b/mmv1/templates/terraform/constants/router_nat_address.go.erb @@ -0,0 +1,93 @@ +<%- # the license inside this block applies to this file + # Copyright 2019 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. +-%> +func addressResourceNameSetFromSelfLinkSet(v interface{}) *schema.Set { + if v == nil { + return schema.NewSet(schema.HashString, nil) + } + vSet := v.(*schema.Set) + ls := make([]interface{}, 0, vSet.Len()) + for _, v := range vSet.List() { + if v == nil { + continue + } + ls = append(ls, tpgresource.GetResourceNameFromSelfLink(v.(string))) + } + return schema.NewSet(schema.HashString, ls) +} + +// drain_nat_ips MUST be set from (just set) previous values of nat_ips +// so this customizeDiff func makes sure drainNatIps values: +// - aren't set at creation time +// - are in old value of nat_ips but not in new values +func resourceComputeRouterNatAddressDrainNatIpsCustomDiff(_ context.Context, diff *schema.ResourceDiff, meta interface{}) error { + o, n := diff.GetChange("drain_nat_ips") + oSet := addressResourceNameSetFromSelfLinkSet(o) + nSet := addressResourceNameSetFromSelfLinkSet(n) + addDrainIps := nSet.Difference(oSet) + + // We don't care if there are no new drainNatIps + if addDrainIps.Len() == 0 { + return nil + } + + // Resource hasn't been created yet - return error + if diff.Id() == "" { + return fmt.Errorf("New RouterNat cannot have drain_nat_ips, got values %+v", addDrainIps.List()) + } + // + o, n = diff.GetChange("nat_ips") + oNatSet := addressResourceNameSetFromSelfLinkSet(o) + nNatSet := addressResourceNameSetFromSelfLinkSet(n) + + // Resource is being updated - make sure new drainNatIps were in natIps prior d and no longer are in natIps. + for _, v := range addDrainIps.List() { + if !oNatSet.Contains(v) { + return fmt.Errorf("drain_nat_ip %q was not previously set in nat_ips %+v", v.(string), oNatSet.List()) + } + if nNatSet.Contains(v) { + return fmt.Errorf("drain_nat_ip %q cannot be drained if still set in nat_ips %+v", v.(string), nNatSet.List()) + } + } + return nil +} + +func resourceComputeRouterNatAddressDeleteOnlyNatIps(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { + items, err := resourceComputeRouterNatAddressListForPatch(d, meta) + if err != nil { + return nil, err + } + + idx, item, err := resourceComputeRouterNatAddressFindNestedObjectInList(d, meta, items) + if err != nil { + return nil, err + } + + // Return error if item to update does not exist. + if item == nil { + return nil, fmt.Errorf("Unable to update RouterNatAddress %q - not found in list", d.Id()) + } + + if item["natIps"] != nil { + croppedNatIps := item["natIps"].([]interface{})[:1] + item["natIps"] = croppedNatIps + } + + items[idx] = item + // Return list with new item added + res := map[string]interface{}{ + "nats": items, + } + return res, nil +} diff --git a/mmv1/templates/terraform/custom_create/router_nat_address_nested_query_create_encoder.go.erb b/mmv1/templates/terraform/custom_create/router_nat_address_nested_query_create_encoder.go.erb new file mode 100644 index 000000000000..c4808262b72d --- /dev/null +++ b/mmv1/templates/terraform/custom_create/router_nat_address_nested_query_create_encoder.go.erb @@ -0,0 +1,93 @@ +// A custom_create function similar to the generated code when using a nested_query, but replaces the encoder with a custom one instead of just injecting it; +userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) +if err != nil { + return err +} + +obj := make(map[string]interface{}) +natIpsProp, err := expandNestedComputeRouterNatAddressNatIps(d.Get("nat_ips"), d, config) +if err != nil { + return err +} else if v, ok := d.GetOkExists("nat_ips"); ok || !reflect.DeepEqual(v, natIpsProp) { + obj["natIps"] = natIpsProp +} +drainNatIpsProp, err := expandNestedComputeRouterNatAddressDrainNatIps(d.Get("drain_nat_ips"), d, config) +if err != nil { + return err +} else if v, ok := d.GetOkExists("drain_nat_ips"); ok || !reflect.DeepEqual(v, drainNatIpsProp) { + obj["drainNatIps"] = drainNatIpsProp +} +nameProp, err := expandNestedComputeRouterNatAddressRouterNat(d.Get("router_nat"), d, config) +if err != nil { + return err +} else if v, ok := d.GetOkExists("router_nat"); !tpgresource.IsEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) { + obj["name"] = nameProp +} + +log.Printf("[DEBUG] Creating new RouterNatAddress: %#v", obj) + +obj, err = resourceComputeRouterNatAddressEncoder(d, meta, obj) +if err != nil { + return err +} + +lockName, err := tpgresource.ReplaceVars(d, config, "router/{{region}}/{{router}}") +if err != nil { + return err +} +transport_tpg.MutexStore.Lock(lockName) +defer transport_tpg.MutexStore.Unlock(lockName) + +url, err := tpgresource.ReplaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/routers/{{router}}") +if err != nil { + return err +} + +billingProject := "" + +project, err := tpgresource.GetProject(d, config) +if err != nil { + return fmt.Errorf("Error fetching project for RouterNatAddress: %s", err) +} +billingProject = project + +// err == nil indicates that the billing_project value was found +if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp +} + +headers := make(http.Header) +res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutCreate), + Headers: headers, +}) +if err != nil { + return fmt.Errorf("Error creating RouterNatAddress: %s", err) +} + +// Store the ID now +id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/regions/{{region}}/routers/{{router}}/{{router_nat}}") +if err != nil { + return fmt.Errorf("Error constructing id: %s", err) +} +d.SetId(id) + +err = ComputeOperationWaitTime( + config, res, project, "Creating RouterNatAddress", userAgent, + d.Timeout(schema.TimeoutCreate)) + +if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error waiting to create RouterNatAddress: %s", err) +} + +log.Printf("[DEBUG] Finished creating RouterNatAddress %q: %#v", d.Id(), res) + +return resourceComputeRouterNatAddressRead(d, meta) diff --git a/mmv1/templates/terraform/custom_expand/go/regional_secret_version_enable.go.tmpl b/mmv1/templates/terraform/custom_expand/go/regional_secret_version_enable.go.tmpl new file mode 100644 index 000000000000..a3a2c9effc47 --- /dev/null +++ b/mmv1/templates/terraform/custom_expand/go/regional_secret_version_enable.go.tmpl @@ -0,0 +1,50 @@ +{{/* + The license inside this block applies to this file + Copyright 2024 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. +*/ -}} +func expand{{$.GetPrefix}}{{$.TitlelizeProperty}}(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + name := d.Get("name").(string) + if name == "" { + return "", nil + } + + url, err := tpgresource.ReplaceVars(d, config, "{{"{{"}}SecretManagerRegionalBasePath{{"}}"}}{{"{{"}}name{{"}}"}}") + if err != nil { + return nil, err + } + + if v == true { + url = fmt.Sprintf("%s:enable", url) + } else { + url = fmt.Sprintf("%s:disable", url) + } + + parts := strings.Split(name, "/") + project := parts[1] + + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return nil, err + } + + _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: project, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return nil, err + } + + return nil, nil +} diff --git a/mmv1/templates/terraform/custom_flatten/bigquery_datapolicy_policy_tag_location_to_lower_case.go.erb b/mmv1/templates/terraform/custom_flatten/bigquery_datapolicy_policy_tag_location_to_lower_case.go.erb new file mode 100644 index 000000000000..76efbc3b8738 --- /dev/null +++ b/mmv1/templates/terraform/custom_flatten/bigquery_datapolicy_policy_tag_location_to_lower_case.go.erb @@ -0,0 +1,31 @@ +<%- # the license inside this block applies to this file + # Copyright 2024 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. +-%> +func flatten<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + + if _, ok := v.(string); !ok { + return v + } + + re := regexp.MustCompile(`(projects/.*)(/locations/.*/)(policyTags/.*)`) + result := re.ReplaceAllStringFunc(v.(string), func(match string) string { + matches := re.FindStringSubmatch(match) + return matches[1] + strings.ToLower(matches[2]) + matches[3] + }) + + return result +} diff --git a/mmv1/templates/terraform/custom_flatten/go/regional_secret_version_access.go.tmpl b/mmv1/templates/terraform/custom_flatten/go/regional_secret_version_access.go.tmpl new file mode 100644 index 000000000000..b9bfd5e47dac --- /dev/null +++ b/mmv1/templates/terraform/custom_flatten/go/regional_secret_version_access.go.tmpl @@ -0,0 +1,56 @@ +{{/* + The license inside this block applies to this file + Copyright 2024 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. +*/ -}} +func flatten{{$.GetPrefix}}{{$.TitlelizeProperty}}(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + transformed := make(map[string]interface{}) + + // if this secret version is disabled, the api will return an error, as the value cannot be accessed, return what we have + if d.Get("enabled").(bool) == false { + transformed["secret_data"] = d.Get("secret_data") + return []interface{}{transformed} + } + + url, err := tpgresource.ReplaceVars(d, config, "{{"{{"}}SecretManagerRegionalBasePath{{"}}"}}{{"{{"}}name{{"}}"}}:access") + if err != nil { + return err + } + + parts := strings.Split(d.Get("name").(string), "/") + project := parts[1] + + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + accessRes, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: project, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return err + } + + if d.Get("is_secret_data_base64").(bool) { + transformed["secret_data"] = accessRes["payload"].(map[string]interface{})["data"].(string) + } else { + data, err := base64.StdEncoding.DecodeString(accessRes["payload"].(map[string]interface{})["data"].(string)) + if err != nil { + return err + } + transformed["secret_data"] = string(data) + } + return []interface{}{transformed} +} diff --git a/mmv1/templates/terraform/custom_flatten/go/regional_secret_version_version.go.tmpl b/mmv1/templates/terraform/custom_flatten/go/regional_secret_version_version.go.tmpl new file mode 100644 index 000000000000..00ba7e872a98 --- /dev/null +++ b/mmv1/templates/terraform/custom_flatten/go/regional_secret_version_version.go.tmpl @@ -0,0 +1,23 @@ +{{/* + The license inside this block applies to this file + Copyright 2024 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. +*/ -}} +func flatten{{$.GetPrefix}}{{$.TitlelizeProperty}}(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + name := d.Get("name").(string) + secretRegex := regexp.MustCompile("projects/(.+)/locations/(.+)/secrets/(.+)/versions/(.+)$") + + parts := secretRegex.FindStringSubmatch(name) + if len(parts) != 5 { + return fmt.Errorf("Version name does not fit the format `projects/{{"{{"}}project{{"}}"}}/locations/{{"{{"}}location{{"}}"}}/secrets/{{"{{"}}secret{{"}}"}}/versions/{{"{{"}}version{{"}}"}}`") + } + + return parts[4] +} diff --git a/mmv1/templates/terraform/custom_import/go/regional_secret_version.go.tmpl b/mmv1/templates/terraform/custom_import/go/regional_secret_version.go.tmpl new file mode 100644 index 000000000000..f17c4a672d91 --- /dev/null +++ b/mmv1/templates/terraform/custom_import/go/regional_secret_version.go.tmpl @@ -0,0 +1,47 @@ +{{/* + The license inside this block applies to this file + Copyright 2024 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. +*/ -}} + config := meta.(*transport_tpg.Config) + + // current import_formats can't import fields with forward slashes in their value + if err := tpgresource.ParseImportId([]string{"(?P.+)"}, d, config); err != nil { + return nil, err + } + + name := d.Get("name").(string) + secretRegex := regexp.MustCompile("(projects/.+/locations/.+/secrets/.+)/versions/.+$") + versionRegex := regexp.MustCompile("projects/(.+)/locations/(.+)/secrets/(.+)/versions/(.+)$") + + parts := secretRegex.FindStringSubmatch(name) + if len(parts) != 2 { + return nil, fmt.Errorf("Version name does not fit the format `projects/{{"{{"}}project{{"}}"}}/locations/{{"{{"}}location{{"}}"}}/secrets/{{"{{"}}secret{{"}}"}}/versions/{{"{{"}}version{{"}}"}}`") + } + if err := d.Set("secret", parts[1]); err != nil { + return nil, fmt.Errorf("Error setting secret: %s", err) + } + + parts = versionRegex.FindStringSubmatch(name) + + if err := d.Set("version", parts[4]); err != nil { + return nil, fmt.Errorf("Error setting version: %s", err) + } + + // Explicitly set virtual fields to default values on import + if err := d.Set("deletion_policy", "DELETE"); err != nil { + return nil, fmt.Errorf("Error setting deletion policy: %s", err) + } + + if err := d.Set("location", parts[2]); err != nil { + return nil, fmt.Errorf("Error setting location: %s", err) + } + + return []*schema.ResourceData{d}, nil diff --git a/mmv1/templates/terraform/custom_update/go/regional_secret_version.go.tmpl b/mmv1/templates/terraform/custom_update/go/regional_secret_version.go.tmpl new file mode 100644 index 000000000000..7cb6e95cdcff --- /dev/null +++ b/mmv1/templates/terraform/custom_update/go/regional_secret_version.go.tmpl @@ -0,0 +1,18 @@ +{{/* + The license inside this block applies to this file + Copyright 2024 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. +*/ -}} +_, err := expandSecretManagerRegionalRegionalSecretVersionEnabled(d.Get("enabled"), d, config) +if err != nil { + return err +} + +return resourceSecretManagerRegionalRegionalSecretVersionRead(d, meta) diff --git a/mmv1/templates/terraform/decoders/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl b/mmv1/templates/terraform/decoders/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl new file mode 100644 index 000000000000..802094cda061 --- /dev/null +++ b/mmv1/templates/terraform/decoders/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl @@ -0,0 +1,16 @@ +rules, predefinedRules, err := regionNetworkFirewallPolicyWithRulesSplitPredefinedRules(res["rules"].([]interface{})) + +if err != nil { + return nil, fmt.Errorf("Error occurred while splitting pre-defined rules: %s", err) +} + +res["rules"] = rules +res["predefinedRules"] = predefinedRules + +config := meta.(*transport_tpg.Config) + +if err := d.Set("predefined_rules", flattenComputeRegionNetworkFirewallPolicyWithRulesPredefinedRules(predefinedRules, d, config)); err != nil { + return nil, fmt.Errorf("Error occurred while setting pre-defined rules: %s", err) +} + +return res, nil diff --git a/mmv1/templates/terraform/decoders/resource_compute_region_network_firewall_policy_with_rules.go.erb b/mmv1/templates/terraform/decoders/resource_compute_region_network_firewall_policy_with_rules.go.erb new file mode 100644 index 000000000000..802094cda061 --- /dev/null +++ b/mmv1/templates/terraform/decoders/resource_compute_region_network_firewall_policy_with_rules.go.erb @@ -0,0 +1,16 @@ +rules, predefinedRules, err := regionNetworkFirewallPolicyWithRulesSplitPredefinedRules(res["rules"].([]interface{})) + +if err != nil { + return nil, fmt.Errorf("Error occurred while splitting pre-defined rules: %s", err) +} + +res["rules"] = rules +res["predefinedRules"] = predefinedRules + +config := meta.(*transport_tpg.Config) + +if err := d.Set("predefined_rules", flattenComputeRegionNetworkFirewallPolicyWithRulesPredefinedRules(predefinedRules, d, config)); err != nil { + return nil, fmt.Errorf("Error occurred while setting pre-defined rules: %s", err) +} + +return res, nil diff --git a/mmv1/templates/terraform/encoders/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl b/mmv1/templates/terraform/encoders/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl new file mode 100644 index 000000000000..939b22280811 --- /dev/null +++ b/mmv1/templates/terraform/encoders/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl @@ -0,0 +1,3 @@ +delete(obj, "rules") // Rules are not supported in the create API +return obj, nil + diff --git a/mmv1/templates/terraform/encoders/logging_log_scope.go.erb b/mmv1/templates/terraform/encoders/logging_log_scope.go.erb new file mode 100644 index 000000000000..758a4138ea27 --- /dev/null +++ b/mmv1/templates/terraform/encoders/logging_log_scope.go.erb @@ -0,0 +1,34 @@ +<%- # the license inside this block applies to this file + # 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. +-%> +// Extract any empty fields from the bucket field. +// Extract parent, location from name +parent := d.Get("parent").(string) +name := d.Get("name").(string) +parent, err := tpgresource.ExtractFieldByPattern("parent", parent, name, "((projects|folders|organizations|billingAccounts)/[a-z0-9A-Z-]*)/locations/.*") +if err != nil { + return nil, fmt.Errorf("error extracting parent field: %s", err) +} +location := d.Get("location").(string) +location, err = tpgresource.ExtractFieldByPattern("location", location, name, "[a-zA-Z]*/[a-z0-9A-Z-]*/locations/([a-z0-9-]*)/logScopes/.*") +if err != nil { + return nil, fmt.Errorf("error extracting location field: %s", err) +} +// Set parent to the extracted value. +d.Set("parent", parent) +// Set all the other fields to their short forms before forming url and setting ID. +name = tpgresource.GetResourceNameFromSelfLink(name) +d.Set("location", location) +d.Set("name", name) +return obj, nil diff --git a/mmv1/templates/terraform/encoders/resource_compute_region_network_firewall_policy_with_rules.go.erb b/mmv1/templates/terraform/encoders/resource_compute_region_network_firewall_policy_with_rules.go.erb new file mode 100644 index 000000000000..939b22280811 --- /dev/null +++ b/mmv1/templates/terraform/encoders/resource_compute_region_network_firewall_policy_with_rules.go.erb @@ -0,0 +1,3 @@ +delete(obj, "rules") // Rules are not supported in the create API +return obj, nil + diff --git a/mmv1/templates/terraform/encoders/router_nat_address_patch_on_create.go.erb b/mmv1/templates/terraform/encoders/router_nat_address_patch_on_create.go.erb new file mode 100644 index 000000000000..d4977c5ae349 --- /dev/null +++ b/mmv1/templates/terraform/encoders/router_nat_address_patch_on_create.go.erb @@ -0,0 +1,33 @@ +currItems, err := resourceComputeRouterNatAddressListForPatch(d, meta) +if err != nil { + return nil, err +} + +idx, found, err := resourceComputeRouterNatAddressFindNestedObjectInList(d, meta, currItems) +if err != nil { + return nil, err +} + +// Merge new with existing item if item was already created (with the router nat resource). +if found != nil { + // Merge new object into old. + for k, v := range obj { + found[k] = v + } + currItems[idx] = found + + // Return list with new item added + resPatch := map[string]interface{}{ + "nats": currItems, + } + + return resPatch, nil +} + +// Prevent creating a RouterNatAddress if no RouterNat has been found +log.Printf("[WARNING] No RouterNat resource %+v found, preventing RouterNatAddress creation", obj) +res := map[string]interface{}{ + "nats": nil, +} + +return res, nil diff --git a/mmv1/templates/terraform/encoders/router_nat_address_update_skip_encoder.go.erb b/mmv1/templates/terraform/encoders/router_nat_address_update_skip_encoder.go.erb new file mode 100644 index 000000000000..8b07a10e08ed --- /dev/null +++ b/mmv1/templates/terraform/encoders/router_nat_address_update_skip_encoder.go.erb @@ -0,0 +1,2 @@ +// Since we only want to change the handling of the CREATE function, this encoder just returns the unchanged obj value +return obj, nil diff --git a/mmv1/templates/terraform/encoders/router_nat_set_initial_nat_ips.go.erb b/mmv1/templates/terraform/encoders/router_nat_set_initial_nat_ips.go.erb new file mode 100644 index 000000000000..68d744db408c --- /dev/null +++ b/mmv1/templates/terraform/encoders/router_nat_set_initial_nat_ips.go.erb @@ -0,0 +1,15 @@ +// initial_nat_ips uses the same api_name as nat_ips +if tpgresource.IsEmptyValue(reflect.ValueOf(obj["initialNatIps"])) { + return obj, nil +} + +newObj := make(map[string]interface{}) +for key, value := range obj { + newObj[key] = value +} + +newObj["natIps"] = obj["initialNatIps"] +delete(newObj, "initialNatIps") + +log.Printf("[DEBUG] Replacing initialNatIps value \n oldObj: %+v \n newObj: %+v", obj, newObj) +return newObj, nil diff --git a/mmv1/templates/terraform/examples/cloud_run_service_gpu.tf.erb b/mmv1/templates/terraform/examples/cloud_run_service_gpu.tf.erb new file mode 100644 index 000000000000..bf1a942b0d97 --- /dev/null +++ b/mmv1/templates/terraform/examples/cloud_run_service_gpu.tf.erb @@ -0,0 +1,35 @@ +resource "google_cloud_run_service" "<%= ctx[:primary_resource_id] %>" { + provider = google-beta + name = "<%= ctx[:vars]['cloud_run_service_name'] %>" + location = "us-central1" + + metadata { + annotations = { + "run.googleapis.com/launch-stage" = "BETA" + } + } + + template { + metadata { + annotations = { + "autoscaling.knative.dev/maxScale": "1" + "run.googleapis.com/cpu-throttling": "false" + } + } + spec { + containers { + image = "gcr.io/cloudrun/hello" + resources { + limits = { + "cpu" = "4" + "memory" = "16Gi" + "nvidia.com/gpu" = "1" + } + } + } + node_selector = { + "run.googleapis.com/accelerator" = "nvidia-l4" + } + } + } +} diff --git a/mmv1/templates/terraform/examples/cloudrunv2_service_gpu.tf.erb b/mmv1/templates/terraform/examples/cloudrunv2_service_gpu.tf.erb new file mode 100644 index 000000000000..f0d83e17bd28 --- /dev/null +++ b/mmv1/templates/terraform/examples/cloudrunv2_service_gpu.tf.erb @@ -0,0 +1,28 @@ +resource "google_cloud_run_v2_service" "<%= ctx[:primary_resource_id] %>" { + provider = google-beta + name = "<%= ctx[:vars]['cloud_run_service_name'] %>" + location = "us-central1" + deletion_protection = false + ingress = "INGRESS_TRAFFIC_ALL" + launch_stage = "BETA" + + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + resources { + limits = { + "cpu" = "4" + "memory" = "16Gi" + "nvidia.com/gpu" = "1" + } + startup_cpu_boost = true + } + } + node_selector { + accelerator = "nvidia-l4" + } + scaling { + max_instance_count = 1 + } + } +} diff --git a/mmv1/templates/terraform/examples/compute_region_network_firewall_policy_with_rules_full.tf.erb b/mmv1/templates/terraform/examples/compute_region_network_firewall_policy_with_rules_full.tf.erb new file mode 100644 index 000000000000..99da631be217 --- /dev/null +++ b/mmv1/templates/terraform/examples/compute_region_network_firewall_policy_with_rules_full.tf.erb @@ -0,0 +1,83 @@ +data "google_project" "project" { + provider = google-beta +} + +resource "google_compute_region_network_firewall_policy_with_rules" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['policy_name'] %>" + region = "us-west2" + description = "Terraform test" + provider = google-beta + + rule { + description = "tcp rule" + priority = 1000 + enable_logging = true + action = "allow" + direction = "EGRESS" + match { + layer4_config { + ip_protocol = "tcp" + ports = [8080, 7070] + } + dest_ip_ranges = ["11.100.0.1/32"] + dest_fqdns = ["www.yyy.com", "www.zzz.com"] + dest_region_codes = ["HK", "IN"] + dest_threat_intelligences = ["iplist-search-engines-crawlers", "iplist-tor-exit-nodes"] + dest_address_groups = [google_network_security_address_group.address_group_1.id] + } + target_secure_tag { + name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + } + } + rule { + description = "udp rule" + rule_name = "test-rule" + priority = 2000 + enable_logging = false + action = "deny" + direction = "INGRESS" + match { + layer4_config { + ip_protocol = "udp" + } + src_ip_ranges = ["0.0.0.0/0"] + src_fqdns = ["www.abc.com", "www.def.com"] + src_region_codes = ["US", "CA"] + src_threat_intelligences = ["iplist-known-malicious-ips", "iplist-public-clouds"] + src_address_groups = [google_network_security_address_group.address_group_1.id] + src_secure_tag { + name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + } + } + disabled = true + } +} + +resource "google_network_security_address_group" "address_group_1" { + provider = google-beta + name = "<%= ctx[:vars]['address_group_name'] %>" + parent = "projects/${data.google_project.project.name}" + description = "Regional address group" + location = "us-west2" + items = ["208.80.154.224/32"] + type = "IPV4" + capacity = 100 +} + +resource "google_tags_tag_key" "secure_tag_key_1" { + provider = google-beta + description = "Tag key" + parent = "projects/${data.google_project.project.name}" + purpose = "GCE_FIREWALL" + short_name = "<%= ctx[:vars]['tag_key_name'] %>" + purpose_data = { + network = "${data.google_project.project.name}/default" + } +} + +resource "google_tags_tag_value" "secure_tag_value_1" { + provider = google-beta + description = "Tag value" + parent = "tagKeys/${google_tags_tag_key.secure_tag_key_1.name}" + short_name = "<%= ctx[:vars]['tag_value_name'] %>" +} diff --git a/mmv1/templates/terraform/examples/go/cloud_run_service_gpu.tf.tmpl b/mmv1/templates/terraform/examples/go/cloud_run_service_gpu.tf.tmpl new file mode 100644 index 000000000000..5606203f1594 --- /dev/null +++ b/mmv1/templates/terraform/examples/go/cloud_run_service_gpu.tf.tmpl @@ -0,0 +1,35 @@ +resource "google_cloud_run_service" "{{$.PrimaryResourceId}}" { + provider = google-beta + name = "{{index $.Vars "cloud_run_service_name"}}" + location = "us-central1" + + metadata { + annotations = { + "run.googleapis.com/launch-stage" = "BETA" + } + } + + template { + metadata { + annotations = { + "autoscaling.knative.dev/maxScale": "1" + "run.googleapis.com/cpu-throttling": "false" + } + } + spec { + containers { + image = "gcr.io/cloudrun/hello" + resources { + limits = { + "cpu" = "4" + "memory" = "16Gi" + "nvidia.com/gpu" = "1" + } + } + } + node_selector = { + "run.googleapis.com/accelerator" = "nvidia-l4" + } + } + } +} diff --git a/mmv1/templates/terraform/examples/go/cloudrunv2_service_gpu.tf.tmpl b/mmv1/templates/terraform/examples/go/cloudrunv2_service_gpu.tf.tmpl new file mode 100644 index 000000000000..9308a5e1d7ea --- /dev/null +++ b/mmv1/templates/terraform/examples/go/cloudrunv2_service_gpu.tf.tmpl @@ -0,0 +1,28 @@ +resource "google_cloud_run_v2_service" "{{$.PrimaryResourceId}}" { + provider = google-beta + name = "{{index $.Vars "cloud_run_service_name"}}" + location = "us-central1" + deletion_protection = false + ingress = "INGRESS_TRAFFIC_ALL" + launch_stage = "BETA" + + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + resources { + limits = { + "cpu" = "4" + "memory" = "16Gi" + "nvidia.com/gpu" = "1" + } + startup_cpu_boost = true + } + } + node_selector { + accelerator = "nvidia-l4" + } + scaling { + max_instance_count = 1 + } + } +} diff --git a/mmv1/templates/terraform/examples/go/compute_region_network_firewall_policy_with_rules_full.tf.tmpl b/mmv1/templates/terraform/examples/go/compute_region_network_firewall_policy_with_rules_full.tf.tmpl new file mode 100644 index 000000000000..1925a3da00de --- /dev/null +++ b/mmv1/templates/terraform/examples/go/compute_region_network_firewall_policy_with_rules_full.tf.tmpl @@ -0,0 +1,83 @@ +data "google_project" "project" { + provider = google-beta +} + +resource "google_compute_region_network_firewall_policy_with_rules" "{{$.PrimaryResourceId}}" { + name = "{{index $.Vars "policy_name"}}" + region = "us-west2" + description = "Terraform test" + provider = google-beta + + rule { + description = "tcp rule" + priority = 1000 + enable_logging = true + action = "allow" + direction = "EGRESS" + match { + layer4_config { + ip_protocol = "tcp" + ports = [8080, 7070] + } + dest_ip_ranges = ["11.100.0.1/32"] + dest_fqdns = ["www.yyy.com", "www.zzz.com"] + dest_region_codes = ["HK", "IN"] + dest_threat_intelligences = ["iplist-search-engines-crawlers", "iplist-tor-exit-nodes"] + dest_address_groups = [google_network_security_address_group.address_group_1.id] + } + target_secure_tag { + name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + } + } + rule { + description = "udp rule" + rule_name = "test-rule" + priority = 2000 + enable_logging = false + action = "deny" + direction = "INGRESS" + match { + layer4_config { + ip_protocol = "udp" + } + src_ip_ranges = ["0.0.0.0/0"] + src_fqdns = ["www.abc.com", "www.def.com"] + src_region_codes = ["US", "CA"] + src_threat_intelligences = ["iplist-known-malicious-ips", "iplist-public-clouds"] + src_address_groups = [google_network_security_address_group.address_group_1.id] + src_secure_tag { + name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + } + } + disabled = true + } +} + +resource "google_network_security_address_group" "address_group_1" { + provider = google-beta + name = "{{index $.Vars "address_group_name"}}" + parent = "projects/${data.google_project.project.name}" + description = "Regional address group" + location = "us-west2" + items = ["208.80.154.224/32"] + type = "IPV4" + capacity = 100 +} + +resource "google_tags_tag_key" "secure_tag_key_1" { + provider = google-beta + description = "Tag key" + parent = "projects/${data.google_project.project.name}" + purpose = "GCE_FIREWALL" + short_name = "{{index $.Vars "tag_key_name"}}" + purpose_data = { + network = "${data.google_project.project.name}/default" + } +} + +resource "google_tags_tag_value" "secure_tag_value_1" { + provider = google-beta + description = "Tag value" + parent = "tagKeys/${google_tags_tag_key.secure_tag_key_1.name}" + short_name = "{{index $.Vars "tag_value_name"}}" +} diff --git a/mmv1/templates/terraform/examples/go/dataproc_metastore_service_deletion_protection.tf.tmpl b/mmv1/templates/terraform/examples/go/dataproc_metastore_service_deletion_protection.tf.tmpl new file mode 100644 index 000000000000..41a8c9146deb --- /dev/null +++ b/mmv1/templates/terraform/examples/go/dataproc_metastore_service_deletion_protection.tf.tmpl @@ -0,0 +1,21 @@ +resource "google_dataproc_metastore_service" "{{$.PrimaryResourceId}}" { + service_id = "{{index $.Vars "metastore_service_name"}}" + location = "us-central1" + port = 9080 + tier = "DEVELOPER" + deletion_protection = "{{index $.Vars "deletion_protection"}}" + + maintenance_window { + hour_of_day = 2 + day_of_week = "SUNDAY" + } + + hive_metastore_config { + version = "2.3.6" + } + + labels = { + env = "test" + } + } + \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/go/healthcare_pipeline_job_backfill.tf.tmpl b/mmv1/templates/terraform/examples/go/healthcare_pipeline_job_backfill.tf.tmpl new file mode 100644 index 000000000000..56667f06ebf3 --- /dev/null +++ b/mmv1/templates/terraform/examples/go/healthcare_pipeline_job_backfill.tf.tmpl @@ -0,0 +1,13 @@ +resource "google_healthcare_pipeline_job" "{{$.PrimaryResourceId}}" { + name = "{{index $.Vars "backfill_pipeline_name"}}" + location = "us-central1" + dataset = google_healthcare_dataset.dataset.id + backfill_pipeline_job { + mapping_pipeline_job = "${google_healthcare_dataset.dataset.id}/pipelinejobs/{{index $.Vars "mapping_pipeline_name"}}" + } +} + +resource "google_healthcare_dataset" "dataset" { + name = "{{index $.Vars "dataset_name"}}" + location = "us-central1" +} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/go/healthcare_pipeline_job_mapping_recon_dest.tf.tmpl b/mmv1/templates/terraform/examples/go/healthcare_pipeline_job_mapping_recon_dest.tf.tmpl new file mode 100644 index 000000000000..e9d25c6b448d --- /dev/null +++ b/mmv1/templates/terraform/examples/go/healthcare_pipeline_job_mapping_recon_dest.tf.tmpl @@ -0,0 +1,81 @@ +resource "google_healthcare_pipeline_job" "recon" { + name = "{{index $.Vars "recon_pipeline_name"}}" + location = "us-central1" + dataset = google_healthcare_dataset.dataset.id + disable_lineage = true + reconciliation_pipeline_job { + merge_config { + description = "sample description for reconciliation rules" + whistle_config_source { + uri = "gs://${google_storage_bucket.bucket.name}/${google_storage_bucket_object.merge_file.name}" + import_uri_prefix = "gs://${google_storage_bucket.bucket.name}" + } + } + matching_uri_prefix = "gs://${google_storage_bucket.bucket.name}" + fhir_store_destination = "${google_healthcare_dataset.dataset.id}/fhirStores/${google_healthcare_fhir_store.dest_fhirstore.name}" + } +} + +resource "google_healthcare_pipeline_job" "{{$.PrimaryResourceId}}" { + depends_on = [google_healthcare_pipeline_job.recon] + name = "{{index $.Vars "pipeline_name"}}" + location = "us-central1" + dataset = google_healthcare_dataset.dataset.id + disable_lineage = true + labels = { + example_label_key = "example_label_value" + } + mapping_pipeline_job { + mapping_config { + whistle_config_source { + uri = "gs://${google_storage_bucket.bucket.name}/${google_storage_bucket_object.mapping_file.name}" + import_uri_prefix = "gs://${google_storage_bucket.bucket.name}" + } + description = "example description for mapping configuration" + } + fhir_streaming_source { + fhir_store = "${google_healthcare_dataset.dataset.id}/fhirStores/${google_healthcare_fhir_store.source_fhirstore.name}" + description = "example description for streaming fhirstore" + } + reconciliation_destination = true + } +} + +resource "google_healthcare_dataset" "dataset" { + name = "{{index $.Vars "dataset_name"}}" + location = "us-central1" +} + +resource "google_healthcare_fhir_store" "source_fhirstore" { + name = "{{index $.Vars "source_fhirstore_name"}}" + dataset = google_healthcare_dataset.dataset.id + version = "R4" + enable_update_create = true + disable_referential_integrity = true +} + +resource "google_healthcare_fhir_store" "dest_fhirstore" { + name = "{{index $.Vars "dest_fhirstore_name"}}" + dataset = google_healthcare_dataset.dataset.id + version = "R4" + enable_update_create = true + disable_referential_integrity = true +} + +resource "google_storage_bucket" "bucket" { + name = "{{index $.Vars "bucket_name"}}" + location = "us-central1" + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_object" "mapping_file" { + name = "mapping.wstl" + content = " " + bucket = google_storage_bucket.bucket.name +} + +resource "google_storage_bucket_object" "merge_file" { + name = "merge.wstl" + content = " " + bucket = google_storage_bucket.bucket.name +} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/go/healthcare_pipeline_job_reconciliation.tf.tmpl b/mmv1/templates/terraform/examples/go/healthcare_pipeline_job_reconciliation.tf.tmpl new file mode 100644 index 000000000000..68c22b8f1cb9 --- /dev/null +++ b/mmv1/templates/terraform/examples/go/healthcare_pipeline_job_reconciliation.tf.tmpl @@ -0,0 +1,42 @@ +resource "google_healthcare_pipeline_job" "{{$.PrimaryResourceId}}" { + name = "{{index $.Vars "pipeline_name"}}" + location = "us-central1" + dataset = google_healthcare_dataset.dataset.id + disable_lineage = true + reconciliation_pipeline_job { + merge_config { + description = "sample description for reconciliation rules" + whistle_config_source { + uri = "gs://${google_storage_bucket.bucket.name}/${google_storage_bucket_object.merge_file.name}" + import_uri_prefix = "gs://${google_storage_bucket.bucket.name}" + } + } + matching_uri_prefix = "gs://${google_storage_bucket.bucket.name}" + fhir_store_destination = "${google_healthcare_dataset.dataset.id}/fhirStores/${google_healthcare_fhir_store.fhirstore.name}" + } +} + +resource "google_healthcare_dataset" "dataset" { + name = "{{index $.Vars "dataset_name"}}" + location = "us-central1" +} + +resource "google_healthcare_fhir_store" "fhirstore" { + name = "{{index $.Vars "fhir_store_name"}}" + dataset = google_healthcare_dataset.dataset.id + version = "R4" + enable_update_create = true + disable_referential_integrity = true +} + +resource "google_storage_bucket" "bucket" { + name = "{{index $.Vars "bucket_name"}}" + location = "us-central1" + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_object" "merge_file" { + name = "merge.wstl" + content = " " + bucket = google_storage_bucket.bucket.name +} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/go/healthcare_pipeline_job_whistle_mapping.tf.tmpl b/mmv1/templates/terraform/examples/go/healthcare_pipeline_job_whistle_mapping.tf.tmpl new file mode 100644 index 000000000000..d75fd9e3552c --- /dev/null +++ b/mmv1/templates/terraform/examples/go/healthcare_pipeline_job_whistle_mapping.tf.tmpl @@ -0,0 +1,56 @@ +resource "google_healthcare_pipeline_job" "{{$.PrimaryResourceId}}" { + name = "{{index $.Vars "pipeline_name"}}" + location = "us-central1" + dataset = google_healthcare_dataset.dataset.id + disable_lineage = true + labels = { + example_label_key = "example_label_value" + } + mapping_pipeline_job { + mapping_config { + whistle_config_source { + uri = "gs://${google_storage_bucket.bucket.name}/${google_storage_bucket_object.mapping_file.name}" + import_uri_prefix = "gs://${google_storage_bucket.bucket.name}" + } + description = "example description for mapping configuration" + } + fhir_streaming_source { + fhir_store = "${google_healthcare_dataset.dataset.id}/fhirStores/${google_healthcare_fhir_store.source_fhirstore.name}" + description = "example description for streaming fhirstore" + } + fhir_store_destination = "${google_healthcare_dataset.dataset.id}/fhirStores/${google_healthcare_fhir_store.dest_fhirstore.name}" + } +} + +resource "google_healthcare_dataset" "dataset" { + name = "{{index $.Vars "dataset_name"}}" + location = "us-central1" +} + +resource "google_healthcare_fhir_store" "source_fhirstore" { + name = "{{index $.Vars "source_fhirstore_name"}}" + dataset = google_healthcare_dataset.dataset.id + version = "R4" + enable_update_create = true + disable_referential_integrity = true +} + +resource "google_healthcare_fhir_store" "dest_fhirstore" { + name = "{{index $.Vars "dest_fhirstore_name"}}" + dataset = google_healthcare_dataset.dataset.id + version = "R4" + enable_update_create = true + disable_referential_integrity = true +} + +resource "google_storage_bucket" "bucket" { + name = "{{index $.Vars "bucket_name"}}" + location = "us-central1" + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_object" "mapping_file" { + name = "mapping.wstl" + content = " " + bucket = google_storage_bucket.bucket.name +} \ No newline at end of file diff --git a/mmv1/templates/terraform/examples/go/looker_instance_fips.tf.tmpl b/mmv1/templates/terraform/examples/go/looker_instance_fips.tf.tmpl new file mode 100644 index 000000000000..05e4becab26d --- /dev/null +++ b/mmv1/templates/terraform/examples/go/looker_instance_fips.tf.tmpl @@ -0,0 +1,11 @@ +resource "google_looker_instance" "{{$.PrimaryResourceId}}" { + name = "{{index $.Vars "instance_name"}}" + platform_edition = "LOOKER_CORE_ENTERPRISE_ANNUAL" + region = "us-central1" + public_ip_enabled = true + fips_enabled = true + oauth_config { + client_id = "{{index $.Vars "client_id"}}" + client_secret = "{{index $.Vars "client_secret"}}" + } +} diff --git a/mmv1/templates/terraform/examples/go/looker_instance_psc.tf.tmpl b/mmv1/templates/terraform/examples/go/looker_instance_psc.tf.tmpl new file mode 100644 index 000000000000..151321ce125f --- /dev/null +++ b/mmv1/templates/terraform/examples/go/looker_instance_psc.tf.tmpl @@ -0,0 +1,17 @@ +resource "google_looker_instance" "{{$.PrimaryResourceId}}" { + name = "{{index $.Vars "instance_name"}}" + platform_edition = "LOOKER_CORE_ENTERPRISE_ANNUAL" + region = "us-central1" + private_ip_enabled = false + public_ip_enabled = false + psc_enabled = true + oauth_config { + client_id = "{{index $.Vars "client_id"}}" + client_secret = "{{index $.Vars "client_secret"}}" + } + psc_config { + allowed_vpcs = ["projects/test-project/global/networks/test"] + # update only + # service_attachments = [{local_fqdn: "www.local-fqdn.com" target_service_attachment_uri: "projects/my-project/regions/us-east1/serviceAttachments/sa"}] + } +} diff --git a/mmv1/templates/terraform/examples/go/network_connectivity_spoke_interconnect_attachment_basic.tf.tmpl b/mmv1/templates/terraform/examples/go/network_connectivity_spoke_interconnect_attachment_basic.tf.tmpl new file mode 100644 index 000000000000..44e35979a8c0 --- /dev/null +++ b/mmv1/templates/terraform/examples/go/network_connectivity_spoke_interconnect_attachment_basic.tf.tmpl @@ -0,0 +1,45 @@ +resource "google_network_connectivity_hub" "basic_hub" { + name = "{{index $.Vars "hub_name"}}" + description = "A sample hub" + labels = { + label-two = "value-one" + } +} + +resource "google_compute_network" "network" { + name = "{{index $.Vars "network_name"}}" + auto_create_subnetworks = false +} + +resource "google_compute_router" "router" { + name = "{{index $.Vars "router_name"}}" + region = "us-central1" + network = google_compute_network.network.name + bgp { + asn = 16550 + } +} + +resource "google_compute_interconnect_attachment" "interconnect-attachment" { + name = "{{index $.Vars "interconnect_attachment_name"}}" + edge_availability_domain = "AVAILABILITY_DOMAIN_1" + type = "PARTNER" + router = google_compute_router.router.id + mtu = 1500 + region = "us-central1" +} + +resource "google_network_connectivity_spoke" "{{$.PrimaryResourceId}}" { + name = "{{index $.Vars "interconnect_attachment_spoke_name"}}" + location = "us-central1" + description = "A sample spoke with a linked Interconnect Attachment" + labels = { + label-one = "value-one" + } + hub = google_network_connectivity_hub.basic_hub.id + linked_interconnect_attachments { + uris = [google_compute_interconnect_attachment.interconnect-attachment.self_link] + site_to_site_data_transfer = true + include_import_ranges = ["ALL_IPV4_RANGES"] + } +} diff --git a/mmv1/templates/terraform/examples/go/network_connectivity_spoke_router_appliance_basic.tf.tmpl b/mmv1/templates/terraform/examples/go/network_connectivity_spoke_router_appliance_basic.tf.tmpl index 693028f32f8c..058a19583736 100644 --- a/mmv1/templates/terraform/examples/go/network_connectivity_spoke_router_appliance_basic.tf.tmpl +++ b/mmv1/templates/terraform/examples/go/network_connectivity_spoke_router_appliance_basic.tf.tmpl @@ -53,5 +53,6 @@ resource "google_network_connectivity_spoke" "primary" { ip_address = "10.0.0.2" } site_to_site_data_transfer = true + include_import_ranges = ["ALL_IPV4_RANGES"] } } diff --git a/mmv1/templates/terraform/examples/go/network_connectivity_spoke_vpn_tunnel_basic.tf.tmpl b/mmv1/templates/terraform/examples/go/network_connectivity_spoke_vpn_tunnel_basic.tf.tmpl new file mode 100644 index 000000000000..94e922e717cc --- /dev/null +++ b/mmv1/templates/terraform/examples/go/network_connectivity_spoke_vpn_tunnel_basic.tf.tmpl @@ -0,0 +1,131 @@ +resource "google_network_connectivity_hub" "basic_hub" { + name = "{{index $.Vars "hub_name"}}" + description = "A sample hub" + labels = { + label-two = "value-one" + } +} + +resource "google_compute_network" "network" { + name = "{{index $.Vars "network_name"}}" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "subnetwork" { + name = "{{index $.Vars "subnetwork_name"}}" + ip_cidr_range = "10.0.0.0/28" + region = "us-central1" + network = google_compute_network.network.self_link +} + +resource "google_compute_ha_vpn_gateway" "gateway" { + name = "{{index $.Vars "gateway_name"}}" + network = google_compute_network.network.id +} + +resource "google_compute_external_vpn_gateway" "external_vpn_gw" { + name = "{{index $.Vars "external_gateway_name"}}" + redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" + description = "An externally managed VPN gateway" + interface { + id = 0 + ip_address = "8.8.8.8" + } +} + +resource "google_compute_router" "router" { + name = "{{index $.Vars "router_name"}}" + region = "us-central1" + network = google_compute_network.network.name + bgp { + asn = 64514 + } +} + +resource "google_compute_vpn_tunnel" "tunnel1" { + name = "{{index $.Vars "vpn_tunnel_1_name"}}" + region = "us-central1" + vpn_gateway = google_compute_ha_vpn_gateway.gateway.id + peer_external_gateway = google_compute_external_vpn_gateway.external_vpn_gw.id + peer_external_gateway_interface = 0 + shared_secret = "a secret message" + router = google_compute_router.router.id + vpn_gateway_interface = 0 +} + +resource "google_compute_vpn_tunnel" "tunnel2" { + name = "{{index $.Vars "vpn_tunnel_2_name"}}" + region = "us-central1" + vpn_gateway = google_compute_ha_vpn_gateway.gateway.id + peer_external_gateway = google_compute_external_vpn_gateway.external_vpn_gw.id + peer_external_gateway_interface = 0 + shared_secret = "a secret message" + router = " ${google_compute_router.router.id}" + vpn_gateway_interface = 1 +} + +resource "google_compute_router_interface" "router_interface1" { + name = "{{index $.Vars "router_interface_1_name"}}" + router = google_compute_router.router.name + region = "us-central1" + ip_range = "169.254.0.1/30" + vpn_tunnel = google_compute_vpn_tunnel.tunnel1.name +} + +resource "google_compute_router_peer" "router_peer1" { + name = "{{index $.Vars "router_peer_1_name"}}" + router = google_compute_router.router.name + region = "us-central1" + peer_ip_address = "169.254.0.2" + peer_asn = 64515 + advertised_route_priority = 100 + interface = google_compute_router_interface.router_interface1.name +} + +resource "google_compute_router_interface" "router_interface2" { + name = "{{index $.Vars "router_interface_2_name"}}" + router = google_compute_router.router.name + region = "us-central1" + ip_range = "169.254.1.1/30" + vpn_tunnel = google_compute_vpn_tunnel.tunnel2.name +} + +resource "google_compute_router_peer" "router_peer2" { + name = "{{index $.Vars "router_peer_2_name"}}" + router = google_compute_router.router.name + region = "us-central1" + peer_ip_address = "169.254.1.2" + peer_asn = 64515 + advertised_route_priority = 100 + interface = google_compute_router_interface.router_interface2.name +} + +resource "google_network_connectivity_spoke" "{{$.PrimaryResourceId}}" { + name = "{{index $.Vars "vpn_tunnel_1_spoke_name"}}" + location = "us-central1" + description = "A sample spoke with a linked VPN Tunnel" + labels = { + label-one = "value-one" + } + hub = google_network_connectivity_hub.basic_hub.id + linked_vpn_tunnels { + uris = [google_compute_vpn_tunnel.tunnel1.self_link] + site_to_site_data_transfer = true + include_import_ranges = ["ALL_IPV4_RANGES"] + } +} + +resource "google_network_connectivity_spoke" "tunnel2" { + name = "{{index $.Vars "vpn_tunnel_2_spoke_name"}}" + location = "us-central1" + description = "A sample spoke with a linked VPN Tunnel" + labels = { + label-one = "value-one" + } + hub = google_network_connectivity_hub.basic_hub.id + linked_vpn_tunnels { + uris = [google_compute_vpn_tunnel.tunnel2.self_link] + site_to_site_data_transfer = true + include_import_ranges = ["ALL_IPV4_RANGES"] + } +} diff --git a/mmv1/templates/terraform/examples/go/regional_secret_version_basic.tf.tmpl b/mmv1/templates/terraform/examples/go/regional_secret_version_basic.tf.tmpl new file mode 100644 index 000000000000..129d5eec661d --- /dev/null +++ b/mmv1/templates/terraform/examples/go/regional_secret_version_basic.tf.tmpl @@ -0,0 +1,9 @@ +resource "google_secret_manager_regional_secret" "secret-basic" { + secret_id = "{{index $.Vars "secret_id"}}" + location = "us-central1" +} + +resource "google_secret_manager_regional_secret_version" "{{$.PrimaryResourceId}}" { + secret = google_secret_manager_regional_secret.secret-basic.id + secret_data = "{{index $.Vars "data"}}" +} diff --git a/mmv1/templates/terraform/examples/go/regional_secret_version_deletion_policy_abandon.tf.tmpl b/mmv1/templates/terraform/examples/go/regional_secret_version_deletion_policy_abandon.tf.tmpl new file mode 100644 index 000000000000..72d884c66774 --- /dev/null +++ b/mmv1/templates/terraform/examples/go/regional_secret_version_deletion_policy_abandon.tf.tmpl @@ -0,0 +1,10 @@ +resource "google_secret_manager_regional_secret" "secret-basic" { + secret_id = "{{index $.Vars "secret_id"}}" + location = "us-central1" +} + +resource "google_secret_manager_regional_secret_version" "{{$.PrimaryResourceId}}" { + secret = google_secret_manager_regional_secret.secret-basic.id + secret_data = "{{index $.Vars "data"}}" + deletion_policy = "ABANDON" +} diff --git a/mmv1/templates/terraform/examples/go/regional_secret_version_deletion_policy_disable.tf.tmpl b/mmv1/templates/terraform/examples/go/regional_secret_version_deletion_policy_disable.tf.tmpl new file mode 100644 index 000000000000..4869268a0a4d --- /dev/null +++ b/mmv1/templates/terraform/examples/go/regional_secret_version_deletion_policy_disable.tf.tmpl @@ -0,0 +1,10 @@ +resource "google_secret_manager_regional_secret" "secret-basic" { + secret_id = "{{index $.Vars "secret_id"}}" + location = "us-central1" +} + +resource "google_secret_manager_regional_secret_version" "{{$.PrimaryResourceId}}" { + secret = google_secret_manager_regional_secret.secret-basic.id + secret_data = "{{index $.Vars "data"}}" + deletion_policy = "DISABLE" +} diff --git a/mmv1/templates/terraform/examples/go/regional_secret_version_disabled.tf.tmpl b/mmv1/templates/terraform/examples/go/regional_secret_version_disabled.tf.tmpl new file mode 100644 index 000000000000..5af98899fa61 --- /dev/null +++ b/mmv1/templates/terraform/examples/go/regional_secret_version_disabled.tf.tmpl @@ -0,0 +1,10 @@ +resource "google_secret_manager_regional_secret" "secret-basic" { + secret_id = "{{index $.Vars "secret_id"}}" + location = "us-central1" +} + +resource "google_secret_manager_regional_secret_version" "{{$.PrimaryResourceId}}" { + secret = google_secret_manager_regional_secret.secret-basic.id + secret_data = "{{index $.Vars "data"}}" + enabled = false +} diff --git a/mmv1/templates/terraform/examples/go/regional_secret_version_with_base64_data.tf.tmpl b/mmv1/templates/terraform/examples/go/regional_secret_version_with_base64_data.tf.tmpl new file mode 100644 index 000000000000..810fa1d0be2a --- /dev/null +++ b/mmv1/templates/terraform/examples/go/regional_secret_version_with_base64_data.tf.tmpl @@ -0,0 +1,10 @@ +resource "google_secret_manager_regional_secret" "secret-basic" { + secret_id = "{{index $.Vars "secret_id"}}" + location = "us-central1" +} + +resource "google_secret_manager_regional_secret_version" "{{$.PrimaryResourceId}}" { + secret = google_secret_manager_regional_secret.secret-basic.id + secret_data = filebase64("{{index $.Vars "data"}}") + is_secret_data_base64 = true +} diff --git a/mmv1/templates/terraform/examples/logging_log_scope_basic.tf.erb b/mmv1/templates/terraform/examples/logging_log_scope_basic.tf.erb new file mode 100644 index 000000000000..855b336105c9 --- /dev/null +++ b/mmv1/templates/terraform/examples/logging_log_scope_basic.tf.erb @@ -0,0 +1,11 @@ +resource "google_logging_log_scope" "<%= ctx[:primary_resource_id] %>" { + parent = "projects/<%= ctx[:test_env_vars]['project'] %>" + location = "global" + name = "projects/<%= ctx[:test_env_vars]['project'] %>/locations/global/logScopes/<%= ctx[:vars]['log_scope_name'] %>" + resource_names = [ + "projects/<%= ctx[:test_env_vars]['project'] %>", + "projects/<%= ctx[:test_env_vars]['project'] %>/locations/global/buckets/_Default/views/<%= ctx[:vars]['log_view_name_1'] %>", + "projects/<%= ctx[:test_env_vars]['project'] %>/locations/global/buckets/_Default/views/<%= ctx[:vars]['log_view_name_2'] %>" + ] + description = "A log scope configured with Terraform" +} diff --git a/mmv1/templates/terraform/examples/looker_instance_psc.tf.erb b/mmv1/templates/terraform/examples/looker_instance_psc.tf.erb new file mode 100644 index 000000000000..403f13a8c0b6 --- /dev/null +++ b/mmv1/templates/terraform/examples/looker_instance_psc.tf.erb @@ -0,0 +1,17 @@ +resource "google_looker_instance" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]["instance_name"] %>" + platform_edition = "LOOKER_CORE_ENTERPRISE_ANNUAL" + region = "us-central1" + private_ip_enabled = false + public_ip_enabled = false + psc_enabled = true + oauth_config { + client_id = "<%= ctx[:vars]["client_id"] %>" + client_secret = "<%= ctx[:vars]["client_secret"] %>" + } + psc_config { + allowed_vpcs = ["projects/test-project/global/networks/test"] + # update only + # service_attachments = [{local_fqdn: "www.local-fqdn.com" target_service_attachment_uri: "projects/my-project/regions/us-east1/serviceAttachments/sa"}] + } +} diff --git a/mmv1/templates/terraform/examples/network_connectivity_spoke_interconnect_attachment_basic.tf.erb b/mmv1/templates/terraform/examples/network_connectivity_spoke_interconnect_attachment_basic.tf.erb new file mode 100644 index 000000000000..03095b8e1b7d --- /dev/null +++ b/mmv1/templates/terraform/examples/network_connectivity_spoke_interconnect_attachment_basic.tf.erb @@ -0,0 +1,45 @@ +resource "google_network_connectivity_hub" "basic_hub" { + name = "<%= ctx[:vars]['hub_name'] %>" + description = "A sample hub" + labels = { + label-two = "value-one" + } +} + +resource "google_compute_network" "network" { + name = "<%= ctx[:vars]['network_name'] %>" + auto_create_subnetworks = false +} + +resource "google_compute_router" "router" { + name = "<%= ctx[:vars]['router_name'] %>" + region = "us-central1" + network = google_compute_network.network.name + bgp { + asn = 16550 + } +} + +resource "google_compute_interconnect_attachment" "interconnect-attachment" { + name = "<%= ctx[:vars]['interconnect_attachment_name'] %>" + edge_availability_domain = "AVAILABILITY_DOMAIN_1" + type = "PARTNER" + router = google_compute_router.router.id + mtu = 1500 + region = "us-central1" +} + +resource "google_network_connectivity_spoke" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['interconnect_attachment_spoke_name'] %>" + location = "us-central1" + description = "A sample spoke with a linked Interconnect Attachment" + labels = { + label-one = "value-one" + } + hub = google_network_connectivity_hub.basic_hub.id + linked_interconnect_attachments { + uris = [google_compute_interconnect_attachment.interconnect-attachment.self_link] + site_to_site_data_transfer = true + include_import_ranges = ["ALL_IPV4_RANGES"] + } +} diff --git a/mmv1/templates/terraform/examples/network_connectivity_spoke_router_appliance_basic.tf.erb b/mmv1/templates/terraform/examples/network_connectivity_spoke_router_appliance_basic.tf.erb index 693028f32f8c..058a19583736 100644 --- a/mmv1/templates/terraform/examples/network_connectivity_spoke_router_appliance_basic.tf.erb +++ b/mmv1/templates/terraform/examples/network_connectivity_spoke_router_appliance_basic.tf.erb @@ -53,5 +53,6 @@ resource "google_network_connectivity_spoke" "primary" { ip_address = "10.0.0.2" } site_to_site_data_transfer = true + include_import_ranges = ["ALL_IPV4_RANGES"] } } diff --git a/mmv1/templates/terraform/examples/network_connectivity_spoke_vpn_tunnel_basic.tf.erb b/mmv1/templates/terraform/examples/network_connectivity_spoke_vpn_tunnel_basic.tf.erb new file mode 100644 index 000000000000..b7663a045d26 --- /dev/null +++ b/mmv1/templates/terraform/examples/network_connectivity_spoke_vpn_tunnel_basic.tf.erb @@ -0,0 +1,131 @@ +resource "google_network_connectivity_hub" "basic_hub" { + name = "<%= ctx[:vars]['hub_name'] %>" + description = "A sample hub" + labels = { + label-two = "value-one" + } +} + +resource "google_compute_network" "network" { + name = "<%= ctx[:vars]['network_name'] %>" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "subnetwork" { + name = "<%= ctx[:vars]['subnetwork_name'] %>" + ip_cidr_range = "10.0.0.0/28" + region = "us-central1" + network = google_compute_network.network.self_link +} + +resource "google_compute_ha_vpn_gateway" "gateway" { + name = "<%= ctx[:vars]['gateway_name'] %>" + network = google_compute_network.network.id +} + +resource "google_compute_external_vpn_gateway" "external_vpn_gw" { + name = "<%= ctx[:vars]['external_gateway_name'] %>" + redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" + description = "An externally managed VPN gateway" + interface { + id = 0 + ip_address = "8.8.8.8" + } +} + +resource "google_compute_router" "router" { + name = "<%= ctx[:vars]['router_name'] %>" + region = "us-central1" + network = google_compute_network.network.name + bgp { + asn = 64514 + } +} + +resource "google_compute_vpn_tunnel" "tunnel1" { + name = "<%= ctx[:vars]['vpn_tunnel_1_name'] %>" + region = "us-central1" + vpn_gateway = google_compute_ha_vpn_gateway.gateway.id + peer_external_gateway = google_compute_external_vpn_gateway.external_vpn_gw.id + peer_external_gateway_interface = 0 + shared_secret = "a secret message" + router = google_compute_router.router.id + vpn_gateway_interface = 0 +} + +resource "google_compute_vpn_tunnel" "tunnel2" { + name = "<%= ctx[:vars]['vpn_tunnel_2_name'] %>" + region = "us-central1" + vpn_gateway = google_compute_ha_vpn_gateway.gateway.id + peer_external_gateway = google_compute_external_vpn_gateway.external_vpn_gw.id + peer_external_gateway_interface = 0 + shared_secret = "a secret message" + router = " ${google_compute_router.router.id}" + vpn_gateway_interface = 1 +} + +resource "google_compute_router_interface" "router_interface1" { + name = "<%= ctx[:vars]['router_interface_1_name'] %>" + router = google_compute_router.router.name + region = "us-central1" + ip_range = "169.254.0.1/30" + vpn_tunnel = google_compute_vpn_tunnel.tunnel1.name +} + +resource "google_compute_router_peer" "router_peer1" { + name = "<%= ctx[:vars]['router_peer_1_name'] %>" + router = google_compute_router.router.name + region = "us-central1" + peer_ip_address = "169.254.0.2" + peer_asn = 64515 + advertised_route_priority = 100 + interface = google_compute_router_interface.router_interface1.name +} + +resource "google_compute_router_interface" "router_interface2" { + name = "<%= ctx[:vars]['router_interface_2_name'] %>" + router = google_compute_router.router.name + region = "us-central1" + ip_range = "169.254.1.1/30" + vpn_tunnel = google_compute_vpn_tunnel.tunnel2.name +} + +resource "google_compute_router_peer" "router_peer2" { + name = "<%= ctx[:vars]['router_peer_2_name'] %>" + router = google_compute_router.router.name + region = "us-central1" + peer_ip_address = "169.254.1.2" + peer_asn = 64515 + advertised_route_priority = 100 + interface = google_compute_router_interface.router_interface2.name +} + +resource "google_network_connectivity_spoke" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['vpn_tunnel_1_spoke_name'] %>" + location = "us-central1" + description = "A sample spoke with a linked VPN Tunnel" + labels = { + label-one = "value-one" + } + hub = google_network_connectivity_hub.basic_hub.id + linked_vpn_tunnels { + uris = [google_compute_vpn_tunnel.tunnel1.self_link] + site_to_site_data_transfer = true + include_import_ranges = ["ALL_IPV4_RANGES"] + } +} + +resource "google_network_connectivity_spoke" "tunnel2" { + name = "<%= ctx[:vars]['vpn_tunnel_2_spoke_name'] %>" + location = "us-central1" + description = "A sample spoke with a linked VPN Tunnel" + labels = { + label-one = "value-one" + } + hub = google_network_connectivity_hub.basic_hub.id + linked_vpn_tunnels { + uris = [google_compute_vpn_tunnel.tunnel2.self_link] + site_to_site_data_transfer = true + include_import_ranges = ["ALL_IPV4_RANGES"] + } +} diff --git a/mmv1/templates/terraform/examples/router_nat_address_count.tf.erb b/mmv1/templates/terraform/examples/router_nat_address_count.tf.erb new file mode 100644 index 000000000000..6a4178aecb46 --- /dev/null +++ b/mmv1/templates/terraform/examples/router_nat_address_count.tf.erb @@ -0,0 +1,48 @@ +resource "google_compute_network" "net" { + name = "<%= ctx[:vars]['network_name'] %>" +} + +resource "google_compute_subnetwork" "subnet" { + name = "<%= ctx[:vars]['subnet_name'] %>" + network = google_compute_network.net.id + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" +} + +resource "google_compute_router" "router" { + name = "<%= ctx[:vars]['router_name'] %>" + region = google_compute_subnetwork.subnet.region + network = google_compute_network.net.id +} + +resource "google_compute_address" "address" { + count = 3 + name = "<%= ctx[:vars]['address_name'] %>-${count.index}" + region = google_compute_subnetwork.subnet.region + + lifecycle { + create_before_destroy = true + } +} + +resource "google_compute_router_nat_address" "<%= ctx[:primary_resource_id] %>" { + nat_ips = google_compute_address.address.*.self_link + router = google_compute_router.router.name + router_nat = google_compute_router_nat.router_nat.name + region = google_compute_router_nat.router_nat.region +} + +resource "google_compute_router_nat" "router_nat" { + name = "<%= ctx[:vars]['nat_name'] %>" + router = google_compute_router.router.name + region = google_compute_router.router.region + + nat_ip_allocate_option = "MANUAL_ONLY" + initial_nat_ips = [google_compute_address.address[0].self_link] + + source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" + subnetwork { + name = google_compute_subnetwork.subnet.id + source_ip_ranges_to_nat = ["ALL_IP_RANGES"] + } +} diff --git a/mmv1/templates/terraform/iam_policy.go.tmpl b/mmv1/templates/terraform/iam_policy.go.tmpl index 8e58a35c4ad6..726b38c9fe63 100644 --- a/mmv1/templates/terraform/iam_policy.go.tmpl +++ b/mmv1/templates/terraform/iam_policy.go.tmpl @@ -12,10 +12,10 @@ 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. */ -}} -{{/* <% if hc_downstream */}} +{{- if ne $.Compiler "terraformgoogleconversion-codegen" }} // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 - +{{ end }} // ---------------------------------------------------------------------------- // // *** AUTO GENERATED CODE *** Type: MMv1 *** diff --git a/mmv1/templates/terraform/operation.go.tmpl b/mmv1/templates/terraform/operation.go.tmpl index 3a778b8f8e2d..5a3e4211c7a0 100644 --- a/mmv1/templates/terraform/operation.go.tmpl +++ b/mmv1/templates/terraform/operation.go.tmpl @@ -1,7 +1,7 @@ -{{/* TODO rewrite: if hc_downstream */ -}} +{{- if ne $.Compiler "terraformgoogleconversion-codegen" }} // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 - +{{ end }} // ---------------------------------------------------------------------------- // // *** AUTO GENERATED CODE *** Type: MMv1 *** diff --git a/mmv1/templates/terraform/post_create/go/regional_secret_version.go.tmpl b/mmv1/templates/terraform/post_create/go/regional_secret_version.go.tmpl new file mode 100644 index 000000000000..8c97a8ae614a --- /dev/null +++ b/mmv1/templates/terraform/post_create/go/regional_secret_version.go.tmpl @@ -0,0 +1,26 @@ +{{/* + The license inside this block applies to this file + Copyright 2024 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. +*/ -}} +// `name` is autogenerated from the api so needs to be set post-create +name, ok := res["name"] +if !ok { + return fmt.Errorf("Create response didn't contain critical fields. Create may not have succeeded.") +} +if err := d.Set("name", name.(string)); err != nil { + return fmt.Errorf("Error setting name: %s", err) +} +d.SetId(name.(string)) + +_, err = expandSecretManagerRegionalRegionalSecretVersionEnabled(d.Get("enabled"), d, config) +if err != nil { + return err +} diff --git a/mmv1/templates/terraform/post_create/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl b/mmv1/templates/terraform/post_create/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl new file mode 100644 index 000000000000..a32fbbe431d4 --- /dev/null +++ b/mmv1/templates/terraform/post_create/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl @@ -0,0 +1,31 @@ +log.Printf("[DEBUG] Post-create for RegionNetworkFirewallPolicyWithRules %q", d.Id()) + +url, err = tpgresource.ReplaceVarsForId(d, config, "{{"{{"}}ComputeBasePath{{"}}"}}projects/{{"{{"}}project{{"}}"}}/regions/{{"{{"}}region{{"}}"}}/firewallPolicies/{{"{{"}}name{{"}}"}}") +if err != nil { + return err +} + +headers = make(http.Header) +res, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Headers: headers, +}) +if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("ComputeRegionNetworkFirewallPolicyWithRules %q", d.Id())) +} + +if err := d.Set("fingerprint", flattenComputeRegionNetworkFirewallPolicyWithRulesFingerprint(res["fingerprint"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionNetworkFirewallPolicyWithRules: %s", err) +} + +res, err = resourceComputeRegionNetworkFirewallPolicyWithRulesDecoder(d, meta, res) +if err != nil { + return err +} + +log.Printf("[DEBUG] Updating RegionNetworkFirewallPolicyWithRules %q", d.Id()) +return resourceComputeRegionNetworkFirewallPolicyWithRulesUpdate(d, meta) diff --git a/mmv1/templates/terraform/post_create/resource_compute_region_network_firewall_policy_with_rules.go.erb b/mmv1/templates/terraform/post_create/resource_compute_region_network_firewall_policy_with_rules.go.erb new file mode 100644 index 000000000000..c88951d8a031 --- /dev/null +++ b/mmv1/templates/terraform/post_create/resource_compute_region_network_firewall_policy_with_rules.go.erb @@ -0,0 +1,31 @@ +log.Printf("[DEBUG] Post-create for RegionNetworkFirewallPolicyWithRules %q", d.Id()) + +url, err = tpgresource.ReplaceVarsForId(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/firewallPolicies/{{name}}") +if err != nil { + return err +} + +headers = make(http.Header) +res, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Headers: headers, +}) +if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("ComputeRegionNetworkFirewallPolicyWithRules %q", d.Id())) +} + +if err := d.Set("fingerprint", flattenComputeRegionNetworkFirewallPolicyWithRulesFingerprint(res["fingerprint"], d, config)); err != nil { + return fmt.Errorf("Error reading RegionNetworkFirewallPolicyWithRules: %s", err) +} + +res, err = resourceComputeRegionNetworkFirewallPolicyWithRulesDecoder(d, meta, res) +if err != nil { + return err +} + +log.Printf("[DEBUG] Updating RegionNetworkFirewallPolicyWithRules %q", d.Id()) +return resourceComputeRegionNetworkFirewallPolicyWithRulesUpdate(d, meta) diff --git a/mmv1/templates/terraform/pre_create/go/secret_manager_regional_secret_version.go.tmpl b/mmv1/templates/terraform/pre_create/go/secret_manager_regional_secret_version.go.tmpl new file mode 100644 index 000000000000..82e59af6c286 --- /dev/null +++ b/mmv1/templates/terraform/pre_create/go/secret_manager_regional_secret_version.go.tmpl @@ -0,0 +1,29 @@ +{{/* + The license inside this block applies to this file + Copyright 2024 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. +*/ -}} +secret := d.Get("secret").(string) +secretRegex := regexp.MustCompile("projects/(.+)/locations/(.+)/secrets/(.+)$") + +parts := secretRegex.FindStringSubmatch(secret) +if len(parts) != 4 { + return fmt.Errorf("secret does not fit the format `projects/{{"{{"}}project{{"}}"}}/locations/{{"{{"}}location{{"}}"}}/secrets/{{"{{"}}secret{{"}}"}}`") +} + +if err := d.Set("location", parts[2]); err!=nil { + return fmt.Errorf("Error setting location: %s", err) +} + +// Override the url after setting the location +url, err = tpgresource.ReplaceVars(d, config, "{{"{{"}}SecretManagerRegionalBasePath{{"}}"}}{{"{{"}}secret{{"}}"}}:addVersion") +if err != nil { + return err +} diff --git a/mmv1/templates/terraform/pre_delete/compute_router_nat_address_delete_nat_ips_only.go.erb b/mmv1/templates/terraform/pre_delete/compute_router_nat_address_delete_nat_ips_only.go.erb new file mode 100644 index 000000000000..29217dccdd4f --- /dev/null +++ b/mmv1/templates/terraform/pre_delete/compute_router_nat_address_delete_nat_ips_only.go.erb @@ -0,0 +1,5 @@ +// Since RouterNatAddress reopresents only the natIps field, we must make sure we only remove this value and not the entire nat +obj, err = resourceComputeRouterNatAddressDeleteOnlyNatIps(d, meta, obj) +if err != nil { + return err +} diff --git a/mmv1/templates/terraform/pre_delete/go/regional_secret_version_deletion_policy.go.tmpl b/mmv1/templates/terraform/pre_delete/go/regional_secret_version_deletion_policy.go.tmpl new file mode 100644 index 000000000000..63b88d34db56 --- /dev/null +++ b/mmv1/templates/terraform/pre_delete/go/regional_secret_version_deletion_policy.go.tmpl @@ -0,0 +1,22 @@ +{{/* + The license inside this block applies to this file + Copyright 2024 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. +*/ -}} +deletionPolicy := d.Get("deletion_policy"); + +if deletionPolicy == "ABANDON" { + return nil +} else if deletionPolicy == "DISABLE" { + url, err = tpgresource.ReplaceVars(d, config, "{{"{{"}}SecretManagerRegionalBasePath{{"}}"}}{{"{{"}}name{{"}}"}}:disable") + if err != nil { + return err + } +} diff --git a/mmv1/templates/terraform/pre_read/go/secret_manager_regional_secret_version.go.tmpl b/mmv1/templates/terraform/pre_read/go/secret_manager_regional_secret_version.go.tmpl new file mode 100644 index 000000000000..4ad72dad2c6b --- /dev/null +++ b/mmv1/templates/terraform/pre_read/go/secret_manager_regional_secret_version.go.tmpl @@ -0,0 +1,36 @@ +{{/* + The license inside this block applies to this file + Copyright 2024 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. +*/ -}} +secret := d.Get("secret").(string) +secretRegex := regexp.MustCompile("projects/(.+)/locations/(.+)/secrets/(.+)$") + +parts := secretRegex.FindStringSubmatch(secret) +if len(parts) != 4 { + return fmt.Errorf("secret does not fit the format `projects/{{"{{"}}project{{"}}"}}/locations/{{"{{"}}location{{"}}"}}/secrets/{{"{{"}}secret{{"}}"}}`") +} + +if err := d.Set("location", parts[2]); err!=nil { + return fmt.Errorf("Error setting location: %s", err) +} + +// Override the url after setting the location +url, err = tpgresource.ReplaceVars(d, config, "{{"{{"}}SecretManagerRegionalBasePath{{"}}"}}{{"{{"}}name{{"}}"}}") +if err != nil { + return err +} + +// Explicitly set the field to default value if unset +if _, ok := d.GetOkExists("is_secret_data_base64"); !ok { + if err := d.Set("is_secret_data_base64", false); err != nil { + return fmt.Errorf("Error setting is_secret_data_base64: %s", err) + } +} diff --git a/mmv1/templates/terraform/sweeper_file.go.tmpl b/mmv1/templates/terraform/sweeper_file.go.tmpl index f5085436c048..1dce3b881e2f 100644 --- a/mmv1/templates/terraform/sweeper_file.go.tmpl +++ b/mmv1/templates/terraform/sweeper_file.go.tmpl @@ -1,7 +1,7 @@ -{{/* <% if hc_downstream */ -}} +{{- if ne $.Compiler "terraformgoogleconversion-codegen" -}} // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 - +{{ end }} // ---------------------------------------------------------------------------- // // *** AUTO GENERATED CODE *** Type: MMv1 *** diff --git a/mmv1/templates/terraform/update_encoder/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl b/mmv1/templates/terraform/update_encoder/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl new file mode 100644 index 000000000000..f90eb2dfa682 --- /dev/null +++ b/mmv1/templates/terraform/update_encoder/go/resource_compute_region_network_firewall_policy_with_rules.go.tmpl @@ -0,0 +1,15 @@ +config := meta.(*transport_tpg.Config) + +predefinedRulesProp, err := expandComputeRegionNetworkFirewallPolicyWithRulesRule(d.Get("predefined_rules"), d, config) +if err != nil { + return nil, err +} + +rules := obj["rules"].([]interface{}) +obj["rules"] = append(rules, predefinedRulesProp) + +return obj, nil + + + + diff --git a/mmv1/templates/terraform/update_encoder/resource_compute_region_network_firewall_policy_with_rules.go.erb b/mmv1/templates/terraform/update_encoder/resource_compute_region_network_firewall_policy_with_rules.go.erb new file mode 100644 index 000000000000..f90eb2dfa682 --- /dev/null +++ b/mmv1/templates/terraform/update_encoder/resource_compute_region_network_firewall_policy_with_rules.go.erb @@ -0,0 +1,15 @@ +config := meta.(*transport_tpg.Config) + +predefinedRulesProp, err := expandComputeRegionNetworkFirewallPolicyWithRulesRule(d.Get("predefined_rules"), d, config) +if err != nil { + return nil, err +} + +rules := obj["rules"].([]interface{}) +obj["rules"] = append(rules, predefinedRulesProp) + +return obj, nil + + + + diff --git a/mmv1/templates/tgc/resource_converter.go.tmpl b/mmv1/templates/tgc/resource_converter.go.tmpl index 6b27838c72f0..f99fc72656f6 100644 --- a/mmv1/templates/tgc/resource_converter.go.tmpl +++ b/mmv1/templates/tgc/resource_converter.go.tmpl @@ -1,3 +1,17 @@ +{{/* The license inside this block applies to this file + Copyright 2024 Google LLC. All Rights Reserved. + + 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. */ -}} // ---------------------------------------------------------------------------- // // *** AUTO GENERATED CODE *** Type: MMv1 *** diff --git a/mmv1/templates/tgc/resource_converter_iam.go.tmpl b/mmv1/templates/tgc/resource_converter_iam.go.tmpl new file mode 100644 index 000000000000..a73eb330d311 --- /dev/null +++ b/mmv1/templates/tgc/resource_converter_iam.go.tmpl @@ -0,0 +1,145 @@ +{{/* The license inside this block applies to this file + Copyright 2024 Google LLC. All Rights Reserved. + + 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. */ -}} +// ---------------------------------------------------------------------------- +// +// *** 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. +// +// ---------------------------------------------------------------------------- + +{{- $productBackendName := $.CaiProductBackendName $.CaiProductBaseUrl }} +{{- $assetNameTemplate := $.CaiIamAssetNameTemplate $productBackendName }} + +package {{ lower $.ProductMetadata.Name }} + +import ( + "fmt" + + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources/cai" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport" +) + +// Provide a separate asset type constant so we don't have to worry about name conflicts between IAM and non-IAM converter files +const {{ $.ResourceName -}}IAMAssetType string = "{{ $productBackendName }}.googleapis.com/{{$.Name}}" + +func ResourceConverter{{ $.ResourceName -}}IamPolicy() cai.ResourceConverter { + return cai.ResourceConverter{ + AssetType: {{ $.ResourceName -}}IAMAssetType, + Convert: Get{{ $.ResourceName -}}IamPolicyCaiObject, + MergeCreateUpdate: Merge{{ $.ResourceName -}}IamPolicy, + } +} + +func ResourceConverter{{ $.ResourceName -}}IamBinding() cai.ResourceConverter { + return cai.ResourceConverter{ + AssetType: {{ $.ResourceName -}}IAMAssetType, + Convert: Get{{ $.ResourceName -}}IamBindingCaiObject, + FetchFullResource: Fetch{{ $.ResourceName -}}IamPolicy, + MergeCreateUpdate: Merge{{ $.ResourceName -}}IamBinding, + MergeDelete: Merge{{ $.ResourceName -}}IamBindingDelete, + } +} + +func ResourceConverter{{ $.ResourceName -}}IamMember() cai.ResourceConverter { + return cai.ResourceConverter{ + AssetType: {{ $.ResourceName -}}IAMAssetType, + Convert: Get{{ $.ResourceName -}}IamMemberCaiObject, + FetchFullResource: Fetch{{ $.ResourceName -}}IamPolicy, + MergeCreateUpdate: Merge{{ $.ResourceName -}}IamMember, + MergeDelete: Merge{{ $.ResourceName -}}IamMemberDelete, + } +} + +func Get{{ $.ResourceName -}}IamPolicyCaiObject(d tpgresource.TerraformResourceData, config *transport_tpg.Config) ([]cai.Asset, error) { + return new{{ $.ResourceName -}}IamAsset(d, config, cai.ExpandIamPolicyBindings) +} + +func Get{{ $.ResourceName -}}IamBindingCaiObject(d tpgresource.TerraformResourceData, config *transport_tpg.Config) ([]cai.Asset, error) { + return new{{ $.ResourceName -}}IamAsset(d, config, cai.ExpandIamRoleBindings) +} + +func Get{{ $.ResourceName -}}IamMemberCaiObject(d tpgresource.TerraformResourceData, config *transport_tpg.Config) ([]cai.Asset, error) { + return new{{ $.ResourceName -}}IamAsset(d, config, cai.ExpandIamMemberBindings) +} + +func Merge{{ $.ResourceName -}}IamPolicy(existing, incoming cai.Asset) cai.Asset { + existing.IAMPolicy = incoming.IAMPolicy + return existing +} + +func Merge{{ $.ResourceName -}}IamBinding(existing, incoming cai.Asset) cai.Asset { + return cai.MergeIamAssets(existing, incoming, cai.MergeAuthoritativeBindings) +} + +func Merge{{ $.ResourceName -}}IamBindingDelete(existing, incoming cai.Asset) cai.Asset { + return cai.MergeDeleteIamAssets(existing, incoming, cai.MergeDeleteAuthoritativeBindings) +} + +func Merge{{ $.ResourceName -}}IamMember(existing, incoming cai.Asset) cai.Asset { + return cai.MergeIamAssets(existing, incoming, cai.MergeAdditiveBindings) +} + +func Merge{{ $.ResourceName -}}IamMemberDelete(existing, incoming cai.Asset) cai.Asset { + return cai.MergeDeleteIamAssets(existing, incoming, cai.MergeDeleteAdditiveBindings) +} + +func new{{ $.ResourceName -}}IamAsset( + d tpgresource.TerraformResourceData, + config *transport_tpg.Config, + expandBindings func(d tpgresource.TerraformResourceData) ([]cai.IAMBinding, error), +) ([]cai.Asset, error) { + bindings, err := expandBindings(d) + if err != nil { + return []cai.Asset{}, fmt.Errorf("expanding bindings: %v", err) + } + + name, err := cai.AssetName(d, config, "{{ $assetNameTemplate }}") + if err != nil { + return []cai.Asset{}, err + } + + return []cai.Asset{{"{{"}} + Name: name, + Type: {{ $.ResourceName -}}IAMAssetType, + IAMPolicy: &cai.IAMPolicy{ + Bindings: bindings, + }, + {{"}}"}}, nil +} + +func Fetch{{ $.ResourceName -}}IamPolicy(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (cai.Asset, error) { + // Check if the identity field returns a value + {{- range $param := $.CaiIamResourceParams }} + if _, ok := d.GetOk("{{ $param }}"); !ok { + return cai.Asset{}, cai.ErrEmptyIdentityField + } + {{- end}} + + return cai.FetchIamPolicy( + {{ $.ResourceName -}}IamUpdaterProducer, + d, + config, + "{{ $assetNameTemplate }}", + {{ $.ResourceName -}}IAMAssetType, + ) +} diff --git a/mmv1/templates/tgc/resource_converters.go.tmpl b/mmv1/templates/tgc/resource_converters.go.tmpl new file mode 100644 index 000000000000..8c8e5baf6beb --- /dev/null +++ b/mmv1/templates/tgc/resource_converters.go.tmpl @@ -0,0 +1,192 @@ +{{/* The license inside this block applies to this file + Copyright 2024 Google LLC. All Rights Reserved. + + 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. */ -}} +// ---------------------------------------------------------------------------- +// +// *** 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 ( + "sort" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources/services/compute" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources/services/resourcemanager" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources/services/spanner" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" +) + + +// ResourceConverter returns a map of terraform resource types (i.e. `google_project`) +// to a slice of ResourceConverters. +// +// Modelling of relationships: +// terraform resources to CAI assets as []cai.ResourceConverter: +// 1:1 = [ResourceConverter{Convert: convertAbc}] (len=1) +// 1:N = [ResourceConverter{Convert: convertAbc}, ...] (len=N) +// N:1 = [ResourceConverter{Convert: convertAbc, merge: mergeAbc}] (len=1) +func ResourceConverters() map[string][]cai.ResourceConverter { + return map[string][]cai.ResourceConverter{ + "google_artifact_registry_repository": {artifactregistry.ResourceConverterArtifactRegistryRepository()}, + "google_app_engine_application": {resourceConverterAppEngineApplication()}, + "google_alloydb_cluster": {alloydb.ResourceConverterAlloydbCluster()}, + "google_alloydb_instance": {alloydb.ResourceConverterAlloydbInstance()}, + "google_apikeys_key": {resourceConverterApikeysKey()}, + "google_compute_address": {compute.ResourceConverterComputeAddress()}, + "google_compute_autoscaler": {compute.ResourceConverterComputeAutoscaler()}, + "google_compute_firewall": {compute.ResourceConverterComputeFirewall()}, + "google_compute_disk": {compute.ResourceConverterComputeDisk()}, + "google_compute_forwarding_rule": {compute.ResourceConverterComputeForwardingRule()}, + "google_gke_hub_membership": {gkehub.ResourceConverterGKEHubMembership()}, + "google_compute_global_address": {compute.ResourceConverterComputeGlobalAddress()}, + "google_compute_global_forwarding_rule": {compute.ResourceConverterComputeGlobalForwardingRule()}, + "google_compute_health_check": {compute.ResourceConverterComputeHealthCheck()}, + "google_compute_instance": {compute.ResourceConverterComputeInstance()}, + "google_compute_instance_group": {resourceConverterComputeInstanceGroup()}, + "google_compute_network": {compute.ResourceConverterComputeNetwork()}, + "google_compute_node_template": {compute.ResourceConverterComputeNodeTemplate()}, + "google_compute_route": {compute.ResourceConverterComputeRoute()}, + "google_compute_router": {compute.ResourceConverterComputeRouter()}, + "google_compute_vpn_tunnel": {compute.ResourceConverterComputeVpnTunnel()}, + "google_compute_resource_policy": {compute.ResourceConverterComputeResourcePolicy()}, + "google_compute_security_policy": {resourceConverterComputeSecurityPolicy()}, + "google_compute_snapshot": {compute.ResourceConverterComputeSnapshot()}, + "google_compute_subnetwork": {compute.ResourceConverterComputeSubnetwork()}, + "google_compute_ssl_policy": {compute.ResourceConverterComputeSslPolicy()}, + "google_compute_ssl_certificate": {compute.ResourceConverterComputeSslCertificate()}, + "google_compute_url_map": {compute.ResourceConverterComputeUrlMap()}, + "google_compute_target_http_proxy": {compute.ResourceConverterComputeTargetHttpProxy()}, + "google_compute_target_https_proxy": {compute.ResourceConverterComputeTargetHttpsProxy()}, + "google_compute_target_ssl_proxy": {compute.ResourceConverterComputeTargetSslProxy()}, + "google_compute_target_pool": {resourceConverterComputeTargetPool()}, + "google_composer_environment": {resourceConverterComposerEnvironment()}, + "google_compute_region_commitment": {resourceConverterCommitment()}, + "google_dataflow_job": {resourceDataflowJob()}, + "google_dataproc_autoscaling_policy": {dataproc.ResourceConverterDataprocAutoscalingPolicy()}, + "google_dataproc_cluster": {resourceConverterDataprocCluster()}, + "google_dns_managed_zone": {dns.ResourceConverterDNSManagedZone()}, + "google_dns_policy": {dns.ResourceConverterDNSPolicy()}, + "google_kms_key_ring_import_job": {kms.ResourceConverterKMSKeyRingImportJob()}, + "google_gke_hub_feature": {gkehub2.ResourceConverterGKEHub2Feature()}, + "google_storage_bucket": {resourceConverterStorageBucket()}, + "google_sql_database_instance": {resourceConverterSQLDatabaseInstance()}, + "google_sql_database": {sql.ResourceConverterSQLDatabase()}, + "google_container_cluster": {resourceConverterContainerCluster()}, + "google_container_node_pool": {resourceConverterContainerNodePool()}, + "google_bigquery_dataset": {bigquery.ResourceConverterBigQueryDataset()}, + "google_bigquery_dataset_iam_policy": {bigquery.ResourceConverterBigqueryDatasetIamPolicy()}, + "google_bigquery_dataset_iam_binding": {bigquery.ResourceConverterBigqueryDatasetIamBinding()}, + "google_bigquery_dataset_iam_member": {bigquery.ResourceConverterBigqueryDatasetIamMember()}, + "google_bigquery_table": {resourceConverterBigQueryTable()}, + "google_datastream_connection_profile": {datastream.ResourceConverterDatastreamConnectionProfile()}, + "google_datastream_private_connection": {datastream.ResourceConverterDatastreamPrivateConnection()}, + "google_datastream_stream": {datastream.ResourceConverterDatastreamStream()}, + "google_firebase_project": {resourceConverterFirebaseProject()}, + "google_org_policy_policy": {resourceConverterOrgPolicyPolicy()}, + "google_redis_instance": {redis.ResourceConverterRedisInstance()}, + "google_spanner_database": {spanner.ResourceConverterSpannerDatabase()}, + "google_spanner_database_iam_policy": {spanner.ResourceConverterSpannerDatabaseIamPolicy()}, + "google_spanner_database_iam_binding": {spanner.ResourceConverterSpannerDatabaseIamBinding()}, + "google_spanner_database_iam_member": {spanner.ResourceConverterSpannerDatabaseIamMember()}, + "google_spanner_instance": {spanner.ResourceConverterSpannerInstance()}, + "google_spanner_instance_iam_policy": {spanner.ResourceConverterSpannerInstanceIamPolicy()}, + "google_spanner_instance_iam_binding": {spanner.ResourceConverterSpannerInstanceIamBinding()}, + "google_spanner_instance_iam_member": {spanner.ResourceConverterSpannerInstanceIamMember()}, + "google_project_service": {resourceConverterServiceUsage()}, + "google_secret_manager_secret_version": {secretmanager.ResourceConverterSecretManagerSecretVersion()}, + "google_pubsub_lite_reservation": {pubsublite.ResourceConverterPubsubLiteReservation()}, + "google_pubsub_lite_subscription": {pubsublite.ResourceConverterPubsubLiteSubscription()}, + "google_pubsub_lite_topic": {pubsublite.ResourceConverterPubsubLiteTopic()}, + "google_pubsub_schema": {pubsub.ResourceConverterPubsubSchema()}, + "google_pubsub_subscription": {pubsub.ResourceConverterPubsubSubscription()}, + "google_pubsub_subscription_iam_policy": {pubsub.ResourceConverterPubsubSubscriptionIamPolicy()}, + "google_pubsub_subscription_iam_binding": {pubsub.ResourceConverterPubsubSubscriptionIamBinding()}, + "google_pubsub_subscription_iam_member": {pubsub.ResourceConverterPubsubSubscriptionIamMember()}, + "google_storage_bucket_iam_policy": {resourceConverterStorageBucketIamPolicy()}, + "google_storage_bucket_iam_binding": {resourceConverterStorageBucketIamBinding()}, + "google_storage_bucket_iam_member": {resourceConverterStorageBucketIamMember()}, + "google_compute_node_group": {compute.ResourceConverterComputeNodeGroup()}, + "google_logging_folder_bucket_config": {resourceConverterLogFolderBucket()}, + "google_app_engine_standard_app_version": {resourceAppEngineStandardAppVersion()}, + "google_logging_organization_bucket_config": {resourceConverterLogOrganizationBucket()}, + "google_logging_project_bucket_config": {resourceConverterLogProjectBucket()}, + "google_logging_billing_account_bucket_config": {resourceConverterLogBillingAccountBucket()}, + "google_cloud_tasks_queue": {cloudtasks.ResourceConverterCloudTasksQueue()}, + "google_pubsub_topic": {pubsub.ResourceConverterPubsubTopic()}, + "google_kms_crypto_key": {kms.ResourceConverterKMSCryptoKey()}, + "google_kms_key_ring": {kms.ResourceConverterKMSKeyRing()}, + "google_filestore_instance": {filestore.ResourceConverterFilestoreInstance()}, + "google_access_context_manager_service_perimeter": {accesscontextmanager.ResourceConverterAccessContextManagerServicePerimeter()}, + "google_access_context_manager_access_policy": {accesscontextmanager.ResourceConverterAccessContextManagerAccessPolicy()}, + "google_cloud_run_service": {cloudrun.ResourceConverterCloudRunService()}, + "google_cloud_run_domain_mapping": {cloudrun.ResourceConverterCloudRunDomainMapping()}, + "google_cloud_run_v2_job": {cloudrunv2.ResourceConverterCloudRunV2Job()}, + "google_cloudfunctions_function": {resourceConverterCloudFunctionsCloudFunction()}, + "google_monitoring_notification_channel": {monitoring.ResourceConverterMonitoringNotificationChannel()}, + "google_monitoring_alert_policy": {monitoring.ResourceConverterMonitoringAlertPolicy()}, + "google_vertex_ai_dataset": {vertexai.ResourceConverterVertexAIDataset()}, + {{- range $object := $.ResourcesForVersion }} + {{- if $object.ResourceName }} + "{{ $object.TerraformName }}": {{ $object.ResourceName }}(), + {{- end }} + {{- if $object.IamClassName }} + "{{ $object.TerraformName }}_iam_binding": tpgiamresource.ResourceIamBinding({{ $object.IamClassName }}IamSchema, {{ $object.IamClassName }}IamUpdaterProducer, {{ $object.IamClassName }}IdParseFunc), + "{{ $object.TerraformName }}_iam_member": tpgiamresource.ResourceIamMember({{ $object.IamClassName }}IamSchema, {{ $object.IamClassName }}IamUpdaterProducer, {{ $object.IamClassName }}IdParseFunc), + "{{ $object.TerraformName }}_iam_policy": tpgiamresource.ResourceIamPolicy({{ $object.IamClassName }}IamSchema, {{ $object.IamClassName }}IamUpdaterProducer, {{ $object.IamClassName }}IdParseFunc), + {{- end }} + {{- end }} + "google_project": { + resourceConverterProject(), + resourceConverterProjectBillingInfo(), + }, + "google_bigtable_instance": { + resourceConverterBigtableInstance(), + resourceConverterBigtableCluster(), + }, + "google_organization_iam_policy": {resourcemanager.ResourceConverterOrganizationIamPolicy()}, + "google_organization_iam_binding": {resourcemanager.ResourceConverterOrganizationIamBinding()}, + "google_organization_iam_member": {resourcemanager.ResourceConverterOrganizationIamMember()}, + "google_organization_policy": {resourceConverterOrganizationPolicy()}, + "google_project_organization_policy": {resourceConverterProjectOrgPolicy()}, + "google_folder": {resourceConverterFolder()}, + "google_folder_iam_policy": {resourcemanager.ResourceConverterFolderIamPolicy()}, + "google_folder_iam_binding": {resourcemanager.ResourceConverterFolderIamBinding()}, + "google_folder_iam_member": {resourcemanager.ResourceConverterFolderIamMember()}, + "google_folder_organization_policy": {resourceConverterFolderOrgPolicy()}, + "google_kms_crypto_key_iam_policy": {resourceConverterKmsCryptoKeyIamPolicy()}, + "google_kms_crypto_key_iam_binding": {resourceConverterKmsCryptoKeyIamBinding()}, + "google_kms_crypto_key_iam_member": {resourceConverterKmsCryptoKeyIamMember()}, + "google_kms_key_ring_iam_policy": {resourceConverterKmsKeyRingIamPolicy()}, + "google_kms_key_ring_iam_binding": {resourceConverterKmsKeyRingIamBinding()}, + "google_kms_key_ring_iam_member": {resourceConverterKmsKeyRingIamMember()}, + "google_project_iam_policy": {resourcemanager.ResourceConverterProjectIamPolicy()}, + "google_project_iam_binding": {resourcemanager.ResourceConverterProjectIamBinding()}, + "google_project_iam_member": {resourcemanager.ResourceConverterProjectIamMember()}, + "google_project_iam_custom_role": {resourceConverterProjectIAMCustomRole()}, + "google_organization_iam_custom_role": {resourceConverterOrganizationIAMCustomRole()}, + "google_vpc_access_connector": {vpcaccess.ResourceConverterVPCAccessConnector()}, + "google_logging_metric": {logging.ResourceConverterLoggingMetric()}, + "google_service_account": {resourceConverterServiceAccount()}, + "google_service_account_key": {resourceConverterServiceAccountKey()}, + + } +} diff --git a/mmv1/third_party/terraform/provider/go/provider_mmv1_resources.go.tmpl b/mmv1/third_party/terraform/provider/go/provider_mmv1_resources.go.tmpl index 51881c4c2df0..a8921bde2496 100644 --- a/mmv1/third_party/terraform/provider/go/provider_mmv1_resources.go.tmpl +++ b/mmv1/third_party/terraform/provider/go/provider_mmv1_resources.go.tmpl @@ -179,7 +179,10 @@ var handwrittenDatasources = map[string]*schema.Resource{ "google_runtimeconfig_config": runtimeconfig.DataSourceGoogleRuntimeconfigConfig(), "google_runtimeconfig_variable": runtimeconfig.DataSourceGoogleRuntimeconfigVariable(), {{- end }} + "google_secret_manager_regional_secret_version_access": secretmanagerregional.DataSourceSecretManagerRegionalRegionalSecretVersionAccess(), + "google_secret_manager_regional_secret_version": secretmanagerregional.DataSourceSecretManagerRegionalRegionalSecretVersion(), "google_secret_manager_regional_secret": secretmanagerregional.DataSourceSecretManagerRegionalRegionalSecret(), + "google_secret_manager_regional_secrets": secretmanagerregional.DataSourceSecretManagerRegionalRegionalSecrets(), "google_secret_manager_secret": secretmanager.DataSourceSecretManagerSecret(), "google_secret_manager_secrets": secretmanager.DataSourceSecretManagerSecrets(), "google_secret_manager_secret_version": secretmanager.DataSourceSecretManagerSecretVersion(), diff --git a/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb b/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb index 847cc147f54d..888f6d2dc553 100644 --- a/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb +++ b/mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb @@ -180,8 +180,10 @@ var handwrittenDatasources = map[string]*schema.Resource{ "google_runtimeconfig_config": runtimeconfig.DataSourceGoogleRuntimeconfigConfig(), "google_runtimeconfig_variable": runtimeconfig.DataSourceGoogleRuntimeconfigVariable(), <% end -%> + "google_secret_manager_regional_secret_version_access": secretmanagerregional.DataSourceSecretManagerRegionalRegionalSecretVersionAccess(), "google_secret_manager_regional_secret_version": secretmanagerregional.DataSourceSecretManagerRegionalRegionalSecretVersion(), "google_secret_manager_regional_secret": secretmanagerregional.DataSourceSecretManagerRegionalRegionalSecret(), + "google_secret_manager_regional_secrets": secretmanagerregional.DataSourceSecretManagerRegionalRegionalSecrets(), "google_secret_manager_secret": secretmanager.DataSourceSecretManagerSecret(), "google_secret_manager_secrets": secretmanager.DataSourceSecretManagerSecrets(), "google_secret_manager_secret_version": secretmanager.DataSourceSecretManagerSecretVersion(), diff --git a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table.go b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table.go index 1a991ee0eb08..5a3f99a8c3b6 100644 --- a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table.go +++ b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table.go @@ -890,6 +890,53 @@ func ResourceBigQueryTable() *schema.Resource { }, }, + // BiglakeConfiguration [Optional] Specifies the configuration of a BigLake managed table. + "biglake_configuration": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + ForceNew: true, + Description: "Specifies the configuration of a BigLake managed table.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + // ConnectionId: [Required] The connection specifying the credentials to be used to read + // and write to external storage, such as Cloud Storage. The connection_id can have the + // form "<project\_id>.<location\_id>.<connection\_id>" or + // "projects/<project\_id>/locations/<location\_id>/connections/<connection\_id>". + "connection_id": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: bigQueryTableConnectionIdSuppress, + ForceNew: true, + Description: `The connection specifying the credentials to be used to read and write to external storage, such as Cloud Storage. The connection_id can have the form "<project\_id>.<location\_id>.<connection\_id>" or "projects/<project\_id>/locations/<location\_id>/connections/<connection\_id>".`, + }, + // StorageUri: [Required] The fully qualified location prefix of the external folder where + // table data is stored. The '*' wildcard character is not allowed. + // The URI should be in the format "gs://bucket/path_to_table/" + "storage_uri": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The fully qualified location prefix of the external folder where table data is stored. The '*' wildcard character is not allowed. The URI should be in the format "gs://bucket/path_to_table/"`, + }, + // FileFormat: [Required] The file format the data is stored in. + "file_format": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The file format the data is stored in.", + }, + // TableFormat: [Required] + "table_format": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The table format the metadata only snapshots are stored in.", + }, + }, + }, + }, + // FriendlyName: [Optional] A descriptive name for this table. "friendly_name": { Type: schema.TypeString, @@ -1848,6 +1895,11 @@ func resourceBigQueryTableUpdate(d *schema.ResourceData, meta interface{}) error return err } + if table.ExternalDataConfiguration != nil && table.ExternalDataConfiguration.Schema != nil { + log.Printf("[INFO] Removing ExternalDataConfiguration.Schema when updating BigQuery table %s", d.Id()) + table.ExternalDataConfiguration.Schema = nil + } + log.Printf("[INFO] Updating BigQuery table: %s", d.Id()) project, err := tpgresource.GetProject(d, config) diff --git a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go index 1f2edf51cf90..3a4f990d6773 100644 --- a/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go +++ b/mmv1/third_party/terraform/services/bigquery/resource_bigquery_table_test.go @@ -228,7 +228,7 @@ func TestAccBigQueryTable_HivePartitioning(t *testing.T) { }) } -func TestAccBigQueryTable_HivePartitioningCustomSchema(t *testing.T) { +func TestAccBigQueryTable_HivePartitioningCustomSchema_update(t *testing.T) { t.Parallel() bucketName := acctest.TestBucketName(t) resourceName := "google_bigquery_table.test" @@ -241,13 +241,22 @@ func TestAccBigQueryTable_HivePartitioningCustomSchema(t *testing.T) { CheckDestroy: testAccCheckBigQueryTableDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccBigQueryTableHivePartitioningCustomSchema(bucketName, datasetID, tableID), + Config: testAccBigQueryTableHivePartitioningCustomSchema(bucketName, datasetID, tableID, "old-label"), }, { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"external_data_configuration.0.schema", "deletion_protection"}, + ImportStateVerifyIgnore: []string{"external_data_configuration.0.schema", "labels", "deletion_protection"}, + }, + { + Config: testAccBigQueryTableHivePartitioningCustomSchema(bucketName, datasetID, tableID, "new-label"), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"external_data_configuration.0.schema", "labels", "deletion_protection"}, }, }, }) @@ -279,6 +288,68 @@ func TestAccBigQueryTable_AvroPartitioning(t *testing.T) { }) } +func TestAccBigQueryBigLakeManagedTable(t *testing.T) { + t.Parallel() + bucketName := acctest.TestBucketName(t) + connectionID := fmt.Sprintf("tf_test_%s", acctest.RandString(t, 10)) + + datasetID := fmt.Sprintf("tf_test_%s", acctest.RandString(t, 10)) + tableID := fmt.Sprintf("tf_test_%s", acctest.RandString(t, 10)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckBigQueryTableDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccBigLakeManagedTable(bucketName, connectionID, datasetID, tableID, TEST_SIMPLE_CSV_SCHEMA), + }, + }, + }) +} + +func testAccBigLakeManagedTable(bucketName, connectionID, datasetID, tableID, schema string) string { + return fmt.Sprintf(` + data "google_project" "project" {} + resource "google_storage_bucket" "test" { + name = "%s" + location = "US" + force_destroy = true + uniform_bucket_level_access = true + } + resource "google_bigquery_connection" "test" { + connection_id = "%s" + location = "US" + cloud_resource {} + } + resource "google_project_iam_member" "test" { + role = "roles/storage.objectViewer" + project = data.google_project.project.id + member = "serviceAccount:${google_bigquery_connection.test.cloud_resource[0].service_account_id}" + } + resource "google_bigquery_dataset" "test" { + dataset_id = "%s" + } + resource "google_bigquery_table" "test" { + deletion_protection = false + table_id = "%s" + dataset_id = google_bigquery_dataset.test.dataset_id + biglake_configuration { + connection_id = google_bigquery_connection.test.name + storage_uri = "gs://${google_storage_bucket.test.name}/data/" + file_format = "PARQUET" + table_format = "ICEBERG" + } + + schema = jsonencode(%s) + + depends_on = [ + google_project_iam_member.test + ] + } + `, bucketName, connectionID, datasetID, tableID, schema) +} + func TestAccBigQueryExternalDataTable_json(t *testing.T) { t.Parallel() bucketName := acctest.TestBucketName(t) @@ -2067,7 +2138,7 @@ resource "google_bigquery_table" "test" { `, bucketName, datasetID, tableID) } -func testAccBigQueryTableHivePartitioningCustomSchema(bucketName, datasetID, tableID string) string { +func testAccBigQueryTableHivePartitioningCustomSchema(bucketName, datasetID, tableID, tableLabel string) string { return fmt.Sprintf(` resource "google_storage_bucket" "test" { name = "%s" @@ -2090,6 +2161,10 @@ resource "google_bigquery_table" "test" { table_id = "%s" dataset_id = google_bigquery_dataset.test.dataset_id + labels = { + label = "%s" + } + external_data_configuration { source_format = "NEWLINE_DELIMITED_JSON" autodetect = false @@ -2116,7 +2191,7 @@ EOH } depends_on = ["google_storage_bucket_object.test"] } -`, bucketName, datasetID, tableID) +`, bucketName, datasetID, tableID, tableLabel) } func testAccBigQueryTableAvroPartitioning(bucketName, avroFilePath, datasetID, tableID string) string { diff --git a/mmv1/third_party/terraform/services/cloudrun/go/resource_cloud_run_service_test.go.tmpl b/mmv1/third_party/terraform/services/cloudrun/go/resource_cloud_run_service_test.go.tmpl index fb6619b5d5ed..0e8d0b38029d 100644 --- a/mmv1/third_party/terraform/services/cloudrun/go/resource_cloud_run_service_test.go.tmpl +++ b/mmv1/third_party/terraform/services/cloudrun/go/resource_cloud_run_service_test.go.tmpl @@ -1483,3 +1483,121 @@ resource "google_cloud_run_service" "default" { `, name, project) } {{- end }} + +{{ if ne $.TargetVersionName `ga` -}} +func TestAccCloudRunService_resourcesRequirements(t *testing.T) { + t.Parallel() + + project := envvar.GetTestProjectFromEnv() + name := "tftest-cloudrun-" + acctest.RandString(t, 6) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudRunV2Service_cloudrunServiceWithoutGpu(name, project), + }, + { + ResourceName: "google_cloud_run_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "metadata.0.annotations", "metadata.0.labels", "metadata.0.terraform_labels", "status.0.conditions"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunServiceWithGpu(name, project), + }, + { + ResourceName: "google_cloud_run_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "metadata.0.annotations", "metadata.0.labels", "metadata.0.terraform_labels", "status.0.conditions"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunServiceWithoutGpu(name, project), + }, + { + ResourceName: "google_cloud_run_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "metadata.0.annotations", "metadata.0.labels", "metadata.0.terraform_labels", "status.0.conditions"}, + }, + }, + }) +} + +func testAccCloudRunV2Service_cloudrunServiceWithoutGpu(name, project string) string { + return fmt.Sprintf(` +resource "google_cloud_run_service" "default" { + provider = google-beta + name = "%s" + location = "us-central1" + + metadata { + namespace = "%s" + } + + template { + metadata { + annotations = { + "autoscaling.knative.dev/maxScale": "1" + "run.googleapis.com/cpu-throttling": "false" + } + } + spec { + containers { + image = "gcr.io/cloudrun/hello" + resources { + limits = { + "cpu" = "4" + "memory" = "16Gi" + } + } + } + } + } +} +`, name, project) +} + +func testAccCloudRunV2Service_cloudrunServiceWithGpu(name, project string) string { + return fmt.Sprintf(` +resource "google_cloud_run_service" "default" { + provider = google-beta + name = "%s" + location = "us-central1" + + metadata { + namespace = "%s" + annotations = { + "run.googleapis.com/launch-stage" = "BETA" + } + } + + template { + metadata { + annotations = { + "autoscaling.knative.dev/maxScale": "1" + "run.googleapis.com/cpu-throttling": "false" + } + } + spec { + containers { + image = "gcr.io/cloudrun/hello" + resources { + limits = { + "cpu" = "4" + "memory" = "16Gi" + "nvidia.com/gpu" = "1" + } + } + } + node_selector = { + "run.googleapis.com/accelerator" = "nvidia-l4" + } + } + } +} +`, name, project) +} +{{- end }} diff --git a/mmv1/third_party/terraform/services/cloudrun/resource_cloud_run_service_test.go.erb b/mmv1/third_party/terraform/services/cloudrun/resource_cloud_run_service_test.go.erb index dd99e80cb7db..0bf8003f33c9 100644 --- a/mmv1/third_party/terraform/services/cloudrun/resource_cloud_run_service_test.go.erb +++ b/mmv1/third_party/terraform/services/cloudrun/resource_cloud_run_service_test.go.erb @@ -1484,3 +1484,121 @@ resource "google_cloud_run_service" "default" { `, name, project) } <% end -%> + +<% unless version == 'ga' -%> +func TestAccCloudRunService_resourcesRequirements(t *testing.T) { + t.Parallel() + + project := envvar.GetTestProjectFromEnv() + name := "tftest-cloudrun-" + acctest.RandString(t, 6) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudRunV2Service_cloudrunServiceWithoutGpu(name, project), + }, + { + ResourceName: "google_cloud_run_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "metadata.0.annotations", "metadata.0.labels", "metadata.0.terraform_labels", "status.0.conditions"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunServiceWithGpu(name, project), + }, + { + ResourceName: "google_cloud_run_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "metadata.0.annotations", "metadata.0.labels", "metadata.0.terraform_labels", "status.0.conditions"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunServiceWithoutGpu(name, project), + }, + { + ResourceName: "google_cloud_run_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "metadata.0.annotations", "metadata.0.labels", "metadata.0.terraform_labels", "status.0.conditions"}, + }, + }, + }) +} + +func testAccCloudRunV2Service_cloudrunServiceWithoutGpu(name, project string) string { + return fmt.Sprintf(` +resource "google_cloud_run_service" "default" { + provider = google-beta + name = "%s" + location = "us-central1" + + metadata { + namespace = "%s" + } + + template { + metadata { + annotations = { + "autoscaling.knative.dev/maxScale": "1" + "run.googleapis.com/cpu-throttling": "false" + } + } + spec { + containers { + image = "gcr.io/cloudrun/hello" + resources { + limits = { + "cpu" = "4" + "memory" = "16Gi" + } + } + } + } + } +} +`, name, project) +} + +func testAccCloudRunV2Service_cloudrunServiceWithGpu(name, project string) string { + return fmt.Sprintf(` +resource "google_cloud_run_service" "default" { + provider = google-beta + name = "%s" + location = "us-central1" + + metadata { + namespace = "%s" + annotations = { + "run.googleapis.com/launch-stage" = "BETA" + } + } + + template { + metadata { + annotations = { + "autoscaling.knative.dev/maxScale": "1" + "run.googleapis.com/cpu-throttling": "false" + } + } + spec { + containers { + image = "gcr.io/cloudrun/hello" + resources { + limits = { + "cpu" = "4" + "memory" = "16Gi" + "nvidia.com/gpu" = "1" + } + } + } + node_selector = { + "run.googleapis.com/accelerator" = "nvidia-l4" + } + } + } +} +`, name, project) +} +<% end -%> diff --git a/mmv1/third_party/terraform/services/cloudrunv2/go/resource_cloud_run_v2_service_test.go.tmpl b/mmv1/third_party/terraform/services/cloudrunv2/go/resource_cloud_run_v2_service_test.go.tmpl index 96026c217727..f44bc016460f 100644 --- a/mmv1/third_party/terraform/services/cloudrunv2/go/resource_cloud_run_v2_service_test.go.tmpl +++ b/mmv1/third_party/terraform/services/cloudrunv2/go/resource_cloud_run_v2_service_test.go.tmpl @@ -1194,3 +1194,122 @@ resource "google_network_services_mesh" "new_mesh" { `, context) } {{- end }} + +{{ if ne $.TargetVersionName `ga` -}} +func TestAccCloudRunV2Service_cloudrunv2ServiceWithResourcesRequirements(t *testing.T) { + t.Parallel() + context := map[string]interface{} { + "random_suffix" : acctest.RandString(t, 10), + } + acctest.VcrTest(t, resource.TestCase { + PreCheck: func() { acctest.AccTestPreCheck(t)}, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceWithoutGpu(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location", "annotations", "labels", "terraform_labels", "launch_stage", "deletion_protection"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceWithGpu(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location", "annotations", "labels", "terraform_labels", "launch_stage", "deletion_protection"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceWithoutGpu(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location", "annotations", "labels", "terraform_labels", "launch_stage", "deletion_protection"}, + }, + }, + }) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceWithoutGpu(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + description = "description creating" + location = "us-central1" + deletion_protection = false + launch_stage = "GA" + annotations = { + generated-by = "magic-modules" + } + ingress = "INGRESS_TRAFFIC_ALL" + labels = { + label-1 = "value-1" + } + client = "client-1" + client_version = "client-version-1" + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + resources { + limits = { + "cpu" = "4" + "memory" = "16Gi" + } + startup_cpu_boost = true + } + } + scaling { + max_instance_count = 1 + } + } +} +`, context) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceWithGpu(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + description = "description creating" + location = "us-central1" + deletion_protection = false + launch_stage = "BETA" + annotations = { + generated-by = "magic-modules" + } + ingress = "INGRESS_TRAFFIC_ALL" + labels = { + label-1 = "value-1" + } + client = "client-1" + client_version = "client-version-1" + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + resources { + limits = { + "cpu" = "4" + "memory" = "16Gi" + "nvidia.com/gpu" = "1" + } + startup_cpu_boost = true + } + } + node_selector { + accelerator = "nvidia-l4" + } + scaling { + max_instance_count = 1 + } + } +} +`, context) +} +{{- end }} diff --git a/mmv1/third_party/terraform/services/cloudrunv2/resource_cloud_run_v2_service_test.go.erb b/mmv1/third_party/terraform/services/cloudrunv2/resource_cloud_run_v2_service_test.go.erb index 3e5cce0ddfe6..df1791526e4c 100644 --- a/mmv1/third_party/terraform/services/cloudrunv2/resource_cloud_run_v2_service_test.go.erb +++ b/mmv1/third_party/terraform/services/cloudrunv2/resource_cloud_run_v2_service_test.go.erb @@ -1195,3 +1195,122 @@ resource "google_network_services_mesh" "new_mesh" { `, context) } <% end -%> + +<% unless version == 'ga' -%> +func TestAccCloudRunV2Service_cloudrunv2ServiceWithResourcesRequirements(t *testing.T) { + t.Parallel() + context := map[string]interface{} { + "random_suffix" : acctest.RandString(t, 10), + } + acctest.VcrTest(t, resource.TestCase { + PreCheck: func() { acctest.AccTestPreCheck(t)}, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckCloudRunV2ServiceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceWithoutGpu(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location", "annotations", "labels", "terraform_labels", "launch_stage", "deletion_protection"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceWithGpu(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location", "annotations", "labels", "terraform_labels", "launch_stage", "deletion_protection"}, + }, + { + Config: testAccCloudRunV2Service_cloudrunv2ServiceWithoutGpu(context), + }, + { + ResourceName: "google_cloud_run_v2_service.default", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "location", "annotations", "labels", "terraform_labels", "launch_stage", "deletion_protection"}, + }, + }, + }) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceWithoutGpu(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + description = "description creating" + location = "us-central1" + deletion_protection = false + launch_stage = "GA" + annotations = { + generated-by = "magic-modules" + } + ingress = "INGRESS_TRAFFIC_ALL" + labels = { + label-1 = "value-1" + } + client = "client-1" + client_version = "client-version-1" + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + resources { + limits = { + "cpu" = "4" + "memory" = "16Gi" + } + startup_cpu_boost = true + } + } + scaling { + max_instance_count = 1 + } + } +} +`, context) +} + +func testAccCloudRunV2Service_cloudrunv2ServiceWithGpu(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_cloud_run_v2_service" "default" { + name = "tf-test-cloudrun-service%{random_suffix}" + description = "description creating" + location = "us-central1" + deletion_protection = false + launch_stage = "BETA" + annotations = { + generated-by = "magic-modules" + } + ingress = "INGRESS_TRAFFIC_ALL" + labels = { + label-1 = "value-1" + } + client = "client-1" + client_version = "client-version-1" + template { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + resources { + limits = { + "cpu" = "4" + "memory" = "16Gi" + "nvidia.com/gpu" = "1" + } + startup_cpu_boost = true + } + } + node_selector { + accelerator = "nvidia-l4" + } + scaling { + max_instance_count = 1 + } + } +} +`, context) +} +<% end -%> diff --git a/mmv1/third_party/terraform/services/composer/resource_composer_environment.go.erb b/mmv1/third_party/terraform/services/composer/resource_composer_environment.go.erb index 110856c16e93..d833e09c9b38 100644 --- a/mmv1/third_party/terraform/services/composer/resource_composer_environment.go.erb +++ b/mmv1/third_party/terraform/services/composer/resource_composer_environment.go.erb @@ -1078,6 +1078,7 @@ func ResourceComposerEnvironment() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, + DiffSuppressFunc: gscBucketNameDiffSuppress, Description: `Optional. Name of an existing Cloud Storage bucket to be used by the environment.`, }, }, @@ -3118,4 +3119,12 @@ func validateComposerInternalIpv4CidrBlock(v any, k string) (warns []string, err errs = append(errs, fmt.Errorf("Composer Internal IPv4 CIDR range must have size /20")) } return +} + +func gscBucketNameDiffSuppress(_, old, new string, _ *schema.ResourceData) bool { + prefix := "gs://" + if prefix + old == new || old == prefix + new { + return true + } + return false } \ No newline at end of file diff --git a/mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.erb b/mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.erb index ea71ec030c88..fb6c49f99d28 100644 --- a/mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.erb +++ b/mmv1/third_party/terraform/services/composer/resource_composer_environment_test.go.erb @@ -1156,6 +1156,39 @@ func TestAccComposerEnvironment_customBucket(t *testing.T) { }) } +func TestAccComposerEnvironment_customBucketWithUrl(t *testing.T) { + t.Parallel() + + bucketName := fmt.Sprintf("%s-%d", testComposerBucketPrefix, acctest.RandInt(t)) + envName := fmt.Sprintf("%s-%d", testComposerEnvironmentPrefix, acctest.RandInt(t)) + network := fmt.Sprintf("%s-%d", testComposerNetworkPrefix, acctest.RandInt(t)) + subnetwork := network + "-1" + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccComposerEnvironmentDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComposerEnvironment_customBucketWithUrl(bucketName, envName, network, subnetwork), + }, + { + ResourceName: "google_composer_environment.test", + ImportState: true, + ImportStateVerify: true, + }, + // This is a terrible clean-up step in order to get destroy to succeed, + // due to dangling firewall rules left by the Composer Environment blocking network deletion. + // TODO: Remove this check if firewall rules bug gets fixed by Composer. + { + PlanOnly: true, + ExpectNonEmptyPlan: false, + Config: testAccComposerEnvironment_customBucketWithUrl(bucketName, envName, network, subnetwork), + Check: testAccCheckClearComposerEnvironmentFirewalls(t, network), + }, + }, + }) +} + <% unless version == "ga" -%> // Checks Composer 3 environment creation with new fields. func TestAccComposerEnvironmentComposer3_basic(t *testing.T) { @@ -1559,6 +1592,47 @@ resource "google_compute_subnetwork" "test" { `, bucketName, envName, network, subnetwork) } +func testAccComposerEnvironment_customBucketWithUrl(bucketName, envName, network, subnetwork string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "test" { + name = "%s" + location = "us-central1" + force_destroy = true +} + +resource "google_composer_environment" "test" { + name = "%s" + region = "us-central1" + config { + node_config { + network = google_compute_network.test.self_link + subnetwork = google_compute_subnetwork.test.self_link + } + software_config { + image_version = "composer-2.9.3-airflow-2.9.1" + } + } + storage_config { + bucket = google_storage_bucket.test.url + } +} + +// use a separate network to avoid conflicts with other tests running in parallel +// that use the default network/subnet +resource "google_compute_network" "test" { + name = "%s" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "test" { + name = "%s" + ip_cidr_range = "10.2.0.0/16" + region = "us-central1" + network = google_compute_network.test.self_link +} +`, bucketName, envName, network, subnetwork) +} + func testAccComposerEnvironment_basic(name, network, subnetwork string) string { return fmt.Sprintf(` resource "google_composer_environment" "test" { diff --git a/mmv1/third_party/terraform/services/compute/go/resource_compute_instance_test.go.tmpl b/mmv1/third_party/terraform/services/compute/go/resource_compute_instance_test.go.tmpl index 04c0535d9a0e..de0014749660 100644 --- a/mmv1/third_party/terraform/services/compute/go/resource_compute_instance_test.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/go/resource_compute_instance_test.go.tmpl @@ -8457,18 +8457,6 @@ data "google_compute_image" "my_image" { data "google_project" "project" {} -resource "google_kms_crypto_key_iam_member" "crypto_key" { - crypto_key_id = "%{key_name}" - role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" - member = "serviceAccount:${data.google_project.project.number}-compute@developer.gserviceaccount.com" -} - -resource "google_kms_crypto_key_iam_member" "crypto_key_2" { - crypto_key_id = "%{key_name}" - role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" - member = "serviceAccount:service-${data.google_project.project.number}@compute-system.iam.gserviceaccount.com" -} - resource "google_compute_instance" "foobar" { name = "%{instance_name}" machine_type = "%{machine_type}" @@ -8490,7 +8478,6 @@ resource "google_compute_instance" "foobar" { network_interface { network = "default" } - depends_on = [google_kms_crypto_key_iam_member.crypto_key] } `, context) diff --git a/mmv1/third_party/terraform/services/compute/go/resource_compute_region_network_firewall_policy_with_rules_test.go.tmpl b/mmv1/third_party/terraform/services/compute/go/resource_compute_region_network_firewall_policy_with_rules_test.go.tmpl new file mode 100644 index 000000000000..28eb6d172d99 --- /dev/null +++ b/mmv1/third_party/terraform/services/compute/go/resource_compute_region_network_firewall_policy_with_rules_test.go.tmpl @@ -0,0 +1,213 @@ +package compute_test +{{- if ne $.TargetVersionName "ga" }} +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccComputeRegionNetworkFirewallPolicyWithRules_update(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + CheckDestroy: testAccCheckComputeRegionNetworkFirewallPolicyWithRulesDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionNetworkFirewallPolicyWithRules_full(context), + }, + { + ResourceName: "google_compute_region_network_firewall_policy_with_rules.region-network-firewall-policy-with-rules", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region"}, + }, + { + Config: testAccComputeRegionNetworkFirewallPolicyWithRules_update(context), + }, + { + ResourceName: "google_compute_region_network_firewall_policy_with_rules.region-network-firewall-policy-with-rules", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region"}, + }, + }, + }) +} + +func testAccComputeRegionNetworkFirewallPolicyWithRules_full(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_project" "project" { + provider = google-beta +} + +resource "google_compute_region_network_firewall_policy_with_rules" "region-network-firewall-policy-with-rules" { + name = "tf-test-tf-region-fw-policy-with-rules%{random_suffix}" + region = "us-west2" + description = "Terraform test" + provider = google-beta + + rule { + description = "tcp rule" + priority = 1000 + enable_logging = true + action = "allow" + direction = "EGRESS" + match { + layer4_config { + ip_protocol = "tcp" + ports = [8080, 7070] + } + dest_ip_ranges = ["11.100.0.1/32"] + dest_fqdns = ["www.yyy.com", "www.zzz.com"] + dest_region_codes = ["HK", "IN"] + dest_threat_intelligences = ["iplist-search-engines-crawlers", "iplist-tor-exit-nodes"] + dest_address_groups = [google_network_security_address_group.address_group_1.id] + } + target_secure_tag { + name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + } + } + rule { + description = "udp rule" + rule_name = "test-rule" + priority = 2000 + enable_logging = false + action = "deny" + direction = "INGRESS" + match { + layer4_config { + ip_protocol = "udp" + } + src_ip_ranges = ["0.0.0.0/0"] + src_fqdns = ["www.abc.com", "www.def.com"] + src_region_codes = ["US", "CA"] + src_threat_intelligences = ["iplist-known-malicious-ips", "iplist-public-clouds"] + src_address_groups = [google_network_security_address_group.address_group_1.id] + src_secure_tag { + name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + } + } + disabled = true + } +} + +resource "google_network_security_address_group" "address_group_1" { + provider = google-beta + name = "tf-test-tf-address-group%{random_suffix}" + parent = "projects/${data.google_project.project.name}" + description = "Regional address group" + location = "us-west2" + items = ["208.80.154.224/32"] + type = "IPV4" + capacity = 100 +} + +resource "google_tags_tag_key" "secure_tag_key_1" { + provider = google-beta + description = "Tag key" + parent = "projects/${data.google_project.project.name}" + purpose = "GCE_FIREWALL" + short_name = "tf-test-tf-tag-key%{random_suffix}" + purpose_data = { + network = "${data.google_project.project.name}/default" + } +} + +resource "google_tags_tag_value" "secure_tag_value_1" { + provider = google-beta + description = "Tag value" + parent = "tagKeys/${google_tags_tag_key.secure_tag_key_1.name}" + short_name = "tf-test-tf-tag-value%{random_suffix}" +} +`, context) +} + +func testAccComputeRegionNetworkFirewallPolicyWithRules_update(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_project" "project" { + provider = google-beta +} + +resource "google_compute_region_network_firewall_policy_with_rules" "region-network-firewall-policy-with-rules" { + name = "tf-test-tf-fw-policy-with-rules%{random_suffix}" + description = "Terraform test - update" + region = "us-west2" + provider = google-beta + + rule { + description = "tcp rule - changed" + priority = 1000 + enable_logging = false + action = "allow" + direction = "EGRESS" + match { + layer4_config { + ip_protocol = "tcp" + ports = [8080, 7070] + } + dest_ip_ranges = ["11.100.0.1/32"] + } + } + rule { + description = "new udp rule" + priority = 4000 + enable_logging = true + action = "deny" + direction = "INGRESS" + match { + layer4_config { + ip_protocol = "udp" + } + src_ip_ranges = ["0.0.0.0/0"] + src_fqdns = ["www.abc.com", "www.ghi.com"] + src_region_codes = ["IT", "FR"] + src_threat_intelligences = ["iplist-public-clouds"] + src_address_groups = [google_network_security_address_group.address_group_1.id] + src_secure_tag { + name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + } + } + disabled = false + } +} + +resource "google_network_security_address_group" "address_group_1" { + provider = google-beta + name = "tf-test-tf-address-group%{random_suffix}" + parent = "projects/${data.google_project.project.name}" + description = "Regional address group" + location = "us-west2" + items = ["208.80.154.224/32"] + type = "IPV4" + capacity = 100 +} + +resource "google_tags_tag_key" "secure_tag_key_1" { + provider = google-beta + description = "Tag key" + parent = "projects/${data.google_project.project.name}" + purpose = "GCE_FIREWALL" + short_name = "tf-test-tf-tag-key%{random_suffix}" + purpose_data = { + network = "${data.google_project.project.name}/default" + } +} + +resource "google_tags_tag_value" "secure_tag_value_1" { + provider = google-beta + description = "Tag value" + parent = "tagKeys/${google_tags_tag_key.secure_tag_key_1.name}" + short_name = "tf-test-tf-tag-value%{random_suffix}" +} +`, context) +} +{{- end }} + diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_region_network_firewall_policy_with_rules_test.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_region_network_firewall_policy_with_rules_test.go.erb new file mode 100644 index 000000000000..5ac904330155 --- /dev/null +++ b/mmv1/third_party/terraform/services/compute/resource_compute_region_network_firewall_policy_with_rules_test.go.erb @@ -0,0 +1,214 @@ +<% autogen_exception -%> +package compute_test +<% unless version == 'ga' -%> +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccComputeRegionNetworkFirewallPolicyWithRules_update(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderBetaFactories(t), + CheckDestroy: testAccCheckComputeRegionNetworkFirewallPolicyWithRulesDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRegionNetworkFirewallPolicyWithRules_full(context), + }, + { + ResourceName: "google_compute_region_network_firewall_policy_with_rules.region-network-firewall-policy-with-rules", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region"}, + }, + { + Config: testAccComputeRegionNetworkFirewallPolicyWithRules_update(context), + }, + { + ResourceName: "google_compute_region_network_firewall_policy_with_rules.region-network-firewall-policy-with-rules", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region"}, + }, + }, + }) +} + +func testAccComputeRegionNetworkFirewallPolicyWithRules_full(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_project" "project" { + provider = google-beta +} + +resource "google_compute_region_network_firewall_policy_with_rules" "region-network-firewall-policy-with-rules" { + name = "tf-test-tf-region-fw-policy-with-rules%{random_suffix}" + region = "us-west2" + description = "Terraform test" + provider = google-beta + + rule { + description = "tcp rule" + priority = 1000 + enable_logging = true + action = "allow" + direction = "EGRESS" + match { + layer4_config { + ip_protocol = "tcp" + ports = [8080, 7070] + } + dest_ip_ranges = ["11.100.0.1/32"] + dest_fqdns = ["www.yyy.com", "www.zzz.com"] + dest_region_codes = ["HK", "IN"] + dest_threat_intelligences = ["iplist-search-engines-crawlers", "iplist-tor-exit-nodes"] + dest_address_groups = [google_network_security_address_group.address_group_1.id] + } + target_secure_tag { + name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + } + } + rule { + description = "udp rule" + rule_name = "test-rule" + priority = 2000 + enable_logging = false + action = "deny" + direction = "INGRESS" + match { + layer4_config { + ip_protocol = "udp" + } + src_ip_ranges = ["0.0.0.0/0"] + src_fqdns = ["www.abc.com", "www.def.com"] + src_region_codes = ["US", "CA"] + src_threat_intelligences = ["iplist-known-malicious-ips", "iplist-public-clouds"] + src_address_groups = [google_network_security_address_group.address_group_1.id] + src_secure_tag { + name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + } + } + disabled = true + } +} + +resource "google_network_security_address_group" "address_group_1" { + provider = google-beta + name = "tf-test-tf-address-group%{random_suffix}" + parent = "projects/${data.google_project.project.name}" + description = "Regional address group" + location = "us-west2" + items = ["208.80.154.224/32"] + type = "IPV4" + capacity = 100 +} + +resource "google_tags_tag_key" "secure_tag_key_1" { + provider = google-beta + description = "Tag key" + parent = "projects/${data.google_project.project.name}" + purpose = "GCE_FIREWALL" + short_name = "tf-test-tf-tag-key%{random_suffix}" + purpose_data = { + network = "${data.google_project.project.name}/default" + } +} + +resource "google_tags_tag_value" "secure_tag_value_1" { + provider = google-beta + description = "Tag value" + parent = "tagKeys/${google_tags_tag_key.secure_tag_key_1.name}" + short_name = "tf-test-tf-tag-value%{random_suffix}" +} +`, context) +} + +func testAccComputeRegionNetworkFirewallPolicyWithRules_update(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_project" "project" { + provider = google-beta +} + +resource "google_compute_region_network_firewall_policy_with_rules" "region-network-firewall-policy-with-rules" { + name = "tf-test-tf-fw-policy-with-rules%{random_suffix}" + description = "Terraform test - update" + region = "us-west2" + provider = google-beta + + rule { + description = "tcp rule - changed" + priority = 1000 + enable_logging = false + action = "allow" + direction = "EGRESS" + match { + layer4_config { + ip_protocol = "tcp" + ports = [8080, 7070] + } + dest_ip_ranges = ["11.100.0.1/32"] + } + } + rule { + description = "new udp rule" + priority = 4000 + enable_logging = true + action = "deny" + direction = "INGRESS" + match { + layer4_config { + ip_protocol = "udp" + } + src_ip_ranges = ["0.0.0.0/0"] + src_fqdns = ["www.abc.com", "www.ghi.com"] + src_region_codes = ["IT", "FR"] + src_threat_intelligences = ["iplist-public-clouds"] + src_address_groups = [google_network_security_address_group.address_group_1.id] + src_secure_tag { + name = "tagValues/${google_tags_tag_value.secure_tag_value_1.name}" + } + } + disabled = false + } +} + +resource "google_network_security_address_group" "address_group_1" { + provider = google-beta + name = "tf-test-tf-address-group%{random_suffix}" + parent = "projects/${data.google_project.project.name}" + description = "Regional address group" + location = "us-west2" + items = ["208.80.154.224/32"] + type = "IPV4" + capacity = 100 +} + +resource "google_tags_tag_key" "secure_tag_key_1" { + provider = google-beta + description = "Tag key" + parent = "projects/${data.google_project.project.name}" + purpose = "GCE_FIREWALL" + short_name = "tf-test-tf-tag-key%{random_suffix}" + purpose_data = { + network = "${data.google_project.project.name}/default" + } +} + +resource "google_tags_tag_value" "secure_tag_value_1" { + provider = google-beta + description = "Tag value" + parent = "tagKeys/${google_tags_tag_key.secure_tag_key_1.name}" + short_name = "tf-test-tf-tag-value%{random_suffix}" +} +`, context) +} +<% end -%> + diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_router_nat_address_test.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_router_nat_address_test.go.erb new file mode 100644 index 000000000000..dfd75243a28f --- /dev/null +++ b/mmv1/third_party/terraform/services/compute/resource_compute_router_nat_address_test.go.erb @@ -0,0 +1,608 @@ +<% autogen_exception -%> +package compute_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func testAccCheckComputeRouterNatAddressDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + config := acctest.GoogleProviderConfig(t) + + routersService := config.NewComputeClient(config.UserAgent).Routers + + for _, rs := range s.RootModule().Resources { + if rs.Type != "google_compute_router" { + continue + } + + project, err := acctest.GetTestProject(rs.Primary, config) + if err != nil { + return err + } + + region, err := acctest.GetTestRegion(rs.Primary, config) + if err != nil { + return err + } + + routerName := rs.Primary.Attributes["router"] + + _, err = routersService.Get(project, region, routerName).Do() + + if err == nil { + return fmt.Errorf("Error, Router %s in region %s still exists", routerName, region) + } + } + + return nil + } +} + +func TestAccComputeRouterNatAddress_withAddressCountDecrease(t *testing.T) { + t.Parallel() + + testId := acctest.RandString(t, 10) + routerName := fmt.Sprintf("tf-test-router-nat-%s", testId) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputeRouterNatAddressDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRouterNatAddress_withAddressCount(routerName, "2"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_compute_router_nat.foobar", "initial_nat_ips.#", "1"), + resource.TestCheckResourceAttr("google_compute_router_nat.foobar", "nat_ips.#", "1"), + resource.TestCheckResourceAttr("google_compute_router_nat_address.foobar", "nat_ips.#", "2"), + resource.TestCheckResourceAttr("data.google_compute_router_nat.foo", "nat_ips.#", "2"), + ), + }, + { + ResourceName: "google_compute_router_nat_address.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeRouterNatAddress_withAddressCount(routerName, "3"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_compute_router_nat.foobar", "initial_nat_ips.#", "1"), + resource.TestCheckResourceAttr("google_compute_router_nat.foobar", "nat_ips.#", "2"), + resource.TestCheckResourceAttr("google_compute_router_nat_address.foobar", "nat_ips.#", "3"), + resource.TestCheckResourceAttr("data.google_compute_router_nat.foo", "nat_ips.#", "3"), + ), + }, + { + ResourceName: "google_compute_router_nat_address.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeRouterNatAddress_withAddressCount(routerName, "2"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_compute_router_nat.foobar", "initial_nat_ips.#", "1"), + resource.TestCheckResourceAttr("google_compute_router_nat.foobar", "nat_ips.#", "3"), + resource.TestCheckResourceAttr("google_compute_router_nat_address.foobar", "nat_ips.#", "2"), + resource.TestCheckResourceAttr("data.google_compute_router_nat.foo", "nat_ips.#", "2"), + ), + }, + { + ResourceName: "google_compute_router_nat_address.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeRouterNatAddress_withAddressRemoved(t *testing.T) { + t.Parallel() + + testId := acctest.RandString(t, 10) + routerName := fmt.Sprintf("tf-test-router-nat-%s", testId) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + }, + CheckDestroy: testAccCheckComputeRouterNatAddressDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRouterNatAddressWithNatIps(routerName), + }, + { + ResourceName: "google_compute_router_nat_address.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeRouterNatAddressWithAddressRemoved(routerName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_compute_router_nat.foo", "nat_ips.#", "1"), + acctest.CheckDataSourceStateMatchesResourceStateWithIgnores( + "data.google_compute_router_nat.foo", + "google_compute_router_nat.foobar", + map[string]struct{}{ + "initial_nat_ips": {}, + "initial_nat_ips.#": {}, + "initial_nat_ips.0": {}, + }, + ), + ), + }, + { + ResourceName: "google_compute_router_nat.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeRouterNatAddress_withAutoAllocateAndAddressRemoved(t *testing.T) { + t.Parallel() + + testId := acctest.RandString(t, 10) + routerName := fmt.Sprintf("tf-test-router-nat-%s", testId) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "random": {}, + }, + CheckDestroy: testAccCheckComputeRouterNatAddressDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccComputeRouterNatAddressWithNatIps(routerName), + }, + { + ResourceName: "google_compute_router_nat_address.foobar", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccComputeRouterNatAddressWithAutoAllocateAndAddressRemoved(routerName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_compute_router_nat.foo", "nat_ips.#", "0"), + acctest.CheckDataSourceStateMatchesResourceStateWithIgnores( + "data.google_compute_router_nat.foo", + "google_compute_router_nat.foobar", + map[string]struct{}{ + "initial_nat_ips": {}, + "initial_nat_ips.#": {}, + "initial_nat_ips.0": {}, + }, + ), + ), + }, + { + ResourceName: "google_compute_router_nat.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccComputeRouterNatAddress_withNatIpsAndDrainNatIps(t *testing.T) { + t.Parallel() + + testId := acctest.RandString(t, 10) + routerName := fmt.Sprintf("tf-test-router-nat-%s", testId) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckComputeRouterNatDestroyProducer(t), + Steps: []resource.TestStep{ + // (ERROR): Creation with drain nat IPs should fail + { + Config: testAccComputeRouterNatAddressWithOneDrainOneRemovedNatIps(routerName), + ExpectError: regexp.MustCompile("New RouterNat cannot have drain_nat_ips"), + }, + // Create NAT with three nat IPs + { + Config: testAccComputeRouterNatAddressWithNatIps(routerName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_compute_router_nat.foobar", "initial_nat_ips.#", "1"), + resource.TestCheckResourceAttr("google_compute_router_nat.foobar", "nat_ips.#", "1"), + resource.TestCheckResourceAttr("google_compute_router_nat_address.foobar", "nat_ips.#", "3"), + resource.TestCheckResourceAttr("data.google_compute_router_nat.foo", "nat_ips.#", "3"), + ), + }, + { + ResourceName: "google_compute_router_nat_address.foobar", + ImportState: true, + ImportStateVerify: true, + }, + // (ERROR) - Should not allow draining IPs still in natIps + { + Config: testAccComputeRouterNatAddressWithInvalidDrainNatIpsStillInNatIps(routerName), + ExpectError: regexp.MustCompile("cannot be drained if still set in nat_ips"), + }, + // natIps #1, #2, #3--> natIp #2, drainNatIp #3 + { + Config: testAccComputeRouterNatAddressWithOneDrainOneRemovedNatIps(routerName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("google_compute_router_nat.foobar", "initial_nat_ips.#", "1"), + resource.TestCheckResourceAttr("google_compute_router_nat.foobar", "nat_ips.#", "3"), + resource.TestCheckResourceAttr("google_compute_router_nat_address.foobar", "nat_ips.#", "1"), + resource.TestCheckResourceAttr("data.google_compute_router_nat.foo", "nat_ips.#", "1"), + ), + }, + { + ResourceName: "google_compute_router_nat_address.foobar", + ImportState: true, + ImportStateVerify: true, + }, + // (ERROR): Should not be able to drain previously removed natIps (#1) + { + Config: testAccComputeRouterNatAddressWithInvalidDrainMissingNatIp(routerName), + ExpectError: regexp.MustCompile("was not previously set in nat_ips"), + }, + { + Config: testAccComputeRouterNatAddressWithAddressRemoved(routerName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckNoResourceAttr("google_compute_router_nat.foobar", "initial_nat_ips"), + resource.TestCheckResourceAttr("google_compute_router_nat.foobar", "nat_ips.#", "1"), + resource.TestCheckResourceAttr("data.google_compute_router_nat.foo", "nat_ips.#", "1"), + acctest.CheckDataSourceStateMatchesResourceStateWithIgnores( + "data.google_compute_router_nat.foo", + "google_compute_router_nat.foobar", + map[string]struct{}{ + "initial_nat_ips": {}, + "initial_nat_ips.#": {}, + "initial_nat_ips.0": {}, + }, + ), + ), + }, + { + ResourceName: "google_compute_router_nat.foobar", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccComputeRouterNatAddress_withAddressCount(routerName, routerCount string) string { + return fmt.Sprintf(` +resource "google_compute_network" "foobar" { + name = "%s-net" +} + +resource "google_compute_subnetwork" "foobar" { + name = "%s-subnet" + network = google_compute_network.foobar.self_link + ip_cidr_range = "10.0.0.0/16" + region = "us-east1" +} + +resource "google_compute_router" "foobar" { + name = "%s" + region = google_compute_subnetwork.foobar.region + network = google_compute_network.foobar.self_link +} + +resource "google_compute_address" "foobar" { + count = %s + name = "%s-address-${count.index}" + region = google_compute_subnetwork.foobar.region + + lifecycle { + create_before_destroy = true + } +} + +resource "google_compute_router_nat_address" "foobar" { + nat_ips = google_compute_address.foobar.*.self_link + router = google_compute_router.foobar.name + router_nat = google_compute_router_nat.foobar.name + region = google_compute_router_nat.foobar.region +} + +resource "google_compute_router_nat" "foobar" { + name = "%s-nat" + router = google_compute_router.foobar.name + region = google_compute_router.foobar.region + + nat_ip_allocate_option = "MANUAL_ONLY" + source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" + + initial_nat_ips = [google_compute_address.foobar[0].self_link] + + subnetwork { + name = google_compute_subnetwork.foobar.self_link + source_ip_ranges_to_nat = ["ALL_IP_RANGES"] + } + + min_ports_per_vm = 1024 + + log_config { + enable = true + filter = "ERRORS_ONLY" + } +} + +data "google_compute_router_nat" "foo" { + name = google_compute_router_nat.foobar.name + router = google_compute_router_nat.foobar.router + region = google_compute_router.foobar.region + + depends_on = [google_compute_router_nat_address.foobar] +} +`, routerName, routerName, routerName, routerCount, routerName, routerName) +} + +func testAccComputeRouterNatAddressBaseResourcesWithNatIps(routerName string) string { + return fmt.Sprintf(` +resource "google_compute_network" "foobar" { + name = "%s-net" + auto_create_subnetworks = "false" +} + +resource "google_compute_subnetwork" "foobar" { + name = "%s-subnet" + network = google_compute_network.foobar.self_link + ip_cidr_range = "10.0.0.0/16" + region = "us-central1" +} + +resource "google_compute_address" "addr1" { + name = "%s-addr1" + region = google_compute_subnetwork.foobar.region +} + +resource "google_compute_address" "addr2" { + name = "%s-addr2" + region = google_compute_subnetwork.foobar.region +} + +resource "google_compute_address" "addr3" { + name = "%s-addr3" + region = google_compute_subnetwork.foobar.region +} + +resource "google_compute_address" "addr4" { + name = "%s-addr4" + region = google_compute_subnetwork.foobar.region +} + +resource "google_compute_router" "foobar" { + name = "%s" + region = google_compute_subnetwork.foobar.region + network = google_compute_network.foobar.self_link +} +`, routerName, routerName, routerName, routerName, routerName, routerName, routerName) +} + +func testAccComputeRouterNatAddressWithNatIps(routerName string) string { + return fmt.Sprintf(` +%s + +resource "google_compute_router_nat_address" "foobar" { + nat_ips = [ + google_compute_address.addr1.self_link, + google_compute_address.addr2.self_link, + google_compute_address.addr3.self_link, + ] + router = google_compute_router.foobar.name + router_nat = google_compute_router_nat.foobar.name + region = google_compute_router_nat.foobar.region +} + +resource "google_compute_router_nat" "foobar" { + name = "%s" + router = google_compute_router.foobar.name + region = google_compute_router.foobar.region + + nat_ip_allocate_option = "MANUAL_ONLY" + initial_nat_ips = [google_compute_address.addr4.self_link] + + source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" + subnetwork { + name = google_compute_subnetwork.foobar.self_link + source_ip_ranges_to_nat = ["ALL_IP_RANGES"] + } +} + +data "google_compute_router_nat" "foo" { + name = google_compute_router_nat.foobar.name + router = google_compute_router_nat.foobar.router + region = google_compute_router.foobar.region + + depends_on = [google_compute_router_nat_address.foobar] +} +`, testAccComputeRouterNatAddressBaseResourcesWithNatIps(routerName), routerName) +} + +func testAccComputeRouterNatAddressWithAddressRemoved(routerName string) string { + return fmt.Sprintf(` +%s + +resource "google_compute_router_nat" "foobar" { + name = "%s" + router = google_compute_router.foobar.name + region = google_compute_router.foobar.region + + nat_ip_allocate_option = "MANUAL_ONLY" + nat_ips = [google_compute_address.addr4.self_link] + + source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" + subnetwork { + name = google_compute_subnetwork.foobar.self_link + source_ip_ranges_to_nat = ["ALL_IP_RANGES"] + } +} + +data "google_compute_router_nat" "foo" { + name = google_compute_router_nat.foobar.name + router = google_compute_router_nat.foobar.router + region = google_compute_router.foobar.region +} +`, testAccComputeRouterNatAddressBaseResourcesWithNatIps(routerName), routerName) +} + +func testAccComputeRouterNatAddressWithAutoAllocateAndAddressRemoved(routerName string) string { + return fmt.Sprintf(` +%s + +resource "google_compute_router_nat" "foobar" { + name = "%s" + router = google_compute_router.foobar.name + region = google_compute_router.foobar.region + + nat_ip_allocate_option = "AUTO_ONLY" + nat_ips = [] + + source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" +} + +data "google_compute_router_nat" "foo" { + name = google_compute_router_nat.foobar.name + router = google_compute_router_nat.foobar.router + region = google_compute_router.foobar.region +} +`, testAccComputeRouterNatAddressBaseResourcesWithNatIps(routerName), routerName) +} + +func testAccComputeRouterNatAddressWithOneDrainOneRemovedNatIps(routerName string) string { + return fmt.Sprintf(` +%s + +resource "google_compute_router_nat_address" "foobar" { + nat_ips = [ + google_compute_address.addr2.self_link, + ] + + drain_nat_ips = [ + google_compute_address.addr3.self_link, + ] + router = google_compute_router.foobar.name + router_nat = google_compute_router_nat.foobar.name + region = google_compute_router_nat.foobar.region +} + +resource "google_compute_router_nat" "foobar" { + name = "%s" + router = google_compute_router.foobar.name + region = google_compute_router.foobar.region + + source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" + subnetwork { + name = google_compute_subnetwork.foobar.self_link + source_ip_ranges_to_nat = ["ALL_IP_RANGES"] + } + + nat_ip_allocate_option = "MANUAL_ONLY" + initial_nat_ips = [google_compute_address.addr4.self_link] +} + +data "google_compute_router_nat" "foo" { + name = google_compute_router_nat.foobar.name + router = google_compute_router_nat.foobar.router + region = google_compute_router.foobar.region + + depends_on = [google_compute_router_nat_address.foobar] +} +`, testAccComputeRouterNatAddressBaseResourcesWithNatIps(routerName), routerName) +} + +func testAccComputeRouterNatAddressWithInvalidDrainNatIpsStillInNatIps(routerName string) string { + return fmt.Sprintf(` +%s + +resource "google_compute_router_nat_address" "foobar" { + nat_ips = [ + google_compute_address.addr1.self_link, + google_compute_address.addr2.self_link, + google_compute_address.addr3.self_link, + ] + + drain_nat_ips = [ + google_compute_address.addr3.self_link, + ] + router = google_compute_router.foobar.name + router_nat = google_compute_router_nat.foobar.name + region = google_compute_router_nat.foobar.region +} + + +resource "google_compute_router_nat" "foobar" { + name = "%s" + router = google_compute_router.foobar.name + region = google_compute_router.foobar.region + + source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" + subnetwork { + name = google_compute_subnetwork.foobar.self_link + source_ip_ranges_to_nat = ["ALL_IP_RANGES"] + } + + nat_ip_allocate_option = "MANUAL_ONLY" + initial_nat_ips = [google_compute_address.addr4.self_link] +} + +data "google_compute_router_nat" "foo" { + name = google_compute_router_nat.foobar.name + router = google_compute_router_nat.foobar.router + region = google_compute_router.foobar.region + + depends_on = [google_compute_router_nat_address.foobar] +} +`, testAccComputeRouterNatAddressBaseResourcesWithNatIps(routerName), routerName) +} + +func testAccComputeRouterNatAddressWithInvalidDrainMissingNatIp(routerName string) string { + return fmt.Sprintf(` +%s + +resource "google_compute_router_nat_address" "foobar" { + nat_ips = [ + google_compute_address.addr2.self_link, + ] + + drain_nat_ips = [ + google_compute_address.addr1.self_link, + google_compute_address.addr3.self_link, + ] + router = google_compute_router.foobar.name + router_nat = google_compute_router_nat.foobar.name + region = google_compute_router_nat.foobar.region +} + +resource "google_compute_router_nat" "foobar" { + name = "%s" + router = google_compute_router.foobar.name + region = google_compute_router.foobar.region + + source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS" + subnetwork { + name = google_compute_subnetwork.foobar.self_link + source_ip_ranges_to_nat = ["ALL_IP_RANGES"] + } + + nat_ip_allocate_option = "MANUAL_ONLY" + initial_nat_ips = [google_compute_address.addr4.self_link] +} + +data "google_compute_router_nat" "foo" { + name = google_compute_router_nat.foobar.name + router = google_compute_router_nat.foobar.router + region = google_compute_router.foobar.region + + depends_on = [google_compute_router_nat_address.foobar] +} +`, testAccComputeRouterNatAddressBaseResourcesWithNatIps(routerName), routerName) +} diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_router_nat_test.go.erb b/mmv1/third_party/terraform/services/compute/resource_compute_router_nat_test.go.erb index d6bb0ce31a41..f5a05cc74a33 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_router_nat_test.go.erb +++ b/mmv1/third_party/terraform/services/compute/resource_compute_router_nat_test.go.erb @@ -1036,6 +1036,7 @@ resource "google_compute_router_nat" "foobar" { router = google_compute_router.foobar.name region = google_compute_router.foobar.region nat_ip_allocate_option = "AUTO_ONLY" + nat_ips = [] source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" log_config { diff --git a/mmv1/third_party/terraform/services/container/go/node_config.go.tmpl b/mmv1/third_party/terraform/services/container/go/node_config.go.tmpl index 74fd6c15f8e6..8b7c70515e06 100644 --- a/mmv1/third_party/terraform/services/container/go/node_config.go.tmpl +++ b/mmv1/third_party/terraform/services/container/go/node_config.go.tmpl @@ -100,12 +100,13 @@ func schemaLoggingVariant() *schema.Schema { } func schemaGcfsConfig() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - Optional: true, - MaxItems: 1, + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, Description: `GCFS configuration for this node.`, - Elem: &schema.Resource{ + Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "enabled": { Type: schema.TypeBool, @@ -114,7 +115,7 @@ func schemaGcfsConfig() *schema.Schema { }, }, }, - } + } } func schemaNodeConfig() *schema.Schema { @@ -645,6 +646,26 @@ func schemaNodeConfig() *schema.Schema { Description: `cgroupMode specifies the cgroup mode to be used on the node.`, DiffSuppressFunc: tpgresource.EmptyOrDefaultStringSuppress("CGROUP_MODE_UNSPECIFIED"), }, + "hugepages_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: `Amounts for 2M and 1G hugepages.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "hugepage_size_2m": { + Type: schema.TypeInt, + Optional: true, + Description: `Amount of 2M hugepages.`, + }, + "hugepage_size_1g": { + Type: schema.TypeInt, + Optional: true, + Description: `Amount of 1G hugepages.`, + }, + }, + }, + }, }, }, }, @@ -814,7 +835,6 @@ func expandNodeConfigDefaults(configured interface{}) *container.NodeConfigDefau if v, ok := config["insecure_kubelet_readonly_port_enabled"]; ok { nodeConfigDefaults.NodeKubeletConfig = &container.NodeKubeletConfig{ InsecureKubeletReadonlyPortEnabled: expandInsecureKubeletReadonlyPortEnabled(v), - ForceSendFields: []string{"InsecureKubeletReadonlyPortEnabled"}, } } if variant, ok := config["logging_variant"]; ok { @@ -1244,6 +1264,10 @@ func expandLinuxNodeConfig(v interface{}) *container.LinuxNodeConfig { linuxNodeConfig.CgroupMode = cgroupMode } + if v, ok := cfg["hugepages_config"]; ok { + linuxNodeConfig.Hugepages = expandHugepagesConfig(v) + } + return linuxNodeConfig } @@ -1268,6 +1292,32 @@ func expandCgroupMode(cfg map[string]interface{}) string { return cgroupMode.(string) } +func expandHugepagesConfig(v interface{}) *container.HugepagesConfig { + if v == nil { + return nil + } + ls := v.([]interface{}) + if len(ls) == 0 { + return nil + } + if ls[0] == nil { + return &container.HugepagesConfig{} + } + cfg := ls[0].(map[string]interface{}) + + hugepagesConfig := &container.HugepagesConfig{} + + if v, ok := cfg["hugepage_size_2m"]; ok { + hugepagesConfig.HugepageSize2m = int64(v.(int)) + } + + if v, ok := cfg["hugepage_size_1g"]; ok { + hugepagesConfig.HugepageSize1g = int64(v.(int)) + } + + return hugepagesConfig +} + func expandContainerdConfig(v interface{}) *container.ContainerdConfig { if v == nil { return nil @@ -1798,8 +1848,20 @@ func flattenLinuxNodeConfig(c *container.LinuxNodeConfig) []map[string]interface result := []map[string]interface{}{} if c != nil { result = append(result, map[string]interface{}{ - "sysctls": c.Sysctls, - "cgroup_mode": c.CgroupMode, + "sysctls": c.Sysctls, + "cgroup_mode": c.CgroupMode, + "hugepages_config": flattenHugepagesConfig(c.Hugepages), + }) + } + return result +} + +func flattenHugepagesConfig(c *container.HugepagesConfig) []map[string]interface{} { + result := []map[string]interface{}{} + if c != nil { + result = append(result, map[string]interface{}{ + "hugepage_size_2m": c.HugepageSize2m, + "hugepage_size_1g": c.HugepageSize1g, }) } return result diff --git a/mmv1/third_party/terraform/services/container/go/resource_container_cluster.go.tmpl b/mmv1/third_party/terraform/services/container/go/resource_container_cluster.go.tmpl index 7d43a21c88c1..ec8a0cd8c432 100644 --- a/mmv1/third_party/terraform/services/container/go/resource_container_cluster.go.tmpl +++ b/mmv1/third_party/terraform/services/container/go/resource_container_cluster.go.tmpl @@ -3882,6 +3882,55 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er log.Printf("[INFO] GKE cluster %s: default-pool setting for insecure_kubelet_readonly_port_enabled updated to %s", d.Id(), it) } } + + if d.HasChange("node_config.0.gcfs_config") { + + defaultPool := "default-pool" + + timeout := d.Timeout(schema.TimeoutCreate) + + nodePoolInfo, err := extractNodePoolInformationFromCluster(d, config, clusterName) + if err != nil { + return err + } + + // Acquire write-lock on nodepool. + npLockKey := nodePoolInfo.nodePoolLockKey(defaultPool) + + gcfsEnabled := d.Get("node_config.0.gcfs_config.0.enabled").(bool) + + // While we're getting the value from the drepcated field in + // node_config.kubelet_config, the actual setting that needs to be updated + // is on the default nodepool. + req := &container.UpdateNodePoolRequest{ + Name: defaultPool, + GcfsConfig: &container.GcfsConfig{ + Enabled: gcfsEnabled, + }, + } + + updateF := func() error { + clusterNodePoolsUpdateCall := config.NewContainerClient(userAgent).Projects.Locations.Clusters.NodePools.Update(nodePoolInfo.fullyQualifiedName(defaultPool), req) + if config.UserProjectOverride { + clusterNodePoolsUpdateCall.Header().Add("X-Goog-User-Project", nodePoolInfo.project) + } + op, err := clusterNodePoolsUpdateCall.Do() + if err != nil { + return err + } + + // Wait until it's updated + return ContainerOperationWait(config, op, nodePoolInfo.project, nodePoolInfo.location, + "updating GKE node pool gcfs_config", userAgent, timeout) + } + + if err := retryWhileIncompatibleOperation(timeout, npLockKey, updateF); err != nil { + return err + } + + log.Printf("[INFO] GKE cluster %s: %s setting for gcfs_config updated to %t", d.Id(), defaultPool, gcfsEnabled) + } + } if d.HasChange("notification_config") { diff --git a/mmv1/third_party/terraform/services/container/go/resource_container_cluster_test.go.tmpl b/mmv1/third_party/terraform/services/container/go/resource_container_cluster_test.go.tmpl index abb1ab9d3d31..afbe1852a239 100644 --- a/mmv1/third_party/terraform/services/container/go/resource_container_cluster_test.go.tmpl +++ b/mmv1/third_party/terraform/services/container/go/resource_container_cluster_test.go.tmpl @@ -1535,6 +1535,49 @@ func TestAccContainerCluster_withNodeConfig(t *testing.T) { }) } +func TestAccContainerCluster_withNodeConfigGcfsConfig(t *testing.T) { + t.Parallel() + clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) + networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster") + subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withNodeConfigGcfsConfig(clusterName, networkName, subnetworkName, false), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, + }, + { + ResourceName: "google_container_cluster.with_node_config_gcfs_config", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + { + Config: testAccContainerCluster_withNodeConfigGcfsConfig(clusterName, networkName, subnetworkName, true), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, + }, + { + ResourceName: "google_container_cluster.with_node_config_gcfs_config", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + }, + }) +} + // Note: Updates for these are currently known to be broken (b/361634104), and // so are not tested here. // They can probably be made similar to, or consolidated with, @@ -3326,6 +3369,32 @@ func TestAccContainerCluster_withAutopilotKubeletConfig(t *testing.T) { }) } +func TestAccContainerCluster_withAutopilot_withNodePoolDefaults(t *testing.T) { + t.Parallel() + + randomSuffix := acctest.RandString(t, 10) + clusterName := fmt.Sprintf("tf-test-cluster-%s", randomSuffix) + networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster") + subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withAutopilot_withNodePoolDefaults(clusterName, networkName, subnetworkName), + }, + { + ResourceName: "google_container_cluster.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + }, + }) +} + func TestAccContainerCluster_withAutopilotResourceManagerTags(t *testing.T) { t.Parallel() @@ -6692,6 +6761,26 @@ resource "google_container_cluster" "with_node_config" { `, clusterName, networkName, subnetworkName) } +func testAccContainerCluster_withNodeConfigGcfsConfig(clusterName, networkName, subnetworkName string, enabled bool) string { + return fmt.Sprintf(` +resource "google_container_cluster" "with_node_config_gcfs_config" { + name = "%s" + location = "us-central1-f" + initial_node_count = 1 + + node_config { + gcfs_config { + enabled = %t + } + } + + deletion_protection = false + network = "%s" + subnetwork = "%s" +} +`, clusterName, enabled, networkName, subnetworkName) +} + func testAccContainerCluster_withNodeConfigKubeletConfigSettings(clusterName, networkName, subnetworkName string) string { return fmt.Sprintf(` resource "google_container_cluster" "with_node_config_kubelet_config_settings" { @@ -10646,6 +10735,25 @@ func testAccContainerCluster_withAutopilotKubeletConfigUpdates(name, insecureKub `, name, insecureKubeletReadonlyPortEnabled) } +func testAccContainerCluster_withAutopilot_withNodePoolDefaults(name, networkName, subnetworkName string) string { + return fmt.Sprintf(` +resource "google_container_cluster" "primary" { + name = "%s" + location = "us-central1" + enable_autopilot = true + + node_pool_defaults { + node_config_defaults { + } + } + + deletion_protection = false + network = "%s" + subnetwork = "%s" + } +`, name, networkName, subnetworkName) +} + func testAccContainerCluster_resourceManagerTags(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string { return fmt.Sprintf(` data "google_project" "project" { diff --git a/mmv1/third_party/terraform/services/container/go/resource_container_node_pool.go.tmpl b/mmv1/third_party/terraform/services/container/go/resource_container_node_pool.go.tmpl index 379a430ddcfe..3a0badc171c7 100644 --- a/mmv1/third_party/terraform/services/container/go/resource_container_node_pool.go.tmpl +++ b/mmv1/third_party/terraform/services/container/go/resource_container_node_pool.go.tmpl @@ -1786,6 +1786,39 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node log.Printf("[INFO] Updated workload_metadata_config for node pool %s", name) } + if d.HasChange(prefix + "node_config.0.gcfs_config") { + gcfsEnabled := bool(d.Get(prefix + "node_config.0.gcfs_config.0.enabled").(bool)) + req := &container.UpdateNodePoolRequest{ + NodePoolId: name, + GcfsConfig: &container.GcfsConfig{ + Enabled: gcfsEnabled, + }, + } + updateF := func() error { + clusterNodePoolsUpdateCall := config.NewContainerClient(userAgent).Projects.Locations.Clusters.NodePools.Update(nodePoolInfo.fullyQualifiedName(name),req) + if config.UserProjectOverride { + clusterNodePoolsUpdateCall.Header().Add("X-Goog-User-Project", nodePoolInfo.project) + } + op, err := clusterNodePoolsUpdateCall.Do() + if err != nil { + return err + } + + // Wait until it's updated + return ContainerOperationWait(config, op, + nodePoolInfo.project, + nodePoolInfo.location, + "updating GKE node pool gcfs_config", userAgent, + timeout) + } + + if err := retryWhileIncompatibleOperation(timeout, npLockKey, updateF); err != nil { + return err + } + + log.Printf("[INFO] Updated gcfs_config for node pool %s", name) + } + if d.HasChange(prefix + "node_config.0.kubelet_config") { req := &container.UpdateNodePoolRequest{ NodePoolId: name, diff --git a/mmv1/third_party/terraform/services/container/go/resource_container_node_pool_test.go.tmpl b/mmv1/third_party/terraform/services/container/go/resource_container_node_pool_test.go.tmpl index 769d43e7d3b0..a066953a01d7 100644 --- a/mmv1/third_party/terraform/services/container/go/resource_container_node_pool_test.go.tmpl +++ b/mmv1/third_party/terraform/services/container/go/resource_container_node_pool_test.go.tmpl @@ -670,6 +670,40 @@ func TestAccContainerNodePool_withCgroupMode(t *testing.T) { }) } +func TestAccContainerNodePool_withHugepageConfig(t *testing.T) { + t.Parallel() + + cluster := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) + np := fmt.Sprintf("tf-test-np-%s", acctest.RandString(t, 10)) + networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster") + subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerNodePool_withHugepageConfig(cluster, np, networkName, subnetworkName, 1), + }, + { + ResourceName: "google_container_node_pool.np", + ImportState: true, + ImportStateVerify: true, + }, + // Perform an update. + { + Config: testAccContainerNodePool_withHugepageConfig(cluster, np, networkName, subnetworkName, 2), + }, + { + ResourceName: "google_container_node_pool.np", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccContainerNodePool_withNetworkConfig(t *testing.T) { t.Parallel() @@ -1674,9 +1708,9 @@ resource "google_container_node_pool" "np" { node_config { machine_type = "n1-standard-8" image_type = "COS_CONTAINERD" - gcfs_config { - enabled = true - } + gcfs_config { + enabled = true + } secondary_boot_disks { disk_image = "" mode = "CONTAINER_IMAGE_CACHE" @@ -1693,9 +1727,9 @@ resource "google_container_node_pool" "np-no-mode" { node_config { machine_type = "n1-standard-8" image_type = "COS_CONTAINERD" - gcfs_config { - enabled = true - } + gcfs_config { + enabled = true + } secondary_boot_disks { disk_image = "" } @@ -1719,10 +1753,14 @@ func TestAccContainerNodePool_gcfsConfig(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccContainerNodePool_gcfsConfig(cluster, np, networkName, subnetworkName, true), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("google_container_node_pool.np", - "node_config.0.gcfs_config.0.enabled", "true"), - ), + }, + { + ResourceName: "google_container_node_pool.np", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccContainerNodePool_gcfsConfig(cluster, np, networkName, subnetworkName, false), }, { ResourceName: "google_container_node_pool.np", @@ -3386,6 +3424,46 @@ resource "google_container_node_pool" "with_tier1_net" { `, network, cluster, np, np, np, np, netTier) } + +func testAccContainerNodePool_withHugepageConfig(cluster, np, networkName, subnetworkName string, hugepage int) string { + return fmt.Sprintf(` +data "google_container_engine_versions" "central1a" { + location = "us-central1-a" +} + +resource "google_container_cluster" "cluster" { + name = "%s" + location = "us-central1-a" + initial_node_count = 1 + min_master_version = data.google_container_engine_versions.central1a.latest_master_version + deletion_protection = false + network = "%s" + subnetwork = "%s" +} + +resource "google_container_node_pool" "np" { + name = "%s" + location = "us-central1-a" + cluster = google_container_cluster.cluster.name + initial_node_count = 1 + node_config { + image_type = "COS_CONTAINERD" + machine_type = "c2d-standard-2" # This is required for hugepage_size_1g https://cloud.google.com/kubernetes-engine/docs/how-to/node-system-config#huge-page-options + linux_node_config { + hugepages_config { + hugepage_size_2m = %d + hugepage_size_1g = %d + } + } + oauth_scopes = [ + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring", + ] + } +} +`, cluster, networkName, subnetworkName, np, hugepage, hugepage) +} + func testAccContainerNodePool_withMultiNicNetworkConfig(cluster, np, network string) string { return fmt.Sprintf(` resource "google_compute_network" "container_network" { diff --git a/mmv1/third_party/terraform/services/container/node_config.go.erb b/mmv1/third_party/terraform/services/container/node_config.go.erb index df45e8d500d9..75a9e3f6a83b 100644 --- a/mmv1/third_party/terraform/services/container/node_config.go.erb +++ b/mmv1/third_party/terraform/services/container/node_config.go.erb @@ -647,6 +647,26 @@ func schemaNodeConfig() *schema.Schema { Description: `cgroupMode specifies the cgroup mode to be used on the node.`, DiffSuppressFunc: tpgresource.EmptyOrDefaultStringSuppress("CGROUP_MODE_UNSPECIFIED"), }, + "hugepages_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: `Amounts for 2M and 1G hugepages.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "hugepage_size_2m": { + Type: schema.TypeInt, + Optional: true, + Description: `Amount of 2M hugepages.`, + }, + "hugepage_size_1g": { + Type: schema.TypeInt, + Optional: true, + Description: `Amount of 1G hugepages.`, + }, + }, + }, + }, }, }, }, @@ -816,7 +836,6 @@ func expandNodeConfigDefaults(configured interface{}) *container.NodeConfigDefau if v, ok := config["insecure_kubelet_readonly_port_enabled"]; ok { nodeConfigDefaults.NodeKubeletConfig = &container.NodeKubeletConfig{ InsecureKubeletReadonlyPortEnabled: expandInsecureKubeletReadonlyPortEnabled(v), - ForceSendFields: []string{"InsecureKubeletReadonlyPortEnabled"}, } } if variant, ok := config["logging_variant"]; ok { @@ -1246,6 +1265,10 @@ func expandLinuxNodeConfig(v interface{}) *container.LinuxNodeConfig { linuxNodeConfig.CgroupMode = cgroupMode } + if v, ok := cfg["hugepages_config"]; ok { + linuxNodeConfig.Hugepages = expandHugepagesConfig(v) + } + return linuxNodeConfig } @@ -1270,6 +1293,32 @@ func expandCgroupMode(cfg map[string]interface{}) string { return cgroupMode.(string) } +func expandHugepagesConfig(v interface{}) *container.HugepagesConfig { + if v == nil { + return nil + } + ls := v.([]interface{}) + if len(ls) == 0 { + return nil + } + if ls[0] == nil { + return &container.HugepagesConfig{} + } + cfg := ls[0].(map[string]interface{}) + + hugepagesConfig := &container.HugepagesConfig{} + + if v, ok := cfg["hugepage_size_2m"]; ok { + hugepagesConfig.HugepageSize2m = int64(v.(int)) + } + + if v, ok := cfg["hugepage_size_1g"]; ok { + hugepagesConfig.HugepageSize1g = int64(v.(int)) + } + + return hugepagesConfig +} + func expandContainerdConfig(v interface{}) *container.ContainerdConfig { if v == nil { return nil @@ -1800,8 +1849,20 @@ func flattenLinuxNodeConfig(c *container.LinuxNodeConfig) []map[string]interface result := []map[string]interface{}{} if c != nil { result = append(result, map[string]interface{}{ - "sysctls": c.Sysctls, - "cgroup_mode": c.CgroupMode, + "sysctls": c.Sysctls, + "cgroup_mode": c.CgroupMode, + "hugepages_config": flattenHugepagesConfig(c.Hugepages), + }) + } + return result +} + +func flattenHugepagesConfig(c *container.HugepagesConfig) []map[string]interface{} { + result := []map[string]interface{}{} + if c != nil { + result = append(result, map[string]interface{}{ + "hugepage_size_2m": c.HugepageSize2m, + "hugepage_size_1g": c.HugepageSize1g, }) } return result diff --git a/mmv1/third_party/terraform/services/container/resource_container_cluster.go.erb b/mmv1/third_party/terraform/services/container/resource_container_cluster.go.erb index 838a636f6e63..a978a3a12aef 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_cluster.go.erb +++ b/mmv1/third_party/terraform/services/container/resource_container_cluster.go.erb @@ -3846,44 +3846,38 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er // Acquire write-lock on nodepool. npLockKey := nodePoolInfo.nodePoolLockKey(defaultPool) - // Note: probably long term this should be handled broadly for all the - // items in kubelet_config in a simpler / DRYer way. + // Still should be further consolidated / DRYed up // See b/361634104 - if d.HasChange("node_config.0.kubelet_config.0.insecure_kubelet_readonly_port_enabled") { - it := d.Get("node_config.0.kubelet_config.0.insecure_kubelet_readonly_port_enabled").(string) - - // While we're getting the value from the drepcated field in - // node_config.kubelet_config, the actual setting that needs to be updated - // is on the default nodepool. - req := &container.UpdateNodePoolRequest{ - Name: defaultPool, - KubeletConfig: &container.NodeKubeletConfig{ - InsecureKubeletReadonlyPortEnabled: expandInsecureKubeletReadonlyPortEnabled(it), - ForceSendFields: []string{"InsecureKubeletReadonlyPortEnabled"}, - }, - } + it := d.Get("node_config.0.kubelet_config") - updateF := func() error { - clusterNodePoolsUpdateCall := config.NewContainerClient(userAgent).Projects.Locations.Clusters.NodePools.Update(nodePoolInfo.fullyQualifiedName(defaultPool), req) - if config.UserProjectOverride { - clusterNodePoolsUpdateCall.Header().Add("X-Goog-User-Project", nodePoolInfo.project) - } - op, err := clusterNodePoolsUpdateCall.Do() - if err != nil { - return err - } + // While we're getting the value from fields in + // node_config.kubelet_config, the actual setting that needs to be + // updated is on the default nodepool. + req := &container.UpdateNodePoolRequest{ + Name: defaultPool, + KubeletConfig: expandKubeletConfig(it), + } - // Wait until it's updated - return ContainerOperationWait(config, op, nodePoolInfo.project, nodePoolInfo.location, - "updating GKE node pool insecure_kubelet_readonly_port_enabled", userAgent, timeout) + updateF := func() error { + clusterNodePoolsUpdateCall := config.NewContainerClient(userAgent).Projects.Locations.Clusters.NodePools.Update(nodePoolInfo.fullyQualifiedName(defaultPool), req) + if config.UserProjectOverride { + clusterNodePoolsUpdateCall.Header().Add("X-Goog-User-Project", nodePoolInfo.project) } - - if err := retryWhileIncompatibleOperation(timeout, npLockKey, updateF); err != nil { + op, err := clusterNodePoolsUpdateCall.Do() + if err != nil { return err } - log.Printf("[INFO] GKE cluster %s: default-pool setting for insecure_kubelet_readonly_port_enabled updated to %s", d.Id(), it) - } + // Wait until it's updated + return ContainerOperationWait(config, op, nodePoolInfo.project, nodePoolInfo.location, + "updating GKE node pool kubelet_config", userAgent, timeout) + } + + if err := retryWhileIncompatibleOperation(timeout, npLockKey, updateF); err != nil { + return err + } + + log.Printf("[INFO] GKE cluster %s: kubelet_config updated", d.Id()) } if d.HasChange("node_config.0.gcfs_config") { diff --git a/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.erb b/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.erb index 3388dba3af02..7ed265e06fa5 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.erb +++ b/mmv1/third_party/terraform/services/container/resource_container_cluster_test.go.erb @@ -1579,12 +1579,7 @@ func TestAccContainerCluster_withNodeConfigGcfsConfig(t *testing.T) { }) } -// Note: Updates for these are currently known to be broken (b/361634104), and -// so are not tested here. -// They can probably be made similar to, or consolidated with, -// TestAccContainerCluster_withInsecureKubeletReadonlyPortEnabledInNodeConfigUpdates -// after that's resolved. -func TestAccContainerCluster_withNodeConfigKubeletConfigSettings(t *testing.T) { +func TestAccContainerCluster_withNodeConfigKubeletConfigSettingsUpdates(t *testing.T) { t.Parallel() clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster") @@ -1596,7 +1591,7 @@ func TestAccContainerCluster_withNodeConfigKubeletConfigSettings(t *testing.T) { CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccContainerCluster_withNodeConfigKubeletConfigSettings(clusterName, networkName, subnetworkName), + Config: testAccContainerCluster_withNodeConfigKubeletConfigSettingsBaseline(clusterName, networkName, subnetworkName), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ acctest.ExpectNoDelete(), @@ -1609,25 +1604,8 @@ func TestAccContainerCluster_withNodeConfigKubeletConfigSettings(t *testing.T) { ImportStateVerify: true, ImportStateVerifyIgnore: []string{"deletion_protection"}, }, - }, - }) -} - -// This is for node_config.kubelet_config, which affects the default node-pool -// (default-pool) when created via the google_container_cluster resource -func TestAccContainerCluster_withInsecureKubeletReadonlyPortEnabledInNodeConfigUpdates(t *testing.T) { - t.Parallel() - clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) - networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster") - subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) - - acctest.VcrTest(t, resource.TestCase{ - PreCheck: func() { acctest.AccTestPreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), - CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), - Steps: []resource.TestStep{ { - Config: testAccContainerCluster_withInsecureKubeletReadonlyPortEnabledInNodeConfig(clusterName, networkName, subnetworkName, "TRUE"), + Config: testAccContainerCluster_withNodeConfigKubeletConfigSettingsUpdates(clusterName, "none", "100ms", "TRUE", networkName, subnetworkName, 2048, true), ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ acctest.ExpectNoDelete(), @@ -1635,16 +1613,21 @@ func TestAccContainerCluster_withInsecureKubeletReadonlyPortEnabledInNodeConfigU }, }, { - ResourceName: "google_container_cluster.with_insecure_kubelet_readonly_port_enabled_in_node_config", + ResourceName: "google_container_cluster.with_node_config_kubelet_config_settings", ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"deletion_protection"}, }, { - Config: testAccContainerCluster_withInsecureKubeletReadonlyPortEnabledInNodeConfig(clusterName, networkName, subnetworkName, "FALSE"), + Config: testAccContainerCluster_withNodeConfigKubeletConfigSettingsUpdates(clusterName, "static", "", "FALSE", networkName, subnetworkName, 1024, true), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + acctest.ExpectNoDelete(), + }, + }, }, { - ResourceName: "google_container_cluster.with_insecure_kubelet_readonly_port_enabled_in_node_config", + ResourceName: "google_container_cluster.with_node_config_kubelet_config_settings", ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"deletion_protection"}, @@ -3370,6 +3353,32 @@ func TestAccContainerCluster_withAutopilotKubeletConfig(t *testing.T) { }) } +func TestAccContainerCluster_withAutopilot_withNodePoolDefaults(t *testing.T) { + t.Parallel() + + randomSuffix := acctest.RandString(t, 10) + clusterName := fmt.Sprintf("tf-test-cluster-%s", randomSuffix) + networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster") + subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withAutopilot_withNodePoolDefaults(clusterName, networkName, subnetworkName), + }, + { + ResourceName: "google_container_cluster.primary", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + }, + }) +} + func TestAccContainerCluster_withAutopilotResourceManagerTags(t *testing.T) { t.Parallel() @@ -6756,7 +6765,7 @@ resource "google_container_cluster" "with_node_config_gcfs_config" { `, clusterName, enabled, networkName, subnetworkName) } -func testAccContainerCluster_withNodeConfigKubeletConfigSettings(clusterName, networkName, subnetworkName string) string { +func testAccContainerCluster_withNodeConfigKubeletConfigSettingsBaseline(clusterName, networkName, subnetworkName string) string { return fmt.Sprintf(` resource "google_container_cluster" "with_node_config_kubelet_config_settings" { name = "%s" @@ -6765,10 +6774,7 @@ resource "google_container_cluster" "with_node_config_kubelet_config_settings" { node_config { kubelet_config { - cpu_manager_policy = "static" - cpu_cfs_quota = true - cpu_cfs_quota_period = "100ms" - pod_pids_limit = 2048 + pod_pids_limit = 1024 } } deletion_protection = false @@ -6778,23 +6784,27 @@ resource "google_container_cluster" "with_node_config_kubelet_config_settings" { `, clusterName, networkName, subnetworkName) } -func testAccContainerCluster_withInsecureKubeletReadonlyPortEnabledInNodeConfig(clusterName, networkName, subnetworkName, insecureKubeletReadonlyPortEnabled string) string { +func testAccContainerCluster_withNodeConfigKubeletConfigSettingsUpdates(clusterName, cpuManagerPolicy, cpuCfsQuotaPeriod, insecureKubeletReadonlyPortEnabled, networkName, subnetworkName string, podPidsLimit int, cpuCfsQuota bool) string { return fmt.Sprintf(` -resource "google_container_cluster" "with_insecure_kubelet_readonly_port_enabled_in_node_config" { +resource "google_container_cluster" "with_node_config_kubelet_config_settings" { name = "%s" location = "us-central1-f" initial_node_count = 1 node_config { kubelet_config { + cpu_manager_policy = "%s" + cpu_cfs_quota = %v + cpu_cfs_quota_period = "%s" insecure_kubelet_readonly_port_enabled = "%s" + pod_pids_limit = %v } } deletion_protection = false - network = "%s" - subnetwork = "%s" + network = "%s" + subnetwork = "%s" } -`, clusterName, insecureKubeletReadonlyPortEnabled, networkName, subnetworkName) +`, clusterName, cpuManagerPolicy, cpuCfsQuota, cpuCfsQuotaPeriod, insecureKubeletReadonlyPortEnabled, podPidsLimit, networkName, subnetworkName) } func testAccContainerCluster_withInsecureKubeletReadonlyPortEnabledInNodePool(clusterName, nodePoolName, networkName, subnetworkName, insecureKubeletReadonlyPortEnabled string) string { @@ -10710,6 +10720,25 @@ func testAccContainerCluster_withAutopilotKubeletConfigUpdates(name, insecureKub `, name, insecureKubeletReadonlyPortEnabled) } +func testAccContainerCluster_withAutopilot_withNodePoolDefaults(name, networkName, subnetworkName string) string { + return fmt.Sprintf(` +resource "google_container_cluster" "primary" { + name = "%s" + location = "us-central1" + enable_autopilot = true + + node_pool_defaults { + node_config_defaults { + } + } + + deletion_protection = false + network = "%s" + subnetwork = "%s" + } +`, name, networkName, subnetworkName) +} + func testAccContainerCluster_resourceManagerTags(projectID, clusterName, networkName, subnetworkName, randomSuffix string) string { return fmt.Sprintf(` data "google_project" "project" { diff --git a/mmv1/third_party/terraform/services/container/resource_container_node_pool_test.go.erb b/mmv1/third_party/terraform/services/container/resource_container_node_pool_test.go.erb index 1a42ea66b7e0..9d0cb361e36b 100644 --- a/mmv1/third_party/terraform/services/container/resource_container_node_pool_test.go.erb +++ b/mmv1/third_party/terraform/services/container/resource_container_node_pool_test.go.erb @@ -671,6 +671,40 @@ func TestAccContainerNodePool_withCgroupMode(t *testing.T) { }) } +func TestAccContainerNodePool_withHugepageConfig(t *testing.T) { + t.Parallel() + + cluster := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) + np := fmt.Sprintf("tf-test-np-%s", acctest.RandString(t, 10)) + networkName := acctest.BootstrapSharedTestNetwork(t, "gke-cluster") + subnetworkName := acctest.BootstrapSubnet(t, "gke-cluster", networkName) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerNodePool_withHugepageConfig(cluster, np, networkName, subnetworkName, 1), + }, + { + ResourceName: "google_container_node_pool.np", + ImportState: true, + ImportStateVerify: true, + }, + // Perform an update. + { + Config: testAccContainerNodePool_withHugepageConfig(cluster, np, networkName, subnetworkName, 2), + }, + { + ResourceName: "google_container_node_pool.np", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccContainerNodePool_withNetworkConfig(t *testing.T) { t.Parallel() @@ -3391,6 +3425,46 @@ resource "google_container_node_pool" "with_tier1_net" { `, network, cluster, np, np, np, np, netTier) } + +func testAccContainerNodePool_withHugepageConfig(cluster, np, networkName, subnetworkName string, hugepage int) string { + return fmt.Sprintf(` +data "google_container_engine_versions" "central1a" { + location = "us-central1-a" +} + +resource "google_container_cluster" "cluster" { + name = "%s" + location = "us-central1-a" + initial_node_count = 1 + min_master_version = data.google_container_engine_versions.central1a.latest_master_version + deletion_protection = false + network = "%s" + subnetwork = "%s" +} + +resource "google_container_node_pool" "np" { + name = "%s" + location = "us-central1-a" + cluster = google_container_cluster.cluster.name + initial_node_count = 1 + node_config { + image_type = "COS_CONTAINERD" + machine_type = "c2d-standard-2" # This is required for hugepage_size_1g https://cloud.google.com/kubernetes-engine/docs/how-to/node-system-config#huge-page-options + linux_node_config { + hugepages_config { + hugepage_size_2m = %d + hugepage_size_1g = %d + } + } + oauth_scopes = [ + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring", + ] + } +} +`, cluster, networkName, subnetworkName, np, hugepage, hugepage) +} + func testAccContainerNodePool_withMultiNicNetworkConfig(cluster, np, network string) string { return fmt.Sprintf(` resource "google_compute_network" "container_network" { diff --git a/mmv1/third_party/terraform/services/dataflow/resource_dataflow_job_test.go.erb b/mmv1/third_party/terraform/services/dataflow/resource_dataflow_job_test.go.erb index 9c31218c3647..9fbd702443fb 100644 --- a/mmv1/third_party/terraform/services/dataflow/resource_dataflow_job_test.go.erb +++ b/mmv1/third_party/terraform/services/dataflow/resource_dataflow_job_test.go.erb @@ -280,12 +280,12 @@ func TestAccDataflowJob_withProviderDefaultLabels(t *testing.T) { resource.TestCheckResourceAttr("google_dataflow_job.big_data", "labels.env", "foo"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "labels.default_expiration_ms", "3600000"), - resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.%", "3"), + resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.%", "4"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.default_key1", "default_value1"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.env", "foo"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.default_expiration_ms", "3600000"), - resource.TestCheckResourceAttr("google_dataflow_job.big_data", "effective_labels.%", "6"), + resource.TestCheckResourceAttr("google_dataflow_job.big_data", "effective_labels.%", "7"), ), }, { @@ -302,12 +302,12 @@ func TestAccDataflowJob_withProviderDefaultLabels(t *testing.T) { resource.TestCheckResourceAttr("google_dataflow_job.big_data", "labels.default_expiration_ms", "3600000"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "labels.default_key1", "value1"), - resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.%", "3"), + resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.%", "4"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.default_key1", "value1"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.env", "foo"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.default_expiration_ms", "3600000"), - resource.TestCheckResourceAttr("google_dataflow_job.big_data", "effective_labels.%", "6"), + resource.TestCheckResourceAttr("google_dataflow_job.big_data", "effective_labels.%", "7"), ), }, { @@ -325,12 +325,12 @@ func TestAccDataflowJob_withProviderDefaultLabels(t *testing.T) { resource.TestCheckResourceAttr("google_dataflow_job.big_data", "labels.default_expiration_ms", "3600000"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "labels.default_key1", "value1"), - resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.%", "3"), + resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.%", "4"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.default_key1", "value1"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.env", "foo"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.default_expiration_ms", "3600000"), - resource.TestCheckResourceAttr("google_dataflow_job.big_data", "effective_labels.%", "6"), + resource.TestCheckResourceAttr("google_dataflow_job.big_data", "effective_labels.%", "7"), ), }, { @@ -347,12 +347,12 @@ func TestAccDataflowJob_withProviderDefaultLabels(t *testing.T) { resource.TestCheckResourceAttr("google_dataflow_job.big_data", "labels.default_expiration_ms", "3600000"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "labels.default_key1", "value1"), - resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.%", "3"), + resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.%", "4"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.default_key1", "value1"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.env", "foo"), resource.TestCheckResourceAttr("google_dataflow_job.big_data", "terraform_labels.default_expiration_ms", "3600000"), - resource.TestCheckResourceAttr("google_dataflow_job.big_data", "effective_labels.%", "6"), + resource.TestCheckResourceAttr("google_dataflow_job.big_data", "effective_labels.%", "7"), ), }, { diff --git a/mmv1/third_party/terraform/services/secretmanagerregional/data_source_secret_manager_regional_secret_version_access.go b/mmv1/third_party/terraform/services/secretmanagerregional/data_source_secret_manager_regional_secret_version_access.go new file mode 100644 index 000000000000..e4de924c3d90 --- /dev/null +++ b/mmv1/third_party/terraform/services/secretmanagerregional/data_source_secret_manager_regional_secret_version_access.go @@ -0,0 +1,161 @@ +package secretmanagerregional + +import ( + "encoding/base64" + "fmt" + "log" + "regexp" + + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DataSourceSecretManagerRegionalRegionalSecretVersionAccess() *schema.Resource { + return &schema.Resource{ + Read: dataSourceSecretManagerRegionalRegionalSecretVersionAccessRead, + Schema: map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "location": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "secret": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName, + }, + "version": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "secret_data": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + }, + } +} +func dataSourceSecretManagerRegionalRegionalSecretVersionAccessRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + secretRegex := regexp.MustCompile("projects/(.+)/locations/(.+)/secrets/(.+)$") + dSecret, ok := d.Get("secret").(string) + if !ok { + return fmt.Errorf("wrong type for secret field (%T), expected string", d.Get("secret")) + } + parts := secretRegex.FindStringSubmatch(dSecret) + + var project string + + // if reference of the secret is provided in the secret field + if len(parts) == 4 { + // Stores value of project to set in state + project = parts[1] + if dProject, ok := d.Get("project").(string); !ok { + return fmt.Errorf("wrong type for project (%T), expected string", d.Get("project")) + } else if dProject != "" && dProject != project { + return fmt.Errorf("project field value (%s) does not match project of secret (%s).", d.Get("project").(string), project) + } + if dLocation, ok := d.Get("location").(string); !ok { + return fmt.Errorf("wrong type for location (%T), expected string", d.Get("location")) + } else if dLocation != "" && dLocation != parts[2] { + return fmt.Errorf("location field value (%s) does not match location of secret (%s).", dLocation, parts[2]) + } + if err := d.Set("location", parts[2]); err != nil { + return fmt.Errorf("error setting location: %s", err) + } + if err := d.Set("secret", parts[3]); err != nil { + return fmt.Errorf("error setting secret: %s", err) + } + } else { // if secret name is provided in the secret field + // Stores value of project to set in state + project, err = tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("error fetching project for Secret: %s", err) + } + if dLocation, ok := d.Get("location").(string); ok && dLocation == "" { + return fmt.Errorf("location must be set when providing only secret name") + } + } + if err := d.Set("project", project); err != nil { + return fmt.Errorf("error setting project: %s", err) + } + + var url string + versionNum := d.Get("version") + + // set version if provided, else set version to latest + if versionNum != "" { + url, err = tpgresource.ReplaceVars(d, config, "{{SecretManagerRegionalBasePath}}projects/{{project}}/locations/{{location}}/secrets/{{secret}}/versions/{{version}}") + if err != nil { + return err + } + } else { + url, err = tpgresource.ReplaceVars(d, config, "{{SecretManagerRegionalBasePath}}projects/{{project}}/locations/{{location}}/secrets/{{secret}}/versions/latest") + if err != nil { + return err + } + } + + url = fmt.Sprintf("%s:access", url) + resp, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: project, + RawURL: url, + UserAgent: userAgent, + }) + + if err != nil { + return fmt.Errorf("error retrieving available secret manager regional secret version access: %s", err.Error()) + } + + nameValue, ok := resp["name"] + if !ok { + return fmt.Errorf("read response didn't contain critical fields. Read may not have succeeded.") + } + if err := d.Set("name", nameValue.(string)); err != nil { + return fmt.Errorf("error setting name: %s", err) + } + + secretVersionRegex := regexp.MustCompile("projects/(.+)/locations/(.+)/secrets/(.+)/versions/(.+)$") + parts = secretVersionRegex.FindStringSubmatch(nameValue.(string)) + if len(parts) != 5 { + return fmt.Errorf("secret name, %s, does not match format, projects/{{project}}/locations/{{location}}/secrets/{{secret}}/versions/{{version}}", nameValue.(string)) + } + + log.Printf("[DEBUG] Received Google SecretManager Version: %q", parts[3]) + + if err := d.Set("version", parts[4]); err != nil { + return fmt.Errorf("error setting version: %s", err) + } + + data := resp["payload"].(map[string]interface{}) + secretData, err := base64.StdEncoding.DecodeString(data["data"].(string)) + if err != nil { + return fmt.Errorf("error decoding secret manager regional secret version data: %s", err.Error()) + } + if err := d.Set("secret_data", string(secretData)); err != nil { + return fmt.Errorf("error setting secret_data: %s", err) + } + + d.SetId(nameValue.(string)) + return nil +} diff --git a/mmv1/third_party/terraform/services/secretmanagerregional/data_source_secret_manager_regional_secret_version_access_test.go b/mmv1/third_party/terraform/services/secretmanagerregional/data_source_secret_manager_regional_secret_version_access_test.go new file mode 100644 index 000000000000..dfd6ab7896bc --- /dev/null +++ b/mmv1/third_party/terraform/services/secretmanagerregional/data_source_secret_manager_regional_secret_version_access_test.go @@ -0,0 +1,178 @@ +package secretmanagerregional_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccDataSourceSecretManagerRegionalRegionalSecretVersionAccess_basicWithResourceReference(t *testing.T) { + t.Parallel() + + randomString := acctest.RandString(t, 10) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckSecretManagerRegionalRegionalSecretVersionDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceSecretManagerRegionalRegionalSecretVersionAccess_basicWithResourceReference(randomString), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataSourceSecretManagerRegionalRegionalSecretVersion("data.google_secret_manager_regional_secret_version_access.basic-1", "1"), + ), + }, + }, + }) +} + +func TestAccDataSourceSecretManagerRegionalRegionalSecretVersionAccess_basicWithSecretName(t *testing.T) { + t.Parallel() + + randomString := acctest.RandString(t, 10) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckSecretManagerRegionalRegionalSecretVersionDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceSecretManagerRegionalRegionalSecretVersionAccess_basicWithSecretName(randomString), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataSourceSecretManagerRegionalRegionalSecretVersion("data.google_secret_manager_regional_secret_version_access.basic-2", "1"), + ), + }, + }, + }) +} + +func TestAccDataSourceSecretManagerRegionalRegionalSecretVersionAccess_latest(t *testing.T) { + t.Parallel() + + randomString := acctest.RandString(t, 10) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckSecretManagerRegionalRegionalSecretVersionDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceSecretManagerRegionalRegionalSecretVersionAccess_latest(randomString), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataSourceSecretManagerRegionalRegionalSecretVersion("data.google_secret_manager_regional_secret_version_access.latest-1", "2"), + ), + }, + }, + }) +} + +func TestAccDataSourceSecretManagerRegionalRegionalSecretVersionAccess_versionField(t *testing.T) { + t.Parallel() + + randomString := acctest.RandString(t, 10) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckSecretManagerRegionalRegionalSecretVersionDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceSecretManagerRegionalRegionalSecretVersionAccess_versionField(randomString), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataSourceSecretManagerRegionalRegionalSecretVersion("data.google_secret_manager_regional_secret_version_access.version-access", "1"), + ), + }, + }, + }) +} + +func testAccDataSourceSecretManagerRegionalRegionalSecretVersionAccess_basicWithResourceReference(randomString string) string { + return fmt.Sprintf(` +resource "google_secret_manager_regional_secret" "secret-basic" { + secret_id = "tf-test-secret-version-%s" + location = "us-central1" +} + +resource "google_secret_manager_regional_secret_version" "secret-version-basic" { + secret = google_secret_manager_regional_secret.secret-basic.id + secret_data = "my-tf-test-secret-%s" +} + +data "google_secret_manager_regional_secret_version_access" "basic-1" { + secret = google_secret_manager_regional_secret_version.secret-version-basic.secret +} +`, randomString, randomString) +} + +func testAccDataSourceSecretManagerRegionalRegionalSecretVersionAccess_basicWithSecretName(randomString string) string { + return fmt.Sprintf(` +resource "google_secret_manager_regional_secret" "secret-basic" { + secret_id = "tf-test-secret-version-%s" + location = "us-central1" +} + +resource "google_secret_manager_regional_secret_version" "secret-version-basic" { + secret = google_secret_manager_regional_secret.secret-basic.id + secret_data = "my-tf-test-secret-%s" +} + +data "google_secret_manager_regional_secret_version_access" "basic-2" { + secret = google_secret_manager_regional_secret.secret-basic.secret_id + location = google_secret_manager_regional_secret_version.secret-version-basic.location +} +`, randomString, randomString) +} + +func testAccDataSourceSecretManagerRegionalRegionalSecretVersionAccess_latest(randomString string) string { + return fmt.Sprintf(` +resource "google_secret_manager_regional_secret" "secret-basic" { + secret_id = "tf-test-secret-version-%s" + location = "us-central1" +} + +resource "google_secret_manager_regional_secret_version" "secret-version-basic-1" { + secret = google_secret_manager_regional_secret.secret-basic.id + secret_data = "my-tf-test-secret-first" +} + +resource "google_secret_manager_regional_secret_version" "secret-version-basic-2" { + secret = google_secret_manager_regional_secret.secret-basic.id + secret_data = "my-tf-test-secret-second" + + depends_on = [google_secret_manager_regional_secret_version.secret-version-basic-1] +} + +data "google_secret_manager_regional_secret_version_access" "latest-1" { + secret = google_secret_manager_regional_secret_version.secret-version-basic-2.secret +} +`, randomString) +} + +func testAccDataSourceSecretManagerRegionalRegionalSecretVersionAccess_versionField(randomString string) string { + return fmt.Sprintf(` +resource "google_secret_manager_regional_secret" "secret-basic" { + secret_id = "tf-test-secret-version-%s" + location = "us-central1" +} + +resource "google_secret_manager_regional_secret_version" "secret-version-basic-1" { + secret = google_secret_manager_regional_secret.secret-basic.id + secret_data = "my-tf-test-secret-first" +} + +resource "google_secret_manager_regional_secret_version" "secret-version-basic-2" { + secret = google_secret_manager_regional_secret.secret-basic.id + secret_data = "my-tf-test-secret-second" + + depends_on = [google_secret_manager_regional_secret_version.secret-version-basic-1] +} + +data "google_secret_manager_regional_secret_version_access" "version-access" { + secret = google_secret_manager_regional_secret_version.secret-version-basic-2.secret + version = "1" +} +`, randomString) +} diff --git a/mmv1/third_party/terraform/services/secretmanagerregional/data_source_secret_manager_regional_secrets.go b/mmv1/third_party/terraform/services/secretmanagerregional/data_source_secret_manager_regional_secrets.go new file mode 100644 index 000000000000..b2cda48defee --- /dev/null +++ b/mmv1/third_party/terraform/services/secretmanagerregional/data_source_secret_manager_regional_secrets.go @@ -0,0 +1,176 @@ +package secretmanagerregional + +import ( + "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func DataSourceSecretManagerRegionalRegionalSecrets() *schema.Resource { + dsSchema := tpgresource.DatasourceSchemaFromResourceSchema(ResourceSecretManagerRegionalRegionalSecret().Schema) + return &schema.Resource{ + Read: dataSourceSecretManagerRegionalRegionalSecretsRead, + Schema: map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "location": { + Type: schema.TypeString, + Required: true, + }, + "filter": { + Type: schema.TypeString, + Description: `Filter string, adhering to the rules in List-operation filtering (https://cloud.google.com/secret-manager/docs/filtering). +List only secrets matching the filter. If filter is empty, all regional secrets are listed from the specified location.`, + Optional: true, + }, + "secrets": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: dsSchema, + }, + }, + }, + } +} + +func dataSourceSecretManagerRegionalRegionalSecretsRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{SecretManagerRegionalBasePath}}projects/{{project}}/locations/{{location}}/secrets") + if err != nil { + return err + } + + filter, has_filter := d.GetOk("filter") + + if has_filter { + url, err = transport_tpg.AddQueryParams(url, map[string]string{"filter": filter.(string)}) + if err != nil { + return err + } + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Secret: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + // To handle pagination locally + allSecrets := make([]interface{}, 0) + token := "" + for paginate := true; paginate; { + if token != "" { + url, err = transport_tpg.AddQueryParams(url, map[string]string{"pageToken": token}) + if err != nil { + return err + } + } + secrets, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("SecretManagerRegionalSecrets %q", d.Id())) + } + secretsInterface := secrets["secrets"] + if secretsInterface == nil { + break + } + allSecrets = append(allSecrets, secretsInterface.([]interface{})...) + tokenInterface := secrets["nextPageToken"] + if tokenInterface == nil { + paginate = false + } else { + paginate = true + token = tokenInterface.(string) + } + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("error setting project: %s", err) + } + + if err := d.Set("filter", filter); err != nil { + return fmt.Errorf("error setting filter: %s", err) + } + + if err := d.Set("secrets", flattenSecretManagerRegionalRegionalSecretsSecrets(allSecrets, d, config)); err != nil { + return fmt.Errorf("error setting secrets: %s", err) + } + + // Store the ID now + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/secrets") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + if has_filter { + id += "/filter=" + filter.(string) + } + d.SetId(id) + + return nil +} + +func flattenSecretManagerRegionalRegionalSecretsSecrets(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + + transformed = append(transformed, map[string]interface{}{ + "annotations": flattenSecretManagerRegionalRegionalSecretEffectiveAnnotations(original["annotations"], d, config), + "effective_annotations": flattenSecretManagerRegionalRegionalSecretEffectiveAnnotations(original["annotations"], d, config), + "expire_time": flattenSecretManagerRegionalRegionalSecretExpireTime(original["expireTime"], d, config), + "labels": flattenSecretManagerRegionalRegionalSecretEffectiveLabels(original["labels"], d, config), + "effective_labels": flattenSecretManagerRegionalRegionalSecretEffectiveLabels(original["labels"], d, config), + "terraform_labels": flattenSecretManagerRegionalRegionalSecretEffectiveLabels(original["labels"], d, config), + "version_aliases": flattenSecretManagerRegionalRegionalSecretVersionAliases(original["versionAliases"], d, config), + "rotation": flattenSecretManagerRegionalRegionalSecretRotation(original["rotation"], d, config), + "topics": flattenSecretManagerRegionalRegionalSecretTopics(original["topics"], d, config), + "version_destroy_ttl": flattenSecretManagerRegionalRegionalSecretVersionDestroyTtl(original["versionDestroyTtl"], d, config), + "customer_managed_encryption": flattenSecretManagerRegionalRegionalSecretCustomerManagedEncryption(original["customerManagedEncryption"], d, config), + "create_time": flattenSecretManagerRegionalRegionalSecretCreateTime(original["createTime"], d, config), + "name": flattenSecretManagerRegionalRegionalSecretName(original["name"], d, config), + "project": getDataFromName(original["name"], 1), + "location": getDataFromName(original["name"], 3), + "secret_id": getDataFromName(original["name"], 5), + }) + } + return transformed +} + +func getDataFromName(v interface{}, part int) string { + name := v.(string) + split := strings.Split(name, "/") + return split[part] +} diff --git a/mmv1/third_party/terraform/services/secretmanagerregional/data_source_secret_manager_regional_secrets_test.go b/mmv1/third_party/terraform/services/secretmanagerregional/data_source_secret_manager_regional_secrets_test.go new file mode 100644 index 000000000000..a3ed4127b7ac --- /dev/null +++ b/mmv1/third_party/terraform/services/secretmanagerregional/data_source_secret_manager_regional_secrets_test.go @@ -0,0 +1,257 @@ +package secretmanagerregional_test + +import ( + "errors" + "fmt" + "strconv" + + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccDataSourceSecretManagerRegionalRegionalSecrets_basic(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckSecretManagerRegionalRegionalSecretDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceSecretManagerRegionalRegionalSecrets_basic(context), + Check: resource.ComposeTestCheckFunc( + checkListDataSourceStateMatchesResourceStateWithIgnores( + "data.google_secret_manager_regional_secrets.foo", + "google_secret_manager_regional_secret.foo", + map[string]struct{}{ + "id": {}, + "project": {}, + }, + ), + ), + }, + }, + }) +} + +func testAccDataSourceSecretManagerRegionalRegionalSecrets_basic(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" { + add_terraform_attribution_label = false +} + +resource "google_secret_manager_regional_secret" "foo" { + secret_id = "tf-test-secret-%{random_suffix}" + location = "us-central1" + + labels = { + label = "my-label" + } + + annotations = { + key1 = "value1" + } +} + +data "google_secret_manager_regional_secrets" "foo" { + location = "us-central1" + depends_on = [ + google_secret_manager_regional_secret.foo + ] +} +`, context) +} + +func TestAccDataSourceSecretManagerRegionalRegionalSecrets_filter(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckSecretManagerRegionalRegionalSecretDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceSecretManagerRegionalRegionalSecrets_filter(context), + Check: resource.ComposeTestCheckFunc( + checkListDataSourceStateMatchesResourceStateWithIgnoresForAppliedFilter( + "data.google_secret_manager_regional_secrets.foo", + "google_secret_manager_regional_secret.foo", + "google_secret_manager_regional_secret.bar", + map[string]struct{}{ + "id": {}, + "project": {}, + }, + ), + ), + }, + }, + }) +} + +func testAccDataSourceSecretManagerRegionalRegionalSecrets_filter(context map[string]interface{}) string { + return acctest.Nprintf(` +provider "google" { + add_terraform_attribution_label = false +} + +resource "google_secret_manager_regional_secret" "foo" { + secret_id = "tf-test-secret-1-%{random_suffix}" + location = "us-central1" + + labels = { + label = "my-label1" + } + + annotations = { + key1 = "value1" + } +} + +resource "google_secret_manager_regional_secret" "bar" { + secret_id = "tf-test-secret-2-%{random_suffix}" + location = "us-central1" + + labels = { + label= "my-label2" + } + + annotations = { + key1 = "value1" + } +} + +data "google_secret_manager_regional_secrets" "foo" { + location = "us-central1" + filter = "labels.label=my-label1" + depends_on = [ + google_secret_manager_regional_secret.foo, + google_secret_manager_regional_secret.bar + ] +} +`, context) +} + +// This function checks data source state matches for resourceName secret manager regional secret state +func checkListDataSourceStateMatchesResourceStateWithIgnores(dataSourceName, resourceName string, ignoreFields map[string]struct{}) func(*terraform.State) error { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[dataSourceName] + if !ok { + return fmt.Errorf("can't find %s in state", dataSourceName) + } + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("can't find %s in state", resourceName) + } + + dsAttr := ds.Primary.Attributes + rsAttr := rs.Primary.Attributes + + err := checkFieldsMatchForDataSourceStateAndResourceState(dsAttr, rsAttr, ignoreFields) + if err != nil { + return err + } + return nil + } +} + +// This function checks whether all the attributes of the secret manager secret resource and the attributes of the secret manager secret inside the data source list are the same +func checkFieldsMatchForDataSourceStateAndResourceState(dsAttr, rsAttr map[string]string, ignoreFields map[string]struct{}) error { + totalSecrets, err := strconv.Atoi(dsAttr["secrets.#"]) + if err != nil { + return errors.New("Couldn't convert length of secrets list to integer") + } + index := "-1" + for i := 0; i < totalSecrets; i++ { + if dsAttr["secrets."+strconv.Itoa(i)+".name"] == rsAttr["name"] { + index = strconv.Itoa(i) + } + } + + if index == "-1" { + return errors.New("The newly created secret is not found in the data source") + } + + errMsg := "" + // Data sources are often derived from resources, so iterate over the resource fields to + // make sure all fields are accounted for in the data source. + // If a field exists in the data source but not in the resource, its expected value should + // be checked separately. + for k := range rsAttr { + if _, ok := ignoreFields[k]; ok { + continue + } + if k == "%" { + continue + } + if dsAttr["secrets."+index+"."+k] != rsAttr[k] { + // ignore data sources where an empty list is being compared against a null list. + if k[len(k)-1:] == "#" && (dsAttr["secrets."+index+"."+k] == "" || dsAttr["secrets."+index+"."+k] == "0") && (rsAttr[k] == "" || rsAttr[k] == "0") { + continue + } + errMsg += fmt.Sprintf("%s is %s; want %s\n", k, dsAttr["secrets."+index+"."+k], rsAttr[k]) + } + } + + if errMsg != "" { + return errors.New(errMsg) + } + + return nil +} + +// This function checks state match for resourceName and asserts the absence of resourceName2 in data source +func checkListDataSourceStateMatchesResourceStateWithIgnoresForAppliedFilter(dataSourceName, resourceName, resourceName2 string, ignoreFields map[string]struct{}) func(*terraform.State) error { + return func(s *terraform.State) error { + ds, ok := s.RootModule().Resources[dataSourceName] + if !ok { + return fmt.Errorf("can't find %s in state", dataSourceName) + } + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("can't find %s in state", resourceName) + } + + rs2, ok := s.RootModule().Resources[resourceName2] + if !ok { + return fmt.Errorf("can't find %s in state", resourceName2) + } + + dsAttr := ds.Primary.Attributes + rsAttr := rs.Primary.Attributes + rsAttr2 := rs2.Primary.Attributes + + err := checkFieldsMatchForDataSourceStateAndResourceState(dsAttr, rsAttr, ignoreFields) + if err != nil { + return err + } + err = checkResourceAbsentInDataSourceAfterFilterApplied(dsAttr, rsAttr2) + return err + } +} + +// This function asserts the absence of the secret manager secret resource which would not be included in the data source list due to the filter applied. +func checkResourceAbsentInDataSourceAfterFilterApplied(dsAttr, rsAttr map[string]string) error { + totalSecrets, err := strconv.Atoi(dsAttr["secrets.#"]) + if err != nil { + return errors.New("Couldn't convert length of secrets list to integer") + } + for i := 0; i < totalSecrets; i++ { + if dsAttr["secrets."+strconv.Itoa(i)+".name"] == rsAttr["name"] { + return errors.New("The resource is present in the data source even after the filter is applied") + } + } + return nil +} diff --git a/mmv1/third_party/terraform/services/secretmanagerregional/go/resource_secret_manager_regional_secret_test.go b/mmv1/third_party/terraform/services/secretmanagerregional/go/resource_secret_manager_regional_secret_test.go index f58ab01270b7..0324798304b8 100644 --- a/mmv1/third_party/terraform/services/secretmanagerregional/go/resource_secret_manager_regional_secret_test.go +++ b/mmv1/third_party/terraform/services/secretmanagerregional/go/resource_secret_manager_regional_secret_test.go @@ -499,58 +499,57 @@ func TestAccSecretManagerRegionalRegionalSecret_versionDestroyTtlUpdate(t *testi }) } -// TODO: Uncomment once google_secret_manager_regional_secret_version is added -// func TestAccSecretManagerRegionalRegionalSecret_versionAliasesUpdate(t *testing.T) { -// t.Parallel() -// -// context := map[string]interface{}{ -// "random_suffix": acctest.RandString(t, 10), -// } -// -// acctest.VcrTest(t, resource.TestCase{ -// PreCheck: func() { acctest.AccTestPreCheck(t) }, -// ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), -// CheckDestroy: testAccCheckSecretManagerRegionalRegionalSecretDestroyProducer(t), -// Steps: []resource.TestStep{ -// { -// Config: testAccSecretManagerRegionalSecret_basicRegionalSecretWithVersions(context), -// }, -// { -// ResourceName: "google_secret_manager_regional_secret.regional-secret-with-version-aliases", -// ImportState: true, -// ImportStateVerify: true, -// ImportStateVerifyIgnore: []string{"ttl", "annotations", "labels", "location", "secret_id", "terraform_labels"}, -// }, -// { -// Config: testAccSecretManagerRegionalSecret_versionAliasesBasic(context), -// }, -// { -// ResourceName: "google_secret_manager_regional_secret.regional-secret-with-version-aliases", -// ImportState: true, -// ImportStateVerify: true, -// ImportStateVerifyIgnore: []string{"ttl", "annotations", "labels", "location", "secret_id", "terraform_labels"}, -// }, -// { -// Config: testAccSecretManagerRegionalSecret_versionAliasesUpdate(context), -// }, -// { -// ResourceName: "google_secret_manager_regional_secret.regional-secret-with-version-aliases", -// ImportState: true, -// ImportStateVerify: true, -// ImportStateVerifyIgnore: []string{"ttl", "annotations", "labels", "location", "secret_id", "terraform_labels"}, -// }, -// { -// Config: testAccSecretManagerRegionalSecret_basicRegionalSecretWithVersions(context), -// }, -// { -// ResourceName: "google_secret_manager_regional_secret.regional-secret-with-version-aliases", -// ImportState: true, -// ImportStateVerify: true, -// ImportStateVerifyIgnore: []string{"ttl", "annotations", "labels", "location", "secret_id", "terraform_labels"}, -// }, -// }, -// }) -// } +func TestAccSecretManagerRegionalRegionalSecret_versionAliasesUpdate(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckSecretManagerRegionalRegionalSecretDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccSecretManagerRegionalSecret_basicRegionalSecretWithVersions(context), + }, + { + ResourceName: "google_secret_manager_regional_secret.regional-secret-with-version-aliases", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"annotations", "labels", "location", "secret_id", "terraform_labels"}, + }, + { + Config: testAccSecretManagerRegionalSecret_versionAliasesBasic(context), + }, + { + ResourceName: "google_secret_manager_regional_secret.regional-secret-with-version-aliases", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"annotations", "labels", "location", "secret_id", "terraform_labels"}, + }, + { + Config: testAccSecretManagerRegionalSecret_versionAliasesUpdate(context), + }, + { + ResourceName: "google_secret_manager_regional_secret.regional-secret-with-version-aliases", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"annotations", "labels", "location", "secret_id", "terraform_labels"}, + }, + { + Config: testAccSecretManagerRegionalSecret_basicRegionalSecretWithVersions(context), + }, + { + ResourceName: "google_secret_manager_regional_secret.regional-secret-with-version-aliases", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"annotations", "labels", "location", "secret_id", "terraform_labels"}, + }, + }, + }) +} func testAccSecretManagerRegionalSecret_basic(context map[string]interface{}) string { return acctest.Nprintf(` @@ -1182,130 +1181,129 @@ resource "google_secret_manager_regional_secret" "regional-secret-with-version-d `, context) } -// TODO: Uncomment once google_secret_manager_regional_secret_version is added -// func testAccSecretManagerRegionalSecret_basicRegionalSecretWithVersions(context map[string]interface{}) string { -// return acctest.Nprintf(` -// resource "google_secret_manager_regional_secret" "regional-secret-with-version-aliases" { -// secret_id = "tf-test-reg-secret%{random_suffix}" -// location = "us-central1" -// -// labels = { -// mylabel = "mykey" -// } -// } -// -// resource "google_secret_manager_regional_secret_version" "reg-secret-version-1" { -// secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id -// -// secret_data = "very secret data keep it down %{random_suffix}-1" -// } -// -// resource "google_secret_manager_regional_secret_version" "reg-secret-version-2" { -// secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id -// -// secret_data = "very secret data keep it down %{random_suffix}-2" -// } -// -// resource "google_secret_manager_regional_secret_version" "reg-secret-version-3" { -// secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id -// -// secret_data = "very secret data keep it down %{random_suffix}-3" -// } -// -// resource "google_secret_manager_regional_secret_version" "reg-secret-version-4" { -// secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id -// -// secret_data = "very secret data keep it down %{random_suffix}-4" -// } -// `, context) -// } -// -// func testAccSecretManagerRegionalSecret_versionAliasesBasic(context map[string]interface{}) string { -// return acctest.Nprintf(` -// resource "google_secret_manager_regional_secret" "regional-secret-with-version-aliases" { -// secret_id = "tf-test-reg-secret%{random_suffix}" -// location = "us-central1" -// -// version_aliases = { -// firstalias = "1", -// secondalias = "2", -// thirdalias = "3", -// otheralias = "2", -// somealias = "3" -// } -// -// labels = { -// mylabel = "mykey" -// } -// } -// -// resource "google_secret_manager_regional_secret_version" "reg-secret-version-1" { -// secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id -// -// secret_data = "very secret data keep it down %{random_suffix}-1" -// } -// -// resource "google_secret_manager_regional_secret_version" "reg-secret-version-2" { -// secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id -// -// secret_data = "very secret data keep it down %{random_suffix}-2" -// } -// -// resource "google_secret_manager_regional_secret_version" "reg-secret-version-3" { -// secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id -// -// secret_data = "very secret data keep it down %{random_suffix}-3" -// } -// -// resource "google_secret_manager_regional_secret_version" "reg-secret-version-4" { -// secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id -// -// secret_data = "very secret data keep it down %{random_suffix}-4" -// } -// `, context) -// } -// -// func testAccSecretManagerRegionalSecret_versionAliasesUpdate(context map[string]interface{}) string { -// return acctest.Nprintf(` -// resource "google_secret_manager_regional_secret" "regional-secret-with-version-aliases" { -// secret_id = "tf-test-reg-secret%{random_suffix}" -// location = "us-central1" -// -// version_aliases = { -// firstalias = "1", -// secondaliasupdated = "2", -// otheralias = "1", -// somealias = "3", -// fourthalias = "4" -// } -// -// labels = { -// mylabel = "mykey" -// } -// } -// -// resource "google_secret_manager_regional_secret_version" "reg-secret-version-1" { -// secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id -// -// secret_data = "very secret data keep it down %{random_suffix}-1" -// } -// -// resource "google_secret_manager_regional_secret_version" "reg-secret-version-2" { -// secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id -// -// secret_data = "very secret data keep it down %{random_suffix}-2" -// } -// -// resource "google_secret_manager_regional_secret_version" "reg-secret-version-3" { -// secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id -// -// secret_data = "very secret data keep it down %{random_suffix}-3" -// } -// -// resource "google_secret_manager_regional_secret_version" "reg-secret-version-4" { -// secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id -// -// secret_data = "very secret data keep it down %{random_suffix}-4" -// } -// `, context) -// } +func testAccSecretManagerRegionalSecret_basicRegionalSecretWithVersions(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_secret_manager_regional_secret" "regional-secret-with-version-aliases" { + secret_id = "tf-test-reg-secret%{random_suffix}" + location = "us-central1" + + labels = { + mylabel = "mykey" + } +} + +resource "google_secret_manager_regional_secret_version" "reg-secret-version-1" { + secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id + + secret_data = "very secret data keep it down %{random_suffix}-1" +} + +resource "google_secret_manager_regional_secret_version" "reg-secret-version-2" { + secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id + + secret_data = "very secret data keep it down %{random_suffix}-2" +} + +resource "google_secret_manager_regional_secret_version" "reg-secret-version-3" { + secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id + + secret_data = "very secret data keep it down %{random_suffix}-3" +} + +resource "google_secret_manager_regional_secret_version" "reg-secret-version-4" { + secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id + + secret_data = "very secret data keep it down %{random_suffix}-4" +} +`, context) +} + +func testAccSecretManagerRegionalSecret_versionAliasesBasic(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_secret_manager_regional_secret" "regional-secret-with-version-aliases" { + secret_id = "tf-test-reg-secret%{random_suffix}" + location = "us-central1" + + version_aliases = { + firstalias = "1", + secondalias = "2", + thirdalias = "3", + otheralias = "2", + somealias = "3" + } + + labels = { + mylabel = "mykey" + } +} + +resource "google_secret_manager_regional_secret_version" "reg-secret-version-1" { + secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id + + secret_data = "very secret data keep it down %{random_suffix}-1" +} + +resource "google_secret_manager_regional_secret_version" "reg-secret-version-2" { + secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id + + secret_data = "very secret data keep it down %{random_suffix}-2" +} + +resource "google_secret_manager_regional_secret_version" "reg-secret-version-3" { + secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id + + secret_data = "very secret data keep it down %{random_suffix}-3" +} + +resource "google_secret_manager_regional_secret_version" "reg-secret-version-4" { + secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id + + secret_data = "very secret data keep it down %{random_suffix}-4" +} +`, context) +} + +func testAccSecretManagerRegionalSecret_versionAliasesUpdate(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_secret_manager_regional_secret" "regional-secret-with-version-aliases" { + secret_id = "tf-test-reg-secret%{random_suffix}" + location = "us-central1" + + version_aliases = { + firstalias = "1", + secondaliasupdated = "2", + otheralias = "1", + somealias = "3", + fourthalias = "4" + } + + labels = { + mylabel = "mykey" + } +} + +resource "google_secret_manager_regional_secret_version" "reg-secret-version-1" { + secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id + + secret_data = "very secret data keep it down %{random_suffix}-1" +} + +resource "google_secret_manager_regional_secret_version" "reg-secret-version-2" { + secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id + + secret_data = "very secret data keep it down %{random_suffix}-2" +} + +resource "google_secret_manager_regional_secret_version" "reg-secret-version-3" { + secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id + + secret_data = "very secret data keep it down %{random_suffix}-3" +} + +resource "google_secret_manager_regional_secret_version" "reg-secret-version-4" { + secret = google_secret_manager_regional_secret.regional-secret-with-version-aliases.id + + secret_data = "very secret data keep it down %{random_suffix}-4" +} +`, context) +} diff --git a/mmv1/third_party/terraform/services/secretmanagerregional/go/resource_secret_manager_regional_secret_version_test.go b/mmv1/third_party/terraform/services/secretmanagerregional/go/resource_secret_manager_regional_secret_version_test.go new file mode 100644 index 000000000000..23af25d99c88 --- /dev/null +++ b/mmv1/third_party/terraform/services/secretmanagerregional/go/resource_secret_manager_regional_secret_version_test.go @@ -0,0 +1,140 @@ +package secretmanagerregional_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccSecretManagerRegionalRegionalSecretVersion_update(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckSecretManagerRegionalRegionalSecretVersionDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccSecretManagerRegionalRegionalSecretVersion_basic(context), + }, + { + ResourceName: "google_secret_manager_regional_secret_version.secret-version-basic", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccSecretManagerRegionalRegionalSecretVersion_disable(context), + }, + { + ResourceName: "google_secret_manager_regional_secret_version.secret-version-basic", + ImportState: true, + ImportStateVerify: true, + // at this point the secret data is disabled and so reading the data on import will + // give an empty string + ImportStateVerifyIgnore: []string{"secret_data"}, + }, + { + Config: testAccSecretManagerRegionalRegionalSecretVersion_basic(context), + }, + { + ResourceName: "google_secret_manager_regional_secret_version.secret-version-basic", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccSecretManagerRegionalRegionalSecretVersion_cmekOutputOnly(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "kms_key_name": acctest.BootstrapKMSKeyWithPurposeInLocationAndName(t, "ENCRYPT_DECRYPT", "us-central1", "tf-secret-manager-managed-central-key5").CryptoKey.Name, + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckSecretManagerRegionalRegionalSecretVersionDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccSecretManagerRegionalRegionalSecretVersion_cmekOutputOnly(context), + }, + { + ResourceName: "google_secret_manager_regional_secret_version.secret-version-cmek", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccSecretManagerRegionalRegionalSecretVersion_basic(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_secret_manager_regional_secret" "secret-basic" { + secret_id = "tf-test-secret-version-%{random_suffix}" + location = "us-central1" + labels = { + label = "my-label" + } +} + +resource "google_secret_manager_regional_secret_version" "secret-version-basic" { + secret = google_secret_manager_regional_secret.secret-basic.name + secret_data = "my-tf-test-secret%{random_suffix}" + enabled = true +} +`, context) +} + +func testAccSecretManagerRegionalRegionalSecretVersion_disable(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_secret_manager_regional_secret" "secret-basic" { + secret_id = "tf-test-secret-version-%{random_suffix}" + location = "us-central1" + labels = { + label = "my-label" + } +} + +resource "google_secret_manager_regional_secret_version" "secret-version-basic" { + secret = google_secret_manager_regional_secret.secret-basic.name + secret_data = "my-tf-test-secret%{random_suffix}" + enabled = false +} +`, context) +} + +func testAccSecretManagerRegionalRegionalSecretVersion_cmekOutputOnly(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_project" "project-ds" {} + +resource "google_kms_crypto_key_iam_member" "kms-secret-binding-reg-sec-ver" { + crypto_key_id = "%{kms_key_name}" + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:service-${data.google_project.project-ds.number}@gcp-sa-secretmanager.iam.gserviceaccount.com" +} + +resource "google_secret_manager_regional_secret" "regional-secret-reg-sec-ver" { + secret_id = "tf-test-reg-secret%{random_suffix}" + location = "us-central1" + + customer_managed_encryption { + kms_key_name = "%{kms_key_name}" + } + + depends_on = [ google_kms_crypto_key_iam_member.kms-secret-binding-reg-sec-ver ] +} + +resource "google_secret_manager_regional_secret_version" "secret-version-cmek" { + secret = google_secret_manager_regional_secret.regional-secret-reg-sec-ver.name + secret_data = "my-tf-test-secret%{random_suffix}" +} +`, context) +} diff --git a/mmv1/third_party/terraform/website/docs/d/secret_manager_regional_secret_version_access.html.markdown b/mmv1/third_party/terraform/website/docs/d/secret_manager_regional_secret_version_access.html.markdown new file mode 100644 index 000000000000..bfb607fc7dfd --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/secret_manager_regional_secret_version_access.html.markdown @@ -0,0 +1,44 @@ +--- +subcategory: "Secret Manager" +page_title: "Google: google_secret_manager_regional_secret_version_access" +description: |- + Get a payload of Secret Manager regional secret's version. +--- + +# google_secret_manager_regional_secret_version_access + +Get the value from a Secret Manager regional secret version. This is similar to the [google_secret_manager_regional_secret_version](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/secret_manager_regional_secret_version) datasource, but it only requires the [Secret Manager Secret Accessor](https://cloud.google.com/secret-manager/docs/access-control#secretmanager.secretAccessor) role. For more information see the [official documentation](https://cloud.google.com/secret-manager/docs/regional-secrets-overview) and [API](https://cloud.google.com/secret-manager/docs/reference/rest/v1/projects.secrets.versions/access). + +## Example Usage + +```hcl +data "google_secret_manager_regional_secret_version_access" "latest" { + secret = "my-secret" + location = "us-central1" +} +``` + +## Argument Reference + +The following arguments are supported: + +- `project` - (Optional) The project to get the secret version for. If it + is not provided, the provider project is used. + +- `secret` - (Required) The regional secret to get the secret version for. + This can be either the reference of the regional secret as in `projects/{{project}}/locations/{{location}}/secrets/{{secret_id}}` or only the name of the regional secret as in `{{secret_id}}`. If only the name of the regional secret is provided, the location must also be provided. + +- `location` - (Optional) Location of Secret Manager regional secret resource. + It must be provided when the `secret` field provided consists of only the name of the regional secret. + +- `version` - (Optional) The version of the regional secret to get. If it + is not provided, the latest version is retrieved. + +## Attributes Reference + +The following attributes are exported: + +- `secret_data` - The secret data. No larger than 64KiB. + +- `name` - The resource name of the regional SecretVersion. Format: + `projects/{{project}}/locations/{{location}}/secrets/{{secret_id}}/versions/{{version}}` diff --git a/mmv1/third_party/terraform/website/docs/d/secret_manager_regional_secrets.html.markdown b/mmv1/third_party/terraform/website/docs/d/secret_manager_regional_secrets.html.markdown new file mode 100644 index 000000000000..5a0f2e9a1051 --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/d/secret_manager_regional_secrets.html.markdown @@ -0,0 +1,82 @@ +--- +subcategory: "Secret Manager" +description: |- + List the Secret Manager Regional Secrets. +--- + +# google_secret_manager_regional_secrets + +Use this data source to list the Secret Manager Regional Secrets. + +## Example Usage + +```hcl +data "google_secret_manager_regional_secrets" "secrets" { + location = "us-central1" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `project` - (optional) The ID of the project. + +* `filter` - (optional) Filter string, adhering to the rules in [List-operation filtering](https://cloud.google.com/secret-manager/docs/filtering). List only secrets matching the filter. If filter is empty, all regional secrets are listed from the specified location. + +* `location` - (Required) The location of the regional secret. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `secrets` - A list of regional secrets present in the specified location and matching the filter. Structure is [defined below](#nested_secrets). + +The `secrets` block supports: + +* `labels` - The labels assigned to this regional secret. + +* `annotations` - Custom metadata about the regional secret. + +* `version_aliases` - Mapping from version alias to version name. + +* `topics` - + A list of up to 10 Pub/Sub topics to which messages are published when control plane operations are called on the regional secret or its versions. + Structure is [documented below](#nested_topics). + +* `expire_time` - Timestamp in UTC when the regional secret is scheduled to expire. + +* `create_time` - The time at which the regional secret was created. + +* `rotation` - + The rotation time and period for a regional secret. + Structure is [documented below](#nested_rotation). + +* `project` - The ID of the project in which the resource belongs. + +* `location` - The location in which the resource belongs. + +* `secret_id` - The unique name of the resource. + +* `name` - The resource name of the regional secret. Format: `projects/{{project}}/locations/{{location}}/secrets/{{secret_id}}` + +* `version_destroy_ttl` - The version destroy ttl for the regional secret version. + +* `customer_managed_encryption` - + Customer Managed Encryption for the regional secret. + Structure is [documented below](#nested_customer_managed_encryption_user_managed). + +The `topics` block supports: + +* `name` - The resource name of the Pub/Sub topic that will be published to. + +The `rotation` block supports: + +* `next_rotation_time` - Timestamp in UTC at which the secret is scheduled to rotate. + +* `rotation_period` - The Duration between rotation notifications. + +The `customer_managed_encryption` block supports: + +* `kms_key_name` - + Describes the Cloud KMS encryption key that will be used to protect destination secret. diff --git a/mmv1/third_party/terraform/website/docs/r/bigquery_table.html.markdown b/mmv1/third_party/terraform/website/docs/r/bigquery_table.html.markdown index e999cc72f321..c844a4974429 100644 --- a/mmv1/third_party/terraform/website/docs/r/bigquery_table.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/bigquery_table.html.markdown @@ -104,6 +104,8 @@ The following arguments are supported: By defining these properties, the data source can then be queried as if it were a standard BigQuery table. Structure is [documented below](#nested_external_data_configuration). +* `biglake_configuration` - (Optional) Specifies the configuration of a BigLake managed table. Structure is [documented below](#nested_biglake_configuration) + * `friendly_name` - (Optional) A descriptive name for the table. * `max_staleness`: (Optional) The maximum staleness of data that could be @@ -492,6 +494,20 @@ The following arguments are supported: * `replication_interval_ms` (Optional) - The interval at which the source materialized view is polled for updates. The default is 300000. +The `biglake_configuration` block supports: + +* `connection_id` - (Required) The connection specifying the credentials to be used to + read and write to external storage, such as Cloud Storage. The connection_id can + have the form "<project\_id>.<location\_id>.<connection\_id>" or + projects/<project\_id>/locations/<location\_id>/connections/<connection\_id>". + +* `storage_uri` - (Required) The fully qualified location prefix of the external folder where table data + is stored. The '*' wildcard character is not allowed. The URI should be in the format "gs://bucket/path_to_table/" + +* `file_format` - (Required) The file format the table data is stored in. + +* `table_format` - (Required) The table format the metadata only snapshots are stored in. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are diff --git a/mmv1/third_party/terraform/website/docs/r/container_cluster.html.markdown b/mmv1/third_party/terraform/website/docs/r/container_cluster.html.markdown index df2af254ebee..c4fc1f7c3a78 100644 --- a/mmv1/third_party/terraform/website/docs/r/container_cluster.html.markdown +++ b/mmv1/third_party/terraform/website/docs/r/container_cluster.html.markdown @@ -1328,6 +1328,14 @@ linux_node_config { * `CGROUP_MODE_V1`: CGROUP_MODE_V1 specifies to use cgroupv1 for the cgroup configuration on the node image. * `CGROUP_MODE_V2`: CGROUP_MODE_V2 specifies to use cgroupv2 for the cgroup configuration on the node image. +* `hugepages_config` - (Optional) Amounts for 2M and 1G hugepages. Structure is [documented below](#nested_hugepages_config). + +The `hugepages_config` block supports: + +* `hugepage_size_2m` - (Optional) Amount of 2M hugepages. + +* `hugepage_size_1g` - (Optional) Amount of 1G hugepages. + The `containerd_config` block supports: * `private_registry_access_config` (Optional) - Configuration for private container registries. There are two fields in this config: diff --git a/mmv1/third_party/tgc/resource_converters.go.tmpl b/mmv1/third_party/tgc/resource_converters.go.tmpl new file mode 100644 index 000000000000..1acd324f3d8a --- /dev/null +++ b/mmv1/third_party/tgc/resource_converters.go.tmpl @@ -0,0 +1,193 @@ +{{/* The license inside this block applies to this file + Copyright 2024 Google LLC. All Rights Reserved. + + 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. */ -}} +// ---------------------------------------------------------------------------- +// +// *** 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 ( + "sort" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources/services/compute" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources/services/kms" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources/services/resourcemanager" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources/services/spanner" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources/services/secretmanagerregional" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources/services/securesourcemanager" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources/services/securitycenterv2" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" +) + + +// ResourceConverter returns a map of terraform resource types (i.e. `google_project`) +// to a slice of ResourceConverters. +// +// Modelling of relationships: +// terraform resources to CAI assets as []cai.ResourceConverter: +// 1:1 = [ResourceConverter{Convert: convertAbc}] (len=1) +// 1:N = [ResourceConverter{Convert: convertAbc}, ...] (len=N) +// N:1 = [ResourceConverter{Convert: convertAbc, merge: mergeAbc}] (len=1) +func ResourceConverters() map[string][]cai.ResourceConverter { + return map[string][]cai.ResourceConverter{ + "google_artifact_registry_repository": {artifactregistry.ResourceConverterArtifactRegistryRepository()}, + "google_app_engine_application": {resourceConverterAppEngineApplication()}, + "google_alloydb_cluster": {alloydb.ResourceConverterAlloydbCluster()}, + "google_alloydb_instance": {alloydb.ResourceConverterAlloydbInstance()}, + "google_apikeys_key": {resourceConverterApikeysKey()}, + "google_compute_address": {compute.ResourceConverterComputeAddress()}, + "google_compute_autoscaler": {compute.ResourceConverterComputeAutoscaler()}, + "google_compute_firewall": {compute.ResourceConverterComputeFirewall()}, + "google_compute_disk": {compute.ResourceConverterComputeDisk()}, + "google_compute_forwarding_rule": {compute.ResourceConverterComputeForwardingRule()}, + "google_gke_hub_membership": {gkehub.ResourceConverterGKEHubMembership()}, + "google_compute_global_address": {compute.ResourceConverterComputeGlobalAddress()}, + "google_compute_global_forwarding_rule": {compute.ResourceConverterComputeGlobalForwardingRule()}, + "google_compute_health_check": {compute.ResourceConverterComputeHealthCheck()}, + "google_compute_instance": {compute.ResourceConverterComputeInstance()}, + "google_compute_instance_group": {resourceConverterComputeInstanceGroup()}, + "google_compute_network": {compute.ResourceConverterComputeNetwork()}, + "google_compute_node_template": {compute.ResourceConverterComputeNodeTemplate()}, + "google_compute_route": {compute.ResourceConverterComputeRoute()}, + "google_compute_router": {compute.ResourceConverterComputeRouter()}, + "google_compute_vpn_tunnel": {compute.ResourceConverterComputeVpnTunnel()}, + "google_compute_resource_policy": {compute.ResourceConverterComputeResourcePolicy()}, + "google_compute_security_policy": {resourceConverterComputeSecurityPolicy()}, + "google_compute_snapshot": {compute.ResourceConverterComputeSnapshot()}, + "google_compute_subnetwork": {compute.ResourceConverterComputeSubnetwork()}, + "google_compute_ssl_policy": {compute.ResourceConverterComputeSslPolicy()}, + "google_compute_ssl_certificate": {compute.ResourceConverterComputeSslCertificate()}, + "google_compute_url_map": {compute.ResourceConverterComputeUrlMap()}, + "google_compute_target_http_proxy": {compute.ResourceConverterComputeTargetHttpProxy()}, + "google_compute_target_https_proxy": {compute.ResourceConverterComputeTargetHttpsProxy()}, + "google_compute_target_ssl_proxy": {compute.ResourceConverterComputeTargetSslProxy()}, + "google_compute_target_pool": {resourceConverterComputeTargetPool()}, + "google_composer_environment": {resourceConverterComposerEnvironment()}, + "google_compute_region_commitment": {resourceConverterCommitment()}, + "google_dataflow_job": {resourceDataflowJob()}, + "google_dataproc_autoscaling_policy": {dataproc.ResourceConverterDataprocAutoscalingPolicy()}, + "google_dataproc_cluster": {resourceConverterDataprocCluster()}, + "google_dns_managed_zone": {dns.ResourceConverterDNSManagedZone()}, + "google_dns_policy": {dns.ResourceConverterDNSPolicy()}, + "google_kms_key_ring_import_job": {kms.ResourceConverterKMSKeyRingImportJob()}, + "google_gke_hub_feature": {gkehub2.ResourceConverterGKEHub2Feature()}, + "google_storage_bucket": {resourceConverterStorageBucket()}, + "google_sql_database_instance": {resourceConverterSQLDatabaseInstance()}, + "google_sql_database": {sql.ResourceConverterSQLDatabase()}, + "google_container_cluster": {resourceConverterContainerCluster()}, + "google_container_node_pool": {resourceConverterContainerNodePool()}, + "google_bigquery_dataset": {bigquery.ResourceConverterBigQueryDataset()}, + "google_bigquery_dataset_iam_policy": {bigquery.ResourceConverterBigqueryDatasetIamPolicy()}, + "google_bigquery_dataset_iam_binding": {bigquery.ResourceConverterBigqueryDatasetIamBinding()}, + "google_bigquery_dataset_iam_member": {bigquery.ResourceConverterBigqueryDatasetIamMember()}, + "google_bigquery_table": {resourceConverterBigQueryTable()}, + "google_datastream_connection_profile": {datastream.ResourceConverterDatastreamConnectionProfile()}, + "google_datastream_private_connection": {datastream.ResourceConverterDatastreamPrivateConnection()}, + "google_datastream_stream": {datastream.ResourceConverterDatastreamStream()}, + "google_firebase_project": {resourceConverterFirebaseProject()}, + "google_org_policy_policy": {resourceConverterOrgPolicyPolicy()}, + "google_redis_instance": {redis.ResourceConverterRedisInstance()}, + "google_spanner_database": {spanner.ResourceConverterSpannerDatabase()}, + "google_spanner_database_iam_policy": {spanner.ResourceConverterSpannerDatabaseIamPolicy()}, + "google_spanner_database_iam_binding": {spanner.ResourceConverterSpannerDatabaseIamBinding()}, + "google_spanner_database_iam_member": {spanner.ResourceConverterSpannerDatabaseIamMember()}, + "google_spanner_instance": {spanner.ResourceConverterSpannerInstance()}, + "google_spanner_instance_iam_policy": {spanner.ResourceConverterSpannerInstanceIamPolicy()}, + "google_spanner_instance_iam_binding": {spanner.ResourceConverterSpannerInstanceIamBinding()}, + "google_spanner_instance_iam_member": {spanner.ResourceConverterSpannerInstanceIamMember()}, + "google_project_service": {resourceConverterServiceUsage()}, + "google_secret_manager_secret_version": {secretmanager.ResourceConverterSecretManagerSecretVersion()}, + "google_pubsub_lite_reservation": {pubsublite.ResourceConverterPubsubLiteReservation()}, + "google_pubsub_lite_subscription": {pubsublite.ResourceConverterPubsubLiteSubscription()}, + "google_pubsub_lite_topic": {pubsublite.ResourceConverterPubsubLiteTopic()}, + "google_pubsub_schema": {pubsub.ResourceConverterPubsubSchema()}, + "google_pubsub_subscription": {pubsub.ResourceConverterPubsubSubscription()}, + "google_pubsub_subscription_iam_policy": {pubsub.ResourceConverterPubsubSubscriptionIamPolicy()}, + "google_pubsub_subscription_iam_binding": {pubsub.ResourceConverterPubsubSubscriptionIamBinding()}, + "google_pubsub_subscription_iam_member": {pubsub.ResourceConverterPubsubSubscriptionIamMember()}, + "google_storage_bucket_iam_policy": {resourceConverterStorageBucketIamPolicy()}, + "google_storage_bucket_iam_binding": {resourceConverterStorageBucketIamBinding()}, + "google_storage_bucket_iam_member": {resourceConverterStorageBucketIamMember()}, + "google_compute_node_group": {compute.ResourceConverterComputeNodeGroup()}, + "google_logging_folder_bucket_config": {resourceConverterLogFolderBucket()}, + "google_app_engine_standard_app_version": {resourceAppEngineStandardAppVersion()}, + "google_logging_organization_bucket_config": {resourceConverterLogOrganizationBucket()}, + "google_logging_project_bucket_config": {resourceConverterLogProjectBucket()}, + "google_logging_billing_account_bucket_config": {resourceConverterLogBillingAccountBucket()}, + "google_cloud_tasks_queue": {cloudtasks.ResourceConverterCloudTasksQueue()}, + "google_pubsub_topic": {pubsub.ResourceConverterPubsubTopic()}, + "google_kms_crypto_key": {kms.ResourceConverterKMSCryptoKey()}, + "google_kms_key_ring": {kms.ResourceConverterKMSKeyRing()}, + "google_filestore_instance": {filestore.ResourceConverterFilestoreInstance()}, + "google_access_context_manager_service_perimeter": {accesscontextmanager.ResourceConverterAccessContextManagerServicePerimeter()}, + "google_access_context_manager_access_policy": {accesscontextmanager.ResourceConverterAccessContextManagerAccessPolicy()}, + "google_cloud_run_service": {cloudrun.ResourceConverterCloudRunService()}, + "google_cloud_run_domain_mapping": {cloudrun.ResourceConverterCloudRunDomainMapping()}, + "google_cloud_run_v2_job": {cloudrunv2.ResourceConverterCloudRunV2Job()}, + "google_cloudfunctions_function": {resourceConverterCloudFunctionsCloudFunction()}, + "google_monitoring_notification_channel": {monitoring.ResourceConverterMonitoringNotificationChannel()}, + "google_monitoring_alert_policy": {monitoring.ResourceConverterMonitoringAlertPolicy()}, + "google_vertex_ai_dataset": {vertexai.ResourceConverterVertexAIDataset()}, + {{- range $object := $.IamResources }} + {{- if $object.IamClassName }} + "{{ $object.TerraformName }}_iam_policy": { {{- $object.IamClassName }}IamPolicy()}, + "{{ $object.TerraformName }}_iam_binding": { {{- $object.IamClassName }}IamBinding()}, + "{{ $object.TerraformName }}_iam_member": { {{- $object.IamClassName }}IamMember()}, + {{- end }} + {{- end }} + "google_project": { + resourceConverterProject(), + resourceConverterProjectBillingInfo(), + }, + "google_bigtable_instance": { + resourceConverterBigtableInstance(), + resourceConverterBigtableCluster(), + }, + "google_organization_iam_policy": {resourcemanager.ResourceConverterOrganizationIamPolicy()}, + "google_organization_iam_binding": {resourcemanager.ResourceConverterOrganizationIamBinding()}, + "google_organization_iam_member": {resourcemanager.ResourceConverterOrganizationIamMember()}, + "google_organization_policy": {resourceConverterOrganizationPolicy()}, + "google_project_organization_policy": {resourceConverterProjectOrgPolicy()}, + "google_folder": {resourceConverterFolder()}, + "google_folder_iam_policy": {resourcemanager.ResourceConverterFolderIamPolicy()}, + "google_folder_iam_binding": {resourcemanager.ResourceConverterFolderIamBinding()}, + "google_folder_iam_member": {resourcemanager.ResourceConverterFolderIamMember()}, + "google_folder_organization_policy": {resourceConverterFolderOrgPolicy()}, + "google_kms_crypto_key_iam_policy": {resourceConverterKmsCryptoKeyIamPolicy()}, + "google_kms_crypto_key_iam_binding": {resourceConverterKmsCryptoKeyIamBinding()}, + "google_kms_crypto_key_iam_member": {resourceConverterKmsCryptoKeyIamMember()}, + "google_kms_key_ring_iam_policy": {resourceConverterKmsKeyRingIamPolicy()}, + "google_kms_key_ring_iam_binding": {resourceConverterKmsKeyRingIamBinding()}, + "google_kms_key_ring_iam_member": {resourceConverterKmsKeyRingIamMember()}, + "google_project_iam_policy": {resourcemanager.ResourceConverterProjectIamPolicy()}, + "google_project_iam_binding": {resourcemanager.ResourceConverterProjectIamBinding()}, + "google_project_iam_member": {resourcemanager.ResourceConverterProjectIamMember()}, + "google_project_iam_custom_role": {resourceConverterProjectIAMCustomRole()}, + "google_organization_iam_custom_role": {resourceConverterOrganizationIAMCustomRole()}, + "google_vpc_access_connector": {vpcaccess.ResourceConverterVPCAccessConnector()}, + "google_logging_metric": {logging.ResourceConverterLoggingMetric()}, + "google_service_account": {resourceConverterServiceAccount()}, + "google_service_account_key": {resourceConverterServiceAccountKey()}, + + } +} diff --git a/mmv1/third_party/tgc/services/compute/compute_instance.go.tmpl b/mmv1/third_party/tgc/services/compute/compute_instance.go.tmpl new file mode 100644 index 000000000000..b5293a31d9b3 --- /dev/null +++ b/mmv1/third_party/tgc/services/compute/compute_instance.go.tmpl @@ -0,0 +1,350 @@ +// ---------------------------------------------------------------------------- +// +// This file is copied here by Magic Modules. The code for building up a +// compute instance object is copied from the manually implemented +// provider file: +// third_party/terraform/resources/resource_compute_instance.go +// +// ---------------------------------------------------------------------------- +package compute + +import ( + "errors" + "fmt" + "strings" + + "google.golang.org/api/googleapi" + + compute "google.golang.org/api/compute/v0.beta" + + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources/cai" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport" +) + +const ComputeInstanceAssetType string = "compute.googleapis.com/Instance" + +func ResourceConverterComputeInstance() cai.ResourceConverter { + return cai.ResourceConverter{ + AssetType: ComputeInstanceAssetType, + Convert: GetComputeInstanceCaiObject, + } +} + +func GetComputeInstanceCaiObject(d tpgresource.TerraformResourceData, config *transport_tpg.Config) ([]cai.Asset, error) { + name, err := cai.AssetName(d, config, "//compute.googleapis.com/projects/{{"{{"}}project{{"}}"}}/zones/{{"{{"}}zone{{"}}"}}/instances/{{"{{"}}name{{"}}"}}") + if err != nil { + return []cai.Asset{}, err + } + if obj, err := GetComputeInstanceApiObject(d, config); err == nil { + return []cai.Asset{{"{{"}} + Name: name, + Type: ComputeInstanceAssetType, + Resource: &cai.AssetResource{ + Version: "v1", + DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest", + DiscoveryName: "Instance", + Data: obj, + }, + {{"}}"}}, nil + } else { + return []cai.Asset{}, err + } +} + +func GetComputeInstanceApiObject(d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]interface{}, error) { + project, err := tpgresource.GetProject(d, config) + if err != nil { + return nil, err + } + + instance, err := expandComputeInstance(project, d, config) + if err != nil { + return nil, err + } + + return cai.JsonMap(instance) +} + +func expandComputeInstance(project string, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (*compute.Instance, error) { + // Get the machine type + var machineTypeUrl string + if mt, ok := d.GetOk("machine_type"); ok { + machineType, err := tpgresource.ParseMachineTypesFieldValue(mt.(string), d, config) + if err != nil { + return nil, fmt.Errorf( + "Error loading machine type: %s", + err) + } + machineTypeUrl = machineType.RelativeLink() + } + + // Build up the list of disks + + disks := []*compute.AttachedDisk{} + if _, hasBootDisk := d.GetOk("boot_disk"); hasBootDisk { + bootDisk, err := expandBootDisk(d, config, project) + if err != nil { + return nil, err + } + disks = append(disks, bootDisk) + } + + if _, hasScratchDisk := d.GetOk("scratch_disk"); hasScratchDisk { + scratchDisks, err := expandScratchDisks(d, config, project) + if err != nil { + return nil, err + } + disks = append(disks, scratchDisks...) + } + + attachedDisksCount := d.Get("attached_disk.#").(int) + + for i := 0; i < attachedDisksCount; i++ { + diskConfig := d.Get(fmt.Sprintf("attached_disk.%d", i)).(map[string]interface{}) + disk, err := expandAttachedDisk(diskConfig, d, config) + if err != nil { + return nil, err + } + + disks = append(disks, disk) + } + + sch := d.Get("scheduling").([]interface{}) + var scheduling *compute.Scheduling + if len(sch) == 0 { + // TF doesn't do anything about defaults inside of nested objects, so if + // scheduling hasn't been set, then send it with its default values. + scheduling = &compute.Scheduling{ + AutomaticRestart: googleapi.Bool(true), + } + } else { + prefix := "scheduling.0" + scheduling = &compute.Scheduling{ + AutomaticRestart: googleapi.Bool(d.Get(prefix + ".automatic_restart").(bool)), + Preemptible: d.Get(prefix + ".preemptible").(bool), + OnHostMaintenance: d.Get(prefix + ".on_host_maintenance").(string), + ProvisioningModel: d.Get(prefix + ".provisioning_model").(string), + ForceSendFields: []string{"AutomaticRestart", "Preemptible"}, + } + } + + metadata, err := resourceInstanceMetadata(d) + if err != nil { + return nil, fmt.Errorf("Error creating metadata: %s", err) + } + + networkInterfaces, err := expandNetworkInterfaces(d, config) + if err != nil { + return nil, fmt.Errorf("Error creating network interfaces: %s", err) + } + + accels, err := expandInstanceGuestAccelerators(d, config) + if err != nil { + return nil, fmt.Errorf("Error creating guest accelerators: %s", err) + } + + // Create the instance information + return &compute.Instance{ + CanIpForward: d.Get("can_ip_forward").(bool), + Description: d.Get("description").(string), + Disks: disks, + MachineType: machineTypeUrl, + Metadata: metadata, + Name: d.Get("name").(string), + Zone: d.Get("zone").(string), + NetworkInterfaces: networkInterfaces, + Tags: resourceInstanceTags(d), + Labels: tpgresource.ExpandLabels(d), + ServiceAccounts: expandServiceAccounts(d.Get("service_account").([]interface{})), + GuestAccelerators: accels, + MinCpuPlatform: d.Get("min_cpu_platform").(string), + Scheduling: scheduling, + DeletionProtection: d.Get("deletion_protection").(bool), + Hostname: d.Get("hostname").(string), + ForceSendFields: []string{"CanIpForward", "DeletionProtection"}, + ShieldedInstanceConfig: expandShieldedVmConfigs(d), + DisplayDevice: expandDisplayDevice(d), + AdvancedMachineFeatures: expandAdvancedMachineFeatures(d), + }, nil +} + +func expandAttachedDisk(diskConfig map[string]interface{}, d tpgresource.TerraformResourceData, meta interface{}) (*compute.AttachedDisk, error) { + config := meta.(*transport_tpg.Config) + + s := diskConfig["source"].(string) + var sourceLink string + if strings.Contains(s, "regions/") { + source, err := tpgresource.ParseRegionDiskFieldValue(s, d, config) + if err != nil { + return nil, err + } + sourceLink = source.RelativeLink() + } else { + source, err := tpgresource.ParseDiskFieldValue(s, d, config) + if err != nil { + return nil, err + } + sourceLink = source.RelativeLink() + } + + disk := &compute.AttachedDisk{ + Source: sourceLink, + } + + if v, ok := diskConfig["mode"]; ok { + disk.Mode = v.(string) + } + + if v, ok := diskConfig["device_name"]; ok { + disk.DeviceName = v.(string) + } + + keyValue, keyOk := diskConfig["disk_encryption_key_raw"] + if keyOk { + if keyValue != "" { + disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{ + RawKey: keyValue.(string), + } + } + } + + kmsValue, kmsOk := diskConfig["kms_key_self_link"] + if kmsOk { + if keyOk && keyValue != "" && kmsValue != "" { + return nil, errors.New("Only one of kms_key_self_link and disk_encryption_key_raw can be set") + } + if kmsValue != "" { + disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{ + KmsKeyName: kmsValue.(string), + } + } + } + return disk, nil +} + +// See comment on expandInstanceTemplateGuestAccelerators regarding why this +// code is duplicated. +func expandInstanceGuestAccelerators(d tpgresource.TerraformResourceData, config *transport_tpg.Config) ([]*compute.AcceleratorConfig, error) { + configs, ok := d.GetOk("guest_accelerator") + if !ok { + return nil, nil + } + accels := configs.([]interface{}) + guestAccelerators := make([]*compute.AcceleratorConfig, 0, len(accels)) + for _, raw := range accels { + data := raw.(map[string]interface{}) + if data["count"].(int) == 0 { + continue + } + at, err := tpgresource.ParseAcceleratorFieldValue(data["type"].(string), d, config) + if err != nil { + return nil, fmt.Errorf("cannot parse accelerator type: %v", err) + } + guestAccelerators = append(guestAccelerators, &compute.AcceleratorConfig{ + AcceleratorCount: int64(data["count"].(int)), + AcceleratorType: at.RelativeLink(), + }) + } + + return guestAccelerators, nil +} + +func expandBootDisk(d tpgresource.TerraformResourceData, config *transport_tpg.Config, project string) (*compute.AttachedDisk, error) { + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return nil, err + } + + disk := &compute.AttachedDisk{ + AutoDelete: d.Get("boot_disk.0.auto_delete").(bool), + Boot: true, + } + + if v, ok := d.GetOk("boot_disk.0.device_name"); ok { + disk.DeviceName = v.(string) + } + + if v, ok := d.GetOk("boot_disk.0.disk_encryption_key_raw"); ok { + if v != "" { + disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{ + RawKey: v.(string), + } + } + } + + if v, ok := d.GetOk("boot_disk.0.kms_key_self_link"); ok { + if v != "" { + disk.DiskEncryptionKey = &compute.CustomerEncryptionKey{ + KmsKeyName: v.(string), + } + } + } + + if v, ok := d.GetOk("boot_disk.0.source"); ok { + source, err := tpgresource.ParseDiskFieldValue(v.(string), d, config) + if err != nil { + return nil, err + } + disk.Source = source.RelativeLink() + } + + if _, ok := d.GetOk("boot_disk.0.initialize_params"); ok { + disk.InitializeParams = &compute.AttachedDiskInitializeParams{} + + if v, ok := d.GetOk("boot_disk.0.initialize_params.0.size"); ok { + disk.InitializeParams.DiskSizeGb = int64(v.(int)) + } + + if v, ok := d.GetOk("boot_disk.0.initialize_params.0.type"); ok { + diskTypeName := v.(string) + diskType, err := readDiskType(config, d, diskTypeName) + if err != nil { + return nil, fmt.Errorf("Error loading disk type '%s': %s", diskTypeName, err) + } + disk.InitializeParams.DiskType = diskType.RelativeLink() + } + + if v, ok := d.GetOk("boot_disk.0.initialize_params.0.image"); ok { + imageName := v.(string) + imageUrl, err := ResolveImage(config, project, imageName, userAgent) + if err != nil { + return nil, fmt.Errorf("Error resolving image name '%s': %s", imageName, err) + } + + disk.InitializeParams.SourceImage = imageUrl + } + + if _, ok := d.GetOk("boot_disk.0.initialize_params.0.labels"); ok { + disk.InitializeParams.Labels = tpgresource.ExpandStringMap(d, "boot_disk.0.initialize_params.0.labels") + } + } + + if v, ok := d.GetOk("boot_disk.0.mode"); ok { + disk.Mode = v.(string) + } + + return disk, nil +} + +func expandScratchDisks(d tpgresource.TerraformResourceData, config *transport_tpg.Config, project string) ([]*compute.AttachedDisk, error) { + diskType, err := readDiskType(config, d, "local-ssd") + if err != nil { + return nil, fmt.Errorf("Error loading disk type 'local-ssd': %s", err) + } + + n := d.Get("scratch_disk.#").(int) + scratchDisks := make([]*compute.AttachedDisk, 0, n) + for i := 0; i < n; i++ { + scratchDisks = append(scratchDisks, &compute.AttachedDisk{ + AutoDelete: true, + Type: "SCRATCH", + Interface: d.Get(fmt.Sprintf("scratch_disk.%d.interface", i)).(string), + InitializeParams: &compute.AttachedDiskInitializeParams{ + DiskType: diskType.RelativeLink(), + }, + }) + } + + return scratchDisks, nil +} diff --git a/mmv1/third_party/tgc/tests/source/cli_test.go.erb b/mmv1/third_party/tgc/tests/source/cli_test.go.erb index a1a648bdb899..aa9ba3db86ef 100644 --- a/mmv1/third_party/tgc/tests/source/cli_test.go.erb +++ b/mmv1/third_party/tgc/tests/source/cli_test.go.erb @@ -1,18 +1,4 @@ <% autogen_exception -%> -// Copyright 2019 Google LLC -// -// 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. - package test import ( diff --git a/mmv1/third_party/tgc/tests/source/go/cli_test.go.tmpl b/mmv1/third_party/tgc/tests/source/go/cli_test.go.tmpl new file mode 100644 index 000000000000..f4dcfe211199 --- /dev/null +++ b/mmv1/third_party/tgc/tests/source/go/cli_test.go.tmpl @@ -0,0 +1,169 @@ +package test + +import ( + "bytes" + "encoding/json" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/caiasset" +) + +// TestCLI tests the "convert" and "validate" subcommand against a generated .tfplan file. +func TestCLI(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test in short mode.") + return + } + + // Test cases for each type of resource is defined here. + cases := []struct { + name string + compareConvertOutput compareConvertOutputFunc + }{ + {name: "example_project_iam_binding", compareConvertOutput: compareMergedIamBindingOutput}, + {name: "example_project_iam_member", compareConvertOutput: compareMergedIamMemberOutput}, + } + + for i := range cases { + // Allocate a variable to make sure test can run in parallel. + c := cases[i] + + // Add default convert comparison func if not set + if c.compareConvertOutput == nil { + c.compareConvertOutput = compareUnmergedConvertOutput + } + + // Test both offline and online mode. + for _, offline := range []bool{true, false} { + offline := offline + t.Run(fmt.Sprintf("tf=%s/offline=%t", c.name, offline), func(t *testing.T) { + t.Parallel() + // Create a temporary directory for running terraform. + dir, err := os.MkdirTemp(tmpDir, "terraform") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) + + // Generate the .tf and _assets.json files into the temporary directory. + generateTestFiles(t, "../testdata/templates", dir, c.name+".tf") + generateTestFiles(t, "../testdata/templates", dir, c.name+".json") + + // Uses glob matching to match generateTestFiles internals. + tfstateMatches, err := filepath.Glob(filepath.Join("../testdata/templates", c.name+".tfstate")) + if err != nil { + t.Fatalf("malformed glob: %v", err) + } + if tfstateMatches != nil { + generateTestFiles(t, "../testdata/templates", dir, c.name+".tfstate") + err = os.Rename( + filepath.Join(dir, c.name+".tfstate"), + filepath.Join(dir, "terraform.tfstate"), + ) + if err != nil { + t.Fatalf("renaming tfstate: %v", err) + } + } + + terraformWorkflow(t, dir, c.name) + + t.Run("cmd=convert", func(t *testing.T) { + testConvertCommand(t, dir, c.name, c.name, offline, true, c.compareConvertOutput) + }) + }) + } + } +} + +type compareConvertOutputFunc func(t *testing.T, expected []caiasset.Asset, actual []caiasset.Asset, offline bool) + +func compareUnmergedConvertOutput(t *testing.T, expected []caiasset.Asset, actual []caiasset.Asset, offline bool) { + expectedAssets := normalizeAssets(t, expected, offline) + actualAssets := normalizeAssets(t, actual, offline) + if diff := cmp.Diff(expectedAssets, actualAssets); diff != "" { + t.Errorf("%v diff(-want, +got):\n%s", t.Name(), diff) + } +} + +// For merged IAM members, only consider whether the expected members are present. +func compareMergedIamMemberOutput(t *testing.T, expected []caiasset.Asset, actual []caiasset.Asset, offline bool) { + var normalizedActual []caiasset.Asset + for i := range expected { + expectedAsset := expected[i] + actualAsset := actual[i] + + // Copy actualAsset + normalizedActualAsset := actualAsset + + expectedBindings := map[string]map[string]struct{}{} + for _, binding := range expectedAsset.IAMPolicy.Bindings { + expectedBindings[binding.Role] = map[string]struct{}{} + for _, member := range binding.Members { + expectedBindings[binding.Role][member] = struct{}{} + } + } + + iamPolicy := caiasset.IAMPolicy{} + for _, binding := range actualAsset.IAMPolicy.Bindings { + if expectedMembers, exists := expectedBindings[binding.Role]; exists { + iamBinding := caiasset.IAMBinding{ + Role: binding.Role, + } + for _, member := range binding.Members { + if _, exists := expectedMembers[member]; exists { + iamBinding.Members = append(iamBinding.Members, member) + } + } + iamPolicy.Bindings = append(iamPolicy.Bindings, iamBinding) + } + } + normalizedActualAsset.IAMPolicy = &iamPolicy + normalizedActual = append(normalizedActual, normalizedActualAsset) + } + + expectedAssets := normalizeAssets(t, expected, offline) + actualAssets := normalizeAssets(t, normalizedActual, offline) + if diff := cmp.Diff(expectedAssets, actualAssets); diff != "" { + t.Errorf("%v diff(-want, +got):\n%s", t.Name(), diff) + } +} + +// For merged IAM bindings, only consider whether the expected bindings are as expected. +func compareMergedIamBindingOutput(t *testing.T, expected []caiasset.Asset, actual []caiasset.Asset, offline bool) { + var normalizedActual []caiasset.Asset + for i := range expected { + expectedAsset := expected[i] + actualAsset := actual[i] + + // Copy actualAsset + normalizedActualAsset := actualAsset + + expectedBindings := map[string]struct{}{} + for _, binding := range expectedAsset.IAMPolicy.Bindings { + expectedBindings[binding.Role] = struct{}{} + } + + iamPolicy := caiasset.IAMPolicy{} + for _, binding := range actualAsset.IAMPolicy.Bindings { + if _, exists := expectedBindings[binding.Role]; exists { + iamPolicy.Bindings = append(iamPolicy.Bindings, binding) + } + } + normalizedActualAsset.IAMPolicy = &iamPolicy + normalizedActual = append(normalizedActual, normalizedActualAsset) + } + + expectedAssets := normalizeAssets(t, expected, offline) + actualAssets := normalizeAssets(t, normalizedActual, offline) + if diff := cmp.Diff(expectedAssets, actualAssets); diff != "" { + t.Errorf("%v diff(-want, +got):\n%s", t.Name(), diff) + } +} diff --git a/mmv1/third_party/tgc/tests/source/go/iam_test.go.tmpl b/mmv1/third_party/tgc/tests/source/go/iam_test.go.tmpl new file mode 100644 index 000000000000..0d64bb921d09 --- /dev/null +++ b/mmv1/third_party/tgc/tests/source/go/iam_test.go.tmpl @@ -0,0 +1,106 @@ +package test + +import ( + "fmt" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + + crmv1 "google.golang.org/api/cloudresourcemanager/v1" + + resources "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/converters/google/resources" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/tfdata" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai/tfplan" + provider "github.com/hashicorp/terraform-provider-google-beta/google-beta/provider" +) + +func TestIAMFetchFullResource(t *testing.T) { + cases := []struct { + name string + }{ +{{- range $test := $.Tests }} +{{- if or (hasSuffix $test "iam_binding") (hasSuffix $test "iam_member") }} + {name: "{{ $test }}"}, +{{- end }} +{{- end }} + } + + converters := resources.ResourceConverters() + schema := provider.Provider() + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var payload []byte + var err error + if strings.Contains(r.URL.String(), "dataset") { + obj := map[string][]interface{}{ + "access": {&crmv1.Policy{}}, + } + payload, err = json.Marshal(obj) + } else { + payload, err = (&crmv1.Policy{}).MarshalJSON() + } + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("failed to MarshalJSON: %s", err))) + return + } + w.Write(payload) + })) + + // Using Cleanup instead of defer because t.Parallel() does not block t.Run. + t.Cleanup(func() { + server.Close() + }) + + cfg := newTestConfig(server) + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + t.Parallel() + + // Create a temporary directory for generating tfplan.json from template. + dir, err := os.MkdirTemp(tmpDir, "terraform") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + generateTestFiles(t, "../testdata/templates", dir, c.name+".tf") + + // Run terraform init and terraform apply to generate tfplan.json files + terraformWorkflow(t, dir, c.name) + + path := filepath.Join(dir, c.name+".tfplan.json") + + data, err := os.ReadFile(path) + if err != nil { + t.Fatalf("opening JSON plan file: %s", err) + } + changes, err := tfplan.ReadResourceChanges(data) + if err != nil { + t.Fatalf("ReadResourceChanges failed: %s", err) + } + + for _, rc := range changes { + resource := schema.ResourcesMap[rc.Type] + rd := tfdata.NewFakeResourceData( + rc.Type, + resource.Schema, + rc.Change.After.(map[string]interface{}), + ) + for _, converter := range converters[rd.Kind()] { + if converter.FetchFullResource == nil { + continue + } + _, err := converter.FetchFullResource(rd, cfg) + if err != nil { + t.Errorf("FetchFullResource() = %s, want = nil", err) + } + } + } + + }) + } +} diff --git a/mmv1/third_party/tgc/tests/source/go/read_test.go.tmpl b/mmv1/third_party/tgc/tests/source/go/read_test.go.tmpl new file mode 100644 index 000000000000..38bde4fb590d --- /dev/null +++ b/mmv1/third_party/tgc/tests/source/go/read_test.go.tmpl @@ -0,0 +1,175 @@ +package test + +import ( + "context" + "encoding/json" + "log" + "os" + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/GoogleCloudPlatform/terraform-google-conversion/v5/tfplan2cai" + "go.uber.org/zap/zaptest" +) + +func TestReadPlannedAssetsCoverage(t *testing.T) { + cases := []struct { + name string + }{ + // read-only, the following tests are not in cli_test or + // have unique parameters that separate them + {name: "example_folder_iam_binding"}, + {name: "example_folder_iam_member"}, + {name: "example_project_create"}, + {name: "example_project_update"}, + {name: "example_project_iam_binding"}, + {name: "example_project_iam_member"}, + {name: "example_storage_bucket"}, + {name: "example_storage_bucket_empty_project_id"}, + {name: "example_storage_bucket_iam_binding"}, + {name: "example_storage_bucket_iam_member"}, + {name: "example_project_create_empty_project_id"}, + {name: "example_project_iam_member_empty_project"}, + // auto inserted tests that are not in list above or manually inserted in cli_test.go + {{- range $test := $.NonDefinedTests }} + {name: "{{- $test }}"}, + {{- end }} + } + for i := range cases { + // Allocate a variable to make sure test can run in parallel. + c := cases[i] + t.Run(c.name, func(t *testing.T) { + t.Parallel() + // Create a temporary directory for running terraform. + dir, err := os.MkdirTemp(tmpDir, "terraform") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) + + generateTestFiles(t, "../testdata/templates", dir, c.name+".json") + generateTestFiles(t, "../testdata/templates", dir, c.name+".tf") + + // tfstate files are for cases testing updates, eg. project update. + // Uses glob matching to match generateTestFiles internals. + tfstateMatches, err := filepath.Glob(filepath.Join("../testdata/templates", c.name+".tfstate")) + if err != nil { + t.Fatalf("malformed glob: %v", err) + } + if tfstateMatches != nil { + generateTestFiles(t, "../testdata/templates", dir, c.name+".tfstate") + err = os.Rename( + filepath.Join(dir, c.name+".tfstate"), + filepath.Join(dir, "terraform.tfstate"), + ) + if err != nil { + t.Fatalf("renaming tfstate: %v", err) + } + } + + // Run terraform init and terraform apply to generate tfplan.json files + terraformWorkflow(t, dir, c.name) + + // Unmarshal payload from testfile into `want` variable. + f := filepath.Join(dir, c.name+".json") + want, err := readExpectedTestFile(f) + if err != nil { + t.Fatal(err) + } + + planfile := filepath.Join(dir, c.name+".tfplan.json") + ctx := context.Background() + ancestryCache := map[string]string{ + data.Provider["project"]: data.Ancestry, + } + + jsonPlan, err := os.ReadFile(planfile) + if err != nil { + t.Fatalf("Error parsing %s: %s", f, err) + } + got, err := tfplan2cai.Convert(ctx, jsonPlan, &tfplan2cai.Options{ + ConvertUnchanged: false, + ErrorLogger: zaptest.NewLogger(t), + Offline: true, + DefaultProject: data.Provider["project"], + DefaultRegion: "", + DefaultZone: "", + UserAgent: "", + AncestryCache: ancestryCache, + }) + if err != nil { + t.Fatalf("Convert(%s, %s, \"\", \"\", %s, offline): %v", planfile, data.Provider["project"], ancestryCache, err) + } + expectedAssets := normalizeAssets(t, want, true) + actualAssets := normalizeAssets(t, got, true) + if diff := cmp.Diff(expectedAssets, actualAssets); diff != "" { + t.Errorf("%v diff(-want, +got):\n%s", t.Name(), diff) + } + }) + } +} + + + +func TestReadPlannedAssetsCoverage_WithoutDefaultProject(t *testing.T) { + cases := []struct { + name string + }{ + {name: "example_project_create_empty_project_id"}, + {name: "example_storage_bucket_empty_project_id"}, + {name: "example_project_iam_member_empty_project"}, + } + for i := range cases { + // Allocate a variable to make sure test can run in parallel. + c := cases[i] + t.Run(c.name, func(t *testing.T) { + t.Parallel() + // Create a temporary directory for running terraform. + dir, err := os.MkdirTemp(tmpDir, "terraform") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(dir) + + generateTestFiles(t, "../testdata/templates", dir, c.name+"_without_default_project.json") + generateTestFiles(t, "../testdata/templates", dir, c.name+".tf") + + // Run terraform init and terraform plan to generate tfplan.json files + terraformWorkflow(t, dir, c.name) + + // Unmarshal payload from testfile into `want` variable. + f := filepath.Join(dir, c.name+"_without_default_project.json") + want, err := readExpectedTestFile(f) + if err != nil { + t.Fatal(err) + } + + planfile := filepath.Join(dir, c.name+".tfplan.json") + ctx := context.Background() + + jsonPlan, err := os.ReadFile(planfile) + if err != nil { + t.Fatalf("Error parsing %s: %s", f, err) + } + got, err := tfplan2cai.Convert(ctx, jsonPlan, &tfplan2cai.Options{ + ConvertUnchanged: false, + ErrorLogger: zaptest.NewLogger(t), + Offline: true, + DefaultProject: "", + DefaultRegion: "", + DefaultZone: "", + UserAgent: "", + AncestryCache: map[string]string{}, + }) + if err != nil { + t.Fatalf("WithoutProject: Convert(%s, offline): %v", planfile, err) + } + expectedAssets := normalizeAssets(t, want, true) + actualAssets := normalizeAssets(t, got, true) + if diff := cmp.Diff(expectedAssets, actualAssets); diff != "" { + t.Errorf("%v diff(-want, +got):\n%s", t.Name(), diff) + } + }) + } +} diff --git a/tools/issue-labeler/labeler/enrolled_teams.yml b/tools/issue-labeler/labeler/enrolled_teams.yml index f400538e5f2e..ea755a64ce05 100755 --- a/tools/issue-labeler/labeler/enrolled_teams.yml +++ b/tools/issue-labeler/labeler/enrolled_teams.yml @@ -199,6 +199,7 @@ service/compute-managed: - google_compute_region_instance_group_manager.* - google_compute_per_instance_config.* - google_compute_region_per_instance_config.* + - google_compute_resize_request.* service/compute-nat: resources: - google_compute_router_nat @@ -351,6 +352,7 @@ service/firestore-controlplane: - google_firestore_database service/firestore-dataplane: resources: + - google_datastore_index - google_firestore_document - google_firestore_field - google_firestore_index