github.com/mikesimons/terraform@v0.6.13-0.20160304043642-f11448c69214/config/interpolate.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 "github.com/hashicorp/hil/ast" 9 ) 10 11 // An InterpolatedVariable is a variable reference within an interpolation. 12 // 13 // Implementations of this interface represents various sources where 14 // variables can come from: user variables, resources, etc. 15 type InterpolatedVariable interface { 16 FullKey() string 17 } 18 19 // CountVariable is a variable for referencing information about 20 // the count. 21 type CountVariable struct { 22 Type CountValueType 23 key string 24 } 25 26 // CountValueType is the type of the count variable that is referenced. 27 type CountValueType byte 28 29 const ( 30 CountValueInvalid CountValueType = iota 31 CountValueIndex 32 ) 33 34 // A ModuleVariable is a variable that is referencing the output 35 // of a module, such as "${module.foo.bar}" 36 type ModuleVariable struct { 37 Name string 38 Field string 39 key string 40 } 41 42 // A PathVariable is a variable that references path information about the 43 // module. 44 type PathVariable struct { 45 Type PathValueType 46 key string 47 } 48 49 type PathValueType byte 50 51 const ( 52 PathValueInvalid PathValueType = iota 53 PathValueCwd 54 PathValueModule 55 PathValueRoot 56 ) 57 58 // A ResourceVariable is a variable that is referencing the field 59 // of a resource, such as "${aws_instance.foo.ami}" 60 type ResourceVariable struct { 61 Type string // Resource type, i.e. "aws_instance" 62 Name string // Resource name 63 Field string // Resource field 64 65 Multi bool // True if multi-variable: aws_instance.foo.*.id 66 Index int // Index for multi-variable: aws_instance.foo.1.id == 1 67 68 key string 69 } 70 71 // SelfVariable is a variable that is referencing the same resource 72 // it is running on: "${self.address}" 73 type SelfVariable struct { 74 Field string 75 76 key string 77 } 78 79 // SimpleVariable is an unprefixed variable, which can show up when users have 80 // strings they are passing down to resources that use interpolation 81 // internally. The template_file resource is an example of this. 82 type SimpleVariable struct { 83 Key string 84 } 85 86 // A UserVariable is a variable that is referencing a user variable 87 // that is inputted from outside the configuration. This looks like 88 // "${var.foo}" 89 type UserVariable struct { 90 Name string 91 Elem string 92 93 key string 94 } 95 96 func NewInterpolatedVariable(v string) (InterpolatedVariable, error) { 97 if strings.HasPrefix(v, "count.") { 98 return NewCountVariable(v) 99 } else if strings.HasPrefix(v, "path.") { 100 return NewPathVariable(v) 101 } else if strings.HasPrefix(v, "self.") { 102 return NewSelfVariable(v) 103 } else if strings.HasPrefix(v, "var.") { 104 return NewUserVariable(v) 105 } else if strings.HasPrefix(v, "module.") { 106 return NewModuleVariable(v) 107 } else if !strings.ContainsRune(v, '.') { 108 return NewSimpleVariable(v) 109 } else { 110 return NewResourceVariable(v) 111 } 112 } 113 114 func NewCountVariable(key string) (*CountVariable, error) { 115 var fieldType CountValueType 116 parts := strings.SplitN(key, ".", 2) 117 switch parts[1] { 118 case "index": 119 fieldType = CountValueIndex 120 } 121 122 return &CountVariable{ 123 Type: fieldType, 124 key: key, 125 }, nil 126 } 127 128 func (c *CountVariable) FullKey() string { 129 return c.key 130 } 131 132 func NewModuleVariable(key string) (*ModuleVariable, error) { 133 parts := strings.SplitN(key, ".", 3) 134 if len(parts) < 3 { 135 return nil, fmt.Errorf( 136 "%s: module variables must be three parts: module.name.attr", 137 key) 138 } 139 140 return &ModuleVariable{ 141 Name: parts[1], 142 Field: parts[2], 143 key: key, 144 }, nil 145 } 146 147 func (v *ModuleVariable) FullKey() string { 148 return v.key 149 } 150 151 func NewPathVariable(key string) (*PathVariable, error) { 152 var fieldType PathValueType 153 parts := strings.SplitN(key, ".", 2) 154 switch parts[1] { 155 case "cwd": 156 fieldType = PathValueCwd 157 case "module": 158 fieldType = PathValueModule 159 case "root": 160 fieldType = PathValueRoot 161 } 162 163 return &PathVariable{ 164 Type: fieldType, 165 key: key, 166 }, nil 167 } 168 169 func (v *PathVariable) FullKey() string { 170 return v.key 171 } 172 173 func NewResourceVariable(key string) (*ResourceVariable, error) { 174 parts := strings.SplitN(key, ".", 3) 175 if len(parts) < 3 { 176 return nil, fmt.Errorf( 177 "%s: resource variables must be three parts: type.name.attr", 178 key) 179 } 180 181 field := parts[2] 182 multi := false 183 var index int 184 185 if idx := strings.Index(field, "."); idx != -1 { 186 indexStr := field[:idx] 187 multi = indexStr == "*" 188 index = -1 189 190 if !multi { 191 indexInt, err := strconv.ParseInt(indexStr, 0, 0) 192 if err == nil { 193 multi = true 194 index = int(indexInt) 195 } 196 } 197 198 if multi { 199 field = field[idx+1:] 200 } 201 } 202 203 return &ResourceVariable{ 204 Type: parts[0], 205 Name: parts[1], 206 Field: field, 207 Multi: multi, 208 Index: index, 209 key: key, 210 }, nil 211 } 212 213 func (v *ResourceVariable) ResourceId() string { 214 return fmt.Sprintf("%s.%s", v.Type, v.Name) 215 } 216 217 func (v *ResourceVariable) FullKey() string { 218 return v.key 219 } 220 221 func NewSelfVariable(key string) (*SelfVariable, error) { 222 field := key[len("self."):] 223 224 return &SelfVariable{ 225 Field: field, 226 227 key: key, 228 }, nil 229 } 230 231 func (v *SelfVariable) FullKey() string { 232 return v.key 233 } 234 235 func (v *SelfVariable) GoString() string { 236 return fmt.Sprintf("*%#v", *v) 237 } 238 239 func NewSimpleVariable(key string) (*SimpleVariable, error) { 240 return &SimpleVariable{key}, nil 241 } 242 243 func (v *SimpleVariable) FullKey() string { 244 return v.Key 245 } 246 247 func (v *SimpleVariable) GoString() string { 248 return fmt.Sprintf("*%#v", *v) 249 } 250 251 func NewUserVariable(key string) (*UserVariable, error) { 252 name := key[len("var."):] 253 elem := "" 254 if idx := strings.Index(name, "."); idx > -1 { 255 elem = name[idx+1:] 256 name = name[:idx] 257 } 258 259 return &UserVariable{ 260 key: key, 261 262 Name: name, 263 Elem: elem, 264 }, nil 265 } 266 267 func (v *UserVariable) FullKey() string { 268 return v.key 269 } 270 271 func (v *UserVariable) GoString() string { 272 return fmt.Sprintf("*%#v", *v) 273 } 274 275 // DetectVariables takes an AST root and returns all the interpolated 276 // variables that are detected in the AST tree. 277 func DetectVariables(root ast.Node) ([]InterpolatedVariable, error) { 278 var result []InterpolatedVariable 279 var resultErr error 280 281 // Visitor callback 282 fn := func(n ast.Node) ast.Node { 283 if resultErr != nil { 284 return n 285 } 286 287 vn, ok := n.(*ast.VariableAccess) 288 if !ok { 289 return n 290 } 291 292 v, err := NewInterpolatedVariable(vn.Name) 293 if err != nil { 294 resultErr = err 295 return n 296 } 297 298 result = append(result, v) 299 return n 300 } 301 302 // Visitor pattern 303 root.Accept(fn) 304 305 if resultErr != nil { 306 return nil, resultErr 307 } 308 309 return result, nil 310 }