From 40b312de9751fe1e2e09d07d71820d52976e57e3 Mon Sep 17 00:00:00 2001 From: Megan Bang Date: Thu, 13 Aug 2020 16:20:17 -0500 Subject: [PATCH] updates post create rather than using customize diff --- products/bigquery/terraform.yaml | 37 ++--- .../constants/bigquery_dataset_access.go | 66 --------- .../constants/bigquery_dataset_access.go.erb | 126 ++++++++++++++++++ .../bigquery_dataset_access.go.erb | 6 +- .../bigquery_dataset_access.go.erb | 34 +++++ .../resource_bigquery_dataset_access_test.go | 3 + 6 files changed, 180 insertions(+), 92 deletions(-) delete mode 100644 templates/terraform/constants/bigquery_dataset_access.go create mode 100644 templates/terraform/constants/bigquery_dataset_access.go.erb rename templates/terraform/{resource_definition => extra_schema_entry}/bigquery_dataset_access.go.erb (71%) create mode 100644 templates/terraform/post_create/bigquery_dataset_access.go.erb diff --git a/products/bigquery/terraform.yaml b/products/bigquery/terraform.yaml index d22070f55982..a033bb317915 100644 --- a/products/bigquery/terraform.yaml +++ b/products/bigquery/terraform.yaml @@ -103,32 +103,19 @@ overrides: !ruby/object:Overrides::ResourceOverrides # before comparison. custom_expand: "templates/terraform/custom_expand/bigquery_access_role.go.erb" iamMember: !ruby/object:Overrides::Terraform::PropertyOverride - # iamMember sometimes gets returned as userByEmail, groupByEmail, domain, or specialGroup - # and needs to be "computed" in order for the customizeDiff to Clear it from the diff - default_from_api: true - userByEmail: !ruby/object:Overrides::Terraform::PropertyOverride - # these need to be "computed" in order for the customizeDiff to be SetNew - # with iamMember. we parse the value passed into iamMember and reconfigure - # one of these fields based in the prefix of the value passed in - default_from_api: true - groupByEmail: !ruby/object:Overrides::Terraform::PropertyOverride - # these need to be "computed" in order for the customizeDiff to be SetNew - # with iamMember. we parse the value passed into iamMember and reconfigure - # one of these fields based in the prefix of the value passed in - default_from_api: true - domain: !ruby/object:Overrides::Terraform::PropertyOverride - # these need to be "computed" in order for the customizeDiff to be SetNew - # with iamMember. we parse the value passed into iamMember and reconfigure - # one of these fields based in the prefix of the value passed in - default_from_api: true - specialGroup: !ruby/object:Overrides::Terraform::PropertyOverride - # these need to be "computed" in order for the customizeDiff to be SetNew - # with iamMember. we parse the value passed into iamMember and reconfigure - # one of these fields based in the prefix of the value passed in - default_from_api: true + diff_suppress_func: resourceBigQueryDatasetAccessIamMemberDiffSuppress + userByEmail: !ruby/object:Overrides::Terraform::PropertyOverride + diff_suppress_func: resourceBigQueryDatasetAccessIamMemberDiffSuppress + groupByEmail: !ruby/object:Overrides::Terraform::PropertyOverride + diff_suppress_func: resourceBigQueryDatasetAccessIamMemberDiffSuppress + specialGroup: !ruby/object:Overrides::Terraform::PropertyOverride + diff_suppress_func: resourceBigQueryDatasetAccessIamMemberDiffSuppress + domain: !ruby/object:Overrides::Terraform::PropertyOverride + diff_suppress_func: resourceBigQueryDatasetAccessIamMemberDiffSuppress custom_code: !ruby/object:Provider::Terraform::CustomCode - constants: templates/terraform/constants/bigquery_dataset_access.go - resource_definition: templates/terraform/resource_definition/bigquery_dataset_access.go.erb + constants: templates/terraform/constants/bigquery_dataset_access.go.erb + post_create: templates/terraform/post_create/bigquery_dataset_access.go.erb + extra_schema_entry: templates/terraform/extra_schema_entry/bigquery_dataset_access.go.erb Job: !ruby/object:Overrides::Terraform::ResourceOverride import_format: ["projects/{{project}}/jobs/{{job_id}}"] skip_delete: true diff --git a/templates/terraform/constants/bigquery_dataset_access.go b/templates/terraform/constants/bigquery_dataset_access.go deleted file mode 100644 index 12a4934edea2..000000000000 --- a/templates/terraform/constants/bigquery_dataset_access.go +++ /dev/null @@ -1,66 +0,0 @@ -var bigqueryAccessRoleToPrimitiveMap = map[string]string { - "roles/bigquery.dataOwner": "OWNER", - "roles/bigquery.dataEditor": "WRITER", - "roles/bigquery.dataViewer": "READER", -} - -var bigqueryAccessIamMemberToTypeMap = map[string]string{ - "serviceAccount": "user_by_email", - "user": "user_by_email", - "group": "group_by_email", - "domain": "domain", - "specialGroup": "special_group", - "allUsers": "iam_member", - "projectOwners": "special_group", - "projectReaders": "special_group", - "projectWriters": "special_group", - "allAuthenticatedUsers": "special_group", -} - -func resourceBigQueryDatasetAccessRoleDiffSuppress(k, old, new string, d *schema.ResourceData) bool { - if primitiveRole, ok := bigqueryAccessRoleToPrimitiveMap[new]; ok { - return primitiveRole == old - } - return false -} - -// iam_member can be passed into the request, but the response will have the value in one of -// UserByEmail, GroupByEmail, Domain, or SpecialGroup fields. The key is determined by the -// prefix of the iam_member value, and the value follows the : of the prefix. -// Instead of dealing with the issues in the response, we'll do the translation before we -// request. -func customDiffBigQueryDatasetAccess(d *schema.ResourceDiff, meta interface{}) error { - if !d.NewValueKnown("iam_member") { - return nil - } - - _, configValue := d.GetChange("iam_member") - - parts := strings.Split(configValue.(string), ":") - if len(parts) > 2 { - return nil - } - - var key string - var value string - if k, ok := bigqueryAccessIamMemberToTypeMap[parts[0]]; !ok { - key = "iam_member" - } else { - key = k - } - - if len(parts) == 1 { - value = parts[0] - } else { - value = parts[1] - } - - if key == "iam_member" { - return nil - } - - if err := d.Clear("iam_member"); err != nil { - return err - } - return d.SetNew(key, value) -} \ No newline at end of file diff --git a/templates/terraform/constants/bigquery_dataset_access.go.erb b/templates/terraform/constants/bigquery_dataset_access.go.erb new file mode 100644 index 000000000000..0023819ecad9 --- /dev/null +++ b/templates/terraform/constants/bigquery_dataset_access.go.erb @@ -0,0 +1,126 @@ +var bigqueryAccessRoleToPrimitiveMap = map[string]string { + "roles/bigquery.dataOwner": "OWNER", + "roles/bigquery.dataEditor": "WRITER", + "roles/bigquery.dataViewer": "READER", +} + +var bigqueryAccessIamMemberToTypeMap = map[string]string{ + "serviceAccount": "user_by_email", + "user": "user_by_email", + "group": "group_by_email", + "domain": "domain", + "specialGroup": "special_group", + "allUsers": "iam_member", + "projectOwners": "special_group", + "projectReaders": "special_group", + "projectWriters": "special_group", + "allAuthenticatedUsers": "special_group", +} + +func resourceBigQueryDatasetAccessRoleDiffSuppress(k, old, new string, d *schema.ResourceData) bool { + if primitiveRole, ok := bigqueryAccessRoleToPrimitiveMap[new]; ok { + return primitiveRole == old + } + return false +} + +func resourceBigQueryDatasetAccessIamMemberDiffSuppress(k, old, new string, d *schema.ResourceData) bool { + if primitiveRole, ok := bigqueryAccessRoleToPrimitiveMap[new]; ok { + return primitiveRole == old + } + + if d.Get("api_updated_member") == true { + expectedIamMember := d.Get("iam_member").(string) + parts := strings.SplitAfter(expectedIamMember, ":") + + strippedIamMember := parts[0] + if len(parts) > 1 { + strippedIamMember = parts[1] + } + + if memberInState := d.Get("user_by_email").(string); memberInState != "" { + return memberInState == strippedIamMember + } + + if memberInState := d.Get("group_by_email").(string); memberInState != "" { + return memberInState == strippedIamMember + } + + if memberInState := d.Get("domain").(string); memberInState != "" { + return memberInState == strippedIamMember + } + + if memberInState := d.Get("special_group").(string); memberInState != "" { + return memberInState == strippedIamMember + } + } + + return false +} + +// this function will go through a response's access list and see if the iam_member has been reassigned to a different member_type +// if it has, it will return the member type, and the member +func resourceBigQueryDatasetAccessReassignIamMemberInNestedObjectList(d *schema.ResourceData, meta interface{}, items []interface{}) (member_type string, member interface{}, err error) { + expectedRole, err := expandNestedBigQueryDatasetAccessRole(d.Get("role"), d, meta.(*Config)) + if err != nil { + return "", nil, err + } + expectedFlattenedRole := flattenNestedBigQueryDatasetAccessRole(expectedRole, d, meta.(*Config)) + + expectedIamMember, err := expandNestedBigQueryDatasetAccessIamMember(d.Get("iam_member"), d, meta.(*Config)) + if err != nil { + return "", nil, err + } + expectedFlattenedIamMember := flattenNestedBigQueryDatasetAccessIamMember(expectedIamMember, d, meta.(*Config)) + + parts := strings.SplitAfter(expectedFlattenedIamMember.(string), ":") + + expectedStrippedIamMember := parts[0] + if len(parts) > 1 { + expectedStrippedIamMember = parts[1] + } + + // Search list for this resource. + for _, itemRaw := range items { + if itemRaw == nil { + continue + } + item := itemRaw.(map[string]interface{}) + + itemRole := flattenNestedBigQueryDatasetAccessRole(item["role"], d, meta.(*Config)) + // isEmptyValue check so that if one is nil and the other is "", that's considered a match + if !(isEmptyValue(reflect.ValueOf(itemRole)) && isEmptyValue(reflect.ValueOf(expectedFlattenedRole))) && !reflect.DeepEqual(itemRole, expectedFlattenedRole) { + log.Printf("[DEBUG] Skipping item with role= %#v, looking for %#v)", itemRole, expectedFlattenedRole) + continue + } + + itemUserByEmail := flattenNestedBigQueryDatasetAccessUserByEmail(item["userByEmail"], d, meta.(*Config)) + if reflect.DeepEqual(itemUserByEmail, expectedStrippedIamMember) { + log.Printf("[DEBUG] Iam Member changed to userByEmail= %#v)", itemUserByEmail) + return "user_by_email", itemUserByEmail, nil + } + itemGroupByEmail := flattenNestedBigQueryDatasetAccessGroupByEmail(item["groupByEmail"], d, meta.(*Config)) + if reflect.DeepEqual(itemGroupByEmail, expectedStrippedIamMember) { + log.Printf("[DEBUG] Iam Member changed to groupByEmail= %#v)", itemGroupByEmail) + return "group_by_email", itemGroupByEmail, nil + } + itemDomain := flattenNestedBigQueryDatasetAccessDomain(item["domain"], d, meta.(*Config)) + if reflect.DeepEqual(itemDomain, expectedStrippedIamMember) { + log.Printf("[DEBUG] Iam Member changed to domain= %#v)", itemDomain) + return "domain", itemDomain, nil + } + itemSpecialGroup := flattenNestedBigQueryDatasetAccessSpecialGroup(item["specialGroup"], d, meta.(*Config)) + if reflect.DeepEqual(itemSpecialGroup, expectedStrippedIamMember) { + log.Printf("[DEBUG] Iam Member changed to specialGroup= %#v)", itemSpecialGroup) + return "special_group", itemSpecialGroup, nil + } + itemIamMember := flattenNestedBigQueryDatasetAccessIamMember(item["iamMember"], d, meta.(*Config)) + if reflect.DeepEqual(itemIamMember, expectedFlattenedIamMember) { + log.Printf("[DEBUG] Iam Member stayed as iamMember= %#v)", itemIamMember) + return "", nil, nil + } + continue + } + log.Printf("[DEBUG] Did not find item for resource %q)", d.Id()) + return "", nil, nil +} \ No newline at end of file diff --git a/templates/terraform/resource_definition/bigquery_dataset_access.go.erb b/templates/terraform/extra_schema_entry/bigquery_dataset_access.go.erb similarity index 71% rename from templates/terraform/resource_definition/bigquery_dataset_access.go.erb rename to templates/terraform/extra_schema_entry/bigquery_dataset_access.go.erb index 3d9689b57ec5..43c02ebc2482 100644 --- a/templates/terraform/resource_definition/bigquery_dataset_access.go.erb +++ b/templates/terraform/extra_schema_entry/bigquery_dataset_access.go.erb @@ -12,4 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -%> -CustomizeDiff: customDiffBigQueryDatasetAccess, +"api_updated_member": { + Type: schema.TypeBool, + Computed: true, + Description: "If true, represents that that the iam_member in the config was translated to a different member type by the API, and is stored in state as a different member type", +}, \ No newline at end of file diff --git a/templates/terraform/post_create/bigquery_dataset_access.go.erb b/templates/terraform/post_create/bigquery_dataset_access.go.erb new file mode 100644 index 000000000000..a023e38ba09e --- /dev/null +++ b/templates/terraform/post_create/bigquery_dataset_access.go.erb @@ -0,0 +1,34 @@ +<%# The license inside this block applies to this file. + # Copyright 2020 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. +-%> + +// by default, we are not updating the member +d.Set("api_updated_member", false) + +// iam_member is a generalized attribute, if the API can map it to a different member type on the backend, it will return +// the correct member_type in the response. If it cannot be mapped to a different member type, it will stay in iam_member. +if d.Get("iam_member") != nil { + member_type, member, err := resourceBigQueryDatasetAccessReassignIamMemberInNestedObjectList(d, meta, res["access"].([]interface{})) + if err != nil { + fmt.Println(err) + } + + // if the member type changed, we set that member_type in state (it's already in the response) and we clear iam_member + // and we set "api_updated_member" to true to acknowledge that we are making this change + if member_type != "" { + d.Set(member_type, member.(string)) + d.Set("iam_member", "") + d.Set("api_updated_member", true) + } +} \ No newline at end of file diff --git a/third_party/terraform/tests/resource_bigquery_dataset_access_test.go b/third_party/terraform/tests/resource_bigquery_dataset_access_test.go index 5b6976cee196..237036de7b74 100644 --- a/third_party/terraform/tests/resource_bigquery_dataset_access_test.go +++ b/third_party/terraform/tests/resource_bigquery_dataset_access_test.go @@ -180,6 +180,9 @@ func TestAccBigQueryDatasetAccess_allUsers(t *testing.T) { { Config: testAccBigQueryDatasetAccess_allUsers(datasetID), }, + { + Config: testAccBigQueryDatasetAccess_allAuthenticatedUsers(datasetID), + }, }, }) }