]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / helper / schema / core_schema.go
1 package schema
2
3 import (
4 "fmt"
5
6 "github.com/hashicorp/terraform/configs/configschema"
7 "github.com/zclconf/go-cty/cty"
8 )
9
10 // The functions and methods in this file are concerned with the conversion
11 // of this package's schema model into the slightly-lower-level schema model
12 // used by Terraform core for configuration parsing.
13
14 // CoreConfigSchema lowers the receiver to the schema model expected by
15 // Terraform core.
16 //
17 // This lower-level model has fewer features than the schema in this package,
18 // describing only the basic structure of configuration and state values we
19 // expect. The full schemaMap from this package is still required for full
20 // validation, handling of default values, etc.
21 //
22 // This method presumes a schema that passes InternalValidate, and so may
23 // panic or produce an invalid result if given an invalid schemaMap.
24 func (m schemaMap) CoreConfigSchema() *configschema.Block {
25 if len(m) == 0 {
26 // We return an actual (empty) object here, rather than a nil,
27 // because a nil result would mean that we don't have a schema at
28 // all, rather than that we have an empty one.
29 return &configschema.Block{}
30 }
31
32 ret := &configschema.Block{
33 Attributes: map[string]*configschema.Attribute{},
34 BlockTypes: map[string]*configschema.NestedBlock{},
35 }
36
37 for name, schema := range m {
38 if schema.Elem == nil {
39 ret.Attributes[name] = schema.coreConfigSchemaAttribute()
40 continue
41 }
42 if schema.Type == TypeMap {
43 // For TypeMap in particular, it isn't valid for Elem to be a
44 // *Resource (since that would be ambiguous in flatmap) and
45 // so Elem is treated as a TypeString schema if so. This matches
46 // how the field readers treat this situation, for compatibility
47 // with configurations targeting Terraform 0.11 and earlier.
48 if _, isResource := schema.Elem.(*Resource); isResource {
49 sch := *schema // shallow copy
50 sch.Elem = &Schema{
51 Type: TypeString,
52 }
53 ret.Attributes[name] = sch.coreConfigSchemaAttribute()
54 continue
55 }
56 }
57 switch schema.ConfigMode {
58 case SchemaConfigModeAttr:
59 ret.Attributes[name] = schema.coreConfigSchemaAttribute()
60 case SchemaConfigModeBlock:
61 ret.BlockTypes[name] = schema.coreConfigSchemaBlock()
62 default: // SchemaConfigModeAuto, or any other invalid value
63 if schema.Computed && !schema.Optional {
64 // Computed-only schemas are always handled as attributes,
65 // because they never appear in configuration.
66 ret.Attributes[name] = schema.coreConfigSchemaAttribute()
67 continue
68 }
69 switch schema.Elem.(type) {
70 case *Schema, ValueType:
71 ret.Attributes[name] = schema.coreConfigSchemaAttribute()
72 case *Resource:
73 ret.BlockTypes[name] = schema.coreConfigSchemaBlock()
74 default:
75 // Should never happen for a valid schema
76 panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", schema.Elem))
77 }
78 }
79 }
80
81 return ret
82 }
83
84 // coreConfigSchemaAttribute prepares a configschema.Attribute representation
85 // of a schema. This is appropriate only for primitives or collections whose
86 // Elem is an instance of Schema. Use coreConfigSchemaBlock for collections
87 // whose elem is a whole resource.
88 func (s *Schema) coreConfigSchemaAttribute() *configschema.Attribute {
89 // The Schema.DefaultFunc capability adds some extra weirdness here since
90 // it can be combined with "Required: true" to create a sitution where
91 // required-ness is conditional. Terraform Core doesn't share this concept,
92 // so we must sniff for this possibility here and conditionally turn
93 // off the "Required" flag if it looks like the DefaultFunc is going
94 // to provide a value.
95 // This is not 100% true to the original interface of DefaultFunc but
96 // works well enough for the EnvDefaultFunc and MultiEnvDefaultFunc
97 // situations, which are the main cases we care about.
98 //
99 // Note that this also has a consequence for commands that return schema
100 // information for documentation purposes: running those for certain
101 // providers will produce different results depending on which environment
102 // variables are set. We accept that weirdness in order to keep this
103 // interface to core otherwise simple.
104 reqd := s.Required
105 opt := s.Optional
106 if reqd && s.DefaultFunc != nil {
107 v, err := s.DefaultFunc()
108 // We can't report errors from here, so we'll instead just force
109 // "Required" to false and let the provider try calling its
110 // DefaultFunc again during the validate step, where it can then
111 // return the error.
112 if err != nil || (err == nil && v != nil) {
113 reqd = false
114 opt = true
115 }
116 }
117
118 return &configschema.Attribute{
119 Type: s.coreConfigSchemaType(),
120 Optional: opt,
121 Required: reqd,
122 Computed: s.Computed,
123 Sensitive: s.Sensitive,
124 Description: s.Description,
125 }
126 }
127
128 // coreConfigSchemaBlock prepares a configschema.NestedBlock representation of
129 // a schema. This is appropriate only for collections whose Elem is an instance
130 // of Resource, and will panic otherwise.
131 func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock {
132 ret := &configschema.NestedBlock{}
133 if nested := s.Elem.(*Resource).coreConfigSchema(); nested != nil {
134 ret.Block = *nested
135 }
136 switch s.Type {
137 case TypeList:
138 ret.Nesting = configschema.NestingList
139 case TypeSet:
140 ret.Nesting = configschema.NestingSet
141 case TypeMap:
142 ret.Nesting = configschema.NestingMap
143 default:
144 // Should never happen for a valid schema
145 panic(fmt.Errorf("invalid s.Type %s for s.Elem being resource", s.Type))
146 }
147
148 ret.MinItems = s.MinItems
149 ret.MaxItems = s.MaxItems
150
151 if s.Required && s.MinItems == 0 {
152 // configschema doesn't have a "required" representation for nested
153 // blocks, but we can fake it by requiring at least one item.
154 ret.MinItems = 1
155 }
156 if s.Optional && s.MinItems > 0 {
157 // Historically helper/schema would ignore MinItems if Optional were
158 // set, so we must mimic this behavior here to ensure that providers
159 // relying on that undocumented behavior can continue to operate as
160 // they did before.
161 ret.MinItems = 0
162 }
163 if s.Computed && !s.Optional {
164 // MinItems/MaxItems are meaningless for computed nested blocks, since
165 // they are never set by the user anyway. This ensures that we'll never
166 // generate weird errors about them.
167 ret.MinItems = 0
168 ret.MaxItems = 0
169 }
170
171 return ret
172 }
173
174 // coreConfigSchemaType determines the core config schema type that corresponds
175 // to a particular schema's type.
176 func (s *Schema) coreConfigSchemaType() cty.Type {
177 switch s.Type {
178 case TypeString:
179 return cty.String
180 case TypeBool:
181 return cty.Bool
182 case TypeInt, TypeFloat:
183 // configschema doesn't distinguish int and float, so helper/schema
184 // will deal with this as an additional validation step after
185 // configuration has been parsed and decoded.
186 return cty.Number
187 case TypeList, TypeSet, TypeMap:
188 var elemType cty.Type
189 switch set := s.Elem.(type) {
190 case *Schema:
191 elemType = set.coreConfigSchemaType()
192 case ValueType:
193 // This represents a mistake in the provider code, but it's a
194 // common one so we'll just shim it.
195 elemType = (&Schema{Type: set}).coreConfigSchemaType()
196 case *Resource:
197 // By default we construct a NestedBlock in this case, but this
198 // behavior is selected either for computed-only schemas or
199 // when ConfigMode is explicitly SchemaConfigModeBlock.
200 // See schemaMap.CoreConfigSchema for the exact rules.
201 elemType = set.coreConfigSchema().ImpliedType()
202 default:
203 if set != nil {
204 // Should never happen for a valid schema
205 panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", s.Elem))
206 }
207 // Some pre-existing schemas assume string as default, so we need
208 // to be compatible with them.
209 elemType = cty.String
210 }
211 switch s.Type {
212 case TypeList:
213 return cty.List(elemType)
214 case TypeSet:
215 return cty.Set(elemType)
216 case TypeMap:
217 return cty.Map(elemType)
218 default:
219 // can never get here in practice, due to the case we're inside
220 panic("invalid collection type")
221 }
222 default:
223 // should never happen for a valid schema
224 panic(fmt.Errorf("invalid Schema.Type %s", s.Type))
225 }
226 }
227
228 // CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema on
229 // the resource's schema. CoreConfigSchema adds the implicitly required "id"
230 // attribute for top level resources if it doesn't exist.
231 func (r *Resource) CoreConfigSchema() *configschema.Block {
232 block := r.coreConfigSchema()
233
234 if block.Attributes == nil {
235 block.Attributes = map[string]*configschema.Attribute{}
236 }
237
238 // Add the implicitly required "id" field if it doesn't exist
239 if block.Attributes["id"] == nil {
240 block.Attributes["id"] = &configschema.Attribute{
241 Type: cty.String,
242 Optional: true,
243 Computed: true,
244 }
245 }
246
247 _, timeoutsAttr := block.Attributes[TimeoutsConfigKey]
248 _, timeoutsBlock := block.BlockTypes[TimeoutsConfigKey]
249
250 // Insert configured timeout values into the schema, as long as the schema
251 // didn't define anything else by that name.
252 if r.Timeouts != nil && !timeoutsAttr && !timeoutsBlock {
253 timeouts := configschema.Block{
254 Attributes: map[string]*configschema.Attribute{},
255 }
256
257 if r.Timeouts.Create != nil {
258 timeouts.Attributes[TimeoutCreate] = &configschema.Attribute{
259 Type: cty.String,
260 Optional: true,
261 }
262 }
263
264 if r.Timeouts.Read != nil {
265 timeouts.Attributes[TimeoutRead] = &configschema.Attribute{
266 Type: cty.String,
267 Optional: true,
268 }
269 }
270
271 if r.Timeouts.Update != nil {
272 timeouts.Attributes[TimeoutUpdate] = &configschema.Attribute{
273 Type: cty.String,
274 Optional: true,
275 }
276 }
277
278 if r.Timeouts.Delete != nil {
279 timeouts.Attributes[TimeoutDelete] = &configschema.Attribute{
280 Type: cty.String,
281 Optional: true,
282 }
283 }
284
285 if r.Timeouts.Default != nil {
286 timeouts.Attributes[TimeoutDefault] = &configschema.Attribute{
287 Type: cty.String,
288 Optional: true,
289 }
290 }
291
292 block.BlockTypes[TimeoutsConfigKey] = &configschema.NestedBlock{
293 Nesting: configschema.NestingSingle,
294 Block: timeouts,
295 }
296 }
297
298 return block
299 }
300
301 func (r *Resource) coreConfigSchema() *configschema.Block {
302 return schemaMap(r.Schema).CoreConfigSchema()
303 }
304
305 // CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema
306 // on the backends's schema.
307 func (r *Backend) CoreConfigSchema() *configschema.Block {
308 return schemaMap(r.Schema).CoreConfigSchema()
309 }