diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/semantics.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/terraform/semantics.go | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/terraform/semantics.go b/vendor/github.com/hashicorp/terraform/terraform/semantics.go new file mode 100644 index 0000000..20f1d8a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/semantics.go | |||
@@ -0,0 +1,132 @@ | |||
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 | } | ||