github.com/mingfang/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  }