Skip to content

Commit e38abc1

Browse files
authored
Add cai_asset_name_format to resource meta.yaml file (#15142)
1 parent 06682c4 commit e38abc1

File tree

7 files changed

+105
-54
lines changed

7 files changed

+105
-54
lines changed

mmv1/third_party/terraform/acctest/resource_inventory_reader.go

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,20 @@ import (
1212

1313
// ResourceYAMLMetadata represents the structure of the metadata files
1414
type ResourceYAMLMetadata struct {
15-
Resource string `yaml:"resource"`
16-
ApiServiceName string `yaml:"api_service_name"`
17-
SourceFile string `yaml:"source_file"`
15+
Resource string `yaml:"resource"`
16+
ApiServiceName string `yaml:"api_service_name"`
17+
CaiAssetNameFormat string `yaml:"cai_asset_name_format"`
18+
SourceFile string `yaml:"source_file"`
1819
}
1920

2021
// Cache structures to avoid repeated file system operations
2122
var (
2223
// Cache for API service names (resourceName -> apiServiceName)
23-
apiServiceNameCache = make(map[string]string)
24+
ApiServiceNameCache = NewGenericCache("unknown")
25+
// Cache for CAI resource name format (resourceName -> CaiAssetNameFormat)
26+
CaiAssetNameFormatCache = NewGenericCache("")
2427
// Cache for service packages (resourceType -> servicePackage)
25-
servicePackageCache = make(map[string]string)
28+
ServicePackageCache = NewGenericCache("unknown")
2629
// Flag to track if cache has been populated
2730
cachePopulated = false
2831
// Mutex to protect cache access
@@ -76,10 +79,14 @@ func PopulateMetadataCache() error {
7679

7780
// Store API service name in cache
7881
if metadata.ApiServiceName != "" {
79-
apiServiceNameCache[metadata.Resource] = metadata.ApiServiceName
82+
ApiServiceNameCache.Set(metadata.Resource, metadata.ApiServiceName)
8083
apiNameCount++
8184
}
8285

86+
if metadata.CaiAssetNameFormat != "" {
87+
CaiAssetNameFormatCache.Set(metadata.Resource, metadata.CaiAssetNameFormat)
88+
}
89+
8390
// Extract and store service package in cache
8491
pathParts := strings.Split(path, string(os.PathSeparator))
8592
servicesIndex := -1
@@ -92,7 +99,7 @@ func PopulateMetadataCache() error {
9299

93100
if servicesIndex >= 0 && len(pathParts) > servicesIndex+1 {
94101
servicePackage := pathParts[servicesIndex+1] // The part after "services"
95-
servicePackageCache[metadata.Resource] = servicePackage
102+
ServicePackageCache.Set(metadata.Resource, servicePackage)
96103
servicePkgCount++
97104
}
98105
}
@@ -109,48 +116,42 @@ func PopulateMetadataCache() error {
109116
return nil
110117
}
111118

112-
// GetAPIServiceNameForResource finds the api_service_name for a given resource name
113-
// If projectRoot is empty, it will attempt to find the project root automatically
114-
func GetAPIServiceNameForResource(resourceName string) string {
115-
// Make sure cache is populated
116-
if !cachePopulated {
117-
if err := PopulateMetadataCache(); err != nil {
118-
return "failed_to_populate_metadata_cache"
119-
}
120-
}
121-
122-
// Check cache
123-
cacheMutex.RLock()
124-
apiServiceName, found := apiServiceNameCache[resourceName]
125-
cacheMutex.RUnlock()
119+
type GenericCache struct {
120+
mu sync.RWMutex
121+
data map[string]string
122+
defaultValue string
123+
}
126124

127-
if !found {
128-
return "unknown"
125+
// NewGenericCache initializes a new GenericCache with a default value.
126+
func NewGenericCache(defaultValue string) *GenericCache {
127+
return &GenericCache{
128+
data: make(map[string]string),
129+
defaultValue: defaultValue,
129130
}
130-
131-
return apiServiceName
132131
}
133132

134-
// GetServicePackageForResourceType finds the service package for a given resource type
135-
// If projectRoot is empty, it will attempt to find the project root automatically
136-
func GetServicePackageForResourceType(resourceType string) string {
133+
// Get retrieves a value from the cache, returning the default if not found.
134+
func (c *GenericCache) Get(key string) string {
137135
// Make sure cache is populated
138136
if !cachePopulated {
139137
if err := PopulateMetadataCache(); err != nil {
140138
return "failed_to_populate_metadata_cache"
141139
}
142140
}
143141

144-
// Check cache
145-
cacheMutex.RLock()
146-
servicePackage, found := servicePackageCache[resourceType]
147-
cacheMutex.RUnlock()
148-
149-
if !found {
150-
return "unknown"
142+
c.mu.RLock()
143+
defer c.mu.RUnlock()
144+
value, ok := c.data[key]
145+
if !ok {
146+
return c.defaultValue
151147
}
148+
return value
149+
}
152150

153-
return servicePackage
151+
func (c *GenericCache) Set(key, value string) {
152+
c.mu.Lock()
153+
defer c.mu.Unlock()
154+
c.data[key] = value
154155
}
155156

156157
// getServicesDir returns the path to the services directory

mmv1/third_party/terraform/acctest/resource_inventory_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ func TestResourceInventoryMetadataFound(t *testing.T) {
3434
// t.Logf("Checking metadata for resource: %s", resourceType)
3535

3636
// Check for service package
37-
servicePackage := acctest.GetServicePackageForResourceType(resourceType)
37+
servicePackage := acctest.ServicePackageCache.Get(resourceType)
3838
if servicePackage == "unknown" {
3939
// t.Logf("WARNING: Could not find service package for resource %s: %v", resourceType)
4040
missingServicePkg++
4141
missingServicePkgResources[resourceType] = true
4242
}
4343

44-
apiServiceName := acctest.GetAPIServiceNameForResource(resourceType)
44+
apiServiceName := acctest.ApiServiceNameCache.Get(resourceType)
4545
// Check for API service name
4646
if apiServiceName == "unknown" {
4747
// t.Logf("WARNING: Could not find API service name for resource %s: %v", resourceType)

mmv1/third_party/terraform/acctest/tgc_utils.go

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -95,26 +95,43 @@ func CollectAllTgcMetadata(tgcPayload TgcMetadataPayload) resource.TestCheckFunc
9595
}
9696

9797
// Resolve the CAI asset name
98-
apiServiceName := GetAPIServiceNameForResource(metadata.ResourceType)
99-
if apiServiceName == "unknown" || apiServiceName == "failed_to_populate_metadata_cache" {
100-
log.Printf("[DEBUG]TGC Terraform error: unknown resource type %s", metadata.ResourceType)
101-
metadata.CaiAssetNames = []string{apiServiceName}
102-
} else {
103-
var rName string
104-
switch metadata.ResourceType {
105-
case "google_project":
106-
rName = fmt.Sprintf("projects/%s", rState.Primary.Attributes["number"])
107-
default:
108-
rName = rState.Primary.ID
109-
}
98+
caiAssetNameFormat := CaiAssetNameFormatCache.Get(metadata.ResourceType)
99+
if caiAssetNameFormat == "" || caiAssetNameFormat == "failed_to_populate_metadata_cache" {
100+
log.Printf("[DEBUG]TGC Terraform error: unknown CAI asset name format for resource type %s", caiAssetNameFormat)
101+
102+
apiServiceName := ApiServiceNameCache.Get(metadata.ResourceType)
103+
if apiServiceName == "unknown" || apiServiceName == "failed_to_populate_metadata_cache" {
104+
log.Printf("[DEBUG]TGC Terraform error: unknown resource type %s", metadata.ResourceType)
105+
metadata.CaiAssetNames = []string{apiServiceName}
106+
} else {
107+
var rName string
108+
switch metadata.ResourceType {
109+
case "google_project":
110+
rName = fmt.Sprintf("projects/%s", rState.Primary.Attributes["number"])
111+
default:
112+
rName = rState.Primary.ID
113+
}
114+
115+
if _, ok := serviceWithProjectNumber[metadata.Service]; ok {
116+
rName = strings.Replace(rName, projectId, projectNumber, 1)
117+
}
110118

111-
if _, ok := serviceWithProjectNumber[metadata.Service]; ok {
112-
rName = strings.Replace(rName, projectId, projectNumber, 1)
119+
metadata.CaiAssetNames = []string{fmt.Sprintf("//%s/%s", apiServiceName, rName)}
120+
}
121+
} else {
122+
paramsMap := make(map[string]any, 0)
123+
params := extractIdentifiers(caiAssetNameFormat)
124+
for _, param := range params {
125+
v := rState.Primary.Attributes[param]
126+
paramsMap[param] = v
113127
}
114128

115-
metadata.CaiAssetNames = []string{fmt.Sprintf("//%s/%s", apiServiceName, rName)}
129+
caiAssetName := replacePlaceholders(caiAssetNameFormat, paramsMap)
130+
metadata.CaiAssetNames = []string{caiAssetName}
116131
}
117132

133+
log.Printf("[DEBUG] CaiAssetNames %#v", metadata.CaiAssetNames)
134+
118135
// Resolve auto IDs in import metadata
119136
if metadata.ImportMetadata.Id != "" {
120137
metadata.ImportMetadata.Id = strings.Replace(metadata.ImportMetadata.Id, "<AUTO_ID>", rState.Primary.ID, 1)
@@ -138,6 +155,35 @@ func CollectAllTgcMetadata(tgcPayload TgcMetadataPayload) resource.TestCheckFunc
138155
}
139156
}
140157

158+
// For example, for the url "projects/{{project}}/schemas/{{schema}}",
159+
// the identifiers are "project", "schema".
160+
func extractIdentifiers(url string) []string {
161+
matches := regexp.MustCompile(`\{\{%?(\w+)\}\}`).FindAllStringSubmatch(url, -1)
162+
var result []string
163+
for _, match := range matches {
164+
result = append(result, match[1])
165+
}
166+
return result
167+
}
168+
169+
// It replaces all instances of {{key}} in the template with the
170+
// corresponding value from the parameters map.
171+
func replacePlaceholders(template string, params map[string]any) string {
172+
re := regexp.MustCompile(`\{\{([a-zA-Z0-9_]+)\}\}`)
173+
174+
result := re.ReplaceAllStringFunc(template, func(match string) string {
175+
key := strings.Trim(match, "{}")
176+
177+
if value, ok := params[key]; ok {
178+
return value.(string)
179+
}
180+
181+
return match
182+
})
183+
184+
return result
185+
}
186+
141187
// parseResources extracts all resources from a Terraform configuration string
142188
func parseResources(config string) []string {
143189
// This regex matches resource blocks in Terraform configurations
@@ -246,7 +292,7 @@ func extendWithTGCData(t *testing.T, c resource.TestCase) resource.TestCase {
246292
ResourceType: resourceType,
247293
ResourceAddress: res,
248294
ImportMetadata: importMeta,
249-
Service: GetServicePackageForResourceType(resourceType),
295+
Service: ApiServiceNameCache.Get(resourceType),
250296
// CaiAssetNames will be populated at runtime in the check function
251297
}
252298
}

mmv1/third_party/terraform/services/appengine/resource_app_engine_application_meta.yaml.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ api_version: 'v1beta'
77
api_version: 'v1'
88
{{- end }}
99
api_resource_type_kind: 'Application'
10+
cai_asset_name_format: '//appengine.googleapis.com/{{"{{"}}name{{"}}"}}'
1011
fields:
1112
- field: 'app_id'
1213
- field: 'auth_domain'

mmv1/third_party/terraform/services/bigtable/resource_bigtable_instance_meta.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
resource: 'google_bigtable_instance'
22
generation_type: 'handwritten'
33
api_service_name: 'bigtableadmin.googleapis.com'
4+
cai_asset_name_format: '//bigtable.googleapis.com/projects/{{project}}/instances/{{name}}'
45
api_version: 'v2'
56
api_resource_type_kind: 'Instance'
67
fields:

mmv1/third_party/terraform/services/dataproc/resource_dataproc_workflow_template_meta.yaml.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ api_version: 'v1'
55
api_resource_type_kind: 'WorkflowTemplate'
66
api_variant_patterns:
77
- 'projects/{project}/locations/{location}/workflowTemplates/{workflowTemplate}'
8+
cai_asset_name_format: {{"//dataproc.googleapis.com/projects/{{project}}/regions/{{location}}/workflowTemplates/{{name}}"}}
89
fields:
910
- field: 'create_time'
1011
- field: 'dag_timeout'

mmv1/third_party/terraform/services/tags/resource_tags_location_tag_binding_meta.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ generation_type: 'handwritten'
33
api_service_name: 'cloudresourcemanager.googleapis.com'
44
api_version: 'v3'
55
api_resource_type_kind: 'TagBinding'
6+
cai_asset_name_format: '//cloudresourcemanager.googleapis.com/{{name}}'
67
fields:
78
- field: 'location'
89
- field: 'name'

0 commit comments

Comments
 (0)