aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/helper/schema/serialize.go
blob: fe6d7504c74820babce819826bb5f05f4915689a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package schema

import (
	"bytes"
	"fmt"
	"sort"
	"strconv"
)

func SerializeValueForHash(buf *bytes.Buffer, val interface{}, schema *Schema) {
	if val == nil {
		buf.WriteRune(';')
		return
	}

	switch schema.Type {
	case TypeBool:
		if val.(bool) {
			buf.WriteRune('1')
		} else {
			buf.WriteRune('0')
		}
	case TypeInt:
		buf.WriteString(strconv.Itoa(val.(int)))
	case TypeFloat:
		buf.WriteString(strconv.FormatFloat(val.(float64), 'g', -1, 64))
	case TypeString:
		buf.WriteString(val.(string))
	case TypeList:
		buf.WriteRune('(')
		l := val.([]interface{})
		for _, innerVal := range l {
			serializeCollectionMemberForHash(buf, innerVal, schema.Elem)
		}
		buf.WriteRune(')')
	case TypeMap:

		m := val.(map[string]interface{})
		var keys []string
		for k := range m {
			keys = append(keys, k)
		}
		sort.Strings(keys)
		buf.WriteRune('[')
		for _, k := range keys {
			innerVal := m[k]
			if innerVal == nil {
				continue
			}
			buf.WriteString(k)
			buf.WriteRune(':')

			switch innerVal := innerVal.(type) {
			case int:
				buf.WriteString(strconv.Itoa(innerVal))
			case float64:
				buf.WriteString(strconv.FormatFloat(innerVal, 'g', -1, 64))
			case string:
				buf.WriteString(innerVal)
			default:
				panic(fmt.Sprintf("unknown value type in TypeMap %T", innerVal))
			}

			buf.WriteRune(';')
		}
		buf.WriteRune(']')
	case TypeSet:
		buf.WriteRune('{')
		s := val.(*Set)
		for _, innerVal := range s.List() {
			serializeCollectionMemberForHash(buf, innerVal, schema.Elem)
		}
		buf.WriteRune('}')
	default:
		panic("unknown schema type to serialize")
	}
	buf.WriteRune(';')
}

// SerializeValueForHash appends a serialization of the given resource config
// to the given buffer, guaranteeing deterministic results given the same value
// and schema.
//
// Its primary purpose is as input into a hashing function in order
// to hash complex substructures when used in sets, and so the serialization
// is not reversible.
func SerializeResourceForHash(buf *bytes.Buffer, val interface{}, resource *Resource) {
	if val == nil {
		return
	}
	sm := resource.Schema
	m := val.(map[string]interface{})
	var keys []string
	for k := range sm {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		innerSchema := sm[k]
		// Skip attributes that are not user-provided. Computed attributes
		// do not contribute to the hash since their ultimate value cannot
		// be known at plan/diff time.
		if !(innerSchema.Required || innerSchema.Optional) {
			continue
		}

		buf.WriteString(k)
		buf.WriteRune(':')
		innerVal := m[k]
		SerializeValueForHash(buf, innerVal, innerSchema)
	}
}

func serializeCollectionMemberForHash(buf *bytes.Buffer, val interface{}, elem interface{}) {
	switch tElem := elem.(type) {
	case *Schema:
		SerializeValueForHash(buf, val, tElem)
	case *Resource:
		buf.WriteRune('<')
		SerializeResourceForHash(buf, val, tElem)
		buf.WriteString(">;")
	default:
		panic(fmt.Sprintf("invalid element type: %T", tElem))
	}
}