github.com/jdextraze/terraform@v0.6.17-0.20160511153921-e33847c8a8af/terraform/eval_variable.go (about)

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