From 5cb12b8ff36eaba7646549498492b2133c96713f Mon Sep 17 00:00:00 2001 From: Zhenhua Li Date: Fri, 6 Sep 2024 11:34:15 -0700 Subject: [PATCH] Cleanup Go rewrite TODOs and remove unused fields (#11648) --- mmv1/api/async.go | 52 -- mmv1/api/object.go | 85 ---- mmv1/api/product.go | 39 +- mmv1/api/product/version.go | 23 +- mmv1/api/resource.go | 78 +-- mmv1/api/resource/custom_code.go | 2 - mmv1/api/resource/docs.go | 7 - mmv1/api/resource/examples.go | 43 -- mmv1/api/resource/iam_policy.go | 2 - mmv1/api/resource/nested_query.go | 2 - mmv1/api/resource/reference_links.go | 10 +- mmv1/api/resource/sweeper.go | 1 - mmv1/api/resource/validation.go | 1 - mmv1/api/resource_test.go | 24 +- mmv1/api/timeouts.go | 10 +- mmv1/api/type.go | 472 +++--------------- mmv1/api/type_test.go | 56 +-- mmv1/google/string_utils.go | 12 +- mmv1/google/yaml_validator.go | 126 ----- mmv1/main.go | 14 +- .../products/cloudquotas/QuotaPreference.yaml | 1 - .../cloudquotas/go_QuotaPreference.yaml | 1 - mmv1/provider/template_data.go | 86 +--- mmv1/provider/terraform.go | 221 -------- mmv1/templates/terraform/operation.go.tmpl | 4 +- mmv1/templates/terraform/resource.go.tmpl | 4 - 26 files changed, 128 insertions(+), 1248 deletions(-) delete mode 100644 mmv1/api/object.go diff --git a/mmv1/api/async.go b/mmv1/api/async.go index 0905853e80a1..a689259ddad5 100644 --- a/mmv1/api/async.go +++ b/mmv1/api/async.go @@ -23,9 +23,6 @@ import ( // Base class from which other Async classes can inherit. type Async struct { - // Embed YamlValidator object - // google.YamlValidator - // Describes an operation Operation *Operation @@ -40,7 +37,6 @@ type Async struct { PollAsync `yaml:",inline"` } -// def allow?(method) func (a Async) Allow(method string) bool { return slices.Contains(a.Actions, strings.ToLower(method)) } @@ -56,9 +52,7 @@ type Operation struct { OpAsyncOperation `yaml:",inline"` } -// def initialize(path, base_url, wait_ms, timeouts) func NewOperation() *Operation { - // super() op := new(Operation) op.Timeouts = NewTimeouts() return op @@ -86,14 +80,6 @@ type OpAsync struct { IncludeProject bool `yaml:"include_project"` } -// def initialize(operation, result, status, error) -// super() -// @operation = operation -// @result = result -// @status = status -// @error = error -// end - type OpAsyncOperation struct { Kind string @@ -107,19 +93,6 @@ type OpAsyncOperation struct { FullUrl string `yaml:"full_url"` } -// def validate -// super - -// check :kind, type: String -// check :path, type: String -// check :base_url, type: String -// check :wait_ms, type: Integer - -// check :full_url, type: String - -// conflicts %i[base_url full_url] -// end - // Represents the results of an Operation request type OpAsyncResult struct { ResourceInsideResponse bool `yaml:"resource_inside_response"` @@ -127,17 +100,9 @@ type OpAsyncResult struct { Path string } -// def initialize(path = nil, resource_inside_response = nil) -// super() -// @path = path -// @resource_inside_response = resource_inside_response -// end - // Provides information to parse the result response to check operation // status type OpAsyncStatus struct { - // google.YamlValidator - Path string Complete bool @@ -145,13 +110,6 @@ type OpAsyncStatus struct { Allowed []bool } -// def initialize(path, complete, allowed) -// super() -// @path = path -// @complete = complete -// @allowed = allowed -// end - // Provides information on how to retrieve errors of the executed operations type OpAsyncError struct { google.YamlValidator @@ -161,12 +119,6 @@ type OpAsyncError struct { Message string } -// def initialize(path, message) -// super() -// @path = path -// @message = message -// end - // Async implementation for polling in Terraform type PollAsync struct { // Details how to poll for an eventually-consistent resource state. @@ -179,10 +131,6 @@ type PollAsync struct { // deleting a resource CheckResponseFuncAbsence string `yaml:"check_response_func_absence"` - // Custom code to get a poll response, if needed. - // Will default to same logic as Read() to get current resource - CustomPollRead string `yaml:"custom_poll_read"` - // If true, will suppress errors from polling and default to the // result of the final Read() SuppressError bool `yaml:"suppress_error"` diff --git a/mmv1/api/object.go b/mmv1/api/object.go deleted file mode 100644 index 7c1c8f8a821b..000000000000 --- a/mmv1/api/object.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2024 Google Inc. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package api - -// require 'google/extensions' -// require 'google/logger' -// require 'google/yaml_validator' - -// Represents an object that has a (mandatory) name -type NamedObject struct { - // google.YamlValidator - - Name string - - // original value of :name before the provider override happens - // same as :name if not overridden in provider - ApiName string `yaml:"api_name"` -} - -// func (n *Named) string_array(arr) { -// types = arr.map(&:class).uniq -// types.size == 1 && types[0] == String -// } - -// func (n *Named) deep_merge(arr1, arr2) { -// if arr1.nil? -// return arr2 -// end -// if arr2.nil? -// return arr1 -// end - -// // Scopes is an array of standard strings. In which case return the -// // version in the overrides. This allows scopes to be removed rather -// // than allowing for a merge of the two arrays -// if string_array?(arr1) -// return arr2 -// end - -// // Merge any elements that exist in both -// result = arr1.map do |el1| -// other = arr2.select { |el2| el1.name == el2.name }.first -// other.nil? ? el1 : el1.merge(other) -// end - -// // Add any elements of arr2 that don't exist in arr1 -// result + arr2.reject do |el2| -// arr1.any? { |el1| el2.name == el1.name } -// end -// } - -// func (n *Named) merge(other) { -// result = self.class.new -// instance_variables.each do |v| -// result.instance_variable_set(v, instance_variable_get(v)) -// end - -// other.instance_variables.each do |v| -// if other.instance_variable_get(v).instance_of?(Array) -// result.instance_variable_set(v, deep_merge(result.instance_variable_get(v), -// other.instance_variable_get(v))) -// else -// result.instance_variable_set(v, other.instance_variable_get(v)) -// end -// end - -// result -// } - -// func (n *Named) validate() { -// super -// check :name, type: String, required: true -// check :api_name, type: String, default: @name -// } diff --git a/mmv1/api/product.go b/mmv1/api/product.go index f157c057d802..ae357d7da75c 100644 --- a/mmv1/api/product.go +++ b/mmv1/api/product.go @@ -25,14 +25,15 @@ import ( // Represents a product to be managed type Product struct { - NamedObject `yaml:",inline"` - - // Inherited: // The name of the product's API capitalised in the appropriate places. // This isn't just the API name because it doesn't meaningfully separate // words in the api name - "accesscontextmanager" vs "AccessContextManager" // Example inputs: "Compute", "AccessContextManager" - // Name string + Name string + + // original value of :name before the provider override happens + // same as :name if not overridden in provider + ApiName string `yaml:"api_name"` // Display Name: The full name of the GCP product; eg "Cloud Bigtable" DisplayName string `yaml:"display_name"` @@ -41,23 +42,19 @@ type Product struct { // The list of permission scopes available for the service // For example: `https://www.googleapis.com/auth/compute` - Scopes []string // The API versions of this product - Versions []*product.Version // The base URL for the service API endpoint // For example: `https://www.googleapis.com/compute/v1/` - BaseUrl string `yaml:"base_url"` // A function reference designed for the rare case where you // need to use retries in operation calls. Used for the service api // as it enables itself (self referential) and can result in occasional // failures on operation_get. see github.com/hashicorp/terraform-provider-google/issues/9489 - OperationRetry string `yaml:"operation_retry"` Async *Async @@ -82,6 +79,10 @@ func (p *Product) UnmarshalYAML(unmarshal func(any) error) error { } func (p *Product) Validate() { + if len(p.Name) == 0 { + log.Fatalf("Missing `name` for product") + } + // product names must start with a capital for i, ch := range p.Name { if !unicode.IsUpper(ch) { @@ -224,31 +225,9 @@ func (p *Product) TerraformName() string { // Debugging Methods // ==================== -// def to_s -// // relies on the custom to_json definitions -// JSON.pretty_generate(self) -// end - // Prints a dot notation path to where the field is nested within the parent // object when called on a property. eg: parent.meta.label.foo // Redefined on Product to terminate the calls up the parent chain. func (p Product) Lineage() string { return p.Name } - -// def to_json(opts = nil) -// json_out = {} - -// instance_variables.each do |v| -// if v == :@objects -// json_out['@resources'] = objects.to_h { |o| [o.name, o] } -// elsif instance_variable_get(v) == false || instance_variable_get(v).nil? -// // ignore false or missing because omitting them cleans up result -// // and both are the effective defaults of their types -// else -// json_out[v] = instance_variable_get(v) -// end -// end - -// JSON.generate(json_out, opts) -// end diff --git a/mmv1/api/product/version.go b/mmv1/api/product/version.go index c6f3b14f884e..16ef54ae51b0 100644 --- a/mmv1/api/product/version.go +++ b/mmv1/api/product/version.go @@ -19,8 +19,6 @@ import ( "golang.org/x/exp/slices" ) -// require 'api/object' - var ORDER = []string{"ga", "beta", "alpha", "private"} // A version of the API for a given product / API group @@ -28,18 +26,9 @@ var ORDER = []string{"ga", "beta", "alpha", "private"} // a superset of beta, and beta a superset of GA. Each version will have a // different version url. type Version struct { - // TODO: Should embed NamedObject or not? - // < Api::NamedObject - // include Comparable - - // attr_reader CaiBaseUrl string `yaml:"cai_base_url"` - - // attr_accessor - BaseUrl string `yaml:"base_url"` - - // attr_accessor - Name string + BaseUrl string `yaml:"base_url"` + Name string } func (v *Version) Validate(pName string) { @@ -51,14 +40,6 @@ func (v *Version) Validate(pName string) { } } -// def to_s -// "//{name}: //{base_url}" -// end - -// def <=>(other) -// ORDER.index(name) <=> ORDER.index(other.name) if other.is_a?(Version) -// end - func (v *Version) CompareTo(other *Version) int { return slices.Index(ORDER, v.Name) - slices.Index(ORDER, other.Name) } diff --git a/mmv1/api/resource.go b/mmv1/api/resource.go index 5406d75b33fb..eeb0355130ee 100644 --- a/mmv1/api/resource.go +++ b/mmv1/api/resource.go @@ -27,8 +27,11 @@ import ( ) type Resource struct { - // Embed NamedObject - NamedObject `yaml:",inline"` + Name string + + // original value of :name before the provider override happens + // same as :name if not overridden in provider + ApiName string `yaml:"api_name"` // [Required] A description of the resource that's surfaced in provider // documentation. @@ -355,6 +358,10 @@ func (r *Resource) SetDefault(product *Product) { } func (r *Resource) Validate() { + if r.Name == "" { + log.Fatalf("Missing `name` for resource") + } + if r.NestedQuery != nil && r.NestedQuery.IsListOfIds && len(r.Identity) != 1 { log.Fatalf("`is_list_of_ids: true` implies resource has exactly one `identity` property") } @@ -426,9 +433,6 @@ func (r *Resource) Validate() { // Returns all properties and parameters including the ones that are // excluded. This is used for PropertyOverride validation - -// TODO: remove the ruby function name -// def all_properties func (r Resource) AllProperties() []*Type { return google.Concat(r.Properties, r.Parameters) } @@ -439,19 +443,16 @@ func (r Resource) AllPropertiesInVersion() []*Type { }) } -// def properties_with_excluded func (r Resource) PropertiesWithExcluded() []*Type { return r.Properties } -// def properties func (r Resource) UserProperites() []*Type { return google.Reject(r.Properties, func(p *Type) bool { return p.Exclude }) } -// def parameters func (r Resource) UserParameters() []*Type { return google.Reject(r.Parameters, func(p *Type) bool { return p.Exclude @@ -461,20 +462,16 @@ func (r Resource) UserParameters() []*Type { // Return the user-facing properties in client tools; this ends up meaning // both properties and parameters but without any that are excluded due to // version mismatches or manual exclusion - -// def all_user_properties func (r Resource) AllUserProperties() []*Type { return google.Concat(r.UserProperites(), r.UserParameters()) } -// def required_properties func (r Resource) RequiredProperties() []*Type { return google.Select(r.AllUserProperties(), func(p *Type) bool { return p.Required }) } -// def all_nested_properties(props) func (r Resource) AllNestedProperties(props []*Type) []*Type { nested := props for _, prop := range props { @@ -486,7 +483,6 @@ func (r Resource) AllNestedProperties(props []*Type) []*Type { return nested } -// sensitive_props func (r Resource) SensitiveProps() []*Type { props := r.AllNestedProperties(r.RootProperties()) return google.Select(props, func(p *Type) bool { @@ -508,8 +504,6 @@ func (r Resource) SensitivePropsToString() string { // Fingerprints aren't *really" settable properties, but they behave like one. // At Create, they have no value but they can just be read in anyways, and after a Read // they will need to be set in every Update. - -// def settable_properties func (r Resource) SettableProperties() []*Type { props := make([]*Type, 0) @@ -539,8 +533,6 @@ func (r Resource) UnorderedListProperties() []*Type { } // Properties that will be returned in the API body - -// def gettable_properties func (r Resource) GettableProperties() []*Type { return google.Reject(r.AllUserProperties(), func(v *Type) bool { return v.UrlParamOnly @@ -549,8 +541,6 @@ func (r Resource) GettableProperties() []*Type { // Returns the list of top-level properties once any nested objects with flatten_object // set to true have been collapsed - -// def root_properties func (r Resource) RootProperties() []*Type { props := make([]*Type, 0) @@ -566,8 +556,6 @@ func (r Resource) RootProperties() []*Type { // Return the product-level async object, or the resource-specific one // if one exists. - -// def async func (r Resource) GetAsync() *Async { if r.Async != nil { return r.Async @@ -578,8 +566,6 @@ func (r Resource) GetAsync() *Async { // Return the resource-specific identity properties, or a best guess of the // `name` value for the resource. - -// def identity func (r Resource) GetIdentity() []*Type { props := r.AllUserProperties() @@ -600,7 +586,6 @@ func (r Resource) GetIdentity() []*Type { }) } -// def add_labels_related_fields(props, parent) func (r *Resource) AddLabelsRelatedFields(props []*Type, parent *Type) []*Type { for _, p := range props { if p.IsA("KeyValueLabels") { @@ -614,7 +599,6 @@ func (r *Resource) AddLabelsRelatedFields(props []*Type, parent *Type) []*Type { return props } -// def add_labels_fields(props, parent, labels) func (r *Resource) addLabelsFields(props []*Type, parent *Type, labels *Type) []*Type { if parent == nil || parent.FlattenObject { if r.SkipAttributionLabel { @@ -650,7 +634,6 @@ func (r *Resource) HasLabelsField() bool { return false } -// def add_annotations_fields(props, parent, annotations) func (r *Resource) addAnnotationsFields(props []*Type, parent *Type, annotations *Type) []*Type { // The effective_annotations field is used to write to API, @@ -669,7 +652,6 @@ func (r *Resource) addAnnotationsFields(props []*Type, parent *Type, annotations return props } -// def build_effective_labels_field(name, labels) func buildEffectiveLabelsField(name string, labels *Type) *Type { description := fmt.Sprintf("All of %s (key/value pairs) present on the resource in GCP, "+ "including the %s configured through Terraform, other clients and services.", name, name) @@ -690,7 +672,6 @@ func buildEffectiveLabelsField(name string, labels *Type) *Type { return NewProperty(n, name, options) } -// def build_terraform_labels_field(name, parent, labels) func buildTerraformLabelsField(name string, parent *Type, labels *Type) *Type { description := fmt.Sprintf("The combination of %s configured directly on the resource\n"+ " and default %s configured on the provider.", name, name) @@ -714,8 +695,7 @@ func buildTerraformLabelsField(name string, parent *Type, labels *Type) *Type { return NewProperty(n, name, options) } -// // Check if the resource has root "labels" field -// def root_labels? +// Check if the resource has root "labels" field func (r Resource) RootLabels() bool { for _, p := range r.RootProperties() { if p.IsA("KeyValueLabels") { @@ -725,8 +705,7 @@ func (r Resource) RootLabels() bool { return false } -// // Return labels fields that should be added to ImportStateVerifyIgnore -// def ignore_read_labels_fields(props) +// Return labels fields that should be added to ImportStateVerifyIgnore func (r Resource) IgnoreReadLabelsFields(props []*Type) []string { fields := make([]string, 0) for _, p := range props { @@ -741,7 +720,6 @@ func (r Resource) IgnoreReadLabelsFields(props []*Type) []string { return fields } -// def get_labels_field_note(title) func getLabelsFieldNote(title string) string { return fmt.Sprintf( "**Note**: This field is non-authoritative, and will only manage the %s present "+ @@ -757,8 +735,6 @@ func (r Resource) StateMigrationFile() string { // ==================== // Version-related methods // ==================== - -// def min_version func (r Resource) MinVersionObj() *product.Version { if r.MinVersion != "" { return r.ProductMetadata.versionObj(r.MinVersion) @@ -767,7 +743,6 @@ func (r Resource) MinVersionObj() *product.Version { } } -// def not_in_version?(version) func (r Resource) NotInVersion(version *product.Version) bool { return version.CompareTo(r.MinVersionObj()) < 0 } @@ -775,8 +750,6 @@ func (r Resource) NotInVersion(version *product.Version) bool { // Recurses through all nested properties and parameters and changes their // 'exclude' instance variable if the property is at a version below the // one that is passed in. - -// def exclude_if_not_in_version!(version) func (r *Resource) ExcludeIfNotInVersion(version *product.Version) { if !r.Exclude { r.Exclude = r.NotInVersion(version) @@ -804,8 +777,6 @@ func (r *Resource) ExcludeIfNotInVersion(version *product.Version) { // product.base_url + resource.base_url + '/name' // In newer resources there is much less standardisation in terms of value. // Generally for them though, it's the product.base_url + resource.name - -// def self_link_url func (r Resource) SelfLinkUrl() string { s := []string{r.ProductMetadata.BaseUrl, r.SelfLinkUri()} return strings.Join(s, "") @@ -814,8 +785,6 @@ func (r Resource) SelfLinkUrl() string { // Returns the partial uri / relative path of a resource. In newer resources, // this is the name. This fn is named self_link_uri for consistency, but // could otherwise be considered to be "path" - -// def self_link_uri func (r Resource) SelfLinkUri() string { // If the terms in this are not snake-cased, this will require // an override in Terraform. @@ -826,18 +795,15 @@ func (r Resource) SelfLinkUri() string { return strings.Join([]string{r.BaseUrl, "{{name}}"}, "/") } -// def collection_url func (r Resource) CollectionUrl() string { s := []string{r.ProductMetadata.BaseUrl, r.collectionUri()} return strings.Join(s, "") } -// def collection_uri func (r Resource) collectionUri() string { return r.BaseUrl } -// def create_uri func (r Resource) CreateUri() string { if r.CreateUrl != "" { return r.CreateUrl @@ -850,7 +816,6 @@ func (r Resource) CreateUri() string { return r.SelfLinkUri() } -// def update_uri func (r Resource) UpdateUri() string { if r.UpdateUrl != "" { return r.UpdateUrl @@ -859,7 +824,6 @@ func (r Resource) UpdateUri() string { return r.SelfLinkUri() } -// def delete_uri func (r Resource) DeleteUri() string { if r.DeleteUrl != "" { return r.DeleteUrl @@ -868,22 +832,18 @@ func (r Resource) DeleteUri() string { return r.SelfLinkUri() } -// def resource_name func (r Resource) ResourceName() string { return fmt.Sprintf("%s%s", r.ProductMetadata.Name, r.Name) } // Filter the properties to keep only the ones don't have custom update // method and group them by update url & verb. - -// def properties_without_custom_update(properties) func propertiesWithoutCustomUpdate(properties []*Type) []*Type { return google.Select(properties, func(p *Type) bool { return p.UpdateUrl == "" || p.UpdateVerb == "" || p.UpdateVerb == "NOOP" }) } -// def update_body_properties func (r Resource) UpdateBodyProperties() []*Type { updateProp := propertiesWithoutCustomUpdate(r.SettableProperties()) if r.UpdateVerb == "PATCH" { @@ -896,8 +856,6 @@ func (r Resource) UpdateBodyProperties() []*Type { // Handwritten TF Operation objects will be shaped like accessContextManager // while the Google Go Client will have a name like accesscontextmanager - -// def client_name_pascal func (r Resource) ClientNamePascal() string { clientName := r.ProductMetadata.ClientName if clientName == "" { @@ -913,8 +871,6 @@ func (r Resource) PackageName() string { // In order of preference, use TF override, // general defined timeouts, or default Timeouts - -// def timeouts func (r Resource) GetTimeouts() *Timeouts { timeoutsFiltered := r.Timeouts if timeoutsFiltered == nil { @@ -930,7 +886,6 @@ func (r Resource) GetTimeouts() *Timeouts { return timeoutsFiltered } -// def project? func (r Resource) HasProject() bool { return strings.Contains(r.BaseUrl, "{{project}}") || strings.Contains(r.CreateUrl, "{{project}}") } @@ -939,7 +894,6 @@ func (r Resource) IncludeProjectForOperation() bool { return strings.Contains(r.BaseUrl, "{{project}}") || (r.GetAsync().IsA("OpAsync") && r.GetAsync().IncludeProject) } -// def region? func (r Resource) HasRegion() bool { found := false for _, p := range r.Parameters { @@ -951,7 +905,6 @@ func (r Resource) HasRegion() bool { return found && strings.Contains(r.BaseUrl, "{{region}}") } -// def zone? func (r Resource) HasZone() bool { found := false for _, p := range r.Parameters { @@ -963,7 +916,8 @@ func (r Resource) HasZone() bool { return found && strings.Contains(r.BaseUrl, "{{zone}}") } -// resource functions needed for template that previously existed in terraform.go but due to how files are being inherited here it was easier to put in here +// resource functions needed for template that previously existed in terraform.go +// but due to how files are being inherited here it was easier to put in here // taken wholesale from tpgtools func (r Resource) Updatable() bool { if !r.Immutable { @@ -984,8 +938,6 @@ func (r Resource) Updatable() bool { // Prints a dot notation path to where the field is nested within the parent // object when called on a property. eg: parent.meta.label.foo // Redefined on Resource to terminate the calls up the parent chain. - -// def lineage func (r Resource) Lineage() string { return r.Name } @@ -1137,7 +1089,6 @@ func (r *Resource) SetCompiler(t string) { // Returns the id format of an object, or self_link_uri if none is explicitly defined // We prefer the long name of a resource as the id so that users can reference // resources in a standard way, and most APIs accept short name, long name or self_link -// def id_format(object) func (r Resource) GetIdFormat() string { idFormat := r.IdFormat if idFormat == "" { @@ -1150,7 +1101,6 @@ func (r Resource) GetIdFormat() string { // Template Methods // ==================== // Functions used to create slices of resource properties that could not otherwise be called from within generating templates. - func (r Resource) ReadProperties() []*Type { return google.Reject(r.GettableProperties(), func(p *Type) bool { return p.IgnoreRead @@ -1230,7 +1180,6 @@ func (r Resource) IamResourceUriStringQualifiers() string { // 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) var result []string @@ -1554,7 +1503,6 @@ type UpdateGroup struct { FingerprintName string } -// def properties_without_custom_update(properties) func (r Resource) propertiesWithCustomUpdate(properties []*Type) []*Type { return google.Reject(properties, func(p *Type) bool { return p.UpdateUrl == "" || p.UpdateVerb == "" || p.UpdateVerb == "NOOP" || diff --git a/mmv1/api/resource/custom_code.go b/mmv1/api/resource/custom_code.go index a00d09ce5467..e45dfc8e8ca3 100644 --- a/mmv1/api/resource/custom_code.go +++ b/mmv1/api/resource/custom_code.go @@ -15,8 +15,6 @@ package resource // Inserts custom code into terraform resources. type CustomCode struct { - // google.YamlValidator - // Collection of fields allowed in the CustomCode section for // Terraform. diff --git a/mmv1/api/resource/docs.go b/mmv1/api/resource/docs.go index 2de8004d2350..1d5d76769131 100644 --- a/mmv1/api/resource/docs.go +++ b/mmv1/api/resource/docs.go @@ -15,8 +15,6 @@ package resource // Inserts custom strings into terraform resource docs. type Docs struct { - // google.YamlValidator - // All these values should be strings, which will be inserted // directly into the terraform resource documentation. The // strings should _not_ be the names of template files @@ -26,18 +24,13 @@ type Docs struct { // template. // The text will be injected at the bottom of the specified // section. - // attr_reader : Warning string - // attr_reader : Note string - // attr_reader : RequiredProperties string `yaml:"required_properties"` - // attr_reader : OptionalProperties string `yaml:"optional_properties"` - // attr_reader : Attributes string } diff --git a/mmv1/api/resource/examples.go b/mmv1/api/resource/examples.go index 8d618b7b94fb..5c82ade99b00 100644 --- a/mmv1/api/resource/examples.go +++ b/mmv1/api/resource/examples.go @@ -31,11 +31,6 @@ import ( // Generates configs to be shown as examples in docs and outputted as tests // from a shared template type Examples struct { - // google.YamlValidator - - // include Compile::Core - // include Google::GolangUtils - // The name of the example in lower snake_case. // Generally takes the form of the resource name followed by some detail // about the specific test. For example, "address_with_subnetwork". @@ -320,9 +315,6 @@ func ExecuteTemplate(e any, templatePath string, appendNewline bool) string { func (e *Examples) OiCSLink() string { v := url.Values{} - // TODO Q2: Values.Encode() sorts the values by key alphabetically. This will produce - // diffs for every URL when we convert to using this function. We should sort the - // Ruby-version query alphabetically beforehand to remove these diffs. v.Add("cloudshell_git_repo", "https://github.com/terraform-google-modules/docs-examples.git") v.Add("cloudshell_working_dir", e.Name) v.Add("cloudshell_image", "gcr.io/cloudshell-images/cloudshell:latest") @@ -367,38 +359,3 @@ func SubstituteTestPaths(config string) string { config = strings.ReplaceAll(config, "path/to/id_rsa.pub", "test-fixtures/ssh_rsa.pub") return config } - -// func (e *Examples) validate() { -// super -// check :name, type: String, required: true -// check :primary_resource_id, type: String -// check :min_version, type: String -// check :vars, type: Hash -// check :test_env_vars, type: Hash -// check :test_vars_overrides, type: Hash -// check :ignore_read_extra, type: Array, item_type: String, default: [] -// check :primary_resource_name, type: String -// check :skip_test, type: TrueClass -// check :skip_import_test, type: TrueClass -// check :skip_docs, type: TrueClass -// check :config_path, type: String, default: "templates/terraform/examples///{name}.tf.erb" -// check :skip_vcr, type: TrueClass -// } - -// func (e *Examples) merge(other) { -// result = self.class.new -// instance_variables.each do |v| -// result.instance_variable_set(v, instance_variable_get(v)) -// end - -// other.instance_variables.each do |v| -// if other.instance_variable_get(v).instance_of?(Array) -// result.instance_variable_set(v, deep_merge(result.instance_variable_get(v), -// other.instance_variable_get(v))) -// else -// result.instance_variable_set(v, other.instance_variable_get(v)) -// end -// end - -// result -// } diff --git a/mmv1/api/resource/iam_policy.go b/mmv1/api/resource/iam_policy.go index 18708e48fd57..bd384fb7a85a 100644 --- a/mmv1/api/resource/iam_policy.go +++ b/mmv1/api/resource/iam_policy.go @@ -23,8 +23,6 @@ import ( // and accessed via their parent resource // See: https://cloud.google.com/iam/docs/overview type IamPolicy struct { - // google.YamlValidator - // boolean of if this binding should be generated Exclude bool diff --git a/mmv1/api/resource/nested_query.go b/mmv1/api/resource/nested_query.go index a0ebea0198e1..0523fef4a494 100644 --- a/mmv1/api/resource/nested_query.go +++ b/mmv1/api/resource/nested_query.go @@ -19,8 +19,6 @@ import "log" // a list of resources or single object within the parent. // e.g. Fine-grained resources type NestedQuery struct { - // google.YamlValidator - // A list of keys to traverse in order. // i.e. backendBucket --> cdnPolicy.signedUrlKeyNames // should be ["cdnPolicy", "signedUrlKeyNames"] diff --git a/mmv1/api/resource/reference_links.go b/mmv1/api/resource/reference_links.go index 5c4862bbaae8..472e7ef4499b 100644 --- a/mmv1/api/resource/reference_links.go +++ b/mmv1/api/resource/reference_links.go @@ -16,14 +16,10 @@ package resource // Represents a list of documentation links. type ReferenceLinks struct { // guides containing - // name: The title of the link - // value: The URL to navigate on click - - //attr_reader + // name: The title of the link + // value: The URL to navigate on click Guides map[string]string - // the url of the API guide - - //attr_reader + // the url of the API guider Api string } diff --git a/mmv1/api/resource/sweeper.go b/mmv1/api/resource/sweeper.go index ebc078c5a770..44a30f598e83 100644 --- a/mmv1/api/resource/sweeper.go +++ b/mmv1/api/resource/sweeper.go @@ -14,7 +14,6 @@ package resource type Sweeper struct { - //Google::YamlValidator // The field checked by sweeper to determine // eligibility for deletion for generated resources SweepableIdentifierField string `yaml:"sweepable_identifier_field"` diff --git a/mmv1/api/resource/validation.go b/mmv1/api/resource/validation.go index 24cbfb557758..7214827327ed 100644 --- a/mmv1/api/resource/validation.go +++ b/mmv1/api/resource/validation.go @@ -15,7 +15,6 @@ package resource // Support for schema ValidateFunc functionality. type Validation struct { - //Google::YamlValidator // Ensures the value matches this regex Regex string Function string diff --git a/mmv1/api/resource_test.go b/mmv1/api/resource_test.go index a9cb0c3c4a65..d52d7313a70e 100644 --- a/mmv1/api/resource_test.go +++ b/mmv1/api/resource_test.go @@ -10,9 +10,7 @@ import ( func TestResourceMinVersionObj(t *testing.T) { t.Parallel() p := Product{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", Versions: []*product.Version{ &product.Version{ Name: "beta", @@ -37,9 +35,7 @@ func TestResourceMinVersionObj(t *testing.T) { { description: "resource minVersion is empty", obj: Resource{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "", ProductMetadata: &p, }, @@ -48,9 +44,7 @@ func TestResourceMinVersionObj(t *testing.T) { { description: "resource minVersion is not empty", obj: Resource{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "beta", ProductMetadata: &p, }, @@ -76,9 +70,7 @@ func TestResourceMinVersionObj(t *testing.T) { func TestResourceNotInVersion(t *testing.T) { t.Parallel() p := Product{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", Versions: []*product.Version{ &product.Version{ Name: "beta", @@ -104,9 +96,7 @@ func TestResourceNotInVersion(t *testing.T) { { description: "ga is in version if MinVersion is empty", obj: Resource{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "", ProductMetadata: &p, }, @@ -118,9 +108,7 @@ func TestResourceNotInVersion(t *testing.T) { { description: "ga is not in version if MinVersion is beta", obj: Resource{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "beta", ProductMetadata: &p, }, diff --git a/mmv1/api/timeouts.go b/mmv1/api/timeouts.go index 93380777e660..8fe0df0aa970 100644 --- a/mmv1/api/timeouts.go +++ b/mmv1/api/timeouts.go @@ -13,7 +13,8 @@ package api -// Default timeout for all operation types is 20, the Terraform default (https://www.terraform.io/plugin/sdkv2/resources/retries-and-customizable-timeouts) +// Default timeout for all operation types is 20, the Terraform default +// (https://www.terraform.io/plugin/sdkv2/resources/retries-and-customizable-timeouts) // minutes. This can be overridden for each resource. const DEFAULT_INSERT_TIMEOUT_MINUTES = 20 const DEFAULT_UPDATE_TIMEOUT_MINUTES = 20 @@ -21,18 +22,11 @@ const DEFAULT_DELETE_TIMEOUT_MINUTES = 20 // Provides timeout information for the different operation types type Timeouts struct { - // google.YamlValidator InsertMinutes int `yaml:"insert_minutes"` UpdateMinutes int `yaml:"update_minutes"` DeleteMinutes int `yaml:"delete_minutes"` } -// def initialize -// super - -// validate -// end - func NewTimeouts() *Timeouts { return &Timeouts{ InsertMinutes: DEFAULT_INSERT_TIMEOUT_MINUTES, diff --git a/mmv1/api/type.go b/mmv1/api/type.go index 0558be2cbb74..9408b41cbe38 100644 --- a/mmv1/api/type.go +++ b/mmv1/api/type.go @@ -26,12 +26,15 @@ import ( // Represents a property type type Type struct { - NamedObject `yaml:",inline"` + Name string - // TODO: improve the parsing of properties based on type in resource yaml files. + // original value of :name before the provider override happens + // same as :name if not overridden in provider + ApiName string `yaml:"api_name"` + + // TODO rewrite: improve the parsing of properties based on type in resource yaml files. Type string - // TODO: set a specific type intead of interface{} DefaultValue interface{} `yaml:"default_value"` Description string @@ -128,10 +131,6 @@ type Type struct { // Can only be overridden - we should never set this ourselves. NewType string - // A pattern that maps expected user input to expected API input. - // TODO: remove? - Pattern string - Properties []*Type EnumValues []string `yaml:"enum_values"` @@ -147,7 +146,6 @@ type Type struct { // Adds a ValidateFunc to the item schema ItemValidation resource.Validation `yaml:"item_validation"` - // __name ParentName string // ==================== @@ -226,6 +224,7 @@ type Type struct { // ==================== // KeyValuePairs Fields // ==================== + // Ignore writing the "effective_labels" and "effective_annotations" fields to API. IgnoreWrite bool `yaml:"ignore_write"` // ==================== @@ -298,6 +297,7 @@ func (t *Type) SetDefault(r *Resource) { switch { case t.IsA("Array"): + t.ItemType.Name = t.Name t.ItemType.ParentName = t.Name t.ItemType.ParentMetadata = t t.ItemType.SetDefault(r) @@ -338,6 +338,10 @@ func (t *Type) SetDefault(r *Resource) { } func (t *Type) Validate(rName string) { + if t.Name == "" { + log.Fatalf("Missing `name` for proprty with type %s in resource %s", t.Type, rName) + } + if t.Output && t.Required { log.Fatalf("Property %s cannot be output and required at the same time in resource %s.", t.Name, rName) } @@ -346,6 +350,8 @@ func (t *Type) Validate(rName string) { log.Fatalf("'default_value' and 'default_from_api' cannot be both set in resource %s", rName) } + t.validateLabelsField() + switch { case t.IsA("Array"): t.ItemType.Validate(rName) @@ -359,70 +365,21 @@ func (t *Type) Validate(rName string) { } } -// super -// check :description, type: ::String, required: true -// check :exclude, type: :boolean, default: false, required: true -// check :deprecation_message, type: ::String -// check :removed_message, type: ::String -// check :min_version, type: ::String -// check :exact_version, type: ::String -// check :output, type: :boolean -// check :required, type: :boolean -// check :send_empty_value, type: :boolean -// check :allow_empty_object, type: :boolean -// check :url_param_only, type: :boolean -// check :read_query_params, type: ::String -// check :immutable, type: :boolean - -// raise 'Property cannot be output and required at the same time.' \ -// if @output && @required - -// check :update_verb, type: Symbol, allowed: %i[POST PUT PATCH NONE], -// default: @__resource&.update_verb - -// check :update_url, type: ::String -// check :update_id, type: ::String -// check :fingerprint_name, type: ::String -// check :pattern, type: ::String - +// TODO rewrite: add validations +// check :description, required: true +// check :update_verb, allowed: %i[POST PUT PATCH NONE], // check_default_value_property // check_conflicts // check_at_least_one_of // check_exactly_one_of // check_required_with - -// check :sensitive, type: :boolean, default: false -// check :is_set, type: :boolean, default: false -// check :default_from_api, type: :boolean, default: false -// check :unordered_list, type: :boolean, default: false -// check :schema_config_mode_attr, type: :boolean, default: false - -// // technically set as a default everywhere, but only maps will use this. -// check :key_expander, type: ::String, default: 'tpgresource.ExpandString' -// check :key_diff_suppress_func, type: ::String - -// check :diff_suppress_func, type: ::String -// check :state_func, type: ::String -// check :validation, type: Provider::Terraform::Validation -// check :set_hash_func, type: ::String - -// check :custom_flatten, type: ::String -// check :custom_expand, type: ::String - -// raise "'default_value' and 'default_from_api' cannot be both set" \ -// if @default_from_api && !@default_value.nil? -// } - -// func (t *Type) to_s() { -// JSON.pretty_generate(self) -// } +// check the allowed types for Type field +// check the allowed fields for each type, for example, KeyName is only allowed for Map // Prints a dot notation path to where the field is nested within the parent // object. eg: parent.meta.label.foo // The only intended purpose is to allow better error messages. Some objects // and at some points in the build this doesn't output a valid output. - -// def lineage func (t Type) Lineage() string { if t.ParentMetadata == nil { return google.Underscore(t.Name) @@ -433,7 +390,6 @@ func (t Type) Lineage() string { // Prints the access path of the field in the configration eg: metadata.0.labels // The only intended purpose is to get the value of the labes field by calling d.Get(). -// func (t *Type) terraform_lineage() { func (t Type) TerraformLineage() string { if t.ParentMetadata == nil || t.ParentMetadata.FlattenObject { return google.Underscore(t.Name) @@ -456,7 +412,6 @@ func (t Type) EnumValuesToString(quoteSeperator string, addEmpty bool) string { return strings.Join(values, ", ") } -// def titlelize_property(property) func (t Type) TitlelizeProperty() string { return google.Camelize(t.Name, "upper") } @@ -496,36 +451,7 @@ func (t Type) ResourceType() string { return path[len(path)-1] } -// func (t *Type) to_json(opts) { -// ignore fields that will contain references to parent resources and -// those which will be added later -// ignored_fields = %i[@resource @__parent @__resource @api_name @update_verb -// @__name @name @properties] -// json_out = {} - -// instance_variables.each do |v| -// if v == :@conflicts && instance_variable_get(v).empty? -// // ignore empty conflict arrays -// elsif v == :@at_least_one_of && instance_variable_get(v).empty? -// // ignore empty at_least_one_of arrays -// elsif v == :@exactly_one_of && instance_variable_get(v).empty? -// // ignore empty exactly_one_of arrays -// elsif v == :@required_with && instance_variable_get(v).empty? -// // ignore empty required_with arrays -// elsif instance_variable_get(v) == false || instance_variable_get(v).nil? -// // ignore false booleans as non-existence indicates falsey -// elsif !ignored_fields.include? v -// json_out[v] = instance_variable_get(v) -// end -// end - -// // convert properties to a hash based on name for nested readability -// json_out.merge!(properties&.map { |p| [p.name, p] }.to_h) \ -// if respond_to? 'properties' - -// JSON.generate(json_out, opts) -// } - +// TODO rewrite: validation // func (t *Type) check_default_value_property() { // return if @default_value.nil? @@ -569,6 +495,7 @@ func (t Type) Conflicting() []string { return t.Conflicts } +// TODO rewrite: validation // Checks that all properties that needs at least one of their fields actually exist. // This currently just returns if empty, because we don't want to do the check, since // this list will have a full path for nested attributes. @@ -588,6 +515,7 @@ func (t Type) AtLeastOneOfList() []string { return t.AtLeastOneOf } +// TODO rewrite: validation // Checks that all properties that needs exactly one of their fields actually exist. // This currently just returns if empty, because we don't want to do the check, since // this list will have a full path for nested attributes. @@ -607,6 +535,7 @@ func (t Type) ExactlyOneOfList() []string { return t.ExactlyOneOf } +// TODO rewrite: validation // Checks that all properties that needs required with their fields actually exist. // This currently just returns if empty, because we don't want to do the check, since // this list will have a full path for nested attributes. @@ -617,7 +546,6 @@ func (t Type) ExactlyOneOfList() []string { // } // Returns list of properties that needs required with their fields set. -// func (t *Type) required_with_list() { func (t Type) RequiredWithList() []string { if t.ResourceMetadata == nil { return []string{} @@ -630,7 +558,6 @@ func (t Type) Parent() *Type { return t.ParentMetadata } -// def min_version func (t Type) MinVersionObj() *product.Version { if t.MinVersion != "" { return t.ResourceMetadata.ProductMetadata.versionObj(t.MinVersion) @@ -639,7 +566,6 @@ func (t Type) MinVersionObj() *product.Version { } } -// def exact_version func (t *Type) exactVersionObj() *product.Version { if t.ExactVersion == "" { return nil @@ -648,7 +574,6 @@ func (t *Type) exactVersionObj() *product.Version { return t.ResourceMetadata.ProductMetadata.versionObj(t.ExactVersion) } -// def exclude_if_not_in_version!(version) func (t *Type) ExcludeIfNotInVersion(version *product.Version) { if !t.Exclude { if versionObj := t.exactVersionObj(); versionObj != nil { @@ -669,12 +594,6 @@ func (t *Type) ExcludeIfNotInVersion(version *product.Version) { } } -// Overriding is_a? to enable class overrides. -// Ruby does not let you natively change types, so this is the next best -// thing. - -// TODO Q1: check the type of superclasses of property t -// func (t *Type) is_a?(clazz) { func (t Type) IsA(clazz string) bool { if clazz == "" { log.Fatalf("class cannot be empty") @@ -685,20 +604,9 @@ func (t Type) IsA(clazz string) bool { } return t.Type == clazz - // super(clazz) } -// // Overriding class to enable class overrides. -// // Ruby does not let you natively change types, so this is the next best -// // thing. -// func (t *Type) class() { -// // return Module.const_get(@new_type) if @new_type - -// // super -// } - // Returns nested properties for this property. -// def nested_properties func (t Type) NestedProperties() []*Type { props := make([]*Type, 0) @@ -720,12 +628,10 @@ func (t Type) NestedProperties() []*Type { return props } -// def removed? func (t Type) Removed() bool { return t.RemovedMessage != "" } -// def deprecated? func (t Type) Deprecated() bool { return t.DeprecationMessage != "" } @@ -734,68 +640,10 @@ func (t *Type) GetDescription() string { return strings.TrimSpace(strings.TrimRight(t.Description, "\n")) } -// // private - -// // A constant value to be provided as field -// type Constant struct { -// // < Type -// value - -// func (t *Type) validate -// @description = "This is always //{value}." -// super -// end -// } - -// // Represents a primitive (non-composite) type. -// class Primitive < Type -// end - -// // Represents a boolean -// class Boolean < Primitive -// end - -// // Represents an integer -// class Integer < Primitive -// end - -// // Represents a double -// class Double < Primitive -// end - -// // Represents a string -// class String < Primitive -// func (t *Type) initialize(name = nil) -// super() - -// @name = name -// end - -// PROJECT = Api::Type::String.new('project') -// NAME = Api::Type::String.new('name') -// end - -// // Properties that are fetched externally -// class FetchedExternal < Type - -// func (t *Type) validate -// @conflicts ||= [] -// @at_least_one_of ||= [] -// @exactly_one_of ||= [] -// @required_with ||= [] -// end - -// func (t *Type) api_name -// name -// end -// end - -// class Path < Primitive -// end - -// // Represents a fingerprint. A fingerprint is an output-only -// // field used for optimistic locking during updates. -// // They are fetched from the GCP response. +// TODO rewrite: validation +// Represents a fingerprint. A fingerprint is an output-only +// field used for optimistic locking during updates. +// They are fetched from the GCP response. // class Fingerprint < FetchedExternal // func (t *Type) validate // super @@ -803,36 +651,8 @@ func (t *Type) GetDescription() string { // end // end -// // Represents a timestamp -// class Time < Primitive -// end - -// // A base class to tag objects that are composed by other objects (arrays, -// // nested objects, etc) -// class Composite < Type -// end - -// // Forwarding declaration to allow defining Array::NESTED_ARRAY_TYPE -// class NestedObject < Composite -// end - -// // Forwarding declaration to allow defining Array::RREF_ARRAY_TYPE -// class ResourceRef < Type -// end - -// // Represents an array, and stores its items' type +// TODO rewrite: validation // class Array < Composite -// item_type -// min_size -// max_size - -// func (t *Type) validate -// super -// if @item_type.is_a?(NestedObject) || @item_type.is_a?(ResourceRef) -// @item_type.set_variable(@name, :__name) -// @item_type.set_variable(@__resource, :__resource) -// @item_type.set_variable(self, :__parent) -// end // check :item_type, type: [::String, NestedObject, ResourceRef, Enum], required: true // unless @item_type.is_a?(NestedObject) || @item_type.is_a?(ResourceRef) \ @@ -840,25 +660,7 @@ func (t *Type) GetDescription() string { // raise "Invalid type //{@item_type}" // end -// check :min_size, type: ::Integer -// check :max_size, type: ::Integer -// end - -// func (t *Type) exclude_if_not_in_version!(version) -// super -// @item_type.exclude_if_not_in_version!(version) \ -// if @item_type.is_a? NestedObject -// end - -// func (t *Type) nested_properties -// return @item_type.nested_properties.reject(&:exclude) \ -// if @item_type.is_a?(Api::Type::NestedObject) - -// super -// end - // This function is for array field -// def item_type_class func (t Type) ItemTypeClass() string { if !t.IsA("Array") { return "" @@ -906,6 +708,7 @@ func (t Type) TFType(s string) string { return "schema.TypeString" } +// TODO rewrite: validation // // Represents an enum, and store is valid values // class Enum < Primitive // values @@ -917,40 +720,6 @@ func (t Type) TFType(s string) string { // check :skip_docs_values, type: :boolean // end -// func (t *Type) merge(other) -// result = self.class.new -// instance_variables.each do |v| -// result.instance_variable_set(v, instance_variable_get(v)) -// end - -// other.instance_variables.each do |v| -// if other.instance_variable_get(v).instance_of?(Array) -// result.instance_variable_set(v, deep_merge(result.instance_variable_get(v), -// other.instance_variable_get(v))) -// else -// result.instance_variable_set(v, other.instance_variable_get(v)) -// end -// end - -// result -// end -// end - -// // Represents a 'selfLink' property, which returns the URI of the resource. -// class SelfLink < FetchedExternal -// EXPORT_KEY = 'selfLink'.freeze - -// resource - -// func (t *Type) name -// EXPORT_KEY -// end - -// func (t *Type) out_name -// EXPORT_KEY.underscore -// end -// end - // // Represents a reference to another resource // class ResourceRef < Type // // The fields which can be overridden in provider.yaml. @@ -977,7 +746,6 @@ func (t Type) TFType(s string) string { // check_resource_ref_property_exists // end -// func (t *Type) resource_ref func (t Type) ResourceRef() *Resource { if !t.IsA("ResourceRef") { return nil @@ -991,8 +759,7 @@ func (t Type) ResourceRef() *Resource { return resources[0] } -// private - +// TODO rewrite: validation // func (t *Type) check_resource_ref_property_exists // return unless defined?(resource_ref.all_user_properties) @@ -1023,12 +790,10 @@ func (t Type) ResourceRef() *Resource { // Returns all properties including the ones that are excluded // This is used for PropertyOverride validation -// def all_properties func (t Type) AllProperties() []*Type { return t.Properties } -// func (t *Type) properties func (t Type) UserProperties() []*Type { if t.IsA("NestedObject") { if t.Properties == nil { @@ -1044,8 +809,6 @@ func (t Type) UserProperties() []*Type { // Returns the list of top-level properties once any nested objects with // flatten_object set to true have been collapsed -// -// func (t *Type) root_properties func (t *Type) RootProperties() []*Type { props := make([]*Type, 0) for _, p := range t.UserProperties() { @@ -1058,23 +821,14 @@ func (t *Type) RootProperties() []*Type { return props } -// func (t *Type) exclude_if_not_in_version!(version) -// super -// @properties.each { |p| p.exclude_if_not_in_version!(version) } -// end -// end - // An array of string -> string key -> value pairs, such as labels. // While this is technically a map, it's split out because it's a much // simpler property to generate and means we can avoid conditional logic // in Map. - func NewProperty(name, apiName string, options []func(*Type)) *Type { p := &Type{ - NamedObject: NamedObject{ - Name: name, - ApiName: apiName, - }, + Name: name, + ApiName: apiName, } for _, option := range options { @@ -1137,80 +891,51 @@ func propertyWithIgnoreWrite(ignoreWrite bool) func(*Type) { } } -// class KeyValuePairs < Composite -// // Ignore writing the "effective_labels" and "effective_annotations" fields to API. -// ignore_write - -// func (t *Type) initialize(name: nil, output: nil, api_name: nil, description: nil, min_version: nil, -// ignore_write: nil, update_verb: nil, update_url: nil, immutable: nil) -// super() - -// @name = name -// @output = output -// @api_name = api_name -// @description = description -// @min_version = min_version -// @ignore_write = ignore_write -// @update_verb = update_verb -// @update_url = update_url -// @immutable = immutable -// end - -// func (t *Type) validate -// super -// check :ignore_write, type: :boolean, default: false - -// return if @__resource.__product.nil? - -// product_name = @__resource.__product.name -// resource_name = @__resource.name +func (t *Type) validateLabelsField() { + productName := t.ResourceMetadata.ProductMetadata.Name + resourceName := t.ResourceMetadata.Name + lineage := t.Lineage() + if lineage == "labels" || lineage == "metadata.labels" || lineage == "configuration.labels" { + if !t.IsA("KeyValueLabels") && + // The label value must be empty string, so skip this resource + !(productName == "CloudIdentity" && resourceName == "Group") && -// if lineage == 'labels' || lineage == 'metadata.labels' || -// lineage == 'configuration.labels' -// if !(is_a? Api::Type::KeyValueLabels) && -// // The label value must be empty string, so skip this resource -// !(product_name == 'CloudIdentity' && resource_name == 'Group') && + // The "labels" field has type Array, so skip this resource + !(productName == "DeploymentManager" && resourceName == "Deployment") && -// // The "labels" field has type Array, so skip this resource -// !(product_name == 'DeploymentManager' && resource_name == 'Deployment') && + // https://github.com/hashicorp/terraform-provider-google/issues/16219 + !(productName == "Edgenetwork" && resourceName == "Network") && -// // https://github.com/hashicorp/terraform-provider-google/issues/16219 -// !(product_name == 'Edgenetwork' && resource_name == 'Network') && + // https://github.com/hashicorp/terraform-provider-google/issues/16219 + !(productName == "Edgenetwork" && resourceName == "Subnet") && -// // https://github.com/hashicorp/terraform-provider-google/issues/16219 -// !(product_name == 'Edgenetwork' && resource_name == 'Subnet') && + // "userLabels" is the resource labels field + !(productName == "Monitoring" && resourceName == "NotificationChannel") && -// // "userLabels" is the resource labels field -// !(product_name == 'Monitoring' && resource_name == 'NotificationChannel') && - -// // The "labels" field has type Array, so skip this resource -// !(product_name == 'Monitoring' && resource_name == 'MetricDescriptor') -// raise "Please use type KeyValueLabels for field //{lineage} " \ -// "in resource //{product_name}///{resource_name}" -// end -// elsif is_a? Api::Type::KeyValueLabels -// raise "Please don't use type KeyValueLabels for field //{lineage} " \ -// "in resource //{product_name}///{resource_name}" -// end + // The "labels" field has type Array, so skip this resource + !(productName == "Monitoring" && resourceName == "MetricDescriptor") { + log.Fatalf("Please use type KeyValueLabels for field %s in resource %s/%s", lineage, productName, resourceName) + } + } else if t.IsA("KeyValueLabels") { + log.Fatalf("Please don't use type KeyValueLabels for field %s in resource %s/%s", lineage, productName, resourceName) + } -// if lineage == 'annotations' || lineage == 'metadata.annotations' -// if !(is_a? Api::Type::KeyValueAnnotations) && -// // The "annotations" field has "ouput: true", so skip this eap resource -// !(product_name == 'Gkeonprem' && resource_name == 'BareMetalAdminClusterEnrollment') -// raise "Please use type KeyValueAnnotations for field //{lineage} " \ -// "in resource //{product_name}///{resource_name}" -// end -// elsif is_a? Api::Type::KeyValueAnnotations -// raise "Please don't use type KeyValueAnnotations for field //{lineage} " \ -// "in resource //{product_name}///{resource_name}" -// end -// end + if lineage == "annotations" || lineage == "metadata.annotations" { + if !t.IsA("KeyValueAnnotations") && + // The "annotations" field has "ouput: true", so skip this eap resource + !(productName == "Gkeonprem" && resourceName == "BareMetalAdminClusterEnrollment") { + log.Fatalf("Please use type KeyValueAnnotations for field %s in resource %s/%s", lineage, productName, resourceName) + } + } else if t.IsA("KeyValueAnnotations") { + log.Fatalf("Please don't use type KeyValueAnnotations for field %s in resource %s/%s", lineage, productName, resourceName) + } +} -// def field_min_version func (t Type) fieldMinVersion() string { return t.MinVersion } +// TODO rewrite: validation // // An array of string -> string key -> value pairs used specifically for the "labels" field. // // The field name with this type should be "labels" literally. // class KeyValueLabels < KeyValuePairs @@ -1243,65 +968,17 @@ func (t Type) fieldMinVersion() string { // end // end +// TODO rewrite: validation // // Map from string keys -> nested object entries // class Map < Composite -// // .yaml. -// module Fields -// // The type definition of the contents of the map. -// value_type - -// // While the API doesn't give keys an explicit name, we specify one -// // because in Terraform the key has to be a property of the object. -// // -// // The name of the key. Used in the Terraform schema as a field name. -// key_name - -// // A description of the key's format. Used in Terraform to describe -// // the field in documentation. -// key_description -// end -// include Fields - // func (t *Type) validate // super // check :key_name, type: ::String, required: true // check :key_description, type: ::String - -// @value_type.set_variable(@name, :__name) -// @value_type.set_variable(@__resource, :__resource) -// @value_type.set_variable(self, :__parent) // check :value_type, type: Api::Type::NestedObject, required: true // raise "Invalid type //{@value_type}" unless type?(@value_type) // end -// func (t *Type) nested_properties -// @value_type.nested_properties.reject(&:exclude) -// end -// end - -// // Support for schema ValidateFunc functionality. -// class Validation < Object -// // Ensures the value matches this regex -// regex -// function - -// func (t *Type) validate -// super - -// check :regex, type: String -// check :function, type: String -// end -// end - -// func (t *Type) type?(type) -// type.is_a?(Type) || !get_type(type).nil? -// end - -// func (t *Type) get_type(type) -// Module.const_get(type) -// end - -// def property_ns_prefix func (t Type) PropertyNsPrefix() []string { return []string{ "Google", @@ -1312,7 +989,6 @@ func (t Type) PropertyNsPrefix() []string { // "Namespace" - prefix with product and resource - a property with // information from the "object" variable - func (t Type) NamespaceProperty() string { name := google.Camelize(t.Name, "upper") p := t @@ -1324,18 +1000,6 @@ func (t Type) NamespaceProperty() string { return fmt.Sprintf("%s%s%s", google.Camelize(t.ResourceMetadata.ProductMetadata.ApiName, "lower"), t.ResourceMetadata.Name, name) } -// def namespace_property_from_object(property, object) -// -// name = property.name.camelize -// until property.parent.nil? -// property = property.parent -// name = property.name.camelize + name -// end -// -// "#{property.__resource.__product.api_name.camelize(:lower)}#{object.name}#{name}" -// -// end - func (t Type) CustomTemplate(templatePath string, appendNewline bool) string { return resource.ExecuteTemplate(&t, templatePath, appendNewline) } @@ -1368,7 +1032,6 @@ func (t *Type) GoLiteral(value interface{}) string { } } -// def force_new?(property, resource) func (t *Type) IsForceNew() bool { if t.IsA("KeyValueLabels") && t.ResourceMetadata.RootLabels() { return false @@ -1403,7 +1066,6 @@ func (t *Type) IsForceNew() bool { // TODO(emilymye): Change format of input for // exactly_one_of/at_least_one_of/etc to use camelcase, MM properities and // convert to snake in this method -// def get_property_schema_path(schema_path, resource) func (t *Type) GetPropertySchemaPath(schemaPath string) string { nestedProps := t.ResourceMetadata.UserProperites() diff --git a/mmv1/api/type_test.go b/mmv1/api/type_test.go index 86c3e2e57b1e..9d592d35a765 100644 --- a/mmv1/api/type_test.go +++ b/mmv1/api/type_test.go @@ -11,9 +11,7 @@ func TestTypeMinVersionObj(t *testing.T) { t.Parallel() p := Product{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", Versions: []*product.Version{ &product.Version{ Name: "beta", @@ -38,14 +36,10 @@ func TestTypeMinVersionObj(t *testing.T) { { description: "type minVersion is empty and resource minVersion is empty", obj: Type{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "", ResourceMetadata: &Resource{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "", ProductMetadata: &p, }, @@ -55,14 +49,10 @@ func TestTypeMinVersionObj(t *testing.T) { { description: "type minVersion is empty and resource minVersion is beta", obj: Type{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "", ResourceMetadata: &Resource{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "beta", ProductMetadata: &p, }, @@ -72,14 +62,10 @@ func TestTypeMinVersionObj(t *testing.T) { { description: "type minVersion is not empty", obj: Type{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "beta", ResourceMetadata: &Resource{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "", ProductMetadata: &p, }, @@ -107,9 +93,7 @@ func TestTypeExcludeIfNotInVersion(t *testing.T) { t.Parallel() p := Product{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", Versions: []*product.Version{ &product.Version{ Name: "beta", @@ -135,15 +119,11 @@ func TestTypeExcludeIfNotInVersion(t *testing.T) { { description: "type has Exclude true", obj: Type{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", Exclude: true, MinVersion: "", ResourceMetadata: &Resource{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "", ProductMetadata: &p, }, @@ -156,16 +136,12 @@ func TestTypeExcludeIfNotInVersion(t *testing.T) { { description: "type has Exclude false and not empty ExactVersion", obj: Type{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "", Exclude: false, ExactVersion: "beta", ResourceMetadata: &Resource{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "beta", ProductMetadata: &p, }, @@ -178,16 +154,12 @@ func TestTypeExcludeIfNotInVersion(t *testing.T) { { description: "type has Exclude false and empty ExactVersion", obj: Type{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "beta", Exclude: false, ExactVersion: "", ResourceMetadata: &Resource{ - NamedObject: NamedObject{ - Name: "test", - }, + Name: "test", MinVersion: "", ProductMetadata: &p, }, diff --git a/mmv1/google/string_utils.go b/mmv1/google/string_utils.go index 63d8fce9c683..338f9b9f4d8a 100644 --- a/mmv1/google/string_utils.go +++ b/mmv1/google/string_utils.go @@ -49,8 +49,7 @@ func SpaceSeparated(source string) string { return tmp } -// // Converts a string to space-separated capitalized words -// def self.title(source) +// Converts a string to space-separated capitalized words func SpaceSeparatedTitle(source string) string { ss := SpaceSeparated(source) return strings.Title(ss) @@ -58,8 +57,6 @@ func SpaceSeparatedTitle(source string) string { // Returns all the characters up until the period (.) or returns text // unchanged if there is no period. -// -// def self.first_sentence(text) func FirstSentence(text string) string { re := regexp.MustCompile(`[.?!]`) periodPos := re.FindStringIndex(text) @@ -117,11 +114,6 @@ func Camelize(term string, firstLetter string) string { return strings.Title(match) }) } else { - // TODO: rewrite with the regular expression. Lookahead(?=) is not supported in Go - // acronymsCamelizeRegex := regexp.MustCompile(`^(?:(?=a)b(?=\b|[A-Z_])|\w)`) - // res = acronymsCamelizeRegex.ReplaceAllStringFunc(res, func(match string) string { - // return strings.ToLower(match) - // }) if len(res) != 0 { r := []rune(res) r[0] = unicode.ToLower(r[0]) @@ -155,7 +147,7 @@ aren't common in JS-based regex flavours, but are in Perl-based ones func Format2Regex(format string) string { re := regexp.MustCompile(`\{\{%([[:word:]]+)\}\}`) result := re.ReplaceAllStringFunc(format, func(match string) string { - // TODO: the trims may not be needed with more effecient regex + // TODO rewrite: the trims may not be needed with more effecient regex word := strings.TrimPrefix(match, "{{") word = strings.TrimSuffix(word, "}}") word = strings.ReplaceAll(word, "%", "") diff --git a/mmv1/google/yaml_validator.go b/mmv1/google/yaml_validator.go index 811246cea98e..31a7e86e2a3f 100644 --- a/mmv1/google/yaml_validator.go +++ b/mmv1/google/yaml_validator.go @@ -23,133 +23,7 @@ import ( type YamlValidator struct{} func (v *YamlValidator) Parse(content []byte, obj interface{}, yamlPath string) { - // TODO(nelsonjr): Allow specifying which symbols to restrict it further. - // But it requires inspecting all configuration files for symbol sources, - // such as Enum values. Leaving it as a nice-to-have for the future. if err := yaml.UnmarshalStrict(content, obj); err != nil { log.Fatalf("Cannot unmarshal data from file %s: %v", yamlPath, err) } } - -// func (v *YamlValidator) allowed_classes() { -// ObjectSpace.each_object(Class).select do |klass| -// klass < Google::YamlValidator -// end.push(Time, Symbol) -// } - -// func (v *YamlValidator) validate() { -// Google::LOGGER.debug "Validating //{self.class} '//{@name}'" -// check_extraneous_properties -// } - -// func (v *YamlValidator) set_variable(value, property) { -// Google::LOGGER.debug "Setting variable of //{value} to //{self}" -// instance_variable_set("@//{property}", value) -// } - -// Does all validation checking for a particular variable. -// options: -// :default - the default value for this variable if its nil -// :type - the allowed types (single or array) that this value can be -// :item_type - the allowed types that all values in this array should be -// (implied that type == array) -// :allowed - the allowed values that this non-array variable should be. -// :required - is the variable required? (defaults: false) -// func (v *YamlValidator) check(variable, **opts) { -// value = instance_variable_get("@//{variable}") - -// // Set default value. -// if !opts[:default].nil? && value.nil? -// instance_variable_set("@//{variable}", opts[:default]) -// value = instance_variable_get("@//{variable}") -// end - -// // Check if value is required. Print nested path if available. -// lineage_path = respond_to?('lineage') ? lineage : '' -// raise "//{lineage_path} > Missing '//{variable}'" if value.nil? && opts[:required] -// return if value.nil? - -// // Check type -// check_property_value(variable, value, opts[:type]) if opts[:type] - -// // Check item_type -// if value.is_a?(Array) -// raise "//{lineage_path} > //{variable} must have item_type on arrays" unless opts[:item_type] - -// value.each_with_index do |o, index| -// check_property_value("//{variable}[//{index}]", o, opts[:item_type]) -// end -// end - -// // Check if value is allowed -// return unless opts[:allowed] -// raise "//{value} on //{variable} should be one of //{opts[:allowed]}" \ -// unless opts[:allowed].include?(value) -// } - -// func (v *YamlValidator) conflicts(list) { -// value_checked = false -// list.each do |item| -// next if instance_variable_get("@//{item}").nil? -// raise "//{list.join(',')} cannot be set at the same time" if value_checked - -// value_checked = true -// end -// } - -// private - -// func (v *YamlValidator) check_type(name, object, type) { -// if type == :boolean -// return unless [TrueClass, FalseClass].find_index(object.class).nil? -// elsif type.is_a? ::Array -// return if type.find_index(:boolean) && [TrueClass, FalseClass].find_index(object.class) -// return unless type.find_index(object.class).nil? -// // check if class is or inherits from type -// elsif object.class <= type -// return -// end -// raise "Property '//{name}' is '//{object.class}' instead of '//{type}'" -// } - -// func (v *YamlValidator) log_check_type(object) { -// if object.respond_to?(:name) -// Google::LOGGER.debug "Checking object //{object.name}" -// else -// Google::LOGGER.debug "Checking object //{object}" -// end -// } - -// func (v *YamlValidator) check_property_value(property, prop_value, type) { -// Google::LOGGER.debug "Checking '//{property}' on //{object_display_name}" -// check_type property, prop_value, type unless type.nil? -// prop_value.validate if prop_value.is_a?(Api::Object) -// } - -// func (v *YamlValidator) check_extraneous_properties() { -// instance_variables.each do |variable| -// var_name = variable.id2name[1..] -// next if var_name.start_with?('__') - -// Google::LOGGER.debug "Validating '//{var_name}' on //{object_display_name}" -// raise "Extraneous variable '//{var_name}' in //{object_display_name}" \ -// unless methods.include?(var_name.intern) -// end -// } - -// func (v *YamlValidator) set_variables(objects, property) { -// return if objects.nil? - -// objects.each do |object| -// object.set_variable(self, property) if object.respond_to?(:set_variable) -// end -// } - -// func (v *YamlValidator) ensure_property_does_not_exist(property) { -// raise "Conflict of property '//{property}' for object '//{self}'" \ -// unless instance_variable_get("@//{property}").nil? -// } - -// func (v *YamlValidator) object_display_name() { -// "//{@name}" -// } diff --git a/mmv1/main.go b/mmv1/main.go index fbedea96bd5d..f23b54cf683f 100644 --- a/mmv1/main.go +++ b/mmv1/main.go @@ -20,7 +20,7 @@ import ( var wg sync.WaitGroup -// TODO Q2: additional flags +// TODO rewrite: additional flags // Example usage: --output $GOPATH/src/github.com/terraform-providers/terraform-provider-google-beta var outputPath = flag.String("output", "", "path to output generated files to") @@ -86,7 +86,7 @@ func main() { dir := filepath.Dir(filePath) allProductFiles = append(allProductFiles, fmt.Sprintf("products/%s", filepath.Base(dir))) } - // TODO Q2: override directory + // TODO rewrite: override directory if allProducts { productsToGenerate = allProductFiles @@ -140,7 +140,7 @@ func main() { if generateCode { providerToGenerate.CompileCommonFiles(*outputPath, productsForVersion, "") - // TODO Q2: product overrides + // TODO rewrite: product overrides } } @@ -151,13 +151,13 @@ func GenerateProduct(productChannel chan string, providerToGenerate *provider.Te productYamlPath := path.Join(productName, "go_product.yaml") - // TODO Q2: uncomment the error check that if the product.yaml exists for each product + // TODO rewrite: uncomment the error check that if the product.yaml exists for each product // after Go-converted product.yaml files are complete for all products // if _, err := os.Stat(productYamlPath); errors.Is(err, os.ErrNotExist) { // log.Fatalf("%s does not contain a product.yaml file", productName) // } - // TODO Q2: product overrides + // TODO rewrite: product overrides if _, err := os.Stat(productYamlPath); err == nil { var resources []*api.Resource = make([]*api.Resource, 0) @@ -194,7 +194,7 @@ func GenerateProduct(productChannel chan string, providerToGenerate *provider.Te resources = append(resources, resource) } - // TODO Q2: override resources + // TODO rewrite: override resources // Sort resources by name sort.Slice(resources, func(i, j int) bool { @@ -204,7 +204,7 @@ func GenerateProduct(productChannel chan string, providerToGenerate *provider.Te productApi.Objects = resources productApi.Validate() - // TODO Q2: set other providers via flag + // TODO rewrite: set other providers via flag providerToGenerate = provider.NewTerraform(productApi, *version, startTime) *productsForVersion = append(*productsForVersion, productApi) diff --git a/mmv1/products/cloudquotas/QuotaPreference.yaml b/mmv1/products/cloudquotas/QuotaPreference.yaml index b31af9878597..caba3b5b9d59 100644 --- a/mmv1/products/cloudquotas/QuotaPreference.yaml +++ b/mmv1/products/cloudquotas/QuotaPreference.yaml @@ -62,7 +62,6 @@ properties: - !ruby/object:Api::Type::String name: 'name' default_from_api: true - pattern: '{{parent}}/locations/global/quotaPreferences/{{name}}' description: | The resource name of the quota preference. Required except in the CREATE requests. diff_suppress_func: 'tpgresource.CompareSelfLinkOrResourceName' diff --git a/mmv1/products/cloudquotas/go_QuotaPreference.yaml b/mmv1/products/cloudquotas/go_QuotaPreference.yaml index 2a48249d5ca1..0e368b1b0eac 100644 --- a/mmv1/products/cloudquotas/go_QuotaPreference.yaml +++ b/mmv1/products/cloudquotas/go_QuotaPreference.yaml @@ -66,7 +66,6 @@ properties: type: String description: | The resource name of the quota preference. Required except in the CREATE requests. - pattern: '{{parent}}/locations/global/quotaPreferences/{{name}}' default_from_api: true diff_suppress_func: 'tpgresource.CompareSelfLinkOrResourceName' custom_flatten: 'templates/terraform/custom_flatten/go/name_from_self_link.tmpl' diff --git a/mmv1/provider/template_data.go b/mmv1/provider/template_data.go index 8824e2a9ae94..10e04969058d 100644 --- a/mmv1/provider/template_data.go +++ b/mmv1/provider/template_data.go @@ -31,15 +31,13 @@ import ( ) type TemplateData struct { - // include Compile::Core - OutputFolder string VersionName string TerraformResourceDirectory string TerraformProviderModule string - // TODO Q2: is this needed? + // TODO rewrite: is this needed? // # Information about the local environment // # (which formatters are enabled, start-time) // attr_accessor :env @@ -209,88 +207,6 @@ func (td *TemplateData) GenerateFile(filePath, templatePath string, input any, g } } -// # path is the output name of the file -// # template is used to determine metadata about the file based on how it is -// # generated -// def format_output_file(path, template) -// return unless path.end_with?('.go') && @env[:goformat_enabled] - -// run_formatter("gofmt -w -s #{path}") -// run_formatter("goimports -w #{path}") unless template.include?('third_party/terraform') -// end - -// def run_formatter(command) -// output = %x(#{command} 2>&1) -// Google::LOGGER.error output unless $CHILD_STATUS.to_i.zero? -// end - -// def relative_path(target, base) -// Pathname.new(target).relative_path_from(Pathname.new(base)) -// end -// end - -// # Responsible for compiling provider-level files, rather than product-specific ones -// class ProviderFileTemplate < Provider::FileTemplate -// # All the products that are being compiled with the provider on this run -// attr_accessor :products - -// # Optional path to the directory where overrides reside. Used to locate files -// # outside of the MM root directory -// attr_accessor :override_path - -// def initialize(output_folder, version, env, products, override_path = nil) -// super() - -// @output_folder = output_folder -// @version = version -// @env = env -// @products = products -// @override_path = override_path -// end -// end - -// # Responsible for generating a file in the context of a product -// # with a given set of parameters. -// class ProductFileTemplate < Provider::FileTemplate -// # The name of the resource -// attr_accessor :name -// # The resource itself. -// attr_accessor :object -// # The entire API object. -// attr_accessor :product - -// class << self -// # Construct a new ProductFileTemplate based on a resource object -// def file_for_resource(output_folder, object, version, env) -// file_template = new(output_folder, object.name, object.__product, version, env) -// file_template.object = object -// file_template -// end -// end - -// def initialize(output_folder, name, product, version, env) -// super() - -// @name = name -// @product = product -// @output_folder = output_folder -// @version = version -// @env = env -// end -// end -// end - -// def import_path -// case @target_version_name -// when 'ga' -// "#{TERRAFORM_PROVIDER_GA}/#{RESOURCE_DIRECTORY_GA}" -// when 'beta' -// "#{TERRAFORM_PROVIDER_BETA}/#{RESOURCE_DIRECTORY_BETA}" -// else -// "#{TERRAFORM_PROVIDER_PRIVATE}/#{RESOURCE_DIRECTORY_PRIVATE}" -// end -// end - func (td *TemplateData) ImportPath() string { if td.VersionName == GA_VERSION { return "github.com/hashicorp/terraform-provider-google/google" diff --git a/mmv1/provider/terraform.go b/mmv1/provider/terraform.go index 427f151f2bef..a204d7575ad5 100644 --- a/mmv1/provider/terraform.go +++ b/mmv1/provider/terraform.go @@ -197,7 +197,6 @@ func (t *Terraform) GenerateOperation(outputFolder string) { // Generate the IAM policy for this object. This is used to query and test // IAM policies separately from the resource itself -// def generate_iam_policy(pwd, data, generate_code, generate_docs) func (t *Terraform) GenerateIamPolicy(object api.Resource, templateData TemplateData, outputFolder string, generateCode, generateDocs bool) { if generateCode && object.IamPolicy != nil && (object.IamPolicy.MinVersion == "" || slices.Index(product.ORDER, object.IamPolicy.MinVersion) <= slices.Index(product.ORDER, t.TargetVersionName)) { productName := t.Product.ApiName @@ -222,7 +221,6 @@ func (t *Terraform) GenerateIamPolicy(object api.Resource, templateData Template } } -// def generate_iam_documentation(pwd, data) func (t *Terraform) GenerateIamDocumentation(object api.Resource, templateData TemplateData, outputFolder string, generateCode, generateDocs bool) { resourceDocFolder := path.Join(outputFolder, "website", "docs", "r") if err := os.MkdirAll(resourceDocFolder, os.ModePerm); err != nil { @@ -269,7 +267,6 @@ func (t *Terraform) FullResourceName(object api.Resource) string { return fmt.Sprintf("%s_%s", productName, name) } -// def copy_common_files(output_folder, generate_code, generate_docs, provider_name = nil) func (t Terraform) CopyCommonFiles(outputFolder string, generateCode, generateDocs bool) { log.Printf("Copying common files for %s", t.providerName()) @@ -346,7 +343,6 @@ func (t Terraform) getCopyFilesInFolder(folderPath, targetDir string) map[string return m } -// def copy_file_list(output_folder, files) func (t Terraform) CopyFileList(outputFolder string, files map[string]string) { for target, source := range files { targetFile := filepath.Join(outputFolder, target) @@ -383,13 +379,6 @@ func (t Terraform) CopyFileList(outputFolder string, files map[string]string) { } // Compiles files that are shared at the provider level -// -// def compile_common_files( -// output_folder, -// products, -// common_compile_file, -// override_path = nil -// ) func (t Terraform) CompileCommonFiles(outputFolder string, products []*api.Product, overridePath string) { t.generateResourcesForVersion(products) files := t.getCommonCompileFiles(t.TargetVersionName) @@ -453,7 +442,6 @@ func (t Terraform) getCompileFilesInFolder(folderPath, targetDir string) map[str return m } -// def compile_file_list(output_folder, files, file_template, pwd = Dir.pwd) func (t Terraform) CompileFileList(outputFolder string, files map[string]string, fileTemplate TemplateData, products []*api.Product) { providerWithProducts := ProviderWithProducts{ Terraform: t, @@ -483,7 +471,6 @@ func (t Terraform) CompileFileList(outputFolder string, files map[string]string, } } -// def add_hashicorp_copyright_header(output_folder, target) func (t Terraform) addHashicorpCopyRightHeader(outputFolder, target string) { if !expectedOutputFolder(outputFolder) { log.Printf("Unexpected output folder (%s) detected"+ @@ -550,7 +537,6 @@ func (t Terraform) addHashicorpCopyRightHeader(outputFolder, target string) { } } -// def expected_output_folder?(output_folder) func expectedOutputFolder(outputFolder string) bool { expectedFolders := []string{"terraform-provider-google", "terraform-provider-google-beta", "terraform-next", "terraform-google-conversion", "tfplan2cai"} folderName := filepath.Base(outputFolder) // Possible issue with Windows OS @@ -565,7 +551,6 @@ func expectedOutputFolder(outputFolder string) bool { return isExpected } -// def replace_import_path(output_folder, target) func (t Terraform) replaceImportPath(outputFolder, target string) { targetFile := filepath.Join(outputFolder, target) sourceByte, err := os.ReadFile(targetFile) @@ -649,7 +634,6 @@ func (t Terraform) ProviderFromVersion() string { // Gets the list of services dependent on the version ga, beta, and private // If there are some resources of a servcie is in GA, // then this service is in GA. Otherwise, the service is in BETA -// def get_mmv1_services_in_version(products, version) func (t Terraform) GetMmv1ServicesInVersion(products []*api.Product) []string { var services []string for _, product := range products { @@ -675,198 +659,6 @@ func (t Terraform) GetMmv1ServicesInVersion(products []*api.Product) []string { return services } -// def generate_newyaml(pwd, data) -// -// # @api.api_name is the service folder name -// product_name = @api.api_name -// target_folder = File.join(folder_name(data.version), 'services', product_name) -// FileUtils.mkpath target_folder -// data.generate(pwd, -// '/templates/terraform/yaml_conversion.erb', -// "#{target_folder}/go_#{data.object.name}.yaml", -// self) -// return if File.exist?("#{target_folder}/go_product.yaml") -// -// data.generate(pwd, -// '/templates/terraform/product_yaml_conversion.erb', -// "#{target_folder}/go_product.yaml", -// self) -// -// end -// -// def build_env -// -// { -// goformat_enabled: @go_format_enabled, -// start_time: @start_time -// } -// -// end -// -// # used to determine and separate objects that have update methods -// # that target individual fields -// def field_specific_update_methods(properties) -// -// properties_by_custom_update(properties).length.positive? -// -// end -// -// # Filter the properties to keep only the ones requiring custom update -// # method and group them by update url & verb. -// def properties_by_custom_update(properties) -// -// update_props = properties.reject do |p| -// p.update_url.nil? || p.update_verb.nil? || p.update_verb == :NOOP || -// p.is_a?(Api::Type::KeyValueTerraformLabels) || -// p.is_a?(Api::Type::KeyValueLabels) # effective_labels is used for update -// end -// -// update_props.group_by do |p| -// { -// update_url: p.update_url, -// update_verb: p.update_verb, -// update_id: p.update_id, -// fingerprint_name: p.fingerprint_name -// } -// end -// -// end -// -// # Filter the properties to keep only the ones don't have custom update -// # method and group them by update url & verb. -// def properties_without_custom_update(properties) -// -// properties.select do |p| -// p.update_url.nil? || p.update_verb.nil? || p.update_verb == :NOOP -// end -// -// end -// -// # Takes a update_url and returns the list of custom updatable properties -// # that can be updated at that URL. This allows flattened objects -// # to determine which parent property in the API should be updated with -// # the contents of the flattened object -// def custom_update_properties_by_key(properties, key) -// -// properties_by_custom_update(properties).select do |k, _| -// k[:update_url] == key[:update_url] && -// k[:update_id] == key[:update_id] && -// k[:fingerprint_name] == key[:fingerprint_name] -// end.first.last -// # .first is to grab the element from the select which returns a list -// # .last is because properties_by_custom_update returns a list of -// # [{update_url}, [properties,...]] and we only need the 2nd part -// -// end -// -// def update_url(resource, url_part) -// -// [resource.__product.base_url, update_uri(resource, url_part)].flatten.join -// -// end -// -// def generating_hashicorp_repo? -// -// # The default Provider is used to generate TPG and TPGB in HashiCorp-owned repos. -// # The compiler deviates from the default behaviour with a -f flag to produce -// # non-HashiCorp downstreams. -// true -// -// end -// -// # ProductFileTemplate with Terraform specific fields -// class TerraformProductFileTemplate < Provider::ProductFileTemplate -// -// # The async object used for making operations. -// # We assume that all resources share the same async properties. -// attr_accessor :async -// -// # When generating OiCS examples, we attach the example we're -// # generating to the data object. -// attr_accessor :example -// -// attr_accessor :resource_name -// -// end -// -// # Sorts properties in the order they should appear in the TF schema: -// # Required, Optional, Computed -// def order_properties(properties) -// -// properties.select(&:required).sort_by(&:name) + -// properties.reject(&:required).reject(&:output).sort_by(&:name) + -// properties.select(&:output).sort_by(&:name) -// -// end -// -// def tf_type(property) -// -// tf_types[property.class] -// -// end -// -// # Converts between the Magic Modules type of an object and its type in the -// # TF schema -// def tf_types -// -// { -// Api::Type::Boolean => 'schema.TypeBool', -// Api::Type::Double => 'schema.TypeFloat', -// Api::Type::Integer => 'schema.TypeInt', -// Api::Type::String => 'schema.TypeString', -// # Anonymous string property used in array of strings. -// 'Api::Type::String' => 'schema.TypeString', -// Api::Type::Time => 'schema.TypeString', -// Api::Type::Enum => 'schema.TypeString', -// Api::Type::ResourceRef => 'schema.TypeString', -// Api::Type::NestedObject => 'schema.TypeList', -// Api::Type::Array => 'schema.TypeList', -// Api::Type::KeyValuePairs => 'schema.TypeMap', -// Api::Type::KeyValueLabels => 'schema.TypeMap', -// Api::Type::KeyValueTerraformLabels => 'schema.TypeMap', -// Api::Type::KeyValueEffectiveLabels => 'schema.TypeMap', -// Api::Type::KeyValueAnnotations => 'schema.TypeMap', -// Api::Type::Map => 'schema.TypeSet', -// Api::Type::Fingerprint => 'schema.TypeString' -// } -// -// end -// -// def updatable?(resource, properties) -// -// !resource.immutable || !properties.reject { |p| p.update_url.nil? }.empty? -// -// end -// -// # Returns tuples of (fieldName, list of update masks) for -// # top-level updatable fields. Schema path refers to a given Terraform -// # field name (e.g. d.GetChange('fieldName)') -// def get_property_update_masks_groups(properties, mask_prefix: ”) -// -// mask_groups = [] -// properties.each do |prop| -// if prop.flatten_object -// mask_groups += get_property_update_masks_groups( -// prop.properties, mask_prefix: "#{prop.api_name}." -// ) -// elsif prop.update_mask_fields -// mask_groups << [prop.name.underscore, prop.update_mask_fields] -// else -// mask_groups << [prop.name.underscore, [mask_prefix + prop.api_name]] -// end -// end -// mask_groups -// -// end -// -// # Capitalize the first letter of a property name. -// # E.g. "creationTimestamp" becomes "CreationTimestamp". -// def titlelize_property(property) -// -// property.name.camelize(:upper) -// -// end -// // # Generates the list of resources, and gets the count of resources and iam resources // # dependent on the version ga, beta or private. // # The resource object has the format @@ -877,7 +669,6 @@ func (t Terraform) GetMmv1ServicesInVersion(products []*api.Product) []string { // # } // # The variable resources_for_version is used to generate resources in file // # mmv1/third_party/terraform/provider/provider_mmv1_resources.go.erb -// def generate_resources_for_version(products, version) func (t *Terraform) generateResourcesForVersion(products []*api.Product) { for _, productDefinition := range products { service := strings.ToLower(productDefinition.Name) @@ -910,20 +701,16 @@ func (t *Terraform) generateResourcesForVersion(products []*api.Product) { }) } } - - // @resources_for_version = @resources_for_version.compact } // # TODO(nelsonjr): Review all object interfaces and move to private methods // # that should not be exposed outside the object hierarchy. -// def provider_name func (t Terraform) providerName() string { return reflect.TypeOf(t).Name() } // # Adapted from the method used in templating // # See: mmv1/compile/core.rb -// def comment_block(text, lang) func commentBlock(text []string, lang string) string { var headers []string switch lang { @@ -953,7 +740,6 @@ func commentText(text []string, symbols string) []string { return header } -// def language_from_filename(filename) func languageFromFilename(filename string) string { switch extension := filepath.Ext(filename); extension { case ".go": @@ -967,13 +753,6 @@ func languageFromFilename(filename string) string { } } -// # Returns the id format of an object, or self_link_uri if none is explicitly defined -// # We prefer the long name of a resource as the id so that users can reference -// # resources in a standard way, and most APIs accept short name, long name or self_link -// def id_format(object) -// object.id_format || object.self_link_uri -// end - // Returns the extension for DCL packages for the given version. This is needed // as the DCL uses "alpha" for preview resources, while we use "private" func (t Terraform) DCLVersion() string { diff --git a/mmv1/templates/terraform/operation.go.tmpl b/mmv1/templates/terraform/operation.go.tmpl index a7a435a672f9..3a778b8f8e2d 100644 --- a/mmv1/templates/terraform/operation.go.tmpl +++ b/mmv1/templates/terraform/operation.go.tmpl @@ -1,4 +1,4 @@ -{{/* TODO: if hc_downstream */ -}} +{{/* TODO rewrite: if hc_downstream */ -}} // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 @@ -94,7 +94,7 @@ func create{{ $.ProductMetadata.Name }}Waiter(config *transport_tpg.Config, op m Might as well just nolint it so we can pass the linter checks. */}} -// nolint: deadcode,unused {{/* TODO: remove the comment */}} +// nolint: deadcode,unused {{/* TODO rewrite: remove the comment */}} func {{ camelize $.ProductMetadata.Name "upper" }}OperationWaitTimeWithResponse(config *transport_tpg.Config, op map[string]interface{}, response *map[string]interface{},{{- if $.IncludeProjectForOperation }} project,{{- end }} activity, userAgent string, timeout time.Duration) error { w, err := create{{ $.ProductMetadata.Name }}Waiter(config, op, {{- if $.IncludeProjectForOperation }} project, {{ end }} activity, userAgent) if err != nil { diff --git a/mmv1/templates/terraform/resource.go.tmpl b/mmv1/templates/terraform/resource.go.tmpl index 28230e200075..bbbfc17802eb 100644 --- a/mmv1/templates/terraform/resource.go.tmpl +++ b/mmv1/templates/terraform/resource.go.tmpl @@ -420,9 +420,6 @@ func resource{{ $.ResourceName -}}Create(d *schema.ResourceData, meta interface{ {{if and ($.GetAsync) ($.GetAsync.IsA "PollAsync")}} func resource{{ $.ResourceName -}}PollRead(d *schema.ResourceData, meta interface{}) transport_tpg.PollReadFunc { return func() (map[string]interface{}, error) { -{{if $.GetAsync.CustomPollRead -}} -//TODO custom poll read -{{ else -}} config := meta.(*transport_tpg.Config) @@ -504,7 +501,6 @@ func resource{{ $.ResourceName -}}PollRead(d *schema.ResourceData, meta interfac } {{ end }} return res, nil -{{ end -}} } } {{ end }}