github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/config/raw_config.go (about)

     1  package config
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/gob"
     6  	"sync"
     7  
     8  	"github.com/hashicorp/terraform/config/lang"
     9  	"github.com/hashicorp/terraform/config/lang/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 te 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  // Copy returns a copy of this RawConfig, uninterpolated.
    52  func (r *RawConfig) Copy() *RawConfig {
    53  	r.lock.Lock()
    54  	defer r.lock.Unlock()
    55  
    56  	result, err := NewRawConfig(r.Raw)
    57  	if err != nil {
    58  		panic("copy failed: " + err.Error())
    59  	}
    60  
    61  	result.Key = r.Key
    62  	return result
    63  }
    64  
    65  // Value returns the value of the configuration if this configuration
    66  // has a Key set. If this does not have a Key set, nil will be returned.
    67  func (r *RawConfig) Value() interface{} {
    68  	if c := r.Config(); c != nil {
    69  		if v, ok := c[r.Key]; ok {
    70  			return v
    71  		}
    72  	}
    73  
    74  	r.lock.Lock()
    75  	defer r.lock.Unlock()
    76  	return r.Raw[r.Key]
    77  }
    78  
    79  // Config returns the entire configuration with the variables
    80  // interpolated from any call to Interpolate.
    81  //
    82  // If any interpolated variables are unknown (value set to
    83  // UnknownVariableValue), the first non-container (map, slice, etc.) element
    84  // will be removed from the config. The keys of unknown variables
    85  // can be found using the UnknownKeys function.
    86  //
    87  // By pruning out unknown keys from the configuration, the raw
    88  // structure will always successfully decode into its ultimate
    89  // structure using something like mapstructure.
    90  func (r *RawConfig) Config() map[string]interface{} {
    91  	return r.config
    92  }
    93  
    94  // Interpolate uses the given mapping of variable values and uses
    95  // those as the values to replace any variables in this raw
    96  // configuration.
    97  //
    98  // Any prior calls to Interpolate are replaced with this one.
    99  //
   100  // If a variable key is missing, this will panic.
   101  func (r *RawConfig) Interpolate(vs map[string]ast.Variable) error {
   102  	r.lock.Lock()
   103  	defer r.lock.Unlock()
   104  
   105  	config := langEvalConfig(vs)
   106  	return r.interpolate(func(root ast.Node) (string, error) {
   107  		// We detect the variables again and check if the value of any
   108  		// of the variables is the computed value. If it is, then we
   109  		// treat this entire value as computed.
   110  		//
   111  		// We have to do this here before the `lang.Eval` because
   112  		// if any of the variables it depends on are computed, then
   113  		// the interpolation can fail at runtime for other reasons. Example:
   114  		// `${count.index+1}`: in a world where `count.index` is computed,
   115  		// this would fail a type check since the computed placeholder is
   116  		// a string, but realistically the whole value is just computed.
   117  		vars, err := DetectVariables(root)
   118  		if err != nil {
   119  			return "", err
   120  		}
   121  		for _, v := range vars {
   122  			varVal, ok := vs[v.FullKey()]
   123  			if ok && varVal.Value == UnknownVariableValue {
   124  				return UnknownVariableValue, nil
   125  			}
   126  		}
   127  
   128  		// None of the variables we need are computed, meaning we should
   129  		// be able to properly evaluate.
   130  		out, _, err := lang.Eval(root, config)
   131  		if err != nil {
   132  			return "", err
   133  		}
   134  
   135  		return out.(string), nil
   136  	})
   137  }
   138  
   139  // Merge merges another RawConfig into this one (overriding any conflicting
   140  // values in this config) and returns a new config. The original config
   141  // is not modified.
   142  func (r *RawConfig) Merge(other *RawConfig) *RawConfig {
   143  	r.lock.Lock()
   144  	defer r.lock.Unlock()
   145  
   146  	// Merge the raw configurations
   147  	raw := make(map[string]interface{})
   148  	for k, v := range r.Raw {
   149  		raw[k] = v
   150  	}
   151  	for k, v := range other.Raw {
   152  		raw[k] = v
   153  	}
   154  
   155  	// Create the result
   156  	result, err := NewRawConfig(raw)
   157  	if err != nil {
   158  		panic(err)
   159  	}
   160  
   161  	// Merge the interpolated results
   162  	result.config = make(map[string]interface{})
   163  	for k, v := range r.config {
   164  		result.config[k] = v
   165  	}
   166  	for k, v := range other.config {
   167  		result.config[k] = v
   168  	}
   169  
   170  	// Build the unknown keys
   171  	unknownKeys := make(map[string]struct{})
   172  	for _, k := range r.unknownKeys {
   173  		unknownKeys[k] = struct{}{}
   174  	}
   175  	for _, k := range other.unknownKeys {
   176  		unknownKeys[k] = struct{}{}
   177  	}
   178  
   179  	result.unknownKeys = make([]string, 0, len(unknownKeys))
   180  	for k, _ := range unknownKeys {
   181  		result.unknownKeys = append(result.unknownKeys, k)
   182  	}
   183  
   184  	return result
   185  }
   186  
   187  func (r *RawConfig) init() error {
   188  	r.config = r.Raw
   189  	r.Interpolations = nil
   190  	r.Variables = nil
   191  
   192  	fn := func(node ast.Node) (string, error) {
   193  		r.Interpolations = append(r.Interpolations, node)
   194  		vars, err := DetectVariables(node)
   195  		if err != nil {
   196  			return "", err
   197  		}
   198  
   199  		for _, v := range vars {
   200  			if r.Variables == nil {
   201  				r.Variables = make(map[string]InterpolatedVariable)
   202  			}
   203  
   204  			r.Variables[v.FullKey()] = v
   205  		}
   206  
   207  		return "", nil
   208  	}
   209  
   210  	walker := &interpolationWalker{F: fn}
   211  	if err := reflectwalk.Walk(r.Raw, walker); err != nil {
   212  		return err
   213  	}
   214  
   215  	return nil
   216  }
   217  
   218  func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error {
   219  	config, err := copystructure.Copy(r.Raw)
   220  	if err != nil {
   221  		return err
   222  	}
   223  	r.config = config.(map[string]interface{})
   224  
   225  	w := &interpolationWalker{F: fn, Replace: true}
   226  	err = reflectwalk.Walk(r.config, w)
   227  	if err != nil {
   228  		return err
   229  	}
   230  
   231  	r.unknownKeys = w.unknownKeys
   232  	return nil
   233  }
   234  
   235  func (r *RawConfig) merge(r2 *RawConfig) *RawConfig {
   236  	rawRaw, err := copystructure.Copy(r.Raw)
   237  	if err != nil {
   238  		panic(err)
   239  	}
   240  
   241  	raw := rawRaw.(map[string]interface{})
   242  	for k, v := range r2.Raw {
   243  		raw[k] = v
   244  	}
   245  
   246  	result, err := NewRawConfig(raw)
   247  	if err != nil {
   248  		panic(err)
   249  	}
   250  
   251  	return result
   252  }
   253  
   254  // UnknownKeys returns the keys of the configuration that are unknown
   255  // because they had interpolated variables that must be computed.
   256  func (r *RawConfig) UnknownKeys() []string {
   257  	return r.unknownKeys
   258  }
   259  
   260  // See GobEncode
   261  func (r *RawConfig) GobDecode(b []byte) error {
   262  	var data gobRawConfig
   263  	err := gob.NewDecoder(bytes.NewReader(b)).Decode(&data)
   264  	if err != nil {
   265  		return err
   266  	}
   267  
   268  	r.Key = data.Key
   269  	r.Raw = data.Raw
   270  
   271  	return r.init()
   272  }
   273  
   274  // GobEncode is a custom Gob encoder to use so that we only include the
   275  // raw configuration. Interpolated variables and such are lost and the
   276  // tree of interpolated variables is recomputed on decode, since it is
   277  // referentially transparent.
   278  func (r *RawConfig) GobEncode() ([]byte, error) {
   279  	r.lock.Lock()
   280  	defer r.lock.Unlock()
   281  
   282  	data := gobRawConfig{
   283  		Key: r.Key,
   284  		Raw: r.Raw,
   285  	}
   286  
   287  	var buf bytes.Buffer
   288  	if err := gob.NewEncoder(&buf).Encode(data); err != nil {
   289  		return nil, err
   290  	}
   291  
   292  	return buf.Bytes(), nil
   293  }
   294  
   295  type gobRawConfig struct {
   296  	Key string
   297  	Raw map[string]interface{}
   298  }
   299  
   300  // langEvalConfig returns the evaluation configuration we use to execute.
   301  func langEvalConfig(vs map[string]ast.Variable) *lang.EvalConfig {
   302  	funcMap := make(map[string]ast.Function)
   303  	for k, v := range Funcs {
   304  		funcMap[k] = v
   305  	}
   306  	funcMap["lookup"] = interpolationFuncLookup(vs)
   307  	funcMap["keys"] = interpolationFuncKeys(vs)
   308  	funcMap["values"] = interpolationFuncValues(vs)
   309  
   310  	return &lang.EvalConfig{
   311  		GlobalScope: &ast.BasicScope{
   312  			VarMap:  vs,
   313  			FuncMap: funcMap,
   314  		},
   315  	}
   316  }