Skip to content

Commit db41bf7

Browse files
authored
Create a registry package for products to self-register resources v2 (GoogleCloudPlatform#16255)
1 parent 101ee55 commit db41bf7

7 files changed

Lines changed: 184 additions & 7 deletions

File tree

mmv1/api/product.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ type Product struct {
7777

7878
// The compiler to generate the downstream files, for example "terraformgoogleconversion-codegen".
7979
Compiler string `yaml:"-"`
80+
81+
// ImportPath contains the prefix used for importing packages in generated files.
82+
ImportPath string `yaml:"-"`
8083
}
8184

8285
func (p *Product) UnmarshalYAML(value *yaml.Node) error {

mmv1/provider/terraform.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ func NewTerraform(product *api.Product, versionName string, startTime time.Time,
6565
}
6666

6767
t.Product.SetCompiler(ProviderName(t))
68+
t.Product.ImportPath = ImportPathFromVersion(versionName)
6869
for _, r := range t.Product.Objects {
6970
r.SetCompiler(ProviderName(t))
70-
r.ImportPath = ImportPathFromVersion(versionName)
71+
r.ImportPath = t.Product.ImportPath
7172
}
7273

7374
return t
@@ -486,7 +487,7 @@ func (t Terraform) getCommonCopyFiles(versionName string, generateCode, generate
486487
// save the folder name to foldersCopiedToGoogleDir
487488
var foldersCopiedToGoogleDir []string
488489
if generateCode {
489-
foldersCopiedToGoogleDir = []string{"third_party/terraform/services", "third_party/terraform/acctest", "third_party/terraform/sweeper", "third_party/terraform/provider", "third_party/terraform/tpgdclresource", "third_party/terraform/tpgiamresource", "third_party/terraform/tpgresource", "third_party/terraform/transport", "third_party/terraform/fwmodels", "third_party/terraform/fwprovider", "third_party/terraform/fwtransport", "third_party/terraform/fwresource", "third_party/terraform/fwutils", "third_party/terraform/fwvalidators", "third_party/terraform/verify", "third_party/terraform/envvar", "third_party/terraform/functions", "third_party/terraform/test-fixtures"}
490+
foldersCopiedToGoogleDir = []string{"third_party/terraform/services", "third_party/terraform/acctest", "third_party/terraform/sweeper", "third_party/terraform/provider", "third_party/terraform/registry", "third_party/terraform/tpgdclresource", "third_party/terraform/tpgiamresource", "third_party/terraform/tpgresource", "third_party/terraform/transport", "third_party/terraform/fwmodels", "third_party/terraform/fwprovider", "third_party/terraform/fwtransport", "third_party/terraform/fwresource", "third_party/terraform/fwutils", "third_party/terraform/fwvalidators", "third_party/terraform/verify", "third_party/terraform/envvar", "third_party/terraform/functions", "third_party/terraform/test-fixtures"}
490491
}
491492
googleDir := "google"
492493
if versionName != "ga" {

mmv1/templates/terraform/iam_policy.go.tmpl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ import (
2929
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
3030
"google.golang.org/api/cloudresourcemanager/v1"
3131

32+
{{ if not (contains $.Compiler "terraformgoogleconversion") -}}
33+
"{{ $.ImportPath }}/registry"
34+
{{- end }}
3235
"{{ $.ImportPath }}/tpgiamresource"
3336
"{{ $.ImportPath }}/tpgresource"
3437
transport_tpg "{{ $.ImportPath }}/transport"
@@ -41,6 +44,35 @@ var (
4144
_ = schema.Noop
4245
)
4346

47+
{{- if not (contains $.Compiler "terraformgoogleconversion") }}
48+
func init() {
49+
registry.Schema{
50+
Name: "{{ $.TerraformName }}_iam_binding",
51+
ProductName: "{{ $.ProductMetadata.Name }}",
52+
Type: registry.SchemaTypeIAMResource,
53+
Schema: tpgiamresource.ResourceIamBinding({{ $.ResourceName }}IamSchema, {{ $.ResourceName }}IamUpdaterProducer, {{ $.ResourceName }}IdParseFunc),
54+
}.Register()
55+
registry.Schema{
56+
Name: "{{ $.TerraformName }}_iam_member",
57+
ProductName: "{{ $.ProductMetadata.Name }}",
58+
Type: registry.SchemaTypeIAMResource,
59+
Schema: tpgiamresource.ResourceIamMember({{ $.ResourceName }}IamSchema, {{ $.ResourceName }}IamUpdaterProducer, {{ $.ResourceName }}IdParseFunc),
60+
}.Register()
61+
registry.Schema{
62+
Name: "{{ $.TerraformName }}_iam_policy",
63+
ProductName: "{{ $.ProductMetadata.Name }}",
64+
Type: registry.SchemaTypeIAMResource,
65+
Schema: tpgiamresource.ResourceIamPolicy({{ $.ResourceName }}IamSchema, {{ $.ResourceName }}IamUpdaterProducer, {{ $.ResourceName }}IdParseFunc),
66+
}.Register()
67+
registry.Schema{
68+
Name: "{{ $.TerraformName }}_iam_policy",
69+
ProductName: "{{ $.ProductMetadata.Name }}",
70+
Type: registry.SchemaTypeIAMDataSource,
71+
Schema: tpgiamresource.DataSourceIamPolicy({{ $.ResourceName }}IamSchema, {{ $.ResourceName }}IamUpdaterProducer),
72+
}.Register()
73+
}
74+
{{- end }}
75+
4476
var {{ $.ResourceName }}IamSchema = map[string]*schema.Schema{
4577
{{- range $i, $param := $.IamResourceParams }}
4678
"{{ underscore $param }}": {

mmv1/templates/terraform/product.go.tmpl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,15 @@
1919
// Package {{ lower $.Name }} contains resources, datasources, etc. for the {{ lower $.DisplayName }} service.
2020
package {{ lower $.Name }}
2121

22+
import (
23+
"{{ $.ImportPath }}/registry"
24+
)
25+
2226
const ProductName = "{{ lower $.Name }}"
27+
28+
func init() {
29+
registry.Product{
30+
Name: "{{ lower $.Name }}",
31+
BaseUrl: "{{ $.Version.BaseUrl }}",
32+
}.Register()
33+
}

mmv1/templates/terraform/resource.go.tmpl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import (
4747
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
4848
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
4949

50+
"{{ $.ImportPath }}/registry"
5051
"{{ $.ImportPath }}/tpgresource"
5152
transport_tpg "{{ $.ImportPath }}/transport"
5253
"{{ $.ImportPath }}/verify"
@@ -90,6 +91,15 @@ var (
9091
_ = googleapi.Error{}
9192
)
9293

94+
func init() {
95+
registry.Schema{
96+
Name: "{{ $.TerraformName }}",
97+
ProductName: "{{ lower $.ProductMetadata.Name }}",
98+
Type: registry.SchemaTypeResource,
99+
Schema: Resource{{ $.ResourceName -}}(),
100+
}.Register()
101+
}
102+
93103
func Resource{{ $.ResourceName -}}() *schema.Resource {
94104
return &schema.Resource{
95105
Create: resource{{ $.ResourceName -}}Create,

mmv1/third_party/terraform/provider/provider_mmv1_resources.go.tmpl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/hashicorp/terraform-provider-google/google/services/{{ $service }}"
88
{{- end }}
99

10+
"github.com/hashicorp/terraform-provider-google/google/registry"
1011
"github.com/hashicorp/terraform-provider-google/google/services/container"
1112
"github.com/hashicorp/terraform-provider-google/google/services/containeraws"
1213
"github.com/hashicorp/terraform-provider-google/google/services/containerazure"
@@ -320,7 +321,7 @@ var generatedIAMDatasources = map[string]*schema.Resource{
320321
// ####### START generated IAM datasources ###########
321322
{{- range $object := $.ResourcesForVersion }}
322323
{{- if $object.IamClassName }}
323-
"{{ $object.TerraformName }}_iam_policy": tpgiamresource.DataSourceIamPolicy({{ $object.IamClassName }}IamSchema, {{ $object.IamClassName }}IamUpdaterProducer),
324+
"{{ $object.TerraformName }}_iam_policy": registry.DataSource("{{ $object.TerraformName }}_iam_policy"),
324325
{{- end }}
325326
{{- end }}
326327
// ####### END generated IAM datasources ###########
@@ -359,12 +360,12 @@ var handwrittenIAMDatasources = map[string]*schema.Resource{
359360
var generatedResources = map[string]*schema.Resource{
360361
{{- range $object := $.ResourcesForVersion }}
361362
{{- if $object.ResourceName }}
362-
"{{ $object.TerraformName }}": {{ $object.ResourceName }}(),
363+
"{{ $object.TerraformName }}": registry.Resource("{{ $object.TerraformName }}"),
363364
{{- end }}
364365
{{- if $object.IamClassName }}
365-
"{{ $object.TerraformName }}_iam_binding": tpgiamresource.ResourceIamBinding({{ $object.IamClassName }}IamSchema, {{ $object.IamClassName }}IamUpdaterProducer, {{ $object.IamClassName }}IdParseFunc),
366-
"{{ $object.TerraformName }}_iam_member": tpgiamresource.ResourceIamMember({{ $object.IamClassName }}IamSchema, {{ $object.IamClassName }}IamUpdaterProducer, {{ $object.IamClassName }}IdParseFunc),
367-
"{{ $object.TerraformName }}_iam_policy": tpgiamresource.ResourceIamPolicy({{ $object.IamClassName }}IamSchema, {{ $object.IamClassName }}IamUpdaterProducer, {{ $object.IamClassName }}IdParseFunc),
366+
"{{ $object.TerraformName }}_iam_binding": registry.Resource("{{ $object.TerraformName }}_iam_binding"),
367+
"{{ $object.TerraformName }}_iam_member": registry.Resource("{{ $object.TerraformName }}_iam_member"),
368+
"{{ $object.TerraformName }}_iam_policy": registry.Resource("{{ $object.TerraformName }}_iam_policy"),
368369
{{- end }}
369370
{{- end }}
370371
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package registry
2+
3+
import (
4+
"log"
5+
"sync"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
)
9+
10+
// Product defines the shared configuration for a single Magic Modules product. It is
11+
// defined in `product.yaml`.
12+
type Product struct {
13+
// Name is the product name in lower case, e.g,. "alloydb".
14+
Name string
15+
// BaseUrl is the base URL for API requests. It may contain Magic Modules templating directives.
16+
BaseUrl string
17+
}
18+
19+
// Register adds the product definition to the internal product registry.
20+
func (p Product) Register() {
21+
products.Lock()
22+
defer products.Unlock()
23+
if _, ok := products.m[p.Name]; ok {
24+
log.Fatalf("Duplicate registration attempt for product %q", p.Name)
25+
}
26+
products.m[p.Name] = p
27+
}
28+
29+
type registeredProducts struct {
30+
sync.RWMutex
31+
m map[string]Product
32+
}
33+
34+
var products = &registeredProducts{
35+
m: make(map[string]Product),
36+
}
37+
38+
// SchemaType differentitates a registered Terraform schema in cases where multiple schemas
39+
// share a name. For instance, resources and their corresponding data sources are identically named.
40+
type SchemaType int
41+
42+
const (
43+
SchemaTypeResource SchemaType = iota
44+
SchemaTypeIAMResource
45+
SchemaTypeDataSource
46+
SchemaTypeIAMDataSource
47+
)
48+
49+
// IsDataSource is a helper method that returns whether a SchemaType refers to a data source or resource.
50+
func (s SchemaType) IsDataSource() bool {
51+
return s == SchemaTypeDataSource || s == SchemaTypeIAMDataSource
52+
}
53+
54+
// Schema is used to configure a resource or data source within the registry.
55+
type Schema struct {
56+
// Name is the externally visible name, e.g., "google_alloydb_cluster".
57+
Name string
58+
// ProductName is the `Product` that this `Schema` is associated with.
59+
ProductName string
60+
// Type defines how the `Schema` should be registered.
61+
Type SchemaType
62+
// Schema contains the underlying Terraform schema. The data within is shared and assumed
63+
// to be immutable.
64+
Schema *schema.Resource
65+
}
66+
67+
// Register adds the schema definition to the internal registry.
68+
func (s Schema) Register() {
69+
products.Lock()
70+
defer products.Unlock()
71+
if s.Type.IsDataSource() {
72+
if _, ok := schemas.d[s.Name]; ok {
73+
log.Fatalf("Duplicate registration attempt for data source %q", s.Name)
74+
}
75+
schemas.d[s.Name] = s
76+
} else {
77+
if _, ok := schemas.r[s.Name]; ok {
78+
log.Fatalf("Duplicate registration attempt for resource %q", s.Name)
79+
}
80+
schemas.r[s.Name] = s
81+
}
82+
}
83+
84+
type registeredSchemas struct {
85+
sync.RWMutex
86+
r map[string]Schema
87+
d map[string]Schema
88+
}
89+
90+
var schemas = &registeredSchemas{
91+
r: make(map[string]Schema),
92+
d: make(map[string]Schema),
93+
}
94+
95+
// Resource returns the Terraform schema for the requested resource. The function panics
96+
// if the requested resource is not registered. This function is called during provider
97+
// intitialization when the absence of a resource is an unrecoverable error.
98+
func Resource(name string) *schema.Resource {
99+
schemas.RLock()
100+
defer schemas.RUnlock()
101+
r, ok := schemas.r[name]
102+
if !ok {
103+
log.Fatalf("No resource schema for %q registered", name)
104+
}
105+
return r.Schema
106+
}
107+
108+
// DataSource returns the Terraform schema for the requested data source. The function panics
109+
// if the requested data source is not registered. This function is called during provider
110+
// intitialization when the absence of a data source is an unrecoverable error.
111+
func DataSource(name string) *schema.Resource {
112+
schemas.RLock()
113+
defer schemas.RUnlock()
114+
d, ok := schemas.d[name]
115+
if !ok {
116+
log.Fatalf("No data source schema for %q registered", name)
117+
}
118+
return d.Schema
119+
}

0 commit comments

Comments
 (0)