summaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp/terraform/config/interpolate.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config/interpolate.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/config/interpolate.go386
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
+}