github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/config/interpolate_walk.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 8 "github.com/hashicorp/terraform/config/lang" 9 "github.com/hashicorp/terraform/config/lang/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 := lang.Parse(v.String()) 117 if err != nil { 118 return err 119 } 120 121 // If the AST we got is just a literal string value, then we ignore it 122 if _, ok := astRoot.(*ast.LiteralNode); ok { 123 return nil 124 } 125 126 if w.ContextF != nil { 127 w.ContextF(w.loc, astRoot) 128 } 129 130 if w.F == nil { 131 return nil 132 } 133 134 replaceVal, err := w.F(astRoot) 135 if err != nil { 136 return fmt.Errorf( 137 "%s in:\n\n%s", 138 err, v.String()) 139 } 140 141 if w.Replace { 142 // We need to determine if we need to remove this element 143 // if the result contains any "UnknownVariableValue" which is 144 // set if it is computed. This behavior is different if we're 145 // splitting (in a SliceElem) or not. 146 remove := false 147 if w.loc == reflectwalk.SliceElem && IsStringList(replaceVal) { 148 parts := StringList(replaceVal).Slice() 149 for _, p := range parts { 150 if p == UnknownVariableValue { 151 remove = true 152 break 153 } 154 } 155 } else if replaceVal == UnknownVariableValue { 156 remove = true 157 } 158 if remove { 159 w.removeCurrent() 160 return nil 161 } 162 163 resultVal := reflect.ValueOf(replaceVal) 164 switch w.loc { 165 case reflectwalk.MapKey: 166 m := w.cs[len(w.cs)-1] 167 168 // Delete the old value 169 var zero reflect.Value 170 m.SetMapIndex(w.csData.(reflect.Value), zero) 171 172 // Set the new key with the existing value 173 m.SetMapIndex(resultVal, w.lastValue) 174 175 // Set the key to be the new key 176 w.csData = resultVal 177 case reflectwalk.MapValue: 178 // If we're in a map, then the only way to set a map value is 179 // to set it directly. 180 m := w.cs[len(w.cs)-1] 181 mk := w.csData.(reflect.Value) 182 m.SetMapIndex(mk, resultVal) 183 default: 184 // Otherwise, we should be addressable 185 setV.Set(resultVal) 186 } 187 } 188 189 return nil 190 } 191 192 func (w *interpolationWalker) removeCurrent() { 193 // Append the key to the unknown keys 194 w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, ".")) 195 196 for i := 1; i <= len(w.cs); i++ { 197 c := w.cs[len(w.cs)-i] 198 switch c.Kind() { 199 case reflect.Map: 200 // Zero value so that we delete the map key 201 var val reflect.Value 202 203 // Get the key and delete it 204 k := w.csData.(reflect.Value) 205 c.SetMapIndex(k, val) 206 return 207 } 208 } 209 210 panic("No container found for removeCurrent") 211 } 212 213 func (w *interpolationWalker) replaceCurrent(v reflect.Value) { 214 c := w.cs[len(w.cs)-2] 215 switch c.Kind() { 216 case reflect.Map: 217 // Get the key and delete it 218 k := w.csKey[len(w.csKey)-1] 219 c.SetMapIndex(k, v) 220 } 221 } 222 223 func (w *interpolationWalker) splitSlice() { 224 // Get the []interface{} slice so we can do some operations on 225 // it without dealing with reflection. We'll document each step 226 // here to be clear. 227 var s []interface{} 228 raw := w.cs[len(w.cs)-1] 229 switch v := raw.Interface().(type) { 230 case []interface{}: 231 s = v 232 case []map[string]interface{}: 233 return 234 default: 235 panic("Unknown kind: " + raw.Kind().String()) 236 } 237 238 // Check if we have any elements that we need to split. If not, then 239 // just return since we're done. 240 split := false 241 for _, v := range s { 242 sv, ok := v.(string) 243 if !ok { 244 continue 245 } 246 if IsStringList(sv) { 247 split = true 248 break 249 } 250 } 251 if !split { 252 return 253 } 254 255 // Make a new result slice that is twice the capacity to fit our growth. 256 result := make([]interface{}, 0, len(s)*2) 257 258 // Go over each element of the original slice and start building up 259 // the resulting slice by splitting where we have to. 260 for _, v := range s { 261 sv, ok := v.(string) 262 if !ok { 263 // Not a string, so just set it 264 result = append(result, v) 265 continue 266 } 267 268 if IsStringList(sv) { 269 for _, p := range StringList(sv).Slice() { 270 result = append(result, p) 271 } 272 continue 273 } 274 275 // Not a string list, so just set it 276 result = append(result, sv) 277 } 278 279 // Our slice is now done, we have to replace the slice now 280 // with this new one that we have. 281 w.replaceCurrent(reflect.ValueOf(result)) 282 }