diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/configs/configschema')
10 files changed, 980 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/coerce_value.go b/vendor/github.com/hashicorp/terraform/configs/configschema/coerce_value.go new file mode 100644 index 0000000..e59f58d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/coerce_value.go | |||
@@ -0,0 +1,274 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/zclconf/go-cty/cty" | ||
7 | "github.com/zclconf/go-cty/cty/convert" | ||
8 | ) | ||
9 | |||
10 | // CoerceValue attempts to force the given value to conform to the type | ||
11 | // implied by the receiever, while also applying the same validation and | ||
12 | // transformation rules that would be applied by the decoder specification | ||
13 | // returned by method DecoderSpec. | ||
14 | // | ||
15 | // This is useful in situations where a configuration must be derived from | ||
16 | // an already-decoded value. It is always better to decode directly from | ||
17 | // configuration where possible since then source location information is | ||
18 | // still available to produce diagnostics, but in special situations this | ||
19 | // function allows a compatible result to be obtained even if the | ||
20 | // configuration objects are not available. | ||
21 | // | ||
22 | // If the given value cannot be converted to conform to the receiving schema | ||
23 | // then an error is returned describing one of possibly many problems. This | ||
24 | // error may be a cty.PathError indicating a position within the nested | ||
25 | // data structure where the problem applies. | ||
26 | func (b *Block) CoerceValue(in cty.Value) (cty.Value, error) { | ||
27 | var path cty.Path | ||
28 | return b.coerceValue(in, path) | ||
29 | } | ||
30 | |||
31 | func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) { | ||
32 | switch { | ||
33 | case in.IsNull(): | ||
34 | return cty.NullVal(b.ImpliedType()), nil | ||
35 | case !in.IsKnown(): | ||
36 | return cty.UnknownVal(b.ImpliedType()), nil | ||
37 | } | ||
38 | |||
39 | ty := in.Type() | ||
40 | if !ty.IsObjectType() { | ||
41 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("an object is required") | ||
42 | } | ||
43 | |||
44 | for name := range ty.AttributeTypes() { | ||
45 | if _, defined := b.Attributes[name]; defined { | ||
46 | continue | ||
47 | } | ||
48 | if _, defined := b.BlockTypes[name]; defined { | ||
49 | continue | ||
50 | } | ||
51 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("unexpected attribute %q", name) | ||
52 | } | ||
53 | |||
54 | attrs := make(map[string]cty.Value) | ||
55 | |||
56 | for name, attrS := range b.Attributes { | ||
57 | var val cty.Value | ||
58 | switch { | ||
59 | case ty.HasAttribute(name): | ||
60 | val = in.GetAttr(name) | ||
61 | case attrS.Computed || attrS.Optional: | ||
62 | val = cty.NullVal(attrS.Type) | ||
63 | default: | ||
64 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("attribute %q is required", name) | ||
65 | } | ||
66 | |||
67 | val, err := attrS.coerceValue(val, append(path, cty.GetAttrStep{Name: name})) | ||
68 | if err != nil { | ||
69 | return cty.UnknownVal(b.ImpliedType()), err | ||
70 | } | ||
71 | |||
72 | attrs[name] = val | ||
73 | } | ||
74 | for typeName, blockS := range b.BlockTypes { | ||
75 | switch blockS.Nesting { | ||
76 | |||
77 | case NestingSingle, NestingGroup: | ||
78 | switch { | ||
79 | case ty.HasAttribute(typeName): | ||
80 | var err error | ||
81 | val := in.GetAttr(typeName) | ||
82 | attrs[typeName], err = blockS.coerceValue(val, append(path, cty.GetAttrStep{Name: typeName})) | ||
83 | if err != nil { | ||
84 | return cty.UnknownVal(b.ImpliedType()), err | ||
85 | } | ||
86 | case blockS.MinItems != 1 && blockS.MaxItems != 1: | ||
87 | if blockS.Nesting == NestingGroup { | ||
88 | attrs[typeName] = blockS.EmptyValue() | ||
89 | } else { | ||
90 | attrs[typeName] = cty.NullVal(blockS.ImpliedType()) | ||
91 | } | ||
92 | default: | ||
93 | // We use the word "attribute" here because we're talking about | ||
94 | // the cty sense of that word rather than the HCL sense. | ||
95 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("attribute %q is required", typeName) | ||
96 | } | ||
97 | |||
98 | case NestingList: | ||
99 | switch { | ||
100 | case ty.HasAttribute(typeName): | ||
101 | coll := in.GetAttr(typeName) | ||
102 | |||
103 | switch { | ||
104 | case coll.IsNull(): | ||
105 | attrs[typeName] = cty.NullVal(cty.List(blockS.ImpliedType())) | ||
106 | continue | ||
107 | case !coll.IsKnown(): | ||
108 | attrs[typeName] = cty.UnknownVal(cty.List(blockS.ImpliedType())) | ||
109 | continue | ||
110 | } | ||
111 | |||
112 | if !coll.CanIterateElements() { | ||
113 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("must be a list") | ||
114 | } | ||
115 | l := coll.LengthInt() | ||
116 | if l < blockS.MinItems { | ||
117 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("insufficient items for attribute %q; must have at least %d", typeName, blockS.MinItems) | ||
118 | } | ||
119 | if l > blockS.MaxItems && blockS.MaxItems > 0 { | ||
120 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("too many items for attribute %q; cannot have more than %d", typeName, blockS.MaxItems) | ||
121 | } | ||
122 | if l == 0 { | ||
123 | attrs[typeName] = cty.ListValEmpty(blockS.ImpliedType()) | ||
124 | continue | ||
125 | } | ||
126 | elems := make([]cty.Value, 0, l) | ||
127 | { | ||
128 | path = append(path, cty.GetAttrStep{Name: typeName}) | ||
129 | for it := coll.ElementIterator(); it.Next(); { | ||
130 | var err error | ||
131 | idx, val := it.Element() | ||
132 | val, err = blockS.coerceValue(val, append(path, cty.IndexStep{Key: idx})) | ||
133 | if err != nil { | ||
134 | return cty.UnknownVal(b.ImpliedType()), err | ||
135 | } | ||
136 | elems = append(elems, val) | ||
137 | } | ||
138 | } | ||
139 | attrs[typeName] = cty.ListVal(elems) | ||
140 | case blockS.MinItems == 0: | ||
141 | attrs[typeName] = cty.ListValEmpty(blockS.ImpliedType()) | ||
142 | default: | ||
143 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("attribute %q is required", typeName) | ||
144 | } | ||
145 | |||
146 | case NestingSet: | ||
147 | switch { | ||
148 | case ty.HasAttribute(typeName): | ||
149 | coll := in.GetAttr(typeName) | ||
150 | |||
151 | switch { | ||
152 | case coll.IsNull(): | ||
153 | attrs[typeName] = cty.NullVal(cty.Set(blockS.ImpliedType())) | ||
154 | continue | ||
155 | case !coll.IsKnown(): | ||
156 | attrs[typeName] = cty.UnknownVal(cty.Set(blockS.ImpliedType())) | ||
157 | continue | ||
158 | } | ||
159 | |||
160 | if !coll.CanIterateElements() { | ||
161 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("must be a set") | ||
162 | } | ||
163 | l := coll.LengthInt() | ||
164 | if l < blockS.MinItems { | ||
165 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("insufficient items for attribute %q; must have at least %d", typeName, blockS.MinItems) | ||
166 | } | ||
167 | if l > blockS.MaxItems && blockS.MaxItems > 0 { | ||
168 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("too many items for attribute %q; cannot have more than %d", typeName, blockS.MaxItems) | ||
169 | } | ||
170 | if l == 0 { | ||
171 | attrs[typeName] = cty.SetValEmpty(blockS.ImpliedType()) | ||
172 | continue | ||
173 | } | ||
174 | elems := make([]cty.Value, 0, l) | ||
175 | { | ||
176 | path = append(path, cty.GetAttrStep{Name: typeName}) | ||
177 | for it := coll.ElementIterator(); it.Next(); { | ||
178 | var err error | ||
179 | idx, val := it.Element() | ||
180 | val, err = blockS.coerceValue(val, append(path, cty.IndexStep{Key: idx})) | ||
181 | if err != nil { | ||
182 | return cty.UnknownVal(b.ImpliedType()), err | ||
183 | } | ||
184 | elems = append(elems, val) | ||
185 | } | ||
186 | } | ||
187 | attrs[typeName] = cty.SetVal(elems) | ||
188 | case blockS.MinItems == 0: | ||
189 | attrs[typeName] = cty.SetValEmpty(blockS.ImpliedType()) | ||
190 | default: | ||
191 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("attribute %q is required", typeName) | ||
192 | } | ||
193 | |||
194 | case NestingMap: | ||
195 | switch { | ||
196 | case ty.HasAttribute(typeName): | ||
197 | coll := in.GetAttr(typeName) | ||
198 | |||
199 | switch { | ||
200 | case coll.IsNull(): | ||
201 | attrs[typeName] = cty.NullVal(cty.Map(blockS.ImpliedType())) | ||
202 | continue | ||
203 | case !coll.IsKnown(): | ||
204 | attrs[typeName] = cty.UnknownVal(cty.Map(blockS.ImpliedType())) | ||
205 | continue | ||
206 | } | ||
207 | |||
208 | if !coll.CanIterateElements() { | ||
209 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("must be a map") | ||
210 | } | ||
211 | l := coll.LengthInt() | ||
212 | if l == 0 { | ||
213 | attrs[typeName] = cty.MapValEmpty(blockS.ImpliedType()) | ||
214 | continue | ||
215 | } | ||
216 | elems := make(map[string]cty.Value) | ||
217 | { | ||
218 | path = append(path, cty.GetAttrStep{Name: typeName}) | ||
219 | for it := coll.ElementIterator(); it.Next(); { | ||
220 | var err error | ||
221 | key, val := it.Element() | ||
222 | if key.Type() != cty.String || key.IsNull() || !key.IsKnown() { | ||
223 | return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("must be a map") | ||
224 | } | ||
225 | val, err = blockS.coerceValue(val, append(path, cty.IndexStep{Key: key})) | ||
226 | if err != nil { | ||
227 | return cty.UnknownVal(b.ImpliedType()), err | ||
228 | } | ||
229 | elems[key.AsString()] = val | ||
230 | } | ||
231 | } | ||
232 | |||
233 | // If the attribute values here contain any DynamicPseudoTypes, | ||
234 | // the concrete type must be an object. | ||
235 | useObject := false | ||
236 | switch { | ||
237 | case coll.Type().IsObjectType(): | ||
238 | useObject = true | ||
239 | default: | ||
240 | // It's possible that we were given a map, and need to coerce it to an object | ||
241 | ety := coll.Type().ElementType() | ||
242 | for _, v := range elems { | ||
243 | if !v.Type().Equals(ety) { | ||
244 | useObject = true | ||
245 | break | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | |||
250 | if useObject { | ||
251 | attrs[typeName] = cty.ObjectVal(elems) | ||
252 | } else { | ||
253 | attrs[typeName] = cty.MapVal(elems) | ||
254 | } | ||
255 | default: | ||
256 | attrs[typeName] = cty.MapValEmpty(blockS.ImpliedType()) | ||
257 | } | ||
258 | |||
259 | default: | ||
260 | // should never happen because above is exhaustive | ||
261 | panic(fmt.Errorf("unsupported nesting mode %#v", blockS.Nesting)) | ||
262 | } | ||
263 | } | ||
264 | |||
265 | return cty.ObjectVal(attrs), nil | ||
266 | } | ||
267 | |||
268 | func (a *Attribute) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) { | ||
269 | val, err := convert.Convert(in, a.Type) | ||
270 | if err != nil { | ||
271 | return cty.UnknownVal(a.Type), path.NewError(err) | ||
272 | } | ||
273 | return val, nil | ||
274 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/decoder_spec.go b/vendor/github.com/hashicorp/terraform/configs/configschema/decoder_spec.go new file mode 100644 index 0000000..d8f41ea --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/decoder_spec.go | |||
@@ -0,0 +1,117 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/hcl2/hcldec" | ||
5 | ) | ||
6 | |||
7 | var mapLabelNames = []string{"key"} | ||
8 | |||
9 | // DecoderSpec returns a hcldec.Spec that can be used to decode a HCL Body | ||
10 | // using the facilities in the hcldec package. | ||
11 | // | ||
12 | // The returned specification is guaranteed to return a value of the same type | ||
13 | // returned by method ImpliedType, but it may contain null values if any of the | ||
14 | // block attributes are defined as optional and/or computed respectively. | ||
15 | func (b *Block) DecoderSpec() hcldec.Spec { | ||
16 | ret := hcldec.ObjectSpec{} | ||
17 | if b == nil { | ||
18 | return ret | ||
19 | } | ||
20 | |||
21 | for name, attrS := range b.Attributes { | ||
22 | ret[name] = attrS.decoderSpec(name) | ||
23 | } | ||
24 | |||
25 | for name, blockS := range b.BlockTypes { | ||
26 | if _, exists := ret[name]; exists { | ||
27 | // This indicates an invalid schema, since it's not valid to | ||
28 | // define both an attribute and a block type of the same name. | ||
29 | // However, we don't raise this here since it's checked by | ||
30 | // InternalValidate. | ||
31 | continue | ||
32 | } | ||
33 | |||
34 | childSpec := blockS.Block.DecoderSpec() | ||
35 | |||
36 | switch blockS.Nesting { | ||
37 | case NestingSingle, NestingGroup: | ||
38 | ret[name] = &hcldec.BlockSpec{ | ||
39 | TypeName: name, | ||
40 | Nested: childSpec, | ||
41 | Required: blockS.MinItems == 1 && blockS.MaxItems >= 1, | ||
42 | } | ||
43 | if blockS.Nesting == NestingGroup { | ||
44 | ret[name] = &hcldec.DefaultSpec{ | ||
45 | Primary: ret[name], | ||
46 | Default: &hcldec.LiteralSpec{ | ||
47 | Value: blockS.EmptyValue(), | ||
48 | }, | ||
49 | } | ||
50 | } | ||
51 | case NestingList: | ||
52 | // We prefer to use a list where possible, since it makes our | ||
53 | // implied type more complete, but if there are any | ||
54 | // dynamically-typed attributes inside we must use a tuple | ||
55 | // instead, at the expense of our type then not being predictable. | ||
56 | if blockS.Block.ImpliedType().HasDynamicTypes() { | ||
57 | ret[name] = &hcldec.BlockTupleSpec{ | ||
58 | TypeName: name, | ||
59 | Nested: childSpec, | ||
60 | MinItems: blockS.MinItems, | ||
61 | MaxItems: blockS.MaxItems, | ||
62 | } | ||
63 | } else { | ||
64 | ret[name] = &hcldec.BlockListSpec{ | ||
65 | TypeName: name, | ||
66 | Nested: childSpec, | ||
67 | MinItems: blockS.MinItems, | ||
68 | MaxItems: blockS.MaxItems, | ||
69 | } | ||
70 | } | ||
71 | case NestingSet: | ||
72 | // We forbid dynamically-typed attributes inside NestingSet in | ||
73 | // InternalValidate, so we don't do anything special to handle | ||
74 | // that here. (There is no set analog to tuple and object types, | ||
75 | // because cty's set implementation depends on knowing the static | ||
76 | // type in order to properly compute its internal hashes.) | ||
77 | ret[name] = &hcldec.BlockSetSpec{ | ||
78 | TypeName: name, | ||
79 | Nested: childSpec, | ||
80 | MinItems: blockS.MinItems, | ||
81 | MaxItems: blockS.MaxItems, | ||
82 | } | ||
83 | case NestingMap: | ||
84 | // We prefer to use a list where possible, since it makes our | ||
85 | // implied type more complete, but if there are any | ||
86 | // dynamically-typed attributes inside we must use a tuple | ||
87 | // instead, at the expense of our type then not being predictable. | ||
88 | if blockS.Block.ImpliedType().HasDynamicTypes() { | ||
89 | ret[name] = &hcldec.BlockObjectSpec{ | ||
90 | TypeName: name, | ||
91 | Nested: childSpec, | ||
92 | LabelNames: mapLabelNames, | ||
93 | } | ||
94 | } else { | ||
95 | ret[name] = &hcldec.BlockMapSpec{ | ||
96 | TypeName: name, | ||
97 | Nested: childSpec, | ||
98 | LabelNames: mapLabelNames, | ||
99 | } | ||
100 | } | ||
101 | default: | ||
102 | // Invalid nesting type is just ignored. It's checked by | ||
103 | // InternalValidate. | ||
104 | continue | ||
105 | } | ||
106 | } | ||
107 | |||
108 | return ret | ||
109 | } | ||
110 | |||
111 | func (a *Attribute) decoderSpec(name string) hcldec.Spec { | ||
112 | return &hcldec.AttrSpec{ | ||
113 | Name: name, | ||
114 | Type: a.Type, | ||
115 | Required: a.Required, | ||
116 | } | ||
117 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/doc.go b/vendor/github.com/hashicorp/terraform/configs/configschema/doc.go new file mode 100644 index 0000000..caf8d73 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/doc.go | |||
@@ -0,0 +1,14 @@ | |||
1 | // Package configschema contains types for describing the expected structure | ||
2 | // of a configuration block whose shape is not known until runtime. | ||
3 | // | ||
4 | // For example, this is used to describe the expected contents of a resource | ||
5 | // configuration block, which is defined by the corresponding provider plugin | ||
6 | // and thus not compiled into Terraform core. | ||
7 | // | ||
8 | // A configschema primarily describes the shape of configuration, but it is | ||
9 | // also suitable for use with other structures derived from the configuration, | ||
10 | // such as the cached state of a resource or a resource diff. | ||
11 | // | ||
12 | // This package should not be confused with the package helper/schema, which | ||
13 | // is the higher-level helper library used to implement providers themselves. | ||
14 | package configschema | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/empty_value.go b/vendor/github.com/hashicorp/terraform/configs/configschema/empty_value.go new file mode 100644 index 0000000..005da56 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/empty_value.go | |||
@@ -0,0 +1,59 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "github.com/zclconf/go-cty/cty" | ||
5 | ) | ||
6 | |||
7 | // EmptyValue returns the "empty value" for the recieving block, which for | ||
8 | // a block type is a non-null object where all of the attribute values are | ||
9 | // the empty values of the block's attributes and nested block types. | ||
10 | // | ||
11 | // In other words, it returns the value that would be returned if an empty | ||
12 | // block were decoded against the recieving schema, assuming that no required | ||
13 | // attribute or block constraints were honored. | ||
14 | func (b *Block) EmptyValue() cty.Value { | ||
15 | vals := make(map[string]cty.Value) | ||
16 | for name, attrS := range b.Attributes { | ||
17 | vals[name] = attrS.EmptyValue() | ||
18 | } | ||
19 | for name, blockS := range b.BlockTypes { | ||
20 | vals[name] = blockS.EmptyValue() | ||
21 | } | ||
22 | return cty.ObjectVal(vals) | ||
23 | } | ||
24 | |||
25 | // EmptyValue returns the "empty value" for the receiving attribute, which is | ||
26 | // the value that would be returned if there were no definition of the attribute | ||
27 | // at all, ignoring any required constraint. | ||
28 | func (a *Attribute) EmptyValue() cty.Value { | ||
29 | return cty.NullVal(a.Type) | ||
30 | } | ||
31 | |||
32 | // EmptyValue returns the "empty value" for when there are zero nested blocks | ||
33 | // present of the receiving type. | ||
34 | func (b *NestedBlock) EmptyValue() cty.Value { | ||
35 | switch b.Nesting { | ||
36 | case NestingSingle: | ||
37 | return cty.NullVal(b.Block.ImpliedType()) | ||
38 | case NestingGroup: | ||
39 | return b.Block.EmptyValue() | ||
40 | case NestingList: | ||
41 | if ty := b.Block.ImpliedType(); ty.HasDynamicTypes() { | ||
42 | return cty.EmptyTupleVal | ||
43 | } else { | ||
44 | return cty.ListValEmpty(ty) | ||
45 | } | ||
46 | case NestingMap: | ||
47 | if ty := b.Block.ImpliedType(); ty.HasDynamicTypes() { | ||
48 | return cty.EmptyObjectVal | ||
49 | } else { | ||
50 | return cty.MapValEmpty(ty) | ||
51 | } | ||
52 | case NestingSet: | ||
53 | return cty.SetValEmpty(b.Block.ImpliedType()) | ||
54 | default: | ||
55 | // Should never get here because the above is intended to be exhaustive, | ||
56 | // but we'll be robust and return a result nonetheless. | ||
57 | return cty.NullVal(cty.DynamicPseudoType) | ||
58 | } | ||
59 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/implied_type.go b/vendor/github.com/hashicorp/terraform/configs/configschema/implied_type.go new file mode 100644 index 0000000..c0ee841 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/implied_type.go | |||
@@ -0,0 +1,42 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/hcl2/hcldec" | ||
5 | "github.com/zclconf/go-cty/cty" | ||
6 | ) | ||
7 | |||
8 | // ImpliedType returns the cty.Type that would result from decoding a | ||
9 | // configuration block using the receiving block schema. | ||
10 | // | ||
11 | // ImpliedType always returns a result, even if the given schema is | ||
12 | // inconsistent. Code that creates configschema.Block objects should be | ||
13 | // tested using the InternalValidate method to detect any inconsistencies | ||
14 | // that would cause this method to fall back on defaults and assumptions. | ||
15 | func (b *Block) ImpliedType() cty.Type { | ||
16 | if b == nil { | ||
17 | return cty.EmptyObject | ||
18 | } | ||
19 | |||
20 | return hcldec.ImpliedType(b.DecoderSpec()) | ||
21 | } | ||
22 | |||
23 | // ContainsSensitive returns true if any of the attributes of the receiving | ||
24 | // block or any of its descendent blocks are marked as sensitive. | ||
25 | // | ||
26 | // Blocks themselves cannot be sensitive as a whole -- sensitivity is a | ||
27 | // per-attribute idea -- but sometimes we want to include a whole object | ||
28 | // decoded from a block in some UI output, and that is safe to do only if | ||
29 | // none of the contained attributes are sensitive. | ||
30 | func (b *Block) ContainsSensitive() bool { | ||
31 | for _, attrS := range b.Attributes { | ||
32 | if attrS.Sensitive { | ||
33 | return true | ||
34 | } | ||
35 | } | ||
36 | for _, blockS := range b.BlockTypes { | ||
37 | if blockS.ContainsSensitive() { | ||
38 | return true | ||
39 | } | ||
40 | } | ||
41 | return false | ||
42 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/internal_validate.go b/vendor/github.com/hashicorp/terraform/configs/configschema/internal_validate.go new file mode 100644 index 0000000..ebf1abb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/internal_validate.go | |||
@@ -0,0 +1,105 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "regexp" | ||
6 | |||
7 | "github.com/zclconf/go-cty/cty" | ||
8 | |||
9 | multierror "github.com/hashicorp/go-multierror" | ||
10 | ) | ||
11 | |||
12 | var validName = regexp.MustCompile(`^[a-z0-9_]+$`) | ||
13 | |||
14 | // InternalValidate returns an error if the receiving block and its child | ||
15 | // schema definitions have any consistencies with the documented rules for | ||
16 | // valid schema. | ||
17 | // | ||
18 | // This is intended to be used within unit tests to detect when a given | ||
19 | // schema is invalid. | ||
20 | func (b *Block) InternalValidate() error { | ||
21 | if b == nil { | ||
22 | return fmt.Errorf("top-level block schema is nil") | ||
23 | } | ||
24 | return b.internalValidate("", nil) | ||
25 | |||
26 | } | ||
27 | |||
28 | func (b *Block) internalValidate(prefix string, err error) error { | ||
29 | for name, attrS := range b.Attributes { | ||
30 | if attrS == nil { | ||
31 | err = multierror.Append(err, fmt.Errorf("%s%s: attribute schema is nil", prefix, name)) | ||
32 | continue | ||
33 | } | ||
34 | if !validName.MatchString(name) { | ||
35 | err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) | ||
36 | } | ||
37 | if attrS.Optional == false && attrS.Required == false && attrS.Computed == false { | ||
38 | err = multierror.Append(err, fmt.Errorf("%s%s: must set Optional, Required or Computed", prefix, name)) | ||
39 | } | ||
40 | if attrS.Optional && attrS.Required { | ||
41 | err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Optional and Required", prefix, name)) | ||
42 | } | ||
43 | if attrS.Computed && attrS.Required { | ||
44 | err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Computed and Required", prefix, name)) | ||
45 | } | ||
46 | if attrS.Type == cty.NilType { | ||
47 | err = multierror.Append(err, fmt.Errorf("%s%s: Type must be set to something other than cty.NilType", prefix, name)) | ||
48 | } | ||
49 | } | ||
50 | |||
51 | for name, blockS := range b.BlockTypes { | ||
52 | if blockS == nil { | ||
53 | err = multierror.Append(err, fmt.Errorf("%s%s: block schema is nil", prefix, name)) | ||
54 | continue | ||
55 | } | ||
56 | |||
57 | if _, isAttr := b.Attributes[name]; isAttr { | ||
58 | err = multierror.Append(err, fmt.Errorf("%s%s: name defined as both attribute and child block type", prefix, name)) | ||
59 | } else if !validName.MatchString(name) { | ||
60 | err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) | ||
61 | } | ||
62 | |||
63 | if blockS.MinItems < 0 || blockS.MaxItems < 0 { | ||
64 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be greater than zero", prefix, name)) | ||
65 | } | ||
66 | |||
67 | switch blockS.Nesting { | ||
68 | case NestingSingle: | ||
69 | switch { | ||
70 | case blockS.MinItems != blockS.MaxItems: | ||
71 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must match in NestingSingle mode", prefix, name)) | ||
72 | case blockS.MinItems < 0 || blockS.MinItems > 1: | ||
73 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must be set to either 0 or 1 in NestingSingle mode", prefix, name)) | ||
74 | } | ||
75 | case NestingGroup: | ||
76 | if blockS.MinItems != 0 || blockS.MaxItems != 0 { | ||
77 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems cannot be used in NestingGroup mode", prefix, name)) | ||
78 | } | ||
79 | case NestingList, NestingSet: | ||
80 | if blockS.MinItems > blockS.MaxItems && blockS.MaxItems != 0 { | ||
81 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems must be less than or equal to MaxItems in %s mode", prefix, name, blockS.Nesting)) | ||
82 | } | ||
83 | if blockS.Nesting == NestingSet { | ||
84 | ety := blockS.Block.ImpliedType() | ||
85 | if ety.HasDynamicTypes() { | ||
86 | // This is not permitted because the HCL (cty) set implementation | ||
87 | // needs to know the exact type of set elements in order to | ||
88 | // properly hash them, and so can't support mixed types. | ||
89 | err = multierror.Append(err, fmt.Errorf("%s%s: NestingSet blocks may not contain attributes of cty.DynamicPseudoType", prefix, name)) | ||
90 | } | ||
91 | } | ||
92 | case NestingMap: | ||
93 | if blockS.MinItems != 0 || blockS.MaxItems != 0 { | ||
94 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be 0 in NestingMap mode", prefix, name)) | ||
95 | } | ||
96 | default: | ||
97 | err = multierror.Append(err, fmt.Errorf("%s%s: invalid nesting mode %s", prefix, name, blockS.Nesting)) | ||
98 | } | ||
99 | |||
100 | subPrefix := prefix + name + "." | ||
101 | err = blockS.Block.internalValidate(subPrefix, err) | ||
102 | } | ||
103 | |||
104 | return err | ||
105 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/nestingmode_string.go b/vendor/github.com/hashicorp/terraform/configs/configschema/nestingmode_string.go new file mode 100644 index 0000000..febe743 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/nestingmode_string.go | |||
@@ -0,0 +1,28 @@ | |||
1 | // Code generated by "stringer -type=NestingMode"; DO NOT EDIT. | ||
2 | |||
3 | package configschema | ||
4 | |||
5 | import "strconv" | ||
6 | |||
7 | func _() { | ||
8 | // An "invalid array index" compiler error signifies that the constant values have changed. | ||
9 | // Re-run the stringer command to generate them again. | ||
10 | var x [1]struct{} | ||
11 | _ = x[nestingModeInvalid-0] | ||
12 | _ = x[NestingSingle-1] | ||
13 | _ = x[NestingGroup-2] | ||
14 | _ = x[NestingList-3] | ||
15 | _ = x[NestingSet-4] | ||
16 | _ = x[NestingMap-5] | ||
17 | } | ||
18 | |||
19 | const _NestingMode_name = "nestingModeInvalidNestingSingleNestingGroupNestingListNestingSetNestingMap" | ||
20 | |||
21 | var _NestingMode_index = [...]uint8{0, 18, 31, 43, 54, 64, 74} | ||
22 | |||
23 | func (i NestingMode) String() string { | ||
24 | if i < 0 || i >= NestingMode(len(_NestingMode_index)-1) { | ||
25 | return "NestingMode(" + strconv.FormatInt(int64(i), 10) + ")" | ||
26 | } | ||
27 | return _NestingMode_name[_NestingMode_index[i]:_NestingMode_index[i+1]] | ||
28 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/none_required.go b/vendor/github.com/hashicorp/terraform/configs/configschema/none_required.go new file mode 100644 index 0000000..0be3b8f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/none_required.go | |||
@@ -0,0 +1,38 @@ | |||
1 | package configschema | ||
2 | |||
3 | // NoneRequired returns a deep copy of the receiver with any required | ||
4 | // attributes translated to optional. | ||
5 | func (b *Block) NoneRequired() *Block { | ||
6 | ret := &Block{} | ||
7 | |||
8 | if b.Attributes != nil { | ||
9 | ret.Attributes = make(map[string]*Attribute, len(b.Attributes)) | ||
10 | } | ||
11 | for name, attrS := range b.Attributes { | ||
12 | ret.Attributes[name] = attrS.forceOptional() | ||
13 | } | ||
14 | |||
15 | if b.BlockTypes != nil { | ||
16 | ret.BlockTypes = make(map[string]*NestedBlock, len(b.BlockTypes)) | ||
17 | } | ||
18 | for name, blockS := range b.BlockTypes { | ||
19 | ret.BlockTypes[name] = blockS.noneRequired() | ||
20 | } | ||
21 | |||
22 | return ret | ||
23 | } | ||
24 | |||
25 | func (b *NestedBlock) noneRequired() *NestedBlock { | ||
26 | ret := *b | ||
27 | ret.Block = *(ret.Block.NoneRequired()) | ||
28 | ret.MinItems = 0 | ||
29 | ret.MaxItems = 0 | ||
30 | return &ret | ||
31 | } | ||
32 | |||
33 | func (a *Attribute) forceOptional() *Attribute { | ||
34 | ret := *a | ||
35 | ret.Optional = true | ||
36 | ret.Required = false | ||
37 | return &ret | ||
38 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/schema.go b/vendor/github.com/hashicorp/terraform/configs/configschema/schema.go new file mode 100644 index 0000000..5a67334 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/schema.go | |||
@@ -0,0 +1,130 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "github.com/zclconf/go-cty/cty" | ||
5 | ) | ||
6 | |||
7 | // Block represents a configuration block. | ||
8 | // | ||
9 | // "Block" here is a logical grouping construct, though it happens to map | ||
10 | // directly onto the physical block syntax of Terraform's native configuration | ||
11 | // syntax. It may be a more a matter of convention in other syntaxes, such as | ||
12 | // JSON. | ||
13 | // | ||
14 | // When converted to a value, a Block always becomes an instance of an object | ||
15 | // type derived from its defined attributes and nested blocks | ||
16 | type Block struct { | ||
17 | // Attributes describes any attributes that may appear directly inside | ||
18 | // the block. | ||
19 | Attributes map[string]*Attribute | ||
20 | |||
21 | // BlockTypes describes any nested block types that may appear directly | ||
22 | // inside the block. | ||
23 | BlockTypes map[string]*NestedBlock | ||
24 | } | ||
25 | |||
26 | // Attribute represents a configuration attribute, within a block. | ||
27 | type Attribute struct { | ||
28 | // Type is a type specification that the attribute's value must conform to. | ||
29 | Type cty.Type | ||
30 | |||
31 | // Description is an English-language description of the purpose and | ||
32 | // usage of the attribute. A description should be concise and use only | ||
33 | // one or two sentences, leaving full definition to longer-form | ||
34 | // documentation defined elsewhere. | ||
35 | Description string | ||
36 | |||
37 | // Required, if set to true, specifies that an omitted or null value is | ||
38 | // not permitted. | ||
39 | Required bool | ||
40 | |||
41 | // Optional, if set to true, specifies that an omitted or null value is | ||
42 | // permitted. This field conflicts with Required. | ||
43 | Optional bool | ||
44 | |||
45 | // Computed, if set to true, specifies that the value comes from the | ||
46 | // provider rather than from configuration. If combined with Optional, | ||
47 | // then the config may optionally provide an overridden value. | ||
48 | Computed bool | ||
49 | |||
50 | // Sensitive, if set to true, indicates that an attribute may contain | ||
51 | // sensitive information. | ||
52 | // | ||
53 | // At present nothing is done with this information, but callers are | ||
54 | // encouraged to set it where appropriate so that it may be used in the | ||
55 | // future to help Terraform mask sensitive information. (Terraform | ||
56 | // currently achieves this in a limited sense via other mechanisms.) | ||
57 | Sensitive bool | ||
58 | } | ||
59 | |||
60 | // NestedBlock represents the embedding of one block within another. | ||
61 | type NestedBlock struct { | ||
62 | // Block is the description of the block that's nested. | ||
63 | Block | ||
64 | |||
65 | // Nesting provides the nesting mode for the child block, which determines | ||
66 | // how many instances of the block are allowed, how many labels it expects, | ||
67 | // and how the resulting data will be converted into a data structure. | ||
68 | Nesting NestingMode | ||
69 | |||
70 | // MinItems and MaxItems set, for the NestingList and NestingSet nesting | ||
71 | // modes, lower and upper limits on the number of child blocks allowed | ||
72 | // of the given type. If both are left at zero, no limit is applied. | ||
73 | // | ||
74 | // As a special case, both values can be set to 1 for NestingSingle in | ||
75 | // order to indicate that a particular single block is required. | ||
76 | // | ||
77 | // These fields are ignored for other nesting modes and must both be left | ||
78 | // at zero. | ||
79 | MinItems, MaxItems int | ||
80 | } | ||
81 | |||
82 | // NestingMode is an enumeration of modes for nesting blocks inside other | ||
83 | // blocks. | ||
84 | type NestingMode int | ||
85 | |||
86 | //go:generate stringer -type=NestingMode | ||
87 | |||
88 | const ( | ||
89 | nestingModeInvalid NestingMode = iota | ||
90 | |||
91 | // NestingSingle indicates that only a single instance of a given | ||
92 | // block type is permitted, with no labels, and its content should be | ||
93 | // provided directly as an object value. | ||
94 | NestingSingle | ||
95 | |||
96 | // NestingGroup is similar to NestingSingle in that it calls for only a | ||
97 | // single instance of a given block type with no labels, but it additonally | ||
98 | // guarantees that its result will never be null, even if the block is | ||
99 | // absent, and instead the nested attributes and blocks will be treated | ||
100 | // as absent in that case. (Any required attributes or blocks within the | ||
101 | // nested block are not enforced unless the block is explicitly present | ||
102 | // in the configuration, so they are all effectively optional when the | ||
103 | // block is not present.) | ||
104 | // | ||
105 | // This is useful for the situation where a remote API has a feature that | ||
106 | // is always enabled but has a group of settings related to that feature | ||
107 | // that themselves have default values. By using NestingGroup instead of | ||
108 | // NestingSingle in that case, generated plans will show the block as | ||
109 | // present even when not present in configuration, thus allowing any | ||
110 | // default values within to be displayed to the user. | ||
111 | NestingGroup | ||
112 | |||
113 | // NestingList indicates that multiple blocks of the given type are | ||
114 | // permitted, with no labels, and that their corresponding objects should | ||
115 | // be provided in a list. | ||
116 | NestingList | ||
117 | |||
118 | // NestingSet indicates that multiple blocks of the given type are | ||
119 | // permitted, with no labels, and that their corresponding objects should | ||
120 | // be provided in a set. | ||
121 | NestingSet | ||
122 | |||
123 | // NestingMap indicates that multiple blocks of the given type are | ||
124 | // permitted, each with a single label, and that their corresponding | ||
125 | // objects should be provided in a map whose keys are the labels. | ||
126 | // | ||
127 | // It's an error, therefore, to use the same label value on multiple | ||
128 | // blocks. | ||
129 | NestingMap | ||
130 | ) | ||
diff --git a/vendor/github.com/hashicorp/terraform/configs/configschema/validate_traversal.go b/vendor/github.com/hashicorp/terraform/configs/configschema/validate_traversal.go new file mode 100644 index 0000000..a41e930 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/configschema/validate_traversal.go | |||
@@ -0,0 +1,173 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "sort" | ||
6 | |||
7 | "github.com/hashicorp/hcl2/hcl" | ||
8 | "github.com/hashicorp/hcl2/hcl/hclsyntax" | ||
9 | "github.com/zclconf/go-cty/cty" | ||
10 | |||
11 | "github.com/hashicorp/terraform/helper/didyoumean" | ||
12 | "github.com/hashicorp/terraform/tfdiags" | ||
13 | ) | ||
14 | |||
15 | // StaticValidateTraversal checks whether the given traversal (which must be | ||
16 | // relative) refers to a construct in the receiving schema, returning error | ||
17 | // diagnostics if any problems are found. | ||
18 | // | ||
19 | // This method is "optimistic" in that it will not return errors for possible | ||
20 | // problems that cannot be detected statically. It is possible that an | ||
21 | // traversal which passed static validation will still fail when evaluated. | ||
22 | func (b *Block) StaticValidateTraversal(traversal hcl.Traversal) tfdiags.Diagnostics { | ||
23 | if !traversal.IsRelative() { | ||
24 | panic("StaticValidateTraversal on absolute traversal") | ||
25 | } | ||
26 | if len(traversal) == 0 { | ||
27 | return nil | ||
28 | } | ||
29 | |||
30 | var diags tfdiags.Diagnostics | ||
31 | |||
32 | next := traversal[0] | ||
33 | after := traversal[1:] | ||
34 | |||
35 | var name string | ||
36 | switch step := next.(type) { | ||
37 | case hcl.TraverseAttr: | ||
38 | name = step.Name | ||
39 | case hcl.TraverseIndex: | ||
40 | // No other traversal step types are allowed directly at a block. | ||
41 | // If it looks like the user was trying to use index syntax to | ||
42 | // access an attribute then we'll produce a specialized message. | ||
43 | key := step.Key | ||
44 | if key.Type() == cty.String && key.IsKnown() && !key.IsNull() { | ||
45 | maybeName := key.AsString() | ||
46 | if hclsyntax.ValidIdentifier(maybeName) { | ||
47 | diags = diags.Append(&hcl.Diagnostic{ | ||
48 | Severity: hcl.DiagError, | ||
49 | Summary: `Invalid index operation`, | ||
50 | Detail: fmt.Sprintf(`Only attribute access is allowed here. Did you mean to access attribute %q using the dot operator?`, maybeName), | ||
51 | Subject: &step.SrcRange, | ||
52 | }) | ||
53 | return diags | ||
54 | } | ||
55 | } | ||
56 | // If it looks like some other kind of index then we'll use a generic error. | ||
57 | diags = diags.Append(&hcl.Diagnostic{ | ||
58 | Severity: hcl.DiagError, | ||
59 | Summary: `Invalid index operation`, | ||
60 | Detail: `Only attribute access is allowed here, using the dot operator.`, | ||
61 | Subject: &step.SrcRange, | ||
62 | }) | ||
63 | return diags | ||
64 | default: | ||
65 | // No other traversal types should appear in a normal valid traversal, | ||
66 | // but we'll handle this with a generic error anyway to be robust. | ||
67 | diags = diags.Append(&hcl.Diagnostic{ | ||
68 | Severity: hcl.DiagError, | ||
69 | Summary: `Invalid operation`, | ||
70 | Detail: `Only attribute access is allowed here, using the dot operator.`, | ||
71 | Subject: next.SourceRange().Ptr(), | ||
72 | }) | ||
73 | return diags | ||
74 | } | ||
75 | |||
76 | if attrS, exists := b.Attributes[name]; exists { | ||
77 | // For attribute validation we will just apply the rest of the | ||
78 | // traversal to an unknown value of the attribute type and pass | ||
79 | // through HCL's own errors, since we don't want to replicate all of | ||
80 | // HCL's type checking rules here. | ||
81 | val := cty.UnknownVal(attrS.Type) | ||
82 | _, hclDiags := after.TraverseRel(val) | ||
83 | diags = diags.Append(hclDiags) | ||
84 | return diags | ||
85 | } | ||
86 | |||
87 | if blockS, exists := b.BlockTypes[name]; exists { | ||
88 | moreDiags := blockS.staticValidateTraversal(name, after) | ||
89 | diags = diags.Append(moreDiags) | ||
90 | return diags | ||
91 | } | ||
92 | |||
93 | // If we get here then the name isn't valid at all. We'll collect up | ||
94 | // all of the names that _are_ valid to use as suggestions. | ||
95 | var suggestions []string | ||
96 | for name := range b.Attributes { | ||
97 | suggestions = append(suggestions, name) | ||
98 | } | ||
99 | for name := range b.BlockTypes { | ||
100 | suggestions = append(suggestions, name) | ||
101 | } | ||
102 | sort.Strings(suggestions) | ||
103 | suggestion := didyoumean.NameSuggestion(name, suggestions) | ||
104 | if suggestion != "" { | ||
105 | suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) | ||
106 | } | ||
107 | diags = diags.Append(&hcl.Diagnostic{ | ||
108 | Severity: hcl.DiagError, | ||
109 | Summary: `Unsupported attribute`, | ||
110 | Detail: fmt.Sprintf(`This object has no argument, nested block, or exported attribute named %q.%s`, name, suggestion), | ||
111 | Subject: next.SourceRange().Ptr(), | ||
112 | }) | ||
113 | |||
114 | return diags | ||
115 | } | ||
116 | |||
117 | func (b *NestedBlock) staticValidateTraversal(typeName string, traversal hcl.Traversal) tfdiags.Diagnostics { | ||
118 | if b.Nesting == NestingSingle || b.Nesting == NestingGroup { | ||
119 | // Single blocks are easy: just pass right through. | ||
120 | return b.Block.StaticValidateTraversal(traversal) | ||
121 | } | ||
122 | |||
123 | if len(traversal) == 0 { | ||
124 | // It's always valid to access a nested block's attribute directly. | ||
125 | return nil | ||
126 | } | ||
127 | |||
128 | var diags tfdiags.Diagnostics | ||
129 | next := traversal[0] | ||
130 | after := traversal[1:] | ||
131 | |||
132 | switch b.Nesting { | ||
133 | |||
134 | case NestingSet: | ||
135 | // Can't traverse into a set at all, since it does not have any keys | ||
136 | // to index with. | ||
137 | diags = diags.Append(&hcl.Diagnostic{ | ||
138 | Severity: hcl.DiagError, | ||
139 | Summary: `Cannot index a set value`, | ||
140 | Detail: fmt.Sprintf(`Block type %q is represented by a set of objects, and set elements do not have addressable keys. To find elements matching specific criteria, use a "for" expression with an "if" clause.`, typeName), | ||
141 | Subject: next.SourceRange().Ptr(), | ||
142 | }) | ||
143 | return diags | ||
144 | |||
145 | case NestingList: | ||
146 | if _, ok := next.(hcl.TraverseIndex); ok { | ||
147 | moreDiags := b.Block.StaticValidateTraversal(after) | ||
148 | diags = diags.Append(moreDiags) | ||
149 | } else { | ||
150 | diags = diags.Append(&hcl.Diagnostic{ | ||
151 | Severity: hcl.DiagError, | ||
152 | Summary: `Invalid operation`, | ||
153 | Detail: fmt.Sprintf(`Block type %q is represented by a list of objects, so it must be indexed using a numeric key, like .%s[0].`, typeName, typeName), | ||
154 | Subject: next.SourceRange().Ptr(), | ||
155 | }) | ||
156 | } | ||
157 | return diags | ||
158 | |||
159 | case NestingMap: | ||
160 | // Both attribute and index steps are valid for maps, so we'll just | ||
161 | // pass through here and let normal evaluation catch an | ||
162 | // incorrectly-typed index key later, if present. | ||
163 | moreDiags := b.Block.StaticValidateTraversal(after) | ||
164 | diags = diags.Append(moreDiags) | ||
165 | return diags | ||
166 | |||
167 | default: | ||
168 | // Invalid nesting type is just ignored. It's checked by | ||
169 | // InternalValidate. (Note that we handled NestingSingle separately | ||
170 | // back at the start of this function.) | ||
171 | return nil | ||
172 | } | ||
173 | } | ||