github.com/cmalfait/terraform@v0.11.12-beta1/helper/schema/serialize.go (about) 1 package schema 2 3 import ( 4 "bytes" 5 "fmt" 6 "sort" 7 "strconv" 8 ) 9 10 func SerializeValueForHash(buf *bytes.Buffer, val interface{}, schema *Schema) { 11 if val == nil { 12 buf.WriteRune(';') 13 return 14 } 15 16 switch schema.Type { 17 case TypeBool: 18 if val.(bool) { 19 buf.WriteRune('1') 20 } else { 21 buf.WriteRune('0') 22 } 23 case TypeInt: 24 buf.WriteString(strconv.Itoa(val.(int))) 25 case TypeFloat: 26 buf.WriteString(strconv.FormatFloat(val.(float64), 'g', -1, 64)) 27 case TypeString: 28 buf.WriteString(val.(string)) 29 case TypeList: 30 buf.WriteRune('(') 31 l := val.([]interface{}) 32 for _, innerVal := range l { 33 serializeCollectionMemberForHash(buf, innerVal, schema.Elem) 34 } 35 buf.WriteRune(')') 36 case TypeMap: 37 38 m := val.(map[string]interface{}) 39 var keys []string 40 for k := range m { 41 keys = append(keys, k) 42 } 43 sort.Strings(keys) 44 buf.WriteRune('[') 45 for _, k := range keys { 46 innerVal := m[k] 47 if innerVal == nil { 48 continue 49 } 50 buf.WriteString(k) 51 buf.WriteRune(':') 52 53 switch innerVal := innerVal.(type) { 54 case int: 55 buf.WriteString(strconv.Itoa(innerVal)) 56 case float64: 57 buf.WriteString(strconv.FormatFloat(innerVal, 'g', -1, 64)) 58 case string: 59 buf.WriteString(innerVal) 60 default: 61 panic(fmt.Sprintf("unknown value type in TypeMap %T", innerVal)) 62 } 63 64 buf.WriteRune(';') 65 } 66 buf.WriteRune(']') 67 case TypeSet: 68 buf.WriteRune('{') 69 s := val.(*Set) 70 for _, innerVal := range s.List() { 71 serializeCollectionMemberForHash(buf, innerVal, schema.Elem) 72 } 73 buf.WriteRune('}') 74 default: 75 panic("unknown schema type to serialize") 76 } 77 buf.WriteRune(';') 78 } 79 80 // SerializeValueForHash appends a serialization of the given resource config 81 // to the given buffer, guaranteeing deterministic results given the same value 82 // and schema. 83 // 84 // Its primary purpose is as input into a hashing function in order 85 // to hash complex substructures when used in sets, and so the serialization 86 // is not reversible. 87 func SerializeResourceForHash(buf *bytes.Buffer, val interface{}, resource *Resource) { 88 if val == nil { 89 return 90 } 91 sm := resource.Schema 92 m := val.(map[string]interface{}) 93 var keys []string 94 for k := range sm { 95 keys = append(keys, k) 96 } 97 sort.Strings(keys) 98 for _, k := range keys { 99 innerSchema := sm[k] 100 // Skip attributes that are not user-provided. Computed attributes 101 // do not contribute to the hash since their ultimate value cannot 102 // be known at plan/diff time. 103 if !(innerSchema.Required || innerSchema.Optional) { 104 continue 105 } 106 107 buf.WriteString(k) 108 buf.WriteRune(':') 109 innerVal := m[k] 110 SerializeValueForHash(buf, innerVal, innerSchema) 111 } 112 } 113 114 func serializeCollectionMemberForHash(buf *bytes.Buffer, val interface{}, elem interface{}) { 115 switch tElem := elem.(type) { 116 case *Schema: 117 SerializeValueForHash(buf, val, tElem) 118 case *Resource: 119 buf.WriteRune('<') 120 SerializeResourceForHash(buf, val, tElem) 121 buf.WriteString(">;") 122 default: 123 panic(fmt.Sprintf("invalid element type: %T", tElem)) 124 } 125 }