github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/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 sm := resource.Schema 89 m := val.(map[string]interface{}) 90 var keys []string 91 for k := range sm { 92 keys = append(keys, k) 93 } 94 sort.Strings(keys) 95 for _, k := range keys { 96 innerSchema := sm[k] 97 // Skip attributes that are not user-provided. Computed attributes 98 // do not contribute to the hash since their ultimate value cannot 99 // be known at plan/diff time. 100 if !(innerSchema.Required || innerSchema.Optional) { 101 continue 102 } 103 104 buf.WriteString(k) 105 buf.WriteRune(':') 106 innerVal := m[k] 107 SerializeValueForHash(buf, innerVal, innerSchema) 108 } 109 } 110 111 func serializeCollectionMemberForHash(buf *bytes.Buffer, val interface{}, elem interface{}) { 112 switch tElem := elem.(type) { 113 case *Schema: 114 SerializeValueForHash(buf, val, tElem) 115 case *Resource: 116 buf.WriteRune('<') 117 SerializeResourceForHash(buf, val, tElem) 118 buf.WriteString(">;") 119 default: 120 panic(fmt.Sprintf("invalid element type: %T", tElem)) 121 } 122 }