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