Skip to content

Commit 4d721b1

Browse files
authored
Update Test Generation: add new sample and step struct (#15531)
1 parent 536b55c commit 4d721b1

File tree

3 files changed

+504
-38
lines changed

3 files changed

+504
-38
lines changed

mmv1/api/resource/examples.go

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ import (
2929
"github.com/golang/glog"
3030
)
3131

32-
type IamMember struct {
33-
Member, Role string
34-
}
35-
3632
// Generates configs to be shown as examples in docs and outputted as tests
3733
// from a shared template
3834
type Examples struct {
@@ -207,22 +203,6 @@ func (e *Examples) Validate(rName string) {
207203
e.ValidateExternalProviders()
208204
}
209205

210-
func validateRegexForContents(r *regexp.Regexp, contents string, configPath string, objName string, vars map[string]string) {
211-
matches := r.FindAllStringSubmatch(contents, -1)
212-
for _, v := range matches {
213-
found := false
214-
for k, _ := range vars {
215-
if k == v[1] {
216-
found = true
217-
break
218-
}
219-
}
220-
if !found {
221-
log.Fatalf("Failed to find %s environment variable defined in YAML file when validating the file %s. Please define this in %s", v[1], configPath, objName)
222-
}
223-
}
224-
}
225-
226206
func (e *Examples) ValidateExternalProviders() {
227207
// Official providers supported by HashiCorp
228208
// https://registry.terraform.io/search/providers?namespace=hashicorp&tier=official
@@ -388,24 +368,6 @@ func (e *Examples) ResourceType(terraformName string) string {
388368
return terraformName
389369
}
390370

391-
func SubstituteExamplePaths(config string) string {
392-
config = strings.ReplaceAll(config, "../static/img/header-logo.png", "../static/header-logo.png")
393-
config = strings.ReplaceAll(config, "path/to/private.key", "../static/ssl_cert/test.key")
394-
config = strings.ReplaceAll(config, "path/to/id_rsa.pub", "../static/ssh_rsa.pub")
395-
config = strings.ReplaceAll(config, "path/to/certificate.crt", "../static/ssl_cert/test.crt")
396-
return config
397-
}
398-
399-
func SubstituteTestPaths(config string) string {
400-
config = strings.ReplaceAll(config, "../static/img/header-logo.png", "test-fixtures/header-logo.png")
401-
config = strings.ReplaceAll(config, "path/to/private.key", "test-fixtures/test.key")
402-
config = strings.ReplaceAll(config, "path/to/certificate.crt", "test-fixtures/test.crt")
403-
config = strings.ReplaceAll(config, "path/to/index.zip", "%{zip_path}")
404-
config = strings.ReplaceAll(config, "verified-domain.com", "tf-test-domain%{random_suffix}.gcp.tfacc.hashicorptest.com")
405-
config = strings.ReplaceAll(config, "path/to/id_rsa.pub", "test-fixtures/ssh_rsa.pub")
406-
return config
407-
}
408-
409371
// Executes example templates for documentation and tests
410372
func (e *Examples) SetOiCSHCLText() {
411373
originalVars := e.Vars

mmv1/api/resource/sample.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright 2025 Google Inc.
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package resource
15+
16+
import (
17+
"fmt"
18+
"log"
19+
"slices"
20+
21+
"github.com/GoogleCloudPlatform/magic-modules/mmv1/api/product"
22+
"github.com/GoogleCloudPlatform/magic-modules/mmv1/google"
23+
)
24+
25+
type IamMember struct {
26+
Member, Role string
27+
}
28+
29+
type Sample struct {
30+
Name string
31+
32+
// If the test should be skipped during VCR testing.
33+
// This is the case when something about the resource or config causes VCR to fail for example
34+
// a resource with a unique identifier generated within the resource via id.UniqueId()
35+
// Or a config with two fine grained resources that have a race condition during create
36+
SkipVcr bool `yaml:"skip_vcr,omitempty"`
37+
38+
// The reason to skip a test. For example, a link to a ticket explaining the issue that needs to be resolved before
39+
// unskipping the test. If this is not empty, the test will be skipped.
40+
SkipTest string `yaml:"skip_test,omitempty"`
41+
42+
// Whether to skip generating tests for this resource
43+
ExcludeTest bool `yaml:"exclude_test,omitempty"`
44+
45+
// Specify which external providers are needed for the testcase.
46+
// Think before adding as there is latency and adds an external dependency to
47+
// your test so avoid if you can.
48+
ExternalProviders []string `yaml:"external_providers,omitempty"`
49+
50+
// BootstrapIam will automatically bootstrap the given member/role pairs.
51+
// This should be used in cases where specific IAM permissions must be
52+
// present on the default test project, to avoid race conditions between
53+
// tests. Permissions attached to resources created in a test should instead
54+
// be provisioned with standard terraform resources.
55+
BootstrapIam []IamMember `yaml:"bootstrap_iam,omitempty"`
56+
57+
// The version name of the sample's version if it's different than the
58+
// resource version, eg. `beta`
59+
MinVersion string `yaml:"min_version,omitempty"`
60+
61+
// The id of the "primary" resource in a Test. Used in import test steps.
62+
// This is the value that will appear in the Terraform config url. For
63+
// example:
64+
// resource "google_compute_address" {{primary_resource_id}} {
65+
// ...
66+
// }
67+
PrimaryResourceId string `yaml:"primary_resource_id"`
68+
69+
// Optional resource type of the "primary" resource. Used in import tests.
70+
// If set, this will override the default resource type implied from the
71+
// object parent
72+
PrimaryResourceType string `yaml:"primary_resource_type,omitempty"`
73+
74+
// The name of the primary resource for use in IAM tests. IAM tests need
75+
// a reference to the primary resource to create IAM policies for
76+
PrimaryResourceName string `yaml:"primary_resource_name,omitempty"`
77+
78+
// Steps
79+
Steps []Step
80+
81+
// The version name provided by the user through CI
82+
TargetVersionName string `yaml:"-"`
83+
84+
// Step configs that first appears
85+
NewConfigFuncs []Step `yaml:"-"`
86+
87+
// The name of the location/region override for use in IAM tests. IAM
88+
// tests may need this if the location is not inherited on the resource
89+
// for one reason or another
90+
RegionOverride string `yaml:"region_override,omitempty"`
91+
92+
// ====================
93+
// TGC
94+
// ====================
95+
// The reason to skip a test. For example, a link to a ticket explaining the issue that needs to be resolved before
96+
// unskipping the test. If this is not empty, the test will be skipped.
97+
TGCSkipTest string `yaml:"tgc_skip_test,omitempty"`
98+
}
99+
100+
// Set default value for fields
101+
func (s *Sample) UnmarshalYAML(unmarshal func(any) error) error {
102+
type sampleAlias Sample
103+
aliasObj := (*sampleAlias)(s)
104+
105+
err := unmarshal(aliasObj)
106+
if err != nil {
107+
return err
108+
}
109+
110+
return nil
111+
}
112+
113+
func (s *Sample) TestSampleSlug(productName, resourceName string) string {
114+
ret := fmt.Sprintf("%s%s_%sExample", productName, resourceName, google.Camelize(s.Name, "lower"))
115+
return ret
116+
}
117+
118+
func (s *Sample) TestSteps() []Step {
119+
return google.Reject(s.Steps, func(st Step) bool {
120+
return st.MinVersion != "" && slices.Index(product.ORDER, s.TargetVersionName) < slices.Index(product.ORDER, st.MinVersion)
121+
})
122+
}
123+
124+
func (s *Sample) ResourceType(terraformName string) string {
125+
if s.PrimaryResourceType != "" {
126+
return s.PrimaryResourceType
127+
}
128+
return terraformName
129+
}
130+
131+
func (s *Sample) Validate(rName string) {
132+
if s.Name == "" {
133+
log.Fatalf("Missing `name` for one sample in resource %s", rName)
134+
}
135+
s.ValidateExternalProviders()
136+
137+
for _, step := range s.Steps {
138+
step.Validate(rName, s.Name)
139+
}
140+
}
141+
142+
func (s *Sample) ValidateExternalProviders() {
143+
// Official providers supported by HashiCorp
144+
// https://registry.terraform.io/search/providers?namespace=hashicorp&tier=official
145+
HASHICORP_PROVIDERS := []string{"aws", "random", "null", "template", "azurerm", "kubernetes", "local",
146+
"external", "time", "vault", "archive", "tls", "helm", "azuread", "http", "cloudinit", "tfe", "dns",
147+
"consul", "vsphere", "nomad", "awscc", "googleworkspace", "hcp", "boundary", "ad", "azurestack", "opc",
148+
"oraclepaas", "hcs", "salesforce"}
149+
150+
var unallowedProviders []string
151+
for _, p := range s.ExternalProviders {
152+
if !slices.Contains(HASHICORP_PROVIDERS, p) {
153+
unallowedProviders = append(unallowedProviders, p)
154+
}
155+
}
156+
157+
if len(unallowedProviders) > 0 {
158+
log.Fatalf("Providers %#v are not allowed. Only providers published by HashiCorp are allowed.", unallowedProviders)
159+
}
160+
}

0 commit comments

Comments
 (0)