github.com/odise/terraform@v0.6.9-0.20160401223921-f7d1e0390da7/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) (string, 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 return nil 58 } 59 60 func (w *interpolationWalker) Exit(loc reflectwalk.Location) error { 61 w.loc = reflectwalk.None 62 63 switch loc { 64 case reflectwalk.Map: 65 w.cs = w.cs[:len(w.cs)-1] 66 case reflectwalk.MapValue: 67 w.key = w.key[:len(w.key)-1] 68 w.csKey = w.csKey[:len(w.csKey)-1] 69 case reflectwalk.Slice: 70 // Split any values that need to be split 71 w.splitSlice() 72 w.cs = w.cs[:len(w.cs)-1] 73 case reflectwalk.SliceElem: 74 w.csKey = w.csKey[:len(w.csKey)-1] 75 } 76 77 return nil 78 } 79 80 func (w *interpolationWalker) Map(m reflect.Value) error { 81 w.cs = append(w.cs, m) 82 return nil 83 } 84 85 func (w *interpolationWalker) MapElem(m, k, v reflect.Value) error { 86 w.csData = k 87 w.csKey = append(w.csKey, k) 88 w.key = append(w.key, k.String()) 89 w.lastValue = v 90 return nil 91 } 92 93 func (w *interpolationWalker) Slice(s reflect.Value) error { 94 w.cs = append(w.cs, s) 95 return nil 96 } 97 98 func (w *interpolationWalker) SliceElem(i int, elem reflect.Value) error { 99 w.csKey = append(w.csKey, reflect.ValueOf(i)) 100 w.sliceIndex = i 101 return nil 102 } 103 104 func (w *interpolationWalker) Primitive(v reflect.Value) error { 105 setV := v 106 107 // We only care about strings 108 if v.Kind() == reflect.Interface { 109 setV = v 110 v = v.Elem() 111 } 112 if v.Kind() != reflect.String { 113 return nil 114 } 115 116 astRoot, err := hil.Parse(v.String()) 117 if err != nil { 118 return err 119 } 120 121 // If the AST we got is just a literal string value with the same 122 // value then we ignore it. We have to check if its the same value 123 // because it is possible to input a string, get out a string, and 124 // have it be different. For example: "foo-$${bar}" turns into 125 // "foo-${bar}" 126 if n, ok := astRoot.(*ast.LiteralNode); ok { 127 if s, ok := n.Value.(string); ok && s == v.String() { 128 return nil 129 } 130 } 131 132 if w.ContextF != nil { 133 w.ContextF(w.loc, astRoot) 134 } 135 136 if w.F == nil { 137 return nil 138 } 139 140 replaceVal, err := w.F(astRoot) 141 if err != nil { 142 return fmt.Errorf( 143 "%s in:\n\n%s", 144 err, v.String()) 145 } 146 147 if w.Replace { 148 // We need to determine if we need to remove this element 149 // if the result contains any "UnknownVariableValue" which is 150 // set if it is computed. This behavior is different if we're 151 // splitting (in a SliceElem) or not. 152 remove := false 153 if w.loc == reflectwalk.SliceElem && IsStringList(replaceVal) { 154 parts := StringList(replaceVal).Slice() 155 for _, p := range parts { 156 if p == UnknownVariableValue { 157 remove = true 158 break 159 } 160 } 161 } else if replaceVal == UnknownVariableValue { 162 remove = true 163 } 164 if remove { 165 w.removeCurrent() 166 return nil 167 } 168 169 resultVal := reflect.ValueOf(replaceVal) 170 switch w.loc { 171 case reflectwalk.MapKey: 172 m := w.cs[len(w.cs)-1] 173 174 // Delete the old value 175 var zero reflect.Value 176 m.SetMapIndex(w.csData.(reflect.Value), zero) 177 178 // Set the new key with the existing value 179 m.SetMapIndex(resultVal, w.lastValue) 180 181 // Set the key to be the new key 182 w.csData = resultVal 183 case reflectwalk.MapValue: 184 // If we're in a map, then the only way to set a map value is 185 // to set it directly. 186 m := w.cs[len(w.cs)-1] 187 mk := w.csData.(reflect.Value) 188 m.SetMapIndex(mk, resultVal) 189 default: 190 // Otherwise, we should be addressable 191 setV.Set(resultVal) 192 } 193 } 194 195 return nil 196 } 197 198 func (w *interpolationWalker) removeCurrent() { 199 // Append the key to the unknown keys 200 w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, ".")) 201 202 for i := 1; i <= len(w.cs); i++ { 203 c := w.cs[len(w.cs)-i] 204 switch c.Kind() { 205 case reflect.Map: 206 // Zero value so that we delete the map key 207 var val reflect.Value 208 209 // Get the key and delete it 210 k := w.csData.(reflect.Value) 211 c.SetMapIndex(k, val) 212 return 213 } 214 } 215 216 panic("No container found for removeCurrent") 217 } 218 219 func (w *interpolationWalker) replaceCurrent(v reflect.Value) { 220 c := w.cs[len(w.cs)-2] 221 switch c.Kind() { 222 case reflect.Map: 223 // Get the key and delete it 224 k := w.csKey[len(w.csKey)-1] 225 c.SetMapIndex(k, v) 226 } 227 } 228 229 func (w *interpolationWalker) splitSlice() { 230 // Get the []interface{} slice so we can do some operations on 231 // it without dealing with reflection. We'll document each step 232 // here to be clear. 233 var s []interface{} 234 raw := w.cs[len(w.cs)-1] 235 switch v := raw.Interface().(type) { 236 case []interface{}: 237 s = v 238 case []map[string]interface{}: 239 return 240 default: 241 panic("Unknown kind: " + raw.Kind().String()) 242 } 243 244 // Check if we have any elements that we need to split. If not, then 245 // just return since we're done. 246 split := false 247 for _, v := range s { 248 sv, ok := v.(string) 249 if !ok { 250 continue 251 } 252 if IsStringList(sv) { 253 split = true 254 break 255 } 256 } 257 if !split { 258 return 259 } 260 261 // Make a new result slice that is twice the capacity to fit our growth. 262 result := make([]interface{}, 0, len(s)*2) 263 264 // Go over each element of the original slice and start building up 265 // the resulting slice by splitting where we have to. 266 for _, v := range s { 267 sv, ok := v.(string) 268 if !ok { 269 // Not a string, so just set it 270 result = append(result, v) 271 continue 272 } 273 274 if IsStringList(sv) { 275 for _, p := range StringList(sv).Slice() { 276 result = append(result, p) 277 } 278 continue 279 } 280 281 // Not a string list, so just set it 282 result = append(result, sv) 283 } 284 285 // Our slice is now done, we have to replace the slice now 286 // with this new one that we have. 287 w.replaceCurrent(reflect.ValueOf(result)) 288 }