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