github.com/ns1/terraform@v0.7.10-0.20161109153551-8949419bef40/config/interpolate_walk.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 8 "github.com/hashicorp/hil" 9 "github.com/hashicorp/hil/ast" 10 "github.com/mitchellh/reflectwalk" 11 ) 12 13 // interpolationWalker implements interfaces for the reflectwalk package 14 // (github.com/mitchellh/reflectwalk) that can be used to automatically 15 // execute a callback for an interpolation. 16 type interpolationWalker struct { 17 // F is the function to call for every interpolation. It can be nil. 18 // 19 // If Replace is true, then the return value of F will be used to 20 // replace the interpolation. 21 F interpolationWalkerFunc 22 Replace bool 23 24 // ContextF is an advanced version of F that also receives the 25 // location of where it is in the structure. This lets you do 26 // context-aware validation. 27 ContextF interpolationWalkerContextFunc 28 29 key []string 30 lastValue reflect.Value 31 loc reflectwalk.Location 32 cs []reflect.Value 33 csKey []reflect.Value 34 csData interface{} 35 sliceIndex int 36 unknownKeys []string 37 } 38 39 // interpolationWalkerFunc is the callback called by interpolationWalk. 40 // It is called with any interpolation found. It should return a value 41 // to replace the interpolation with, along with any errors. 42 // 43 // If Replace is set to false in interpolationWalker, then the replace 44 // value can be anything as it will have no effect. 45 type interpolationWalkerFunc func(ast.Node) (interface{}, error) 46 47 // interpolationWalkerContextFunc is called by interpolationWalk if 48 // ContextF is set. This receives both the interpolation and the location 49 // where the interpolation is. 50 // 51 // This callback can be used to validate the location of the interpolation 52 // within the configuration. 53 type interpolationWalkerContextFunc func(reflectwalk.Location, ast.Node) 54 55 func (w *interpolationWalker) Enter(loc reflectwalk.Location) error { 56 w.loc = loc 57 if loc == reflectwalk.WalkLoc { 58 w.sliceIndex = -1 59 } 60 return nil 61 } 62 63 func (w *interpolationWalker) Exit(loc reflectwalk.Location) error { 64 w.loc = reflectwalk.None 65 66 switch loc { 67 case reflectwalk.Map: 68 w.cs = w.cs[:len(w.cs)-1] 69 case reflectwalk.MapValue: 70 w.key = w.key[:len(w.key)-1] 71 w.csKey = w.csKey[:len(w.csKey)-1] 72 case reflectwalk.Slice: 73 // Split any values that need to be split 74 w.splitSlice() 75 w.cs = w.cs[:len(w.cs)-1] 76 case reflectwalk.SliceElem: 77 w.csKey = w.csKey[:len(w.csKey)-1] 78 w.sliceIndex = -1 79 } 80 81 return nil 82 } 83 84 func (w *interpolationWalker) Map(m reflect.Value) error { 85 w.cs = append(w.cs, m) 86 return nil 87 } 88 89 func (w *interpolationWalker) MapElem(m, k, v reflect.Value) error { 90 w.csData = k 91 w.csKey = append(w.csKey, k) 92 93 if w.sliceIndex != -1 { 94 w.key = append(w.key, fmt.Sprintf("%d.%s", w.sliceIndex, k.String())) 95 } else { 96 w.key = append(w.key, k.String()) 97 } 98 99 w.lastValue = v 100 return nil 101 } 102 103 func (w *interpolationWalker) Slice(s reflect.Value) error { 104 w.cs = append(w.cs, s) 105 return nil 106 } 107 108 func (w *interpolationWalker) SliceElem(i int, elem reflect.Value) error { 109 w.csKey = append(w.csKey, reflect.ValueOf(i)) 110 w.sliceIndex = i 111 return nil 112 } 113 114 func (w *interpolationWalker) Primitive(v reflect.Value) error { 115 setV := v 116 117 // We only care about strings 118 if v.Kind() == reflect.Interface { 119 setV = v 120 v = v.Elem() 121 } 122 if v.Kind() != reflect.String { 123 return nil 124 } 125 126 astRoot, err := hil.Parse(v.String()) 127 if err != nil { 128 return err 129 } 130 131 // If the AST we got is just a literal string value with the same 132 // value then we ignore it. We have to check if its the same value 133 // because it is possible to input a string, get out a string, and 134 // have it be different. For example: "foo-$${bar}" turns into 135 // "foo-${bar}" 136 if n, ok := astRoot.(*ast.LiteralNode); ok { 137 if s, ok := n.Value.(string); ok && s == v.String() { 138 return nil 139 } 140 } 141 142 if w.ContextF != nil { 143 w.ContextF(w.loc, astRoot) 144 } 145 146 if w.F == nil { 147 return nil 148 } 149 150 replaceVal, err := w.F(astRoot) 151 if err != nil { 152 return fmt.Errorf( 153 "%s in:\n\n%s", 154 err, v.String()) 155 } 156 157 if w.Replace { 158 // We need to determine if we need to remove this element 159 // if the result contains any "UnknownVariableValue" which is 160 // set if it is computed. This behavior is different if we're 161 // splitting (in a SliceElem) or not. 162 remove := false 163 if w.loc == reflectwalk.SliceElem { 164 switch typedReplaceVal := replaceVal.(type) { 165 case string: 166 if typedReplaceVal == UnknownVariableValue { 167 remove = true 168 } 169 case []interface{}: 170 if hasUnknownValue(typedReplaceVal) { 171 remove = true 172 } 173 } 174 } else if replaceVal == UnknownVariableValue { 175 remove = true 176 } 177 178 if remove { 179 w.removeCurrent() 180 return nil 181 } 182 183 resultVal := reflect.ValueOf(replaceVal) 184 switch w.loc { 185 case reflectwalk.MapKey: 186 m := w.cs[len(w.cs)-1] 187 188 // Delete the old value 189 var zero reflect.Value 190 m.SetMapIndex(w.csData.(reflect.Value), zero) 191 192 // Set the new key with the existing value 193 m.SetMapIndex(resultVal, w.lastValue) 194 195 // Set the key to be the new key 196 w.csData = resultVal 197 case reflectwalk.MapValue: 198 // If we're in a map, then the only way to set a map value is 199 // to set it directly. 200 m := w.cs[len(w.cs)-1] 201 mk := w.csData.(reflect.Value) 202 m.SetMapIndex(mk, resultVal) 203 default: 204 // Otherwise, we should be addressable 205 setV.Set(resultVal) 206 } 207 } 208 209 return nil 210 } 211 212 func (w *interpolationWalker) removeCurrent() { 213 // Append the key to the unknown keys 214 w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, ".")) 215 216 for i := 1; i <= len(w.cs); i++ { 217 c := w.cs[len(w.cs)-i] 218 switch c.Kind() { 219 case reflect.Map: 220 // Zero value so that we delete the map key 221 var val reflect.Value 222 223 // Get the key and delete it 224 k := w.csData.(reflect.Value) 225 c.SetMapIndex(k, val) 226 return 227 } 228 } 229 230 panic("No container found for removeCurrent") 231 } 232 233 func (w *interpolationWalker) replaceCurrent(v reflect.Value) { 234 c := w.cs[len(w.cs)-2] 235 switch c.Kind() { 236 case reflect.Map: 237 // Get the key and delete it 238 k := w.csKey[len(w.csKey)-1] 239 c.SetMapIndex(k, v) 240 } 241 } 242 243 func hasUnknownValue(variable []interface{}) bool { 244 for _, value := range variable { 245 if strVal, ok := value.(string); ok { 246 if strVal == UnknownVariableValue { 247 return true 248 } 249 } 250 } 251 return false 252 } 253 254 func (w *interpolationWalker) splitSlice() { 255 raw := w.cs[len(w.cs)-1] 256 257 var s []interface{} 258 switch v := raw.Interface().(type) { 259 case []interface{}: 260 s = v 261 case []map[string]interface{}: 262 return 263 } 264 265 split := false 266 for _, val := range s { 267 if varVal, ok := val.(ast.Variable); ok && varVal.Type == ast.TypeList { 268 split = true 269 } 270 if _, ok := val.([]interface{}); ok { 271 split = true 272 } 273 } 274 275 if !split { 276 return 277 } 278 279 result := make([]interface{}, 0) 280 for _, v := range s { 281 switch val := v.(type) { 282 case ast.Variable: 283 switch val.Type { 284 case ast.TypeList: 285 elements := val.Value.([]ast.Variable) 286 for _, element := range elements { 287 result = append(result, element.Value) 288 } 289 default: 290 result = append(result, val.Value) 291 } 292 case []interface{}: 293 for _, element := range val { 294 result = append(result, element) 295 } 296 default: 297 result = append(result, v) 298 } 299 } 300 301 w.replaceCurrent(reflect.ValueOf(result)) 302 }