Skip to content

Instantly share code, notes, and snippets.

@nfx
Last active October 12, 2021 15:16
Show Gist options
  • Save nfx/620721f593ca42447528598294e8ddc1 to your computer and use it in GitHub Desktop.
Save nfx/620721f593ca42447528598294e8ddc1 to your computer and use it in GitHub Desktop.
Databricks Terraform provider to respect DiffSuppressFuncs for `slice_set` fields
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