github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/config/interpolate.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "regexp" 6 "strconv" 7 "strings" 8 ) 9 10 // We really need to replace this with a real parser. 11 var funcRegexp *regexp.Regexp = regexp.MustCompile( 12 `(?i)([a-z0-9_]+)\(\s*(?:([.a-z0-9_]+)\s*,\s*)*([.a-z0-9_]+)\s*\)`) 13 14 // Interpolation is something that can be contained in a "${}" in a 15 // configuration value. 16 // 17 // Interpolations might be simple variable references, or it might be 18 // function calls, or even nested function calls. 19 type Interpolation interface { 20 Interpolate(map[string]string) (string, error) 21 Variables() map[string]InterpolatedVariable 22 } 23 24 // InterpolationFunc is the function signature for implementing 25 // callable functions in Terraform configurations. 26 type InterpolationFunc func(map[string]string, ...string) (string, error) 27 28 // An InterpolatedVariable is a variable reference within an interpolation. 29 // 30 // Implementations of this interface represents various sources where 31 // variables can come from: user variables, resources, etc. 32 type InterpolatedVariable interface { 33 FullKey() string 34 } 35 36 // FunctionInterpolation is an Interpolation that executes a function 37 // with some variable number of arguments to generate a value. 38 type FunctionInterpolation struct { 39 Func InterpolationFunc 40 Args []Interpolation 41 } 42 43 // LiteralInterpolation implements Interpolation for literals. Ex: 44 // ${"foo"} will equal "foo". 45 type LiteralInterpolation struct { 46 Literal string 47 } 48 49 // VariableInterpolation implements Interpolation for simple variable 50 // interpolation. Ex: "${var.foo}" or "${aws_instance.foo.bar}" 51 type VariableInterpolation struct { 52 Variable InterpolatedVariable 53 } 54 55 // CountVariable is a variable for referencing information about 56 // the count. 57 type CountVariable struct { 58 Type CountValueType 59 key string 60 } 61 62 // CountValueType is the type of the count variable that is referenced. 63 type CountValueType byte 64 65 const ( 66 CountValueInvalid CountValueType = iota 67 CountValueIndex 68 ) 69 70 // A ModuleVariable is a variable that is referencing the output 71 // of a module, such as "${module.foo.bar}" 72 type ModuleVariable struct { 73 Name string 74 Field string 75 key string 76 } 77 78 // A PathVariable is a variable that references path information about the 79 // module. 80 type PathVariable struct { 81 Type PathValueType 82 key string 83 } 84 85 type PathValueType byte 86 87 const ( 88 PathValueInvalid PathValueType = iota 89 PathValueCwd 90 PathValueModule 91 PathValueRoot 92 ) 93 94 // A ResourceVariable is a variable that is referencing the field 95 // of a resource, such as "${aws_instance.foo.ami}" 96 type ResourceVariable struct { 97 Type string // Resource type, i.e. "aws_instance" 98 Name string // Resource name 99 Field string // Resource field 100 101 Multi bool // True if multi-variable: aws_instance.foo.*.id 102 Index int // Index for multi-variable: aws_instance.foo.1.id == 1 103 104 key string 105 } 106 107 // A UserVariable is a variable that is referencing a user variable 108 // that is inputted from outside the configuration. This looks like 109 // "${var.foo}" 110 type UserVariable struct { 111 Name string 112 Elem string 113 114 key string 115 } 116 117 func NewInterpolatedVariable(v string) (InterpolatedVariable, error) { 118 if strings.HasPrefix(v, "count.") { 119 return NewCountVariable(v) 120 } else if strings.HasPrefix(v, "path.") { 121 return NewPathVariable(v) 122 } else if strings.HasPrefix(v, "var.") { 123 return NewUserVariable(v) 124 } else if strings.HasPrefix(v, "module.") { 125 return NewModuleVariable(v) 126 } else { 127 return NewResourceVariable(v) 128 } 129 } 130 131 func (i *FunctionInterpolation) Interpolate( 132 vs map[string]string) (string, error) { 133 args := make([]string, len(i.Args)) 134 for idx, a := range i.Args { 135 v, err := a.Interpolate(vs) 136 if err != nil { 137 return "", err 138 } 139 140 args[idx] = v 141 } 142 143 return i.Func(vs, args...) 144 } 145 146 func (i *FunctionInterpolation) GoString() string { 147 return fmt.Sprintf("*%#v", *i) 148 } 149 150 func (i *FunctionInterpolation) Variables() map[string]InterpolatedVariable { 151 result := make(map[string]InterpolatedVariable) 152 for _, a := range i.Args { 153 for k, v := range a.Variables() { 154 result[k] = v 155 } 156 } 157 158 return result 159 } 160 161 func (i *LiteralInterpolation) Interpolate( 162 map[string]string) (string, error) { 163 return i.Literal, nil 164 } 165 166 func (i *LiteralInterpolation) Variables() map[string]InterpolatedVariable { 167 return nil 168 } 169 170 func (i *VariableInterpolation) Interpolate( 171 vs map[string]string) (string, error) { 172 v, ok := vs[i.Variable.FullKey()] 173 if !ok { 174 return "", fmt.Errorf( 175 "%s: value for variable not found", 176 i.Variable.FullKey()) 177 } 178 179 return v, nil 180 } 181 182 func (i *VariableInterpolation) GoString() string { 183 return fmt.Sprintf("*%#v", *i) 184 } 185 186 func (i *VariableInterpolation) Variables() map[string]InterpolatedVariable { 187 return map[string]InterpolatedVariable{i.Variable.FullKey(): i.Variable} 188 } 189 190 func NewCountVariable(key string) (*CountVariable, error) { 191 var fieldType CountValueType 192 parts := strings.SplitN(key, ".", 2) 193 switch parts[1] { 194 case "index": 195 fieldType = CountValueIndex 196 } 197 198 return &CountVariable{ 199 Type: fieldType, 200 key: key, 201 }, nil 202 } 203 204 func (c *CountVariable) FullKey() string { 205 return c.key 206 } 207 208 func NewModuleVariable(key string) (*ModuleVariable, error) { 209 parts := strings.SplitN(key, ".", 3) 210 if len(parts) < 3 { 211 return nil, fmt.Errorf( 212 "%s: module variables must be three parts: module.name.attr", 213 key) 214 } 215 216 return &ModuleVariable{ 217 Name: parts[1], 218 Field: parts[2], 219 key: key, 220 }, nil 221 } 222 223 func (v *ModuleVariable) FullKey() string { 224 return v.key 225 } 226 227 func NewPathVariable(key string) (*PathVariable, error) { 228 var fieldType PathValueType 229 parts := strings.SplitN(key, ".", 2) 230 switch parts[1] { 231 case "cwd": 232 fieldType = PathValueCwd 233 case "module": 234 fieldType = PathValueModule 235 case "root": 236 fieldType = PathValueRoot 237 } 238 239 return &PathVariable{ 240 Type: fieldType, 241 key: key, 242 }, nil 243 } 244 245 func (v *PathVariable) FullKey() string { 246 return v.key 247 } 248 249 func NewResourceVariable(key string) (*ResourceVariable, error) { 250 parts := strings.SplitN(key, ".", 3) 251 if len(parts) < 3 { 252 return nil, fmt.Errorf( 253 "%s: resource variables must be three parts: type.name.attr", 254 key) 255 } 256 257 field := parts[2] 258 multi := false 259 var index int 260 261 if idx := strings.Index(field, "."); idx != -1 { 262 indexStr := field[:idx] 263 multi = indexStr == "*" 264 index = -1 265 266 if !multi { 267 indexInt, err := strconv.ParseInt(indexStr, 0, 0) 268 if err == nil { 269 multi = true 270 index = int(indexInt) 271 } 272 } 273 274 if multi { 275 field = field[idx+1:] 276 } 277 } 278 279 return &ResourceVariable{ 280 Type: parts[0], 281 Name: parts[1], 282 Field: field, 283 Multi: multi, 284 Index: index, 285 key: key, 286 }, nil 287 } 288 289 func (v *ResourceVariable) ResourceId() string { 290 return fmt.Sprintf("%s.%s", v.Type, v.Name) 291 } 292 293 func (v *ResourceVariable) FullKey() string { 294 return v.key 295 } 296 297 func NewUserVariable(key string) (*UserVariable, error) { 298 name := key[len("var."):] 299 elem := "" 300 if idx := strings.Index(name, "."); idx > -1 { 301 elem = name[idx+1:] 302 name = name[:idx] 303 } 304 305 return &UserVariable{ 306 key: key, 307 308 Name: name, 309 Elem: elem, 310 }, nil 311 } 312 313 func (v *UserVariable) FullKey() string { 314 return v.key 315 } 316 317 func (v *UserVariable) GoString() string { 318 return fmt.Sprintf("*%#v", *v) 319 }