github.com/sylr/terraform@v0.11.12-beta1/helper/schema/field_writer_map.go (about)

     1  package schema
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strconv"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/mitchellh/mapstructure"
    11  )
    12  
    13  // MapFieldWriter writes data into a single map[string]string structure.
    14  type MapFieldWriter struct {
    15  	Schema map[string]*Schema
    16  
    17  	lock   sync.Mutex
    18  	result map[string]string
    19  }
    20  
    21  // Map returns the underlying map that is being written to.
    22  func (w *MapFieldWriter) Map() map[string]string {
    23  	w.lock.Lock()
    24  	defer w.lock.Unlock()
    25  	if w.result == nil {
    26  		w.result = make(map[string]string)
    27  	}
    28  
    29  	return w.result
    30  }
    31  
    32  func (w *MapFieldWriter) unsafeWriteField(addr string, value string) {
    33  	w.lock.Lock()
    34  	defer w.lock.Unlock()
    35  	if w.result == nil {
    36  		w.result = make(map[string]string)
    37  	}
    38  
    39  	w.result[addr] = value
    40  }
    41  
    42  // clearTree clears a field and any sub-fields of the given address out of the
    43  // map. This should be used to reset some kind of complex structures (namely
    44  // sets) before writing to make sure that any conflicting data is removed (for
    45  // example, if the set was previously written to the writer's layer).
    46  func (w *MapFieldWriter) clearTree(addr []string) {
    47  	prefix := strings.Join(addr, ".") + "."
    48  	for k := range w.result {
    49  		if strings.HasPrefix(k, prefix) {
    50  			delete(w.result, k)
    51  		}
    52  	}
    53  }
    54  
    55  func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error {
    56  	w.lock.Lock()
    57  	defer w.lock.Unlock()
    58  	if w.result == nil {
    59  		w.result = make(map[string]string)
    60  	}
    61  
    62  	schemaList := addrToSchema(addr, w.Schema)
    63  	if len(schemaList) == 0 {
    64  		return fmt.Errorf("Invalid address to set: %#v", addr)
    65  	}
    66  
    67  	// If we're setting anything other than a list root or set root,
    68  	// then disallow it.
    69  	for _, schema := range schemaList[:len(schemaList)-1] {
    70  		if schema.Type == TypeList {
    71  			return fmt.Errorf(
    72  				"%s: can only set full list",
    73  				strings.Join(addr, "."))
    74  		}
    75  
    76  		if schema.Type == TypeMap {
    77  			return fmt.Errorf(
    78  				"%s: can only set full map",
    79  				strings.Join(addr, "."))
    80  		}
    81  
    82  		if schema.Type == TypeSet {
    83  			return fmt.Errorf(
    84  				"%s: can only set full set",
    85  				strings.Join(addr, "."))
    86  		}
    87  	}
    88  
    89  	return w.set(addr, value)
    90  }
    91  
    92  func (w *MapFieldWriter) set(addr []string, value interface{}) error {
    93  	schemaList := addrToSchema(addr, w.Schema)
    94  	if len(schemaList) == 0 {
    95  		return fmt.Errorf("Invalid address to set: %#v", addr)
    96  	}
    97  
    98  	schema := schemaList[len(schemaList)-1]
    99  	switch schema.Type {
   100  	case TypeBool, TypeInt, TypeFloat, TypeString:
   101  		return w.setPrimitive(addr, value, schema)
   102  	case TypeList:
   103  		return w.setList(addr, value, schema)
   104  	case TypeMap:
   105  		return w.setMap(addr, value, schema)
   106  	case TypeSet:
   107  		return w.setSet(addr, value, schema)
   108  	case typeObject:
   109  		return w.setObject(addr, value, schema)
   110  	default:
   111  		panic(fmt.Sprintf("Unknown type: %#v", schema.Type))
   112  	}
   113  }
   114  
   115  func (w *MapFieldWriter) setList(
   116  	addr []string,
   117  	v interface{},
   118  	schema *Schema) error {
   119  	k := strings.Join(addr, ".")
   120  	setElement := func(idx string, value interface{}) error {
   121  		addrCopy := make([]string, len(addr), len(addr)+1)
   122  		copy(addrCopy, addr)
   123  		return w.set(append(addrCopy, idx), value)
   124  	}
   125  
   126  	var vs []interface{}
   127  	if err := mapstructure.Decode(v, &vs); err != nil {
   128  		return fmt.Errorf("%s: %s", k, err)
   129  	}
   130  
   131  	// Wipe the set from the current writer prior to writing if it exists.
   132  	// Multiple writes to the same layer is a lot safer for lists than sets due
   133  	// to the fact that indexes are always deterministic and the length will
   134  	// always be updated with the current length on the last write, but making
   135  	// sure we have a clean namespace removes any chance for edge cases to pop up
   136  	// and ensures that the last write to the set is the correct value.
   137  	w.clearTree(addr)
   138  
   139  	// Set the entire list.
   140  	var err error
   141  	for i, elem := range vs {
   142  		is := strconv.FormatInt(int64(i), 10)
   143  		err = setElement(is, elem)
   144  		if err != nil {
   145  			break
   146  		}
   147  	}
   148  	if err != nil {
   149  		for i, _ := range vs {
   150  			is := strconv.FormatInt(int64(i), 10)
   151  			setElement(is, nil)
   152  		}
   153  
   154  		return err
   155  	}
   156  
   157  	w.result[k+".#"] = strconv.FormatInt(int64(len(vs)), 10)
   158  	return nil
   159  }
   160  
   161  func (w *MapFieldWriter) setMap(
   162  	addr []string,
   163  	value interface{},
   164  	schema *Schema) error {
   165  	k := strings.Join(addr, ".")
   166  	v := reflect.ValueOf(value)
   167  	vs := make(map[string]interface{})
   168  
   169  	if value == nil {
   170  		// The empty string here means the map is removed.
   171  		w.result[k] = ""
   172  		return nil
   173  	}
   174  
   175  	if v.Kind() != reflect.Map {
   176  		return fmt.Errorf("%s: must be a map", k)
   177  	}
   178  	if v.Type().Key().Kind() != reflect.String {
   179  		return fmt.Errorf("%s: keys must strings", k)
   180  	}
   181  	for _, mk := range v.MapKeys() {
   182  		mv := v.MapIndex(mk)
   183  		vs[mk.String()] = mv.Interface()
   184  	}
   185  
   186  	// Wipe this address tree. The contents of the map should always reflect the
   187  	// last write made to it.
   188  	w.clearTree(addr)
   189  
   190  	// Remove the pure key since we're setting the full map value
   191  	delete(w.result, k)
   192  
   193  	// Set each subkey
   194  	addrCopy := make([]string, len(addr), len(addr)+1)
   195  	copy(addrCopy, addr)
   196  	for subKey, v := range vs {
   197  		if err := w.set(append(addrCopy, subKey), v); err != nil {
   198  			return err
   199  		}
   200  	}
   201  
   202  	// Set the count
   203  	w.result[k+".%"] = strconv.Itoa(len(vs))
   204  
   205  	return nil
   206  }
   207  
   208  func (w *MapFieldWriter) setObject(
   209  	addr []string,
   210  	value interface{},
   211  	schema *Schema) error {
   212  	// Set the entire object. First decode into a proper structure
   213  	var v map[string]interface{}
   214  	if err := mapstructure.Decode(value, &v); err != nil {
   215  		return fmt.Errorf("%s: %s", strings.Join(addr, "."), err)
   216  	}
   217  
   218  	// Make space for additional elements in the address
   219  	addrCopy := make([]string, len(addr), len(addr)+1)
   220  	copy(addrCopy, addr)
   221  
   222  	// Set each element in turn
   223  	var err error
   224  	for k1, v1 := range v {
   225  		if err = w.set(append(addrCopy, k1), v1); err != nil {
   226  			break
   227  		}
   228  	}
   229  	if err != nil {
   230  		for k1, _ := range v {
   231  			w.set(append(addrCopy, k1), nil)
   232  		}
   233  	}
   234  
   235  	return err
   236  }
   237  
   238  func (w *MapFieldWriter) setPrimitive(
   239  	addr []string,
   240  	v interface{},
   241  	schema *Schema) error {
   242  	k := strings.Join(addr, ".")
   243  
   244  	if v == nil {
   245  		// The empty string here means the value is removed.
   246  		w.result[k] = ""
   247  		return nil
   248  	}
   249  
   250  	var set string
   251  	switch schema.Type {
   252  	case TypeBool:
   253  		var b bool
   254  		if err := mapstructure.Decode(v, &b); err != nil {
   255  			return fmt.Errorf("%s: %s", k, err)
   256  		}
   257  
   258  		set = strconv.FormatBool(b)
   259  	case TypeString:
   260  		if err := mapstructure.Decode(v, &set); err != nil {
   261  			return fmt.Errorf("%s: %s", k, err)
   262  		}
   263  	case TypeInt:
   264  		var n int
   265  		if err := mapstructure.Decode(v, &n); err != nil {
   266  			return fmt.Errorf("%s: %s", k, err)
   267  		}
   268  		set = strconv.FormatInt(int64(n), 10)
   269  	case TypeFloat:
   270  		var n float64
   271  		if err := mapstructure.Decode(v, &n); err != nil {
   272  			return fmt.Errorf("%s: %s", k, err)
   273  		}
   274  		set = strconv.FormatFloat(float64(n), 'G', -1, 64)
   275  	default:
   276  		return fmt.Errorf("Unknown type: %#v", schema.Type)
   277  	}
   278  
   279  	w.result[k] = set
   280  	return nil
   281  }
   282  
   283  func (w *MapFieldWriter) setSet(
   284  	addr []string,
   285  	value interface{},
   286  	schema *Schema) error {
   287  	addrCopy := make([]string, len(addr), len(addr)+1)
   288  	copy(addrCopy, addr)
   289  	k := strings.Join(addr, ".")
   290  
   291  	if value == nil {
   292  		w.result[k+".#"] = "0"
   293  		return nil
   294  	}
   295  
   296  	// If it is a slice, then we have to turn it into a *Set so that
   297  	// we get the proper order back based on the hash code.
   298  	if v := reflect.ValueOf(value); v.Kind() == reflect.Slice {
   299  		// Build a temp *ResourceData to use for the conversion
   300  		tempSchema := *schema
   301  		tempSchema.Type = TypeList
   302  		tempSchemaMap := map[string]*Schema{addr[0]: &tempSchema}
   303  		tempW := &MapFieldWriter{Schema: tempSchemaMap}
   304  
   305  		// Set the entire list, this lets us get sane values out of it
   306  		if err := tempW.WriteField(addr, value); err != nil {
   307  			return err
   308  		}
   309  
   310  		// Build the set by going over the list items in order and
   311  		// hashing them into the set. The reason we go over the list and
   312  		// not the `value` directly is because this forces all types
   313  		// to become []interface{} (generic) instead of []string, which
   314  		// most hash functions are expecting.
   315  		s := schema.ZeroValue().(*Set)
   316  		tempR := &MapFieldReader{
   317  			Map:    BasicMapReader(tempW.Map()),
   318  			Schema: tempSchemaMap,
   319  		}
   320  		for i := 0; i < v.Len(); i++ {
   321  			is := strconv.FormatInt(int64(i), 10)
   322  			result, err := tempR.ReadField(append(addrCopy, is))
   323  			if err != nil {
   324  				return err
   325  			}
   326  			if !result.Exists {
   327  				panic("set item just set doesn't exist")
   328  			}
   329  
   330  			s.Add(result.Value)
   331  		}
   332  
   333  		value = s
   334  	}
   335  
   336  	// Clear any keys that match the set address first. This is necessary because
   337  	// it's always possible and sometimes may be necessary to write to a certain
   338  	// writer layer more than once with different set data each time, which will
   339  	// lead to different keys being inserted, which can lead to determinism
   340  	// problems when the old data isn't wiped first.
   341  	w.clearTree(addr)
   342  
   343  	for code, elem := range value.(*Set).m {
   344  		if err := w.set(append(addrCopy, code), elem); err != nil {
   345  			return err
   346  		}
   347  	}
   348  
   349  	w.result[k+".#"] = strconv.Itoa(value.(*Set).Len())
   350  	return nil
   351  }