github.com/burmuley/terraform@v0.11.12-beta1/terraform/semantics.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/hashicorp/go-multierror" 8 "github.com/hashicorp/terraform/config" 9 "github.com/hashicorp/terraform/dag" 10 ) 11 12 // GraphSemanticChecker is the interface that semantic checks across 13 // the entire Terraform graph implement. 14 // 15 // The graph should NOT be modified by the semantic checker. 16 type GraphSemanticChecker interface { 17 Check(*dag.Graph) error 18 } 19 20 // UnorderedSemanticCheckRunner is an implementation of GraphSemanticChecker 21 // that runs a list of SemanticCheckers against the vertices of the graph 22 // in no specified order. 23 type UnorderedSemanticCheckRunner struct { 24 Checks []SemanticChecker 25 } 26 27 func (sc *UnorderedSemanticCheckRunner) Check(g *dag.Graph) error { 28 var err error 29 for _, v := range g.Vertices() { 30 for _, check := range sc.Checks { 31 if e := check.Check(g, v); e != nil { 32 err = multierror.Append(err, e) 33 } 34 } 35 } 36 37 return err 38 } 39 40 // SemanticChecker is the interface that semantic checks across the 41 // Terraform graph implement. Errors are accumulated. Even after an error 42 // is returned, child vertices in the graph will still be visited. 43 // 44 // The graph should NOT be modified by the semantic checker. 45 // 46 // The order in which vertices are visited is left unspecified, so the 47 // semantic checks should not rely on that. 48 type SemanticChecker interface { 49 Check(*dag.Graph, dag.Vertex) error 50 } 51 52 // smcUserVariables does all the semantic checks to verify that the 53 // variables given satisfy the configuration itself. 54 func smcUserVariables(c *config.Config, vs map[string]interface{}) []error { 55 var errs []error 56 57 cvs := make(map[string]*config.Variable) 58 for _, v := range c.Variables { 59 cvs[v.Name] = v 60 } 61 62 // Check that all required variables are present 63 required := make(map[string]struct{}) 64 for _, v := range c.Variables { 65 if v.Required() { 66 required[v.Name] = struct{}{} 67 } 68 } 69 for k, _ := range vs { 70 delete(required, k) 71 } 72 if len(required) > 0 { 73 for k, _ := range required { 74 errs = append(errs, fmt.Errorf( 75 "Required variable not set: %s", k)) 76 } 77 } 78 79 // Check that types match up 80 for name, proposedValue := range vs { 81 // Check for "map.key" fields. These stopped working with Terraform 82 // 0.7 but we do this to surface a better error message informing 83 // the user what happened. 84 if idx := strings.Index(name, "."); idx > 0 { 85 key := name[:idx] 86 if _, ok := cvs[key]; ok { 87 errs = append(errs, fmt.Errorf( 88 "%s: Overriding map keys with the format `name.key` is no "+ 89 "longer allowed. You may still override keys by setting "+ 90 "`name = { key = value }`. The maps will be merged. This "+ 91 "behavior appeared in 0.7.0.", name)) 92 continue 93 } 94 } 95 96 schema, ok := cvs[name] 97 if !ok { 98 continue 99 } 100 101 declaredType := schema.Type() 102 103 switch declaredType { 104 case config.VariableTypeString: 105 switch proposedValue.(type) { 106 case string: 107 continue 108 } 109 case config.VariableTypeMap: 110 switch v := proposedValue.(type) { 111 case map[string]interface{}: 112 continue 113 case []map[string]interface{}: 114 // if we have a list of 1 map, it will get coerced later as needed 115 if len(v) == 1 { 116 continue 117 } 118 } 119 case config.VariableTypeList: 120 switch proposedValue.(type) { 121 case []interface{}: 122 continue 123 } 124 } 125 errs = append(errs, fmt.Errorf("variable %s should be type %s, got %s", 126 name, declaredType.Printable(), hclTypeName(proposedValue))) 127 } 128 129 // TODO(mitchellh): variables that are unknown 130 131 return errs 132 }