github.com/tomaszheflik/terraform@v0.7.3-0.20160827060421-32f990b41594/terraform/semantics.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/go-multierror" 7 "github.com/hashicorp/terraform/config" 8 "github.com/hashicorp/terraform/dag" 9 ) 10 11 // GraphSemanticChecker is the interface that semantic checks across 12 // the entire Terraform graph implement. 13 // 14 // The graph should NOT be modified by the semantic checker. 15 type GraphSemanticChecker interface { 16 Check(*dag.Graph) error 17 } 18 19 // UnorderedSemanticCheckRunner is an implementation of GraphSemanticChecker 20 // that runs a list of SemanticCheckers against the vertices of the graph 21 // in no specified order. 22 type UnorderedSemanticCheckRunner struct { 23 Checks []SemanticChecker 24 } 25 26 func (sc *UnorderedSemanticCheckRunner) Check(g *dag.Graph) error { 27 var err error 28 for _, v := range g.Vertices() { 29 for _, check := range sc.Checks { 30 if e := check.Check(g, v); e != nil { 31 err = multierror.Append(err, e) 32 } 33 } 34 } 35 36 return err 37 } 38 39 // SemanticChecker is the interface that semantic checks across the 40 // Terraform graph implement. Errors are accumulated. Even after an error 41 // is returned, child vertices in the graph will still be visited. 42 // 43 // The graph should NOT be modified by the semantic checker. 44 // 45 // The order in which vertices are visited is left unspecified, so the 46 // semantic checks should not rely on that. 47 type SemanticChecker interface { 48 Check(*dag.Graph, dag.Vertex) error 49 } 50 51 // SemanticCheckModulesExist is an implementation of SemanticChecker that 52 // verifies that all the modules that are referenced in the graph exist. 53 type SemanticCheckModulesExist struct{} 54 55 // TODO: test 56 func (*SemanticCheckModulesExist) Check(g *dag.Graph, v dag.Vertex) error { 57 mn, ok := v.(*GraphNodeConfigModule) 58 if !ok { 59 return nil 60 } 61 62 if mn.Tree == nil { 63 return fmt.Errorf( 64 "module '%s' not found", mn.Module.Name) 65 } 66 67 return nil 68 } 69 70 // smcUserVariables does all the semantic checks to verify that the 71 // variables given satisfy the configuration itself. 72 func smcUserVariables(c *config.Config, vs map[string]interface{}) []error { 73 var errs []error 74 75 cvs := make(map[string]*config.Variable) 76 for _, v := range c.Variables { 77 cvs[v.Name] = v 78 } 79 80 // Check that all required variables are present 81 required := make(map[string]struct{}) 82 for _, v := range c.Variables { 83 if v.Required() { 84 required[v.Name] = struct{}{} 85 } 86 } 87 for k, _ := range vs { 88 delete(required, k) 89 } 90 if len(required) > 0 { 91 for k, _ := range required { 92 errs = append(errs, fmt.Errorf( 93 "Required variable not set: %s", k)) 94 } 95 } 96 97 // Check that types match up 98 for name, proposedValue := range vs { 99 schema, ok := cvs[name] 100 if !ok { 101 continue 102 } 103 104 declaredType := schema.Type() 105 106 switch declaredType { 107 case config.VariableTypeString: 108 switch proposedValue.(type) { 109 case string: 110 continue 111 default: 112 errs = append(errs, fmt.Errorf("variable %s should be type %s, got %s", 113 name, declaredType.Printable(), hclTypeName(proposedValue))) 114 } 115 case config.VariableTypeMap: 116 switch proposedValue.(type) { 117 case map[string]interface{}: 118 continue 119 default: 120 errs = append(errs, fmt.Errorf("variable %s should be type %s, got %s", 121 name, declaredType.Printable(), hclTypeName(proposedValue))) 122 } 123 case config.VariableTypeList: 124 switch proposedValue.(type) { 125 case []interface{}: 126 continue 127 default: 128 errs = append(errs, fmt.Errorf("variable %s should be type %s, got %s", 129 name, declaredType.Printable(), hclTypeName(proposedValue))) 130 } 131 default: 132 errs = append(errs, fmt.Errorf("variable %s should be type %s, got %s", 133 name, declaredType.Printable(), hclTypeName(proposedValue))) 134 } 135 } 136 137 // TODO(mitchellh): variables that are unknown 138 139 return errs 140 }