github.com/markdia/terraform@v0.5.1-0.20150508012022-f1ae920aa970/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 // InterpSplitDelim is the delimeter that is looked for to split when 14 // it is returned. This is a comma right now but should eventually become 15 // a value that a user is very unlikely to use (such as UUID). 16 const InterpSplitDelim = `B780FFEC-B661-4EB8-9236-A01737AD98B6` 17 18 // interpolationWalker implements interfaces for the reflectwalk package 19 // (github.com/mitchellh/reflectwalk) that can be used to automatically 20 // execute a callback for an interpolation. 21 type interpolationWalker struct { 22 // F is the function to call for every interpolation. It can be nil. 23 // 24 // If Replace is true, then the return value of F will be used to 25 // replace the interpolation. 26 F interpolationWalkerFunc 27 Replace bool 28 29 // ContextF is an advanced version of F that also receives the 30 // location of where it is in the structure. This lets you do 31 // context-aware validation. 32 ContextF interpolationWalkerContextFunc 33 34 key []string 35 lastValue reflect.Value 36 loc reflectwalk.Location 37 cs []reflect.Value 38 csKey []reflect.Value 39 csData interface{} 40 sliceIndex int 41 unknownKeys []string 42 } 43 44 // interpolationWalkerFunc is the callback called by interpolationWalk. 45 // It is called with any interpolation found. It should return a value 46 // to replace the interpolation with, along with any errors. 47 // 48 // If Replace is set to false in interpolationWalker, then the replace 49 // value can be anything as it will have no effect. 50 type interpolationWalkerFunc func(ast.Node) (string, error) 51 52 // interpolationWalkerContextFunc is called by interpolationWalk if 53 // ContextF is set. This receives both the interpolation and the location 54 // where the interpolation is. 55 // 56 // This callback can be used to validate the location of the interpolation 57 // within the configuration. 58 type interpolationWalkerContextFunc func(reflectwalk.Location, ast.Node) 59 60 func (w *interpolationWalker) Enter(loc reflectwalk.Location) error { 61 w.loc = loc 62 return nil 63 } 64 65 func (w *interpolationWalker) Exit(loc reflectwalk.Location) error { 66 w.loc = reflectwalk.None 67 68 switch loc { 69 case reflectwalk.Map: 70 w.cs = w.cs[:len(w.cs)-1] 71 case reflectwalk.MapValue: 72 w.key = w.key[:len(w.key)-1] 73 w.csKey = w.csKey[:len(w.csKey)-1] 74 case reflectwalk.Slice: 75 // Split any values that need to be split 76 w.splitSlice() 77 w.cs = w.cs[:len(w.cs)-1] 78 case reflectwalk.SliceElem: 79 w.csKey = w.csKey[:len(w.csKey)-1] 80 } 81 82 return nil 83 } 84 85 func (w *interpolationWalker) Map(m reflect.Value) error { 86 w.cs = append(w.cs, m) 87 return nil 88 } 89 90 func (w *interpolationWalker) MapElem(m, k, v reflect.Value) error { 91 w.csData = k 92 w.csKey = append(w.csKey, k) 93 w.key = append(w.key, k.String()) 94 w.lastValue = v 95 return nil 96 } 97 98 func (w *interpolationWalker) Slice(s reflect.Value) error { 99 w.cs = append(w.cs, s) 100 return nil 101 } 102 103 func (w *interpolationWalker) SliceElem(i int, elem reflect.Value) error { 104 w.csKey = append(w.csKey, reflect.ValueOf(i)) 105 w.sliceIndex = i 106 return nil 107 } 108 109 func (w *interpolationWalker) Primitive(v reflect.Value) error { 110 setV := v 111 112 // We only care about strings 113 if v.Kind() == reflect.Interface { 114 setV = v 115 v = v.Elem() 116 } 117 if v.Kind() != reflect.String { 118 return nil 119 } 120 121 astRoot, err := lang.Parse(v.String()) 122 if err != nil { 123 return err 124 } 125 126 // If the AST we got is just a literal string value, then we ignore it 127 if _, ok := astRoot.(*ast.LiteralNode); ok { 128 return nil 129 } 130 131 if w.ContextF != nil { 132 w.ContextF(w.loc, astRoot) 133 } 134 135 if w.F == nil { 136 return nil 137 } 138 139 replaceVal, err := w.F(astRoot) 140 if err != nil { 141 return fmt.Errorf( 142 "%s in:\n\n%s", 143 err, v.String()) 144 } 145 146 if w.Replace { 147 // We need to determine if we need to remove this element 148 // if the result contains any "UnknownVariableValue" which is 149 // set if it is computed. This behavior is different if we're 150 // splitting (in a SliceElem) or not. 151 remove := false 152 if w.loc == reflectwalk.SliceElem { 153 parts := strings.Split(replaceVal, InterpSplitDelim) 154 for _, p := range parts { 155 if p == UnknownVariableValue { 156 remove = true 157 break 158 } 159 } 160 } else if replaceVal == UnknownVariableValue { 161 remove = true 162 } 163 if remove { 164 w.removeCurrent() 165 return nil 166 } 167 168 resultVal := reflect.ValueOf(replaceVal) 169 switch w.loc { 170 case reflectwalk.MapKey: 171 m := w.cs[len(w.cs)-1] 172 173 // Delete the old value 174 var zero reflect.Value 175 m.SetMapIndex(w.csData.(reflect.Value), zero) 176 177 // Set the new key with the existing value 178 m.SetMapIndex(resultVal, w.lastValue) 179 180 // Set the key to be the new key 181 w.csData = resultVal 182 case reflectwalk.MapValue: 183 // If we're in a map, then the only way to set a map value is 184 // to set it directly. 185 m := w.cs[len(w.cs)-1] 186 mk := w.csData.(reflect.Value) 187 m.SetMapIndex(mk, resultVal) 188 default: 189 // Otherwise, we should be addressable 190 setV.Set(resultVal) 191 } 192 } 193 194 return nil 195 } 196 197 func (w *interpolationWalker) removeCurrent() { 198 // Append the key to the unknown keys 199 w.unknownKeys = append(w.unknownKeys, strings.Join(w.key, ".")) 200 201 for i := 1; i <= len(w.cs); i++ { 202 c := w.cs[len(w.cs)-i] 203 switch c.Kind() { 204 case reflect.Map: 205 // Zero value so that we delete the map key 206 var val reflect.Value 207 208 // Get the key and delete it 209 k := w.csData.(reflect.Value) 210 c.SetMapIndex(k, val) 211 return 212 } 213 } 214 215 panic("No container found for removeCurrent") 216 } 217 218 func (w *interpolationWalker) replaceCurrent(v reflect.Value) { 219 c := w.cs[len(w.cs)-2] 220 switch c.Kind() { 221 case reflect.Map: 222 // Get the key and delete it 223 k := w.csKey[len(w.csKey)-1] 224 c.SetMapIndex(k, v) 225 } 226 } 227 228 func (w *interpolationWalker) splitSlice() { 229 // Get the []interface{} slice so we can do some operations on 230 // it without dealing with reflection. We'll document each step 231 // here to be clear. 232 var s []interface{} 233 raw := w.cs[len(w.cs)-1] 234 switch v := raw.Interface().(type) { 235 case []interface{}: 236 s = v 237 case []map[string]interface{}: 238 return 239 default: 240 panic("Unknown kind: " + raw.Kind().String()) 241 } 242 243 // Check if we have any elements that we need to split. If not, then 244 // just return since we're done. 245 split := false 246 for _, v := range s { 247 sv, ok := v.(string) 248 if !ok { 249 continue 250 } 251 if idx := strings.Index(sv, InterpSplitDelim); idx >= 0 { 252 split = true 253 break 254 } 255 } 256 if !split { 257 return 258 } 259 260 // Make a new result slice that is twice the capacity to fit our growth. 261 result := make([]interface{}, 0, len(s)*2) 262 263 // Go over each element of the original slice and start building up 264 // the resulting slice by splitting where we have to. 265 for _, v := range s { 266 sv, ok := v.(string) 267 if !ok { 268 // Not a string, so just set it 269 result = append(result, v) 270 continue 271 } 272 273 // Split on the delimiter 274 for _, p := range strings.Split(sv, InterpSplitDelim) { 275 result = append(result, p) 276 } 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 }