github.com/adamar/terraform@v0.2.2-0.20141016210445-2e703afdad0e/config/raw_config.go (about)

     1  package config
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/gob"
     6  
     7  	"github.com/mitchellh/copystructure"
     8  	"github.com/mitchellh/reflectwalk"
     9  )
    10  
    11  // UnknownVariableValue is a sentinel value that can be used
    12  // to denote that the value of a variable is unknown at this time.
    13  // RawConfig uses this information to build up data about
    14  // unknown keys.
    15  const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66"
    16  
    17  // RawConfig is a structure that holds a piece of configuration
    18  // where te overall structure is unknown since it will be used
    19  // to configure a plugin or some other similar external component.
    20  //
    21  // RawConfigs can be interpolated with variables that come from
    22  // other resources, user variables, etc.
    23  //
    24  // RawConfig supports a query-like interface to request
    25  // information from deep within the structure.
    26  type RawConfig struct {
    27  	Key            string
    28  	Raw            map[string]interface{}
    29  	Interpolations []Interpolation
    30  	Variables      map[string]InterpolatedVariable
    31  
    32  	config      map[string]interface{}
    33  	unknownKeys []string
    34  }
    35  
    36  // NewRawConfig creates a new RawConfig structure and populates the
    37  // publicly readable struct fields.
    38  func NewRawConfig(raw map[string]interface{}) (*RawConfig, error) {
    39  	result := &RawConfig{Raw: raw}
    40  	if err := result.init(); err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	return result, nil
    45  }
    46  
    47  // Value returns the value of the configuration if this configuration
    48  // has a Key set. If this does not have a Key set, nil will be returned.
    49  func (r *RawConfig) Value() interface{} {
    50  	if c := r.Config(); c != nil {
    51  		if v, ok := c[r.Key]; ok {
    52  			return v
    53  		}
    54  	}
    55  
    56  	return r.Raw[r.Key]
    57  }
    58  
    59  // Config returns the entire configuration with the variables
    60  // interpolated from any call to Interpolate.
    61  //
    62  // If any interpolated variables are unknown (value set to
    63  // UnknownVariableValue), the first non-container (map, slice, etc.) element
    64  // will be removed from the config. The keys of unknown variables
    65  // can be found using the UnknownKeys function.
    66  //
    67  // By pruning out unknown keys from the configuration, the raw
    68  // structure will always successfully decode into its ultimate
    69  // structure using something like mapstructure.
    70  func (r *RawConfig) Config() map[string]interface{} {
    71  	return r.config
    72  }
    73  
    74  // Interpolate uses the given mapping of variable values and uses
    75  // those as the values to replace any variables in this raw
    76  // configuration.
    77  //
    78  // Any prior calls to Interpolate are replaced with this one.
    79  //
    80  // If a variable key is missing, this will panic.
    81  func (r *RawConfig) Interpolate(vs map[string]string) error {
    82  	return r.interpolate(func(i Interpolation) (string, error) {
    83  		return i.Interpolate(vs)
    84  	})
    85  }
    86  
    87  func (r *RawConfig) init() error {
    88  	r.config = r.Raw
    89  	r.Interpolations = nil
    90  	r.Variables = nil
    91  
    92  	fn := func(i Interpolation) (string, error) {
    93  		r.Interpolations = append(r.Interpolations, i)
    94  
    95  		for k, v := range i.Variables() {
    96  			if r.Variables == nil {
    97  				r.Variables = make(map[string]InterpolatedVariable)
    98  			}
    99  
   100  			r.Variables[k] = v
   101  		}
   102  
   103  		return "", nil
   104  	}
   105  
   106  	walker := &interpolationWalker{F: fn}
   107  	if err := reflectwalk.Walk(r.Raw, walker); err != nil {
   108  		return err
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error {
   115  	config, err := copystructure.Copy(r.Raw)
   116  	if err != nil {
   117  		return err
   118  	}
   119  	r.config = config.(map[string]interface{})
   120  
   121  	w := &interpolationWalker{F: fn, Replace: true}
   122  	err = reflectwalk.Walk(r.config, w)
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	r.unknownKeys = w.unknownKeys
   128  	return nil
   129  }
   130  
   131  func (r *RawConfig) merge(r2 *RawConfig) *RawConfig {
   132  	rawRaw, err := copystructure.Copy(r.Raw)
   133  	if err != nil {
   134  		panic(err)
   135  	}
   136  
   137  	raw := rawRaw.(map[string]interface{})
   138  	for k, v := range r2.Raw {
   139  		raw[k] = v
   140  	}
   141  
   142  	result, err := NewRawConfig(raw)
   143  	if err != nil {
   144  		panic(err)
   145  	}
   146  
   147  	return result
   148  }
   149  
   150  // UnknownKeys returns the keys of the configuration that are unknown
   151  // because they had interpolated variables that must be computed.
   152  func (r *RawConfig) UnknownKeys() []string {
   153  	return r.unknownKeys
   154  }
   155  
   156  // See GobEncode
   157  func (r *RawConfig) GobDecode(b []byte) error {
   158  	var data gobRawConfig
   159  	err := gob.NewDecoder(bytes.NewReader(b)).Decode(&data)
   160  	if err != nil {
   161  		return err
   162  	}
   163  
   164  	r.Key = data.Key
   165  	r.Raw = data.Raw
   166  
   167  	return r.init()
   168  }
   169  
   170  // GobEncode is a custom Gob encoder to use so that we only include the
   171  // raw configuration. Interpolated variables and such are lost and the
   172  // tree of interpolated variables is recomputed on decode, since it is
   173  // referentially transparent.
   174  func (r *RawConfig) GobEncode() ([]byte, error) {
   175  	data := gobRawConfig{
   176  		Key: r.Key,
   177  		Raw: r.Raw,
   178  	}
   179  
   180  	var buf bytes.Buffer
   181  	if err := gob.NewEncoder(&buf).Encode(data); err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	return buf.Bytes(), nil
   186  }
   187  
   188  type gobRawConfig struct {
   189  	Key string
   190  	Raw map[string]interface{}
   191  }