Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions mmv1/products/compute/Image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,26 @@ properties:
description: Labels to apply to this Image.
update_url: 'projects/{{project}}/global/images/{{name}}/setLabels'
update_verb: 'POST'
- name: "params"
type: NestedObject
ignore_read: true
immutable: true
description: |
Additional params passed with the request, but not persisted as part of resource payload.
properties:
- name: "resourceManagerTags"
type: KeyValuePairs
description: |
Resource manager tags to be bound to the image. Tag keys and values have the
same definition as resource manager tags. Keys and values can be either in numeric format,
such as tagKeys/{tag_key_id} and tagValues/{tag_value_id} or in namespaced format such as
{org_id|projectId}/{tag_key_short_name} and {tag_value_short_name}. The field is ignored when empty.
The field is immutable and causes resource replacement when mutated. This field is only
set at create time and modifying this field after creation will trigger recreation.
To apply tags to an existing resource, see the google_tags_tag_binding resource.
api_name: "resourceManagerTags"
ignore_read: true
immutable: true
- name: 'labelFingerprint'
type: Fingerprint
description: |
Expand Down
32 changes: 26 additions & 6 deletions mmv1/products/compute/StoragePool.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ update_verb: "PATCH"
update_mask: false
autogen_async: true
async:
type: 'OpAsync'
type: "OpAsync"
operation:
base_url: "{{op_id}}"
iam_policy:
Expand All @@ -45,14 +45,14 @@ examples:
vars:
storage_pool_name: "storage-pool-basic"
ignore_read_extra:
- 'deletion_protection'
- "deletion_protection"
exclude_test: true
- name: "compute_storage_pool_full"
primary_resource_id: "test-storage-pool-full"
vars:
storage_pool_name: "storage-pool-full"
ignore_read_extra:
- 'deletion_protection'
- "deletion_protection"
exclude_test: true
parameters:
- name: "zone"
Expand Down Expand Up @@ -194,9 +194,9 @@ properties:
* `hyperdisk-throughput`
required: true
immutable: true
custom_expand: 'templates/terraform/custom_expand/resourceref_with_validation.go.tmpl'
resource: 'StoragePoolType'
imports: 'selfLink'
custom_expand: "templates/terraform/custom_expand/resourceref_with_validation.go.tmpl"
resource: "StoragePoolType"
imports: "selfLink"
- name: "status"
type: NestedObject
description: |
Expand Down Expand Up @@ -279,6 +279,26 @@ properties:
type: KeyValueLabels
description: |
Labels to apply to this storage pool. These can be later modified by the setLabels method.
- name: "params"
type: NestedObject
ignore_read: true
immutable: true
description: |
Additional params passed with the request, but not persisted as part of resource payload
properties:
- name: "resourceManagerTags"
type: KeyValuePairs
description: |
Resource manager tags to be bound to the storage pool. Tag keys and values have the
same definition as resource manager tags. Keys and values can be either in numeric format,
such as tagKeys/{tag_key_id} and tagValues/{tag_value_id} or in namespaced format such as
{org_id|projectId}/{tag_key_short_name} and {tag_value_short_name}. The field is ignored when empty.
The field is immutable and causes resource replacement when mutated. This field is only
set at create time and modifying this field after creation will trigger recreation.
To apply tags to an existing resource, see the google_tags_tag_binding resource.
api_name: "resourceManagerTags"
ignore_read: true
immutable: true
virtual_fields:
- name: "deletion_protection"
type: Boolean
Expand Down
141 changes: 141 additions & 0 deletions mmv1/third_party/terraform/acctest/resource_test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"context"
"errors"
"fmt"
"net/url"
"os"
"slices"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -126,6 +128,145 @@ func BuildIAMImportId(name, role, member, condition string) string {
return ret
}

// TagBindingCheckConfig configures a CheckTagBindings assertion.
// BuildParent must return the full resource name used as the tagBindings parent.
// If GetLocation is nil, the global tagBindings endpoint is used.
// If GetLocation is set, the location-scoped endpoint is used.
type TagBindingCheckConfig struct {
ResourceName string
ExpectedTagValueResources []string
UnexpectedTagValueResources []string
BuildParent func(rs *terraform.ResourceState) (string, error)
GetLocation func(rs *terraform.ResourceState) (string, error)
}

// CheckTagBindings verifies that the target resource has the expected tag value
// bindings and does not have the unexpected ones.
func CheckTagBindings(t *testing.T, cfg TagBindingCheckConfig) resource.TestCheckFunc {
return func(s *terraform.State) error {
if cfg.ResourceName == "" {
return fmt.Errorf("resource name must be set for CheckTagBindings")
}
if cfg.BuildParent == nil {
return fmt.Errorf("BuildParent must be set for CheckTagBindings on resource %s", cfg.ResourceName)
}

rs, err := getResourceState(s, cfg.ResourceName)
if err != nil {
return err
}

expectedTagValues := make([]string, 0, len(cfg.ExpectedTagValueResources))
for _, resourceName := range cfg.ExpectedTagValueResources {
tagValueID, err := getResourceID(s, resourceName)
if err != nil {
return err
}
expectedTagValues = append(expectedTagValues, tagValueID)
}

unexpectedTagValues := make([]string, 0, len(cfg.UnexpectedTagValueResources))
for _, resourceName := range cfg.UnexpectedTagValueResources {
tagValueID, err := getResourceID(s, resourceName)
if err != nil {
return err
}
unexpectedTagValues = append(unexpectedTagValues, tagValueID)
}

parent, err := cfg.BuildParent(rs)
if err != nil {
return err
}

config := GoogleProviderConfig(t)
basePath := config.TagsBasePath
if cfg.GetLocation != nil {
location, err := cfg.GetLocation(rs)
if err != nil {
return err
}
basePath = strings.Replace(config.TagsLocationBasePath, "{{location}}", location, 1)
}

listBindingsURL := fmt.Sprintf("%stagBindings/?parent=%s&pageSize=300", basePath, url.QueryEscape(parent))
resp, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "GET",
RawURL: listBindingsURL,
UserAgent: config.UserAgent,
})
if err != nil {
return fmt.Errorf("error calling tagBindings API for resource %s: %v", rs.Primary.ID, err)
}

tagBindingsVal, exists := resp["tagBindings"]
if !exists {
tagBindingsVal = []interface{}{}
}

tagBindings, ok := tagBindingsVal.([]interface{})
if !ok {
return fmt.Errorf("'tagBindings' is not a slice in response for resource %s. response: %v", rs.Primary.ID, resp)
}

foundExpected := make(map[string]bool, len(expectedTagValues))
foundUnexpected := make(map[string]bool, len(unexpectedTagValues))

for _, binding := range tagBindings {
bindingMap, ok := binding.(map[string]interface{})
if !ok {
continue
}

tagValue, _ := bindingMap["tagValue"].(string)
for _, expectedTagValue := range expectedTagValues {
if tagValue == expectedTagValue {
foundExpected[expectedTagValue] = true
}
}
for _, unexpectedTagValue := range unexpectedTagValues {
if tagValue == unexpectedTagValue {
foundUnexpected[unexpectedTagValue] = true
}
}
}

for _, expectedTagValue := range expectedTagValues {
if !foundExpected[expectedTagValue] {
return fmt.Errorf("expected tag value %s not found in tag bindings for resource %s. bindings: %v", expectedTagValue, rs.Primary.ID, tagBindings)
}
}

for _, unexpectedTagValue := range unexpectedTagValues {
if foundUnexpected[unexpectedTagValue] {
return fmt.Errorf("unexpected tag value %s found in tag bindings for resource %s. bindings: %v", unexpectedTagValue, rs.Primary.ID, tagBindings)
}
}

return nil
}
}

func getResourceState(s *terraform.State, resourceName string) (*terraform.ResourceState, error) {
rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return nil, fmt.Errorf("terraform resource not found: %s", resourceName)
}
return rs, nil
}

func getResourceID(s *terraform.State, resourceName string) (string, error) {
rs, err := getResourceState(s, resourceName)
if err != nil {
return "", err
}
if rs.Primary.ID == "" {
return "", fmt.Errorf("terraform resource %s has no id", resourceName)
}
return rs.Primary.ID, nil
}

// testStringValue returns string values from string pointers, handling nil pointers.
func testStringValue(sPtr *string) string {
if sPtr == nil {
Expand Down
Loading
Loading