github.com/ns1/terraform@v0.7.10-0.20161109153551-8949419bef40/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 // We detect the variables again and check if the value of any 131 // of the variables is the computed value. If it is, then we 132 // treat this entire value as computed. 133 // 134 // We have to do this here before the `lang.Eval` because 135 // if any of the variables it depends on are computed, then 136 // the interpolation can fail at runtime for other reasons. Example: 137 // `${count.index+1}`: in a world where `count.index` is computed, 138 // this would fail a type check since the computed placeholder is 139 // a string, but realistically the whole value is just computed. 140 vars, err := DetectVariables(root) 141 if err != nil { 142 return "", err 143 } 144 for _, v := range vars { 145 varVal, ok := vs[v.FullKey()] 146 if ok && varVal.Value == UnknownVariableValue { 147 return UnknownVariableValue, nil 148 } 149 } 150 151 // None of the variables we need are computed, meaning we should 152 // be able to properly evaluate. 153 result, err := hil.Eval(root, config) 154 if err != nil { 155 return "", err 156 } 157 158 return result.Value, nil 159 }) 160 } 161 162 // Merge merges another RawConfig into this one (overriding any conflicting 163 // values in this config) and returns a new config. The original config 164 // is not modified. 165 func (r *RawConfig) Merge(other *RawConfig) *RawConfig { 166 r.lock.Lock() 167 defer r.lock.Unlock() 168 169 // Merge the raw configurations 170 raw := make(map[string]interface{}) 171 for k, v := range r.Raw { 172 raw[k] = v 173 } 174 for k, v := range other.Raw { 175 raw[k] = v 176 } 177 178 // Create the result 179 result, err := NewRawConfig(raw) 180 if err != nil { 181 panic(err) 182 } 183 184 // Merge the interpolated results 185 result.config = make(map[string]interface{}) 186 for k, v := range r.config { 187 result.config[k] = v 188 } 189 for k, v := range other.config { 190 result.config[k] = v 191 } 192 193 // Build the unknown keys 194 if len(r.unknownKeys) > 0 || len(other.unknownKeys) > 0 { 195 unknownKeys := make(map[string]struct{}) 196 for _, k := range r.unknownKeys { 197 unknownKeys[k] = struct{}{} 198 } 199 for _, k := range other.unknownKeys { 200 unknownKeys[k] = struct{}{} 201 } 202 203 result.unknownKeys = make([]string, 0, len(unknownKeys)) 204 for k, _ := range unknownKeys { 205 result.unknownKeys = append(result.unknownKeys, k) 206 } 207 } 208 209 return result 210 } 211 212 func (r *RawConfig) init() error { 213 r.lock.Lock() 214 defer r.lock.Unlock() 215 216 r.config = r.Raw 217 r.Interpolations = nil 218 r.Variables = nil 219 220 fn := func(node ast.Node) (interface{}, error) { 221 r.Interpolations = append(r.Interpolations, node) 222 vars, err := DetectVariables(node) 223 if err != nil { 224 return "", err 225 } 226 227 for _, v := range vars { 228 if r.Variables == nil { 229 r.Variables = make(map[string]InterpolatedVariable) 230 } 231 232 r.Variables[v.FullKey()] = v 233 } 234 235 return "", nil 236 } 237 238 walker := &interpolationWalker{F: fn} 239 if err := reflectwalk.Walk(r.Raw, walker); err != nil { 240 return err 241 } 242 243 return nil 244 } 245 246 func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error { 247 config, err := copystructure.Copy(r.Raw) 248 if err != nil { 249 return err 250 } 251 r.config = config.(map[string]interface{}) 252 253 w := &interpolationWalker{F: fn, Replace: true} 254 err = reflectwalk.Walk(r.config, w) 255 if err != nil { 256 return err 257 } 258 259 r.unknownKeys = w.unknownKeys 260 return nil 261 } 262 263 func (r *RawConfig) merge(r2 *RawConfig) *RawConfig { 264 rawRaw, err := copystructure.Copy(r.Raw) 265 if err != nil { 266 panic(err) 267 } 268 269 raw := rawRaw.(map[string]interface{}) 270 for k, v := range r2.Raw { 271 raw[k] = v 272 } 273 274 result, err := NewRawConfig(raw) 275 if err != nil { 276 panic(err) 277 } 278 279 return result 280 } 281 282 // UnknownKeys returns the keys of the configuration that are unknown 283 // because they had interpolated variables that must be computed. 284 func (r *RawConfig) UnknownKeys() []string { 285 r.lock.Lock() 286 defer r.lock.Unlock() 287 return r.unknownKeys 288 } 289 290 // See GobEncode 291 func (r *RawConfig) GobDecode(b []byte) error { 292 var data gobRawConfig 293 err := gob.NewDecoder(bytes.NewReader(b)).Decode(&data) 294 if err != nil { 295 return err 296 } 297 298 r.Key = data.Key 299 r.Raw = data.Raw 300 301 return r.init() 302 } 303 304 // GobEncode is a custom Gob encoder to use so that we only include the 305 // raw configuration. Interpolated variables and such are lost and the 306 // tree of interpolated variables is recomputed on decode, since it is 307 // referentially transparent. 308 func (r *RawConfig) GobEncode() ([]byte, error) { 309 r.lock.Lock() 310 defer r.lock.Unlock() 311 312 data := gobRawConfig{ 313 Key: r.Key, 314 Raw: r.Raw, 315 } 316 317 var buf bytes.Buffer 318 if err := gob.NewEncoder(&buf).Encode(data); err != nil { 319 return nil, err 320 } 321 322 return buf.Bytes(), nil 323 } 324 325 type gobRawConfig struct { 326 Key string 327 Raw map[string]interface{} 328 } 329 330 // langEvalConfig returns the evaluation configuration we use to execute. 331 func langEvalConfig(vs map[string]ast.Variable) *hil.EvalConfig { 332 funcMap := make(map[string]ast.Function) 333 for k, v := range Funcs() { 334 funcMap[k] = v 335 } 336 funcMap["lookup"] = interpolationFuncLookup(vs) 337 funcMap["keys"] = interpolationFuncKeys(vs) 338 funcMap["values"] = interpolationFuncValues(vs) 339 340 return &hil.EvalConfig{ 341 GlobalScope: &ast.BasicScope{ 342 VarMap: vs, 343 FuncMap: funcMap, 344 }, 345 } 346 }