diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config/interpolate.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/config/interpolate.go | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/interpolate.go b/vendor/github.com/hashicorp/terraform/config/interpolate.go new file mode 100644 index 00000000..bbb35554 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/interpolate.go @@ -0,0 +1,386 @@ +package config + +import ( + "fmt" + "strconv" + "strings" + + "github.com/hashicorp/hil/ast" +) + +// An InterpolatedVariable is a variable reference within an interpolation. +// +// Implementations of this interface represents various sources where +// variables can come from: user variables, resources, etc. +type InterpolatedVariable interface { + FullKey() string +} + +// CountVariable is a variable for referencing information about +// the count. +type CountVariable struct { + Type CountValueType + key string +} + +// CountValueType is the type of the count variable that is referenced. +type CountValueType byte + +const ( + CountValueInvalid CountValueType = iota + CountValueIndex +) + +// A ModuleVariable is a variable that is referencing the output +// of a module, such as "${module.foo.bar}" +type ModuleVariable struct { + Name string + Field string + key string +} + +// A PathVariable is a variable that references path information about the +// module. +type PathVariable struct { + Type PathValueType + key string +} + +type PathValueType byte + +const ( + PathValueInvalid PathValueType = iota + PathValueCwd + PathValueModule + PathValueRoot +) + +// A ResourceVariable is a variable that is referencing the field +// of a resource, such as "${aws_instance.foo.ami}" +type ResourceVariable struct { + Mode ResourceMode + Type string // Resource type, i.e. "aws_instance" + Name string // Resource name + Field string // Resource field + + Multi bool // True if multi-variable: aws_instance.foo.*.id + Index int // Index for multi-variable: aws_instance.foo.1.id == 1 + + key string +} + +// SelfVariable is a variable that is referencing the same resource +// it is running on: "${self.address}" +type SelfVariable struct { + Field string + + key string +} + +// SimpleVariable is an unprefixed variable, which can show up when users have +// strings they are passing down to resources that use interpolation +// internally. The template_file resource is an example of this. +type SimpleVariable struct { + Key string +} + +// TerraformVariable is a "terraform."-prefixed variable used to access +// metadata about the Terraform run. +type TerraformVariable struct { + Field string + key string +} + +// A UserVariable is a variable that is referencing a user variable +// that is inputted from outside the configuration. This looks like +// "${var.foo}" +type UserVariable struct { + Name string + Elem string + + key string +} + +func NewInterpolatedVariable(v string) (InterpolatedVariable, error) { + if strings.HasPrefix(v, "count.") { + return NewCountVariable(v) + } else if strings.HasPrefix(v, "path.") { + return NewPathVariable(v) + } else if strings.HasPrefix(v, "self.") { + return NewSelfVariable(v) + } else if strings.HasPrefix(v, "terraform.") { + return NewTerraformVariable(v) + } else if strings.HasPrefix(v, "var.") { + return NewUserVariable(v) + } else if strings.HasPrefix(v, "module.") { + return NewModuleVariable(v) + } else if !strings.ContainsRune(v, '.') { + return NewSimpleVariable(v) + } else { + return NewResourceVariable(v) + } +} + +func NewCountVariable(key string) (*CountVariable, error) { + var fieldType CountValueType + parts := strings.SplitN(key, ".", 2) + switch parts[1] { + case "index": + fieldType = CountValueIndex + } + + return &CountVariable{ + Type: fieldType, + key: key, + }, nil +} + +func (c *CountVariable) FullKey() string { + return c.key +} + +func NewModuleVariable(key string) (*ModuleVariable, error) { + parts := strings.SplitN(key, ".", 3) + if len(parts) < 3 { + return nil, fmt.Errorf( + "%s: module variables must be three parts: module.name.attr", + key) + } + + return &ModuleVariable{ + Name: parts[1], + Field: parts[2], + key: key, + }, nil +} + +func (v *ModuleVariable) FullKey() string { + return v.key +} + +func (v *ModuleVariable) GoString() string { + return fmt.Sprintf("*%#v", *v) +} + +func NewPathVariable(key string) (*PathVariable, error) { + var fieldType PathValueType + parts := strings.SplitN(key, ".", 2) + switch parts[1] { + case "cwd": + fieldType = PathValueCwd + case "module": + fieldType = PathValueModule + case "root": + fieldType = PathValueRoot + } + + return &PathVariable{ + Type: fieldType, + key: key, + }, nil +} + +func (v *PathVariable) FullKey() string { + return v.key +} + +func NewResourceVariable(key string) (*ResourceVariable, error) { + var mode ResourceMode + var parts []string + if strings.HasPrefix(key, "data.") { + mode = DataResourceMode + parts = strings.SplitN(key, ".", 4) + if len(parts) < 4 { + return nil, fmt.Errorf( + "%s: data variables must be four parts: data.TYPE.NAME.ATTR", + key) + } + + // Don't actually need the "data." prefix for parsing, since it's + // always constant. + parts = parts[1:] + } else { + mode = ManagedResourceMode + parts = strings.SplitN(key, ".", 3) + if len(parts) < 3 { + return nil, fmt.Errorf( + "%s: resource variables must be three parts: TYPE.NAME.ATTR", + key) + } + } + + field := parts[2] + multi := false + var index int + + if idx := strings.Index(field, "."); idx != -1 { + indexStr := field[:idx] + multi = indexStr == "*" + index = -1 + + if !multi { + indexInt, err := strconv.ParseInt(indexStr, 0, 0) + if err == nil { + multi = true + index = int(indexInt) + } + } + + if multi { + field = field[idx+1:] + } + } + + return &ResourceVariable{ + Mode: mode, + Type: parts[0], + Name: parts[1], + Field: field, + Multi: multi, + Index: index, + key: key, + }, nil +} + +func (v *ResourceVariable) ResourceId() string { + switch v.Mode { + case ManagedResourceMode: + return fmt.Sprintf("%s.%s", v.Type, v.Name) + case DataResourceMode: + return fmt.Sprintf("data.%s.%s", v.Type, v.Name) + default: + panic(fmt.Errorf("unknown resource mode %s", v.Mode)) + } +} + +func (v *ResourceVariable) FullKey() string { + return v.key +} + +func NewSelfVariable(key string) (*SelfVariable, error) { + field := key[len("self."):] + + return &SelfVariable{ + Field: field, + + key: key, + }, nil +} + +func (v *SelfVariable) FullKey() string { + return v.key +} + +func (v *SelfVariable) GoString() string { + return fmt.Sprintf("*%#v", *v) +} + +func NewSimpleVariable(key string) (*SimpleVariable, error) { + return &SimpleVariable{key}, nil +} + +func (v *SimpleVariable) FullKey() string { + return v.Key +} + +func (v *SimpleVariable) GoString() string { + return fmt.Sprintf("*%#v", *v) +} + +func NewTerraformVariable(key string) (*TerraformVariable, error) { + field := key[len("terraform."):] + return &TerraformVariable{ + Field: field, + key: key, + }, nil +} + +func (v *TerraformVariable) FullKey() string { + return v.key +} + +func (v *TerraformVariable) GoString() string { + return fmt.Sprintf("*%#v", *v) +} + +func NewUserVariable(key string) (*UserVariable, error) { + name := key[len("var."):] + elem := "" + if idx := strings.Index(name, "."); idx > -1 { + elem = name[idx+1:] + name = name[:idx] + } + + if len(elem) > 0 { + return nil, fmt.Errorf("Invalid dot index found: 'var.%s.%s'. Values in maps and lists can be referenced using square bracket indexing, like: 'var.mymap[\"key\"]' or 'var.mylist[1]'.", name, elem) + } + + return &UserVariable{ + key: key, + + Name: name, + Elem: elem, + }, nil +} + +func (v *UserVariable) FullKey() string { + return v.key +} + +func (v *UserVariable) GoString() string { + return fmt.Sprintf("*%#v", *v) +} + +// DetectVariables takes an AST root and returns all the interpolated +// variables that are detected in the AST tree. +func DetectVariables(root ast.Node) ([]InterpolatedVariable, error) { + var result []InterpolatedVariable + var resultErr error + + // Visitor callback + fn := func(n ast.Node) ast.Node { + if resultErr != nil { + return n + } + + switch vn := n.(type) { + case *ast.VariableAccess: + v, err := NewInterpolatedVariable(vn.Name) + if err != nil { + resultErr = err + return n + } + result = append(result, v) + case *ast.Index: + if va, ok := vn.Target.(*ast.VariableAccess); ok { + v, err := NewInterpolatedVariable(va.Name) + if err != nil { + resultErr = err + return n + } + result = append(result, v) + } + if va, ok := vn.Key.(*ast.VariableAccess); ok { + v, err := NewInterpolatedVariable(va.Name) + if err != nil { + resultErr = err + return n + } + result = append(result, v) + } + default: + return n + } + + return n + } + + // Visitor pattern + root.Accept(fn) + + if resultErr != nil { + return nil, resultErr + } + + return result, nil +} |