github.com/anfernee/terraform@v0.6.16-0.20160430000239-06e5085a92f2/terraform/eval_variable.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/errwrap"
     8  	"github.com/hashicorp/terraform/config"
     9  	"github.com/hashicorp/terraform/config/module"
    10  	"github.com/mitchellh/mapstructure"
    11  )
    12  
    13  // EvalTypeCheckVariable is an EvalNode which ensures that the variable
    14  // values which are assigned as inputs to a module (including the root)
    15  // match the types which are either declared for the variables explicitly
    16  // or inferred from the default values.
    17  //
    18  // In order to achieve this three things are required:
    19  //     - a map of the proposed variable values
    20  //     - the configuration tree of the module in which the variable is
    21  //       declared
    22  //     - the path to the module (so we know which part of the tree to
    23  //       compare the values against).
    24  //
    25  // Currently since the type system is simple, we currently do not make
    26  // use of the values since it is only valid to pass string values. The
    27  // structure is in place for extension of the type system, however.
    28  type EvalTypeCheckVariable struct {
    29  	Variables  map[string]string
    30  	ModulePath []string
    31  	ModuleTree *module.Tree
    32  }
    33  
    34  func (n *EvalTypeCheckVariable) Eval(ctx EvalContext) (interface{}, error) {
    35  	currentTree := n.ModuleTree
    36  	for _, pathComponent := range n.ModulePath[1:] {
    37  		currentTree = currentTree.Children()[pathComponent]
    38  	}
    39  	targetConfig := currentTree.Config()
    40  
    41  	prototypes := make(map[string]config.VariableType)
    42  	for _, variable := range targetConfig.Variables {
    43  		prototypes[variable.Name] = variable.Type()
    44  	}
    45  
    46  	for name, declaredType := range prototypes {
    47  		// This is only necessary when we _actually_ check. It is left as a reminder
    48  		// that at the current time we are dealing with a type system consisting only
    49  		// of strings and maps - where the only valid inter-module variable type is
    50  		// string.
    51  		_, ok := n.Variables[name]
    52  		if !ok {
    53  			// This means the default value should be used as no overriding value
    54  			// has been set. Therefore we should continue as no check is necessary.
    55  			continue
    56  		}
    57  
    58  		switch declaredType {
    59  		case config.VariableTypeString:
    60  			// This will need actual verification once we aren't dealing with
    61  			// a map[string]string but this is sufficient for now.
    62  			continue
    63  		default:
    64  			// Only display a module if we are not in the root module
    65  			modulePathDescription := fmt.Sprintf(" in module %s", strings.Join(n.ModulePath[1:], "."))
    66  			if len(n.ModulePath) == 1 {
    67  				modulePathDescription = ""
    68  			}
    69  			// This will need the actual type substituting when we have more than
    70  			// just strings and maps.
    71  			return nil, fmt.Errorf("variable %s%s should be type %s, got type string",
    72  				name, modulePathDescription, declaredType.Printable())
    73  		}
    74  	}
    75  
    76  	return nil, nil
    77  }
    78  
    79  // EvalSetVariables is an EvalNode implementation that sets the variables
    80  // explicitly for interpolation later.
    81  type EvalSetVariables struct {
    82  	Module    *string
    83  	Variables map[string]string
    84  }
    85  
    86  // TODO: test
    87  func (n *EvalSetVariables) Eval(ctx EvalContext) (interface{}, error) {
    88  	ctx.SetVariables(*n.Module, n.Variables)
    89  	return nil, nil
    90  }
    91  
    92  // EvalVariableBlock is an EvalNode implementation that evaluates the
    93  // given configuration, and uses the final values as a way to set the
    94  // mapping.
    95  type EvalVariableBlock struct {
    96  	Config    **ResourceConfig
    97  	Variables map[string]string
    98  }
    99  
   100  // TODO: test
   101  func (n *EvalVariableBlock) Eval(ctx EvalContext) (interface{}, error) {
   102  	// Clear out the existing mapping
   103  	for k, _ := range n.Variables {
   104  		delete(n.Variables, k)
   105  	}
   106  
   107  	// Get our configuration
   108  	rc := *n.Config
   109  	for k, v := range rc.Config {
   110  		var vStr string
   111  		if err := mapstructure.WeakDecode(v, &vStr); err != nil {
   112  			return nil, errwrap.Wrapf(fmt.Sprintf(
   113  				"%s: error reading value: {{err}}", k), err)
   114  		}
   115  
   116  		n.Variables[k] = vStr
   117  	}
   118  	for k, _ := range rc.Raw {
   119  		if _, ok := n.Variables[k]; !ok {
   120  			n.Variables[k] = config.UnknownVariableValue
   121  		}
   122  	}
   123  
   124  	return nil, nil
   125  }