github.com/ndarilek/terraform@v0.3.8-0.20150320140257-d3135c1b2bac/config/raw_config.go (about)

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