github.com/nevins-b/terraform@v0.3.8-0.20170215184714-bbae22007d5a/config/raw_config.go (about)

     1  package config
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/gob"
     6  	"sync"
     7  
     8  	"github.com/hashicorp/hil"
     9  	"github.com/hashicorp/hil/ast"
    10  	"github.com/mitchellh/copystructure"
    11  	"github.com/mitchellh/reflectwalk"
    12  )
    13  
    14  // UnknownVariableValue is a sentinel value that can be used
    15  // to denote that the value of a variable is unknown at this time.
    16  // RawConfig uses this information to build up data about
    17  // unknown keys.
    18  const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66"
    19  
    20  // RawConfig is a structure that holds a piece of configuration
    21  // where the overall structure is unknown since it will be used
    22  // to configure a plugin or some other similar external component.
    23  //
    24  // RawConfigs can be interpolated with variables that come from
    25  // other resources, user variables, etc.
    26  //
    27  // RawConfig supports a query-like interface to request
    28  // information from deep within the structure.
    29  type RawConfig struct {
    30  	Key            string
    31  	Raw            map[string]interface{}
    32  	Interpolations []ast.Node
    33  	Variables      map[string]InterpolatedVariable
    34  
    35  	lock        sync.Mutex
    36  	config      map[string]interface{}
    37  	unknownKeys []string
    38  }
    39  
    40  // NewRawConfig creates a new RawConfig structure and populates the
    41  // publicly readable struct fields.
    42  func NewRawConfig(raw map[string]interface{}) (*RawConfig, error) {
    43  	result := &RawConfig{Raw: raw}
    44  	if err := result.init(); err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	return result, nil
    49  }
    50  
    51  // RawMap returns a copy of the RawConfig.Raw map.
    52  func (r *RawConfig) RawMap() map[string]interface{} {
    53  	r.lock.Lock()
    54  	defer r.lock.Unlock()
    55  
    56  	m := make(map[string]interface{})
    57  	for k, v := range r.Raw {
    58  		m[k] = v
    59  	}
    60  	return m
    61  }
    62  
    63  // Copy returns a copy of this RawConfig, uninterpolated.
    64  func (r *RawConfig) Copy() *RawConfig {
    65  	if r == nil {
    66  		return nil
    67  	}
    68  
    69  	r.lock.Lock()
    70  	defer r.lock.Unlock()
    71  
    72  	newRaw := make(map[string]interface{})
    73  	for k, v := range r.Raw {
    74  		newRaw[k] = v
    75  	}
    76  
    77  	result, err := NewRawConfig(newRaw)
    78  	if err != nil {
    79  		panic("copy failed: " + err.Error())
    80  	}
    81  
    82  	result.Key = r.Key
    83  	return result
    84  }
    85  
    86  // Value returns the value of the configuration if this configuration
    87  // has a Key set. If this does not have a Key set, nil will be returned.
    88  func (r *RawConfig) Value() interface{} {
    89  	if c := r.Config(); c != nil {
    90  		if v, ok := c[r.Key]; ok {
    91  			return v
    92  		}
    93  	}
    94  
    95  	r.lock.Lock()
    96  	defer r.lock.Unlock()
    97  	return r.Raw[r.Key]
    98  }
    99  
   100  // Config returns the entire configuration with the variables
   101  // interpolated from any call to Interpolate.
   102  //
   103  // If any interpolated variables are unknown (value set to
   104  // UnknownVariableValue), the first non-container (map, slice, etc.) element
   105  // will be removed from the config. The keys of unknown variables
   106  // can be found using the UnknownKeys function.
   107  //
   108  // By pruning out unknown keys from the configuration, the raw
   109  // structure will always successfully decode into its ultimate
   110  // structure using something like mapstructure.
   111  func (r *RawConfig) Config() map[string]interface{} {
   112  	r.lock.Lock()
   113  	defer r.lock.Unlock()
   114  	return r.config
   115  }
   116  
   117  // Interpolate uses the given mapping of variable values and uses
   118  // those as the values to replace any variables in this raw
   119  // configuration.
   120  //
   121  // Any prior calls to Interpolate are replaced with this one.
   122  //
   123  // If a variable key is missing, this will panic.
   124  func (r *RawConfig) Interpolate(vs map[string]ast.Variable) error {
   125  	r.lock.Lock()
   126  	defer r.lock.Unlock()
   127  
   128  	config := langEvalConfig(vs)
   129  	return r.interpolate(func(root ast.Node) (interface{}, error) {
   130  		// None of the variables we need are computed, meaning we should
   131  		// be able to properly evaluate.
   132  		result, err := hil.Eval(root, config)
   133  		if err != nil {
   134  			return "", err
   135  		}
   136  
   137  		return result.Value, nil
   138  	})
   139  }
   140  
   141  // Merge merges another RawConfig into this one (overriding any conflicting
   142  // values in this config) and returns a new config. The original config
   143  // is not modified.
   144  func (r *RawConfig) Merge(other *RawConfig) *RawConfig {
   145  	r.lock.Lock()
   146  	defer r.lock.Unlock()
   147  
   148  	// Merge the raw configurations
   149  	raw := make(map[string]interface{})
   150  	for k, v := range r.Raw {
   151  		raw[k] = v
   152  	}
   153  	for k, v := range other.Raw {
   154  		raw[k] = v
   155  	}
   156  
   157  	// Create the result
   158  	result, err := NewRawConfig(raw)
   159  	if err != nil {
   160  		panic(err)
   161  	}
   162  
   163  	// Merge the interpolated results
   164  	result.config = make(map[string]interface{})
   165  	for k, v := range r.config {
   166  		result.config[k] = v
   167  	}
   168  	for k, v := range other.config {
   169  		result.config[k] = v
   170  	}
   171  
   172  	// Build the unknown keys
   173  	if len(r.unknownKeys) > 0 || len(other.unknownKeys) > 0 {
   174  		unknownKeys := make(map[string]struct{})
   175  		for _, k := range r.unknownKeys {
   176  			unknownKeys[k] = struct{}{}
   177  		}
   178  		for _, k := range other.unknownKeys {
   179  			unknownKeys[k] = struct{}{}
   180  		}
   181  
   182  		result.unknownKeys = make([]string, 0, len(unknownKeys))
   183  		for k, _ := range unknownKeys {
   184  			result.unknownKeys = append(result.unknownKeys, k)
   185  		}
   186  	}
   187  
   188  	return result
   189  }
   190  
   191  func (r *RawConfig) init() error {
   192  	r.lock.Lock()
   193  	defer r.lock.Unlock()
   194  
   195  	r.config = r.Raw
   196  	r.Interpolations = nil
   197  	r.Variables = nil
   198  
   199  	fn := func(node ast.Node) (interface{}, error) {
   200  		r.Interpolations = append(r.Interpolations, node)
   201  		vars, err := DetectVariables(node)
   202  		if err != nil {
   203  			return "", err
   204  		}
   205  
   206  		for _, v := range vars {
   207  			if r.Variables == nil {
   208  				r.Variables = make(map[string]InterpolatedVariable)
   209  			}
   210  
   211  			r.Variables[v.FullKey()] = v
   212  		}
   213  
   214  		return "", nil
   215  	}
   216  
   217  	walker := &interpolationWalker{F: fn}
   218  	if err := reflectwalk.Walk(r.Raw, walker); err != nil {
   219  		return err
   220  	}
   221  
   222  	return nil
   223  }
   224  
   225  func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error {
   226  	config, err := copystructure.Copy(r.Raw)
   227  	if err != nil {
   228  		return err
   229  	}
   230  	r.config = config.(map[string]interface{})
   231  
   232  	w := &interpolationWalker{F: fn, Replace: true}
   233  	err = reflectwalk.Walk(r.config, w)
   234  	if err != nil {
   235  		return err
   236  	}
   237  
   238  	r.unknownKeys = w.unknownKeys
   239  	return nil
   240  }
   241  
   242  func (r *RawConfig) merge(r2 *RawConfig) *RawConfig {
   243  	if r == nil && r2 == nil {
   244  		return nil
   245  	}
   246  
   247  	if r == nil {
   248  		r = &RawConfig{}
   249  	}
   250  
   251  	rawRaw, err := copystructure.Copy(r.Raw)
   252  	if err != nil {
   253  		panic(err)
   254  	}
   255  
   256  	raw := rawRaw.(map[string]interface{})
   257  	if r2 != nil {
   258  		for k, v := range r2.Raw {
   259  			raw[k] = v
   260  		}
   261  	}
   262  
   263  	result, err := NewRawConfig(raw)
   264  	if err != nil {
   265  		panic(err)
   266  	}
   267  
   268  	return result
   269  }
   270  
   271  // UnknownKeys returns the keys of the configuration that are unknown
   272  // because they had interpolated variables that must be computed.
   273  func (r *RawConfig) UnknownKeys() []string {
   274  	r.lock.Lock()
   275  	defer r.lock.Unlock()
   276  	return r.unknownKeys
   277  }
   278  
   279  // See GobEncode
   280  func (r *RawConfig) GobDecode(b []byte) error {
   281  	var data gobRawConfig
   282  	err := gob.NewDecoder(bytes.NewReader(b)).Decode(&data)
   283  	if err != nil {
   284  		return err
   285  	}
   286  
   287  	r.Key = data.Key
   288  	r.Raw = data.Raw
   289  
   290  	return r.init()
   291  }
   292  
   293  // GobEncode is a custom Gob encoder to use so that we only include the
   294  // raw configuration. Interpolated variables and such are lost and the
   295  // tree of interpolated variables is recomputed on decode, since it is
   296  // referentially transparent.
   297  func (r *RawConfig) GobEncode() ([]byte, error) {
   298  	r.lock.Lock()
   299  	defer r.lock.Unlock()
   300  
   301  	data := gobRawConfig{
   302  		Key: r.Key,
   303  		Raw: r.Raw,
   304  	}
   305  
   306  	var buf bytes.Buffer
   307  	if err := gob.NewEncoder(&buf).Encode(data); err != nil {
   308  		return nil, err
   309  	}
   310  
   311  	return buf.Bytes(), nil
   312  }
   313  
   314  type gobRawConfig struct {
   315  	Key string
   316  	Raw map[string]interface{}
   317  }
   318  
   319  // langEvalConfig returns the evaluation configuration we use to execute.
   320  func langEvalConfig(vs map[string]ast.Variable) *hil.EvalConfig {
   321  	funcMap := make(map[string]ast.Function)
   322  	for k, v := range Funcs() {
   323  		funcMap[k] = v
   324  	}
   325  	funcMap["lookup"] = interpolationFuncLookup(vs)
   326  	funcMap["keys"] = interpolationFuncKeys(vs)
   327  	funcMap["values"] = interpolationFuncValues(vs)
   328  
   329  	return &hil.EvalConfig{
   330  		GlobalScope: &ast.BasicScope{
   331  			VarMap:  vs,
   332  			FuncMap: funcMap,
   333  		},
   334  	}
   335  }