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 }