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