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