github.com/armen/terraform@v0.5.2-0.20150529052519-caa8117a08f1/config/interpolate.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 "github.com/hashicorp/terraform/config/lang/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 // A UserVariable is a variable that is referencing a user variable 80 // that is inputted from outside the configuration. This looks like 81 // "${var.foo}" 82 type UserVariable struct { 83 Name string 84 Elem string 85 86 key string 87 } 88 89 func NewInterpolatedVariable(v string) (InterpolatedVariable, error) { 90 if strings.HasPrefix(v, "count.") { 91 return NewCountVariable(v) 92 } else if strings.HasPrefix(v, "path.") { 93 return NewPathVariable(v) 94 } else if strings.HasPrefix(v, "self.") { 95 return NewSelfVariable(v) 96 } else if strings.HasPrefix(v, "var.") { 97 return NewUserVariable(v) 98 } else if strings.HasPrefix(v, "module.") { 99 return NewModuleVariable(v) 100 } else { 101 return NewResourceVariable(v) 102 } 103 } 104 105 func NewCountVariable(key string) (*CountVariable, error) { 106 var fieldType CountValueType 107 parts := strings.SplitN(key, ".", 2) 108 switch parts[1] { 109 case "index": 110 fieldType = CountValueIndex 111 } 112 113 return &CountVariable{ 114 Type: fieldType, 115 key: key, 116 }, nil 117 } 118 119 func (c *CountVariable) FullKey() string { 120 return c.key 121 } 122 123 func NewModuleVariable(key string) (*ModuleVariable, error) { 124 parts := strings.SplitN(key, ".", 3) 125 if len(parts) < 3 { 126 return nil, fmt.Errorf( 127 "%s: module variables must be three parts: module.name.attr", 128 key) 129 } 130 131 return &ModuleVariable{ 132 Name: parts[1], 133 Field: parts[2], 134 key: key, 135 }, nil 136 } 137 138 func (v *ModuleVariable) FullKey() string { 139 return v.key 140 } 141 142 func NewPathVariable(key string) (*PathVariable, error) { 143 var fieldType PathValueType 144 parts := strings.SplitN(key, ".", 2) 145 switch parts[1] { 146 case "cwd": 147 fieldType = PathValueCwd 148 case "module": 149 fieldType = PathValueModule 150 case "root": 151 fieldType = PathValueRoot 152 } 153 154 return &PathVariable{ 155 Type: fieldType, 156 key: key, 157 }, nil 158 } 159 160 func (v *PathVariable) FullKey() string { 161 return v.key 162 } 163 164 func NewResourceVariable(key string) (*ResourceVariable, error) { 165 parts := strings.SplitN(key, ".", 3) 166 if len(parts) < 3 { 167 return nil, fmt.Errorf( 168 "%s: resource variables must be three parts: type.name.attr", 169 key) 170 } 171 172 field := parts[2] 173 multi := false 174 var index int 175 176 if idx := strings.Index(field, "."); idx != -1 { 177 indexStr := field[:idx] 178 multi = indexStr == "*" 179 index = -1 180 181 if !multi { 182 indexInt, err := strconv.ParseInt(indexStr, 0, 0) 183 if err == nil { 184 multi = true 185 index = int(indexInt) 186 } 187 } 188 189 if multi { 190 field = field[idx+1:] 191 } 192 } 193 194 return &ResourceVariable{ 195 Type: parts[0], 196 Name: parts[1], 197 Field: field, 198 Multi: multi, 199 Index: index, 200 key: key, 201 }, nil 202 } 203 204 func (v *ResourceVariable) ResourceId() string { 205 return fmt.Sprintf("%s.%s", v.Type, v.Name) 206 } 207 208 func (v *ResourceVariable) FullKey() string { 209 return v.key 210 } 211 212 func NewSelfVariable(key string) (*SelfVariable, error) { 213 field := key[len("self."):] 214 215 return &SelfVariable{ 216 Field: field, 217 218 key: key, 219 }, nil 220 } 221 222 func (v *SelfVariable) FullKey() string { 223 return v.key 224 } 225 226 func (v *SelfVariable) GoString() string { 227 return fmt.Sprintf("*%#v", *v) 228 } 229 230 func NewUserVariable(key string) (*UserVariable, error) { 231 name := key[len("var."):] 232 elem := "" 233 if idx := strings.Index(name, "."); idx > -1 { 234 elem = name[idx+1:] 235 name = name[:idx] 236 } 237 238 return &UserVariable{ 239 key: key, 240 241 Name: name, 242 Elem: elem, 243 }, nil 244 } 245 246 func (v *UserVariable) FullKey() string { 247 return v.key 248 } 249 250 func (v *UserVariable) GoString() string { 251 return fmt.Sprintf("*%#v", *v) 252 } 253 254 // DetectVariables takes an AST root and returns all the interpolated 255 // variables that are detected in the AST tree. 256 func DetectVariables(root ast.Node) ([]InterpolatedVariable, error) { 257 var result []InterpolatedVariable 258 var resultErr error 259 260 // Visitor callback 261 fn := func(n ast.Node) ast.Node { 262 if resultErr != nil { 263 return n 264 } 265 266 vn, ok := n.(*ast.VariableAccess) 267 if !ok { 268 return n 269 } 270 271 v, err := NewInterpolatedVariable(vn.Name) 272 if err != nil { 273 resultErr = err 274 return n 275 } 276 277 result = append(result, v) 278 return n 279 } 280 281 // Visitor pattern 282 root.Accept(fn) 283 284 if resultErr != nil { 285 return nil, resultErr 286 } 287 288 return result, nil 289 }