|
| 1 | +// Copyright (c) HashiCorp, Inc. |
| 2 | +// SPDX-License-Identifier: MPL-2.0 |
| 3 | +// ---------------------------------------------------------------------------- |
| 4 | +// |
| 5 | +// *** AUTO GENERATED CODE *** Type: Handwritten *** |
| 6 | +// |
| 7 | +// ---------------------------------------------------------------------------- |
| 8 | +// |
| 9 | +// This code is generated by Magic Modules using the following: |
| 10 | +// |
| 11 | +// Source file: https://github.com/GoogleCloudPlatform/magic-modules/tree/main/mmv1/third_party/terraform/acctest/resource_inventory_reader.go |
| 12 | +// |
| 13 | +// DO NOT EDIT this file directly. Any changes made to this file will be |
| 14 | +// overwritten during the next generation cycle. |
| 15 | +// |
| 16 | +// ---------------------------------------------------------------------------- |
| 17 | +package acctest |
| 18 | + |
| 19 | +import ( |
| 20 | + "fmt" |
| 21 | + "os" |
| 22 | + "path/filepath" |
| 23 | + "strings" |
| 24 | + "sync" |
| 25 | + |
| 26 | + "gopkg.in/yaml.v2" |
| 27 | +) |
| 28 | + |
| 29 | +// ResourceYAMLMetadata represents the structure of the metadata files |
| 30 | +type ResourceYAMLMetadata struct { |
| 31 | + Resource string `yaml:"resource"` |
| 32 | + ApiServiceName string `yaml:"api_service_name"` |
| 33 | + SourceFile string `yaml:"source_file"` |
| 34 | +} |
| 35 | + |
| 36 | +// Cache structures to avoid repeated file system operations |
| 37 | +var ( |
| 38 | + // Cache for API service names (resourceName -> apiServiceName) |
| 39 | + apiServiceNameCache = make(map[string]string) |
| 40 | + // Cache for service packages (resourceType -> servicePackage) |
| 41 | + servicePackageCache = make(map[string]string) |
| 42 | + // Flag to track if cache has been populated |
| 43 | + cachePopulated = false |
| 44 | + // Mutex to protect cache access |
| 45 | + cacheMutex sync.RWMutex |
| 46 | +) |
| 47 | + |
| 48 | +// PopulateMetadataCache walks through all metadata files once and populates |
| 49 | +// both the API service name and service package caches for improved performance |
| 50 | +func PopulateMetadataCache() error { |
| 51 | + cacheMutex.Lock() |
| 52 | + defer cacheMutex.Unlock() |
| 53 | + |
| 54 | + // If cache is already populated, we can skip |
| 55 | + if cachePopulated { |
| 56 | + return nil |
| 57 | + } |
| 58 | + |
| 59 | + baseDir, err := getServicesDir() |
| 60 | + if err != nil { |
| 61 | + return fmt.Errorf("failed to find services directory: %v", err) |
| 62 | + } |
| 63 | + |
| 64 | + // Count for statistics |
| 65 | + apiNameCount := 0 |
| 66 | + servicePkgCount := 0 |
| 67 | + |
| 68 | + // Walk through all service directories once |
| 69 | + err = filepath.Walk(baseDir, func(path string, info os.FileInfo, err error) error { |
| 70 | + if err != nil { |
| 71 | + return nil // Skip files with errors but continue walking |
| 72 | + } |
| 73 | + |
| 74 | + // Look for metadata files |
| 75 | + if !info.IsDir() && strings.HasPrefix(info.Name(), "resource_") && strings.HasSuffix(info.Name(), "_meta.yaml") { |
| 76 | + // Read the file |
| 77 | + content, err := os.ReadFile(path) |
| 78 | + if err != nil { |
| 79 | + return nil // Continue to next file |
| 80 | + } |
| 81 | + |
| 82 | + // Parse YAML |
| 83 | + var metadata ResourceYAMLMetadata |
| 84 | + if err := yaml.Unmarshal(content, &metadata); err != nil { |
| 85 | + return nil // Continue to next file |
| 86 | + } |
| 87 | + |
| 88 | + // Skip if resource is empty |
| 89 | + if metadata.Resource == "" { |
| 90 | + return nil |
| 91 | + } |
| 92 | + |
| 93 | + // Store API service name in cache |
| 94 | + if metadata.ApiServiceName != "" { |
| 95 | + apiServiceNameCache[metadata.Resource] = metadata.ApiServiceName |
| 96 | + apiNameCount++ |
| 97 | + } |
| 98 | + |
| 99 | + // Extract and store service package in cache |
| 100 | + pathParts := strings.Split(path, string(os.PathSeparator)) |
| 101 | + servicesIndex := -1 |
| 102 | + for i, part := range pathParts { |
| 103 | + if part == "services" { |
| 104 | + servicesIndex = i |
| 105 | + break |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + if servicesIndex >= 0 && len(pathParts) > servicesIndex+1 { |
| 110 | + servicePackage := pathParts[servicesIndex+1] // The part after "services" |
| 111 | + servicePackageCache[metadata.Resource] = servicePackage |
| 112 | + servicePkgCount++ |
| 113 | + } |
| 114 | + } |
| 115 | + return nil |
| 116 | + }) |
| 117 | + |
| 118 | + if err != nil { |
| 119 | + return fmt.Errorf("error walking directory: %v", err) |
| 120 | + } |
| 121 | + |
| 122 | + // Mark cache as populated |
| 123 | + cachePopulated = true |
| 124 | + |
| 125 | + return nil |
| 126 | +} |
| 127 | + |
| 128 | +// GetAPIServiceNameForResource finds the api_service_name for a given resource name |
| 129 | +// If projectRoot is empty, it will attempt to find the project root automatically |
| 130 | +func GetAPIServiceNameForResource(resourceName string) string { |
| 131 | + // Make sure cache is populated |
| 132 | + if !cachePopulated { |
| 133 | + if err := PopulateMetadataCache(); err != nil { |
| 134 | + return "failed_to_populate_metadata_cache" |
| 135 | + } |
| 136 | + } |
| 137 | + |
| 138 | + // Check cache |
| 139 | + cacheMutex.RLock() |
| 140 | + apiServiceName, found := apiServiceNameCache[resourceName] |
| 141 | + cacheMutex.RUnlock() |
| 142 | + |
| 143 | + if !found { |
| 144 | + return "unknown" |
| 145 | + } |
| 146 | + |
| 147 | + return apiServiceName |
| 148 | +} |
| 149 | + |
| 150 | +// GetServicePackageForResourceType finds the service package for a given resource type |
| 151 | +// If projectRoot is empty, it will attempt to find the project root automatically |
| 152 | +func GetServicePackageForResourceType(resourceType string) string { |
| 153 | + // Make sure cache is populated |
| 154 | + if !cachePopulated { |
| 155 | + if err := PopulateMetadataCache(); err != nil { |
| 156 | + return "failed_to_populate_metadata_cache" |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + // Check cache |
| 161 | + cacheMutex.RLock() |
| 162 | + servicePackage, found := servicePackageCache[resourceType] |
| 163 | + cacheMutex.RUnlock() |
| 164 | + |
| 165 | + if !found { |
| 166 | + return "unknown" |
| 167 | + } |
| 168 | + |
| 169 | + return servicePackage |
| 170 | +} |
| 171 | + |
| 172 | +// getServicesDir returns the path to the services directory |
| 173 | +// It will attempt to find the project root relative to cwd |
| 174 | +func getServicesDir() (string, error) { |
| 175 | + // Try to find project root |
| 176 | + root, err := findProjectRoot() |
| 177 | + if err == nil { |
| 178 | + servicesDir := filepath.Join(root, "google-beta", "services") |
| 179 | + if _, err := os.Stat(servicesDir); err == nil { |
| 180 | + return servicesDir, nil |
| 181 | + } |
| 182 | + } |
| 183 | + |
| 184 | + // Last resort: try relative to current directory |
| 185 | + currentDir, err := os.Getwd() |
| 186 | + if err != nil { |
| 187 | + return "", fmt.Errorf("failed to determine current directory: %v", err) |
| 188 | + } |
| 189 | + |
| 190 | + // Try a few common relative paths |
| 191 | + potentialPaths := []string{ |
| 192 | + filepath.Join(currentDir, "google-beta", "services"), |
| 193 | + filepath.Join(currentDir, "..", "google-beta", "services"), |
| 194 | + filepath.Join(currentDir, "..", "..", "google-beta", "services"), |
| 195 | + } |
| 196 | + |
| 197 | + for _, path := range potentialPaths { |
| 198 | + if _, err := os.Stat(path); err == nil { |
| 199 | + return path, nil |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | + return "", fmt.Errorf("unable to locate services directory, please provide explicit project root path") |
| 204 | +} |
| 205 | + |
| 206 | +// findProjectRoot walks up from the current directory to find the project root |
| 207 | +// by looking for the go.mod file |
| 208 | +func findProjectRoot() (string, error) { |
| 209 | + dir, err := os.Getwd() |
| 210 | + if err != nil { |
| 211 | + return "", err |
| 212 | + } |
| 213 | + |
| 214 | + for { |
| 215 | + // Check if go.mod exists in the current directory |
| 216 | + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { |
| 217 | + return dir, nil |
| 218 | + } |
| 219 | + |
| 220 | + // Move up to the parent directory |
| 221 | + parentDir := filepath.Dir(dir) |
| 222 | + if parentDir == dir { |
| 223 | + // Reached the filesystem root without finding go.mod |
| 224 | + return "", fmt.Errorf("could not find go.mod file in any parent directory") |
| 225 | + } |
| 226 | + dir = parentDir |
| 227 | + } |
| 228 | +} |
0 commit comments