]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/helper/config/validator.go
Initial transfer of provider code
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / helper / config / validator.go
1 package config
2
3 import (
4 "fmt"
5 "strconv"
6 "strings"
7
8 "github.com/hashicorp/terraform/flatmap"
9 "github.com/hashicorp/terraform/terraform"
10 )
11
12 // Validator is a helper that helps you validate the configuration
13 // of your resource, resource provider, etc.
14 //
15 // At the most basic level, set the Required and Optional lists to be
16 // specifiers of keys that are required or optional. If a key shows up
17 // that isn't in one of these two lists, then an error is generated.
18 //
19 // The "specifiers" allowed in this is a fairly rich syntax to help
20 // describe the format of your configuration:
21 //
22 // * Basic keys are just strings. For example: "foo" will match the
23 // "foo" key.
24 //
25 // * Nested structure keys can be matched by doing
26 // "listener.*.foo". This will verify that there is at least one
27 // listener element that has the "foo" key set.
28 //
29 // * The existence of a nested structure can be checked by simply
30 // doing "listener.*" which will verify that there is at least
31 // one element in the "listener" structure. This is NOT
32 // validating that "listener" is an array. It is validating
33 // that it is a nested structure in the configuration.
34 //
35 type Validator struct {
36 Required []string
37 Optional []string
38 }
39
40 func (v *Validator) Validate(
41 c *terraform.ResourceConfig) (ws []string, es []error) {
42 // Flatten the configuration so it is easier to reason about
43 flat := flatmap.Flatten(c.Raw)
44
45 keySet := make(map[string]validatorKey)
46 for i, vs := range [][]string{v.Required, v.Optional} {
47 req := i == 0
48 for _, k := range vs {
49 vk, err := newValidatorKey(k, req)
50 if err != nil {
51 es = append(es, err)
52 continue
53 }
54
55 keySet[k] = vk
56 }
57 }
58
59 purged := make([]string, 0)
60 for _, kv := range keySet {
61 p, w, e := kv.Validate(flat)
62 if len(w) > 0 {
63 ws = append(ws, w...)
64 }
65 if len(e) > 0 {
66 es = append(es, e...)
67 }
68
69 purged = append(purged, p...)
70 }
71
72 // Delete all the keys we processed in order to find
73 // the unknown keys.
74 for _, p := range purged {
75 delete(flat, p)
76 }
77
78 // The rest are unknown
79 for k, _ := range flat {
80 es = append(es, fmt.Errorf("Unknown configuration: %s", k))
81 }
82
83 return
84 }
85
86 type validatorKey interface {
87 // Validate validates the given configuration and returns viewed keys,
88 // warnings, and errors.
89 Validate(map[string]string) ([]string, []string, []error)
90 }
91
92 func newValidatorKey(k string, req bool) (validatorKey, error) {
93 var result validatorKey
94
95 parts := strings.Split(k, ".")
96 if len(parts) > 1 && parts[1] == "*" {
97 result = &nestedValidatorKey{
98 Parts: parts,
99 Required: req,
100 }
101 } else {
102 result = &basicValidatorKey{
103 Key: k,
104 Required: req,
105 }
106 }
107
108 return result, nil
109 }
110
111 // basicValidatorKey validates keys that are basic such as "foo"
112 type basicValidatorKey struct {
113 Key string
114 Required bool
115 }
116
117 func (v *basicValidatorKey) Validate(
118 m map[string]string) ([]string, []string, []error) {
119 for k, _ := range m {
120 // If we have the exact key its a match
121 if k == v.Key {
122 return []string{k}, nil, nil
123 }
124 }
125
126 if !v.Required {
127 return nil, nil, nil
128 }
129
130 return nil, nil, []error{fmt.Errorf(
131 "Key not found: %s", v.Key)}
132 }
133
134 type nestedValidatorKey struct {
135 Parts []string
136 Required bool
137 }
138
139 func (v *nestedValidatorKey) validate(
140 m map[string]string,
141 prefix string,
142 offset int) ([]string, []string, []error) {
143 if offset >= len(v.Parts) {
144 // We're at the end. Look for a specific key.
145 v2 := &basicValidatorKey{Key: prefix, Required: v.Required}
146 return v2.Validate(m)
147 }
148
149 current := v.Parts[offset]
150
151 // If we're at offset 0, special case to start at the next one.
152 if offset == 0 {
153 return v.validate(m, current, offset+1)
154 }
155
156 // Determine if we're doing a "for all" or a specific key
157 if current != "*" {
158 // We're looking at a specific key, continue on.
159 return v.validate(m, prefix+"."+current, offset+1)
160 }
161
162 // We're doing a "for all", so we loop over.
163 countStr, ok := m[prefix+".#"]
164 if !ok {
165 if !v.Required {
166 // It wasn't required, so its no problem.
167 return nil, nil, nil
168 }
169
170 return nil, nil, []error{fmt.Errorf(
171 "Key not found: %s", prefix)}
172 }
173
174 count, err := strconv.ParseInt(countStr, 0, 0)
175 if err != nil {
176 // This shouldn't happen if flatmap works properly
177 panic("invalid flatmap array")
178 }
179
180 var e []error
181 var w []string
182 u := make([]string, 1, count+1)
183 u[0] = prefix + ".#"
184 for i := 0; i < int(count); i++ {
185 prefix := fmt.Sprintf("%s.%d", prefix, i)
186
187 // Mark that we saw this specific key
188 u = append(u, prefix)
189
190 // Mark all prefixes of this
191 for k, _ := range m {
192 if !strings.HasPrefix(k, prefix+".") {
193 continue
194 }
195 u = append(u, k)
196 }
197
198 // If we have more parts, then validate deeper
199 if offset+1 < len(v.Parts) {
200 u2, w2, e2 := v.validate(m, prefix, offset+1)
201
202 u = append(u, u2...)
203 w = append(w, w2...)
204 e = append(e, e2...)
205 }
206 }
207
208 return u, w, e
209 }
210
211 func (v *nestedValidatorKey) Validate(
212 m map[string]string) ([]string, []string, []error) {
213 return v.validate(m, "", 0)
214 }