diff --git a/mmv1/products/tags/api.yaml b/mmv1/products/tags/api.yaml index c093ba0ab640..324afd2c4e39 100644 --- a/mmv1/products/tags/api.yaml +++ b/mmv1/products/tags/api.yaml @@ -99,6 +99,23 @@ objects: A timestamp in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits. Examples: "2014-10-02T15:01:23Z" and "2014-10-02T15:01:23.045123456Z". output: true + - !ruby/object:Api::Type::Enum + name: purpose + description: | + Optional. A purpose cannot be changed once set. + + A purpose denotes that this Tag is intended for use in policies of a specific policy engine, and will involve that policy engine in management operations involving this Tag. + values: + - :GCE_FIREWALL + input: true + - !ruby/object:Api::Type::KeyValuePairs + name: purposeData + description: | + Optional. Purpose data cannot be changed once set. + + Purpose data corresponds to the policy system that the tag is intended for. For example, the GCE_FIREWALL purpose expects data in the following format: `network = "/"`. + input: true + - !ruby/object:Api::Resource name: 'TagValue' base_url: tagValues diff --git a/mmv1/products/tags/terraform.yaml b/mmv1/products/tags/terraform.yaml index 019e30520ce0..81e74b2308f0 100644 --- a/mmv1/products/tags/terraform.yaml +++ b/mmv1/products/tags/terraform.yaml @@ -28,6 +28,12 @@ overrides: !ruby/object:Overrides::ResourceOverrides description: !ruby/object:Overrides::Terraform::PropertyOverride validation: !ruby/object:Provider::Terraform::Validation function: 'validation.StringLenBetween(0, 256)' + purposeData: !ruby/object:Overrides::Terraform::PropertyOverride + # This property expects input like: network = "/" or selfLinkWithId (selfLink is not supported) + # However, the API response stored in Terraform state looks like: network = "https://www.googleapis.com/compute/v1/projects//global/networks/" + # This results in persistent diffs, so we surpress them by using ignore_read. The API will reject incorrect references to non-existent or inaccessible VPCs. + # Since this property is configured as 'input: true', any modifications to this property will result in a forced replacement of the resource. + ignore_read: true examples: - !ruby/object:Provider::Terraform::Examples name: "tag_key_basic" diff --git a/mmv1/third_party/terraform/tests/resource_tags_test.go b/mmv1/third_party/terraform/tests/resource_tags_test.go index 8d2e92ea3429..cfa2e290dd4e 100644 --- a/mmv1/third_party/terraform/tests/resource_tags_test.go +++ b/mmv1/third_party/terraform/tests/resource_tags_test.go @@ -14,17 +14,18 @@ import ( func TestAccTags(t *testing.T) { testCases := map[string]func(t *testing.T){ - "tagKeyBasic": testAccTagsTagKey_tagKeyBasic, - "tagKeyUpdate": testAccTagsTagKey_tagKeyUpdate, - "tagKeyIamBinding": testAccTagsTagKeyIamBinding, - "tagKeyIamMember": testAccTagsTagKeyIamMember, - "tagKeyIamPolicy": testAccTagsTagKeyIamPolicy, - "tagValueBasic": testAccTagsTagValue_tagValueBasic, - "tagValueUpdate": testAccTagsTagValue_tagValueUpdate, - "tagBindingBasic": testAccTagsTagBinding_tagBindingBasic, - "tagValueIamBinding": testAccTagsTagValueIamBinding, - "tagValueIamMember": testAccTagsTagValueIamMember, - "tagValueIamPolicy": testAccTagsTagValueIamPolicy, + "tagKeyBasic": testAccTagsTagKey_tagKeyBasic, + "tagKeyBasicWithPurposeGceFirewall": testAccTagsTagKey_tagKeyBasicWithPurposeGceFirewall, + "tagKeyUpdate": testAccTagsTagKey_tagKeyUpdate, + "tagKeyIamBinding": testAccTagsTagKeyIamBinding, + "tagKeyIamMember": testAccTagsTagKeyIamMember, + "tagKeyIamPolicy": testAccTagsTagKeyIamPolicy, + "tagValueBasic": testAccTagsTagValue_tagValueBasic, + "tagValueUpdate": testAccTagsTagValue_tagValueUpdate, + "tagBindingBasic": testAccTagsTagBinding_tagBindingBasic, + "tagValueIamBinding": testAccTagsTagValueIamBinding, + "tagValueIamMember": testAccTagsTagValueIamMember, + "tagValueIamPolicy": testAccTagsTagValueIamPolicy, } for name, tc := range testCases { @@ -68,6 +69,44 @@ resource "google_tags_tag_key" "key" { `, context) } +func testAccTagsTagKey_tagKeyBasicWithPurposeGceFirewall(t *testing.T) { + context := map[string]interface{}{ + "org_id": getTestOrgFromEnv(t), + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckTagsTagKeyDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccTagsTagKey_tagKeyBasicWithPurposeGceFirewallExample(context), + }, + }, + }) +} + +func testAccTagsTagKey_tagKeyBasicWithPurposeGceFirewallExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_compute_network" "tag_network" { + name = "vpc-%{random_suffix}" + auto_create_subnetworks = false +} + +resource "google_tags_tag_key" "key" { + parent = "organizations/%{org_id}" + short_name = "foo%{random_suffix}" + description = "For foo%{random_suffix} resources." + purpose = "GCE_FIREWALL" + # purpose_data expects either a selfLinkWithId (not a property of google_compute_network) or the format /. + # selfLink is not sufficient and will result in an error, so we build a string to match the second option. + purpose_data = {network = "${google_compute_network.tag_network.project}/${google_compute_network.tag_network.name}"} + } + +`, context) +} + func testAccTagsTagKey_tagKeyUpdate(t *testing.T) { context := map[string]interface{}{ "org_id": getTestOrgFromEnv(t),