Last active
October 12, 2021 15:16
-
-
Save nfx/620721f593ca42447528598294e8ddc1 to your computer and use it in GitHub Desktop.
Databricks Terraform provider to respect DiffSuppressFuncs for `slice_set` fields
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/common/reflect_resource.go b/common/reflect_resource.go | |
index 12341451..1f832d98 100644 | |
--- a/common/reflect_resource.go | |
+++ b/common/reflect_resource.go | |
@@ -1,9 +1,11 @@ | |
package common | |
import ( | |
+ "bytes" | |
"fmt" | |
"log" | |
"reflect" | |
+ "sort" | |
"strconv" | |
"strings" | |
@@ -128,6 +130,112 @@ func chooseFieldName(typeField reflect.StructField) string { | |
return strings.Split(jsonTag, ",")[0] | |
} | |
+func hashPrepResource(buf *bytes.Buffer, r *schema.Resource, path string, raw interface{}) { | |
+ if raw == nil { | |
+ return | |
+ } | |
+ var keys []string | |
+ for k := range r.Schema { | |
+ keys = append(keys, k) | |
+ } | |
+ sort.Strings(keys) | |
+ mv := raw.(map[string]interface{}) | |
+ for _, field := range keys { | |
+ s := r.Schema[field] | |
+ if s.Computed { | |
+ continue | |
+ } | |
+ buf.WriteString(field) | |
+ buf.WriteRune(':') | |
+ fieldPath := path + "." + field | |
+ hashPrepValue(buf, s, fieldPath, mv[field]) | |
+ } | |
+} | |
+ | |
+func hashPrepValue(buf *bytes.Buffer, s *schema.Schema, path string, raw interface{}) { | |
+ if raw == nil { | |
+ buf.WriteRune(';') | |
+ return | |
+ } | |
+ switch s.Type { | |
+ case schema.TypeBool, schema.TypeInt, schema.TypeFloat, schema.TypeString: | |
+ fmt.Fprintf(buf, "%v", raw) | |
+ case schema.TypeMap: | |
+ valAsMap := raw.(map[string]interface{}) | |
+ var keys []string | |
+ for k := range valAsMap { | |
+ keys = append(keys, k) | |
+ } | |
+ sort.Strings(keys) | |
+ buf.WriteRune('[') | |
+ if s.DiffSuppressFunc != nil { | |
+ if s.DiffSuppressFunc(path+".%", fmt.Sprintf("%v", len(keys)), "0", nil) { | |
+ buf.WriteRune(']') | |
+ buf.WriteRune(';') | |
+ return | |
+ } | |
+ } | |
+ for _, mk := range keys { | |
+ mapKeyPath := path + "." + mk | |
+ if s.DiffSuppressFunc != nil { | |
+ if s.DiffSuppressFunc(mapKeyPath, fmt.Sprintf("%v", valAsMap[mk]), "", nil) { | |
+ continue | |
+ } | |
+ } | |
+ buf.WriteString(mk) | |
+ buf.WriteRune(':') | |
+ hashPrepValue(buf, s.Elem.(*schema.Schema), path+"."+mk, valAsMap[mk]) | |
+ } | |
+ buf.WriteRune(']') | |
+ case schema.TypeList: | |
+ buf.WriteRune('(') | |
+ rl := raw.([]interface{}) | |
+ if s.DiffSuppressFunc != nil { | |
+ if s.DiffSuppressFunc(path+".#", fmt.Sprintf("%v", len(rl)), "0", nil) { | |
+ buf.WriteRune(')') | |
+ buf.WriteRune(';') | |
+ return | |
+ } | |
+ } | |
+ if rs, ok := s.Elem.(*schema.Resource); ok { | |
+ for idx, raw := range rl { | |
+ hashPrepResource(buf, rs, fmt.Sprintf("%s.%d", path, idx), raw) | |
+ } | |
+ } else { | |
+ for idx, raw := range rl { | |
+ hashPrepValue(buf, s.Elem.(*schema.Schema), fmt.Sprintf("%s.%d", path, idx), raw) | |
+ } | |
+ } | |
+ buf.WriteRune(')') | |
+ case schema.TypeSet: | |
+ buf.WriteRune('{') | |
+ set := raw.(*schema.Set) | |
+ rs := s.Elem.(*schema.Resource) | |
+ // TODO: diff suppress is a must here | |
+ for idx, raw := range set.List() { | |
+ // even though "real" paths have more digits of hashes, | |
+ // this is still good enough for regex replacement | |
+ hashPrepResource(buf, rs, fmt.Sprintf("%s.%d", path, idx), raw) | |
+ } | |
+ buf.WriteRune('}') | |
+ default: | |
+ panic("unknown schema type to serialize") | |
+ } | |
+ buf.WriteRune(';') | |
+} | |
+ | |
+// SchemaSetFunc that respects diff suppressions | |
+func respectiveHashcode(s *schema.Schema, path string) schema.SchemaSetFunc { | |
+ return func(raw interface{}) int { | |
+ var buf bytes.Buffer | |
+ hashPrepResource(&buf, s.Elem.(*schema.Resource), path, raw) | |
+ h := buf.String() | |
+ hashCode := schema.HashString(h) | |
+ log.Printf("[TRACE] respectiveHashcode %d: %s", hashCode, h) | |
+ return hashCode | |
+ } | |
+} | |
+ | |
// typeToSchema converts struct into terraform schema. `path` is used for block suppressions | |
// special path element `"0"` is used to denote either arrays or sets of elements | |
func typeToSchema(v reflect.Value, t reflect.Type, path []string) map[string]*schema.Schema { | |
@@ -185,7 +293,7 @@ func typeToSchema(v reflect.Value, t reflect.Type, path []string) map[string]*sc | |
if strings.Contains(tfTag, "suppress_diff") { | |
// TODO: we may also suppress count diffs on all json:"..,omitempty" (101 occurences) | |
// find . -type f -name '*.go' -not -path "vendor/*" | xargs grep ',omitempty' | grep '*' | |
- blockCount := strings.Join(append(path, "#"), ".") | |
+ blockCount := strings.Join(append(path, fieldName, "#"), ".") | |
scm[fieldName].DiffSuppressFunc = makeEmptyBlockSuppressFunc(blockCount) | |
} | |
scm[fieldName].Elem = &schema.Resource{ | |
@@ -195,6 +303,8 @@ func typeToSchema(v reflect.Value, t reflect.Type, path []string) map[string]*sc | |
ft := schema.TypeList | |
if strings.Contains(tfTag, "slice_set") { | |
ft = schema.TypeSet | |
+ prefix := strings.Join(append(path, fieldName, "0"), ".") | |
+ scm[fieldName].Set = respectiveHashcode(scm[fieldName], prefix) | |
} | |
scm[fieldName].Type = ft | |
elem := typeField.Type.Elem() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment