Skip to content

Commit

Permalink
Convert iam test template with Go (GoogleCloudPlatform#10824)
Browse files Browse the repository at this point in the history
  • Loading branch information
zli82016 authored and Cheriit committed Jun 4, 2024
1 parent 0aad9a4 commit cf46ce9
Show file tree
Hide file tree
Showing 7 changed files with 764 additions and 12 deletions.
81 changes: 77 additions & 4 deletions mmv1/api/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,9 @@ func (r Resource) IsInIdentity(t Type) bool {
return false
}

// ====================
// Iam Methods
// ====================
func (r Resource) IamParentResourceName() string {
var parentResourceName string

Expand All @@ -1028,6 +1031,7 @@ func (r Resource) IamParentResourceName() string {
return parentResourceName
}

// For example: "projects/{{project}}/schemas/{{name}}"
func (r Resource) IamResourceUri() string {
var resourceUri string
if r.IamPolicy != nil {
Expand All @@ -1039,13 +1043,15 @@ func (r Resource) IamResourceUri() string {
return resourceUri
}

func (r Resource) IamImportUrl() string {
r.IamResourceUri()
// For example: "projects/%s/schemas/%s"
func (r Resource) IamResourceUriFormat() string {
return regexp.MustCompile(`\{\{%?(\w+)\}\}`).ReplaceAllString(r.IamResourceUri(), "%s")
}

// For example: the uri "projects/{{project}}/schemas/{{name}}"
// The paramerters are "project", "schema".
func (r Resource) IamResourceParams() []string {
resourceUri := strings.ReplaceAll(r.IamResourceUri(), "{{name}}", fmt.Sprintf("{{%s}}}", r.IamParentResourceName()))
resourceUri := strings.ReplaceAll(r.IamResourceUri(), "{{name}}", fmt.Sprintf("{{%s}}", r.IamParentResourceName()))

return r.ExtractIdentifiers(resourceUri)
}
Expand All @@ -1054,14 +1060,18 @@ func (r Resource) IsInIamResourceParams(param string) bool {
return slices.Contains(r.IamResourceParams(), param)
}

func (r Resource) IamStringQualifiers() string {
// For example: for the uri "projects/{{project}}/schemas/{{name}}",
// the string qualifiers are "u.project, u.schema"
func (r Resource) IamResourceUriStringQualifiers() string {
var transformed []string
for _, param := range r.IamResourceParams() {
transformed = append(transformed, fmt.Sprintf("u.%s", google.Camelize(param, "lower")))
}
return strings.Join(transformed[:], ", ")
}

// For example, for the url "projects/{{project}}/schemas/{{schema}}",
// the identifiers are "project", "schema".
// def extract_identifiers(url)
func (r Resource) ExtractIdentifiers(url string) []string {
matches := regexp.MustCompile(`\{\{%?(\w+)\}\}`).FindAllStringSubmatch(url, -1)
Expand All @@ -1072,6 +1082,7 @@ func (r Resource) ExtractIdentifiers(url string) []string {
return result
}

// For example, "projects/{{project}}/schemas/{{name}}", "{{project}}/{{name}}", "{{name}}"
func (r Resource) RawImportIdFormatsFromIam() []string {
var importFormat []string

Expand All @@ -1085,6 +1096,7 @@ func (r Resource) RawImportIdFormatsFromIam() []string {
return ImportIdFormats(importFormat, r.Identity, r.BaseUrl)
}

// For example, projects/(?P<project>[^/]+)/schemas/(?P<schema>[^/]+)", "(?P<project>[^/]+)/(?P<schema>[^/]+)", "(?P<schema>[^/]+)
func (r Resource) ImportIdRegexesFromIam() string {
var transformed []string

Expand All @@ -1098,6 +1110,7 @@ func (r Resource) ImportIdRegexesFromIam() string {
return strings.Join(transformed[:], "\", \"")
}

// For example, "projects/{{project}}/schemas/{{name}}", "{{project}}/{{name}}", "{{name}}"
func (r Resource) ImportIdFormatsFromIam() []string {
importIdFormats := r.RawImportIdFormatsFromIam()
var transformed []string
Expand All @@ -1107,6 +1120,7 @@ func (r Resource) ImportIdFormatsFromIam() []string {
return transformed
}

// For example, projects/{{project}}/schemas/{{schema}}
func (r Resource) FirstIamImportIdFormat() string {
importIdFormats := r.ImportIdFormatsFromIam()
if len(importIdFormats) == 0 {
Expand All @@ -1133,6 +1147,7 @@ func (r Resource) IamSelfLinkIdentifiers() []string {
return r.ExtractIdentifiers(selfLink)
}

// Returns the resource properties that are idenfifires in the selflink url
func (r Resource) IamSelfLinkProperties() []*Type {
params := r.IamSelfLinkIdentifiers()

Expand All @@ -1143,6 +1158,7 @@ func (r Resource) IamSelfLinkProperties() []*Type {
return urlProperties
}

// Returns the attributes from the selflink url
func (r Resource) IamAttributes() []string {
var attributes []string
ids := r.IamSelfLinkIdentifiers()
Expand All @@ -1161,6 +1177,19 @@ func (r Resource) IamAttributes() []string {
return attributes
}

// Since most resources define a "basic" config as their first example,
// we can reuse that config to create a resource to test IAM resources with.
func (r Resource) FirstTestExample() resource.Examples {
examples := google.Reject(r.Examples, func(e resource.Examples) bool {
return e.SkipTest
})
examples = google.Reject(examples, func(e resource.Examples) bool {
return (r.ProductMetadata.VersionObjOrClosest(r.TargetVersionName).CompareTo(r.ProductMetadata.VersionObjOrClosest(e.MinVersion)) < 0)
})

return examples[0]
}

func (r Resource) ExamplePrimaryResourceId() string {
examples := google.Reject(r.Examples, func(e resource.Examples) bool {
return e.SkipTest
Expand All @@ -1185,6 +1214,50 @@ func (r Resource) IamParentSourceType() string {
return t
}

func (r Resource) IamImportQualifiersForTest() string {
var importFormat string
if len(r.IamPolicy.ImportFormat) > 0 {
importFormat = r.IamPolicy.ImportFormat[0]
} else {
importFormat = r.IamPolicy.SelfLink
if importFormat == "" {
importFormat = r.SelfLinkUrl()
}
}

params := r.ExtractIdentifiers(importFormat)
var importQualifiers []string
for i, param := range params {
if param == "project" {
if i != len(params)-1 {
// If the last parameter is project then we want to create a new project to use for the test, so don't default from the environment
importQualifiers = append(importQualifiers, "envvar.GetTestProjectFromEnv()")
} else {
importQualifiers = append(importQualifiers, `context["project_id"]`)
}
} else if param == "zone" && r.IamPolicy.SubstituteZoneValue {
importQualifiers = append(importQualifiers, "envvar.GetTestZoneFromEnv()")
} else if param == "region" || param == "location" {
example := r.FirstTestExample()
if example.RegionOverride == "" {
importQualifiers = append(importQualifiers, "envvar.GetTestRegionFromEnv()")
} else {
importQualifiers = append(importQualifiers, example.RegionOverride)
}
} else if param == "universe_domain" {
importQualifiers = append(importQualifiers, "envvar.GetTestUniverseDomainFromEnv()")
} else {
break
}
}

if len(importQualifiers) == 0 {
return ""
}

return strings.Join(importQualifiers, ", ")
}

func OrderProperties(props []*Type) []*Type {
req := google.Select(props, func(p *Type) bool {
return p.Required
Expand Down
8 changes: 4 additions & 4 deletions mmv1/api/resource/iam_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ type IamPolicy struct {
// While Compute subnetwork uses {resource}/getIamPolicy
MethodNameSeparator string `yaml:"method_name_separator"`

// The terraform type of the parent resource if it is not the same as the
// IAM resource. The IAP product needs these as its IAM policies refer
// to compute resources
// The terraform type (e.g. 'google_endpoints_service') of the parent resource
// if it is not the same as the IAM resource. The IAP product needs these
// as its IAM policies refer to compute resources.
ParentResourceType string `yaml:"parent_resource_type"`

// Some resources allow retrieving the IAM policy with GET requests,
Expand Down Expand Up @@ -84,7 +84,7 @@ type IamPolicy struct {

// Some resources (IAP) use fields named differently from the parent resource.
// We need to use the parent's attributes to create an IAM policy, but they may not be
// named as the IAM IAM resource expects.
// named as the IAM resource expects.
// This allows us to specify a file (relative to MM root) containing a partial terraform
// config with the test/example attributes of the IAM resource.
ExampleConfigBody string `yaml:"example_config_body"`
Expand Down
5 changes: 5 additions & 0 deletions mmv1/provider/template_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ func (td *TemplateData) GenerateIamDatasourceDocumentationFile(filePath string,
}

func (td *TemplateData) GenerateIamPolicyTestFile(filePath string, resource api.Resource) {
templatePath := "templates/terraform/examples/base_configs/iam_test_file.go.tmpl"
templates := []string{
templatePath,
}
td.GenerateFile(filePath, templatePath, resource, false, templates...)
}

func (td *TemplateData) GenerateFile(filePath, templatePath string, input any, goFormat bool, templates ...string) {
Expand Down
2 changes: 1 addition & 1 deletion mmv1/templates/terraform/datasource_iam.html.markdown.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ data "{{ $.IamTerraformName }}_policy" "policy" {
{{ if eq $.MinVersionObj.Name "beta" }}
provider = google-beta
{{- end }}
{{- $.CustomTemplate $.IamPolicy.ExampleConfigBody}}
{{- $.CustomTemplate $.IamPolicy.ExampleConfigBody false }}
}
```

Expand Down
Loading

0 comments on commit cf46ce9

Please sign in to comment.