github.com/jsoriano/terraform@v0.6.7-0.20151026070445-8b70867fdd95/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  }