diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go | 192 |
1 files changed, 173 insertions, 19 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go index bf952f6..875677e 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go | |||
@@ -3,7 +3,7 @@ package schema | |||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | 5 | ||
6 | "github.com/hashicorp/terraform/config/configschema" | 6 | "github.com/hashicorp/terraform/configs/configschema" |
7 | "github.com/zclconf/go-cty/cty" | 7 | "github.com/zclconf/go-cty/cty" |
8 | ) | 8 | ) |
9 | 9 | ||
@@ -39,14 +39,42 @@ func (m schemaMap) CoreConfigSchema() *configschema.Block { | |||
39 | ret.Attributes[name] = schema.coreConfigSchemaAttribute() | 39 | ret.Attributes[name] = schema.coreConfigSchemaAttribute() |
40 | continue | 40 | continue |
41 | } | 41 | } |
42 | switch schema.Elem.(type) { | 42 | if schema.Type == TypeMap { |
43 | case *Schema: | 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: | ||
44 | ret.Attributes[name] = schema.coreConfigSchemaAttribute() | 59 | ret.Attributes[name] = schema.coreConfigSchemaAttribute() |
45 | case *Resource: | 60 | case SchemaConfigModeBlock: |
46 | ret.BlockTypes[name] = schema.coreConfigSchemaBlock() | 61 | ret.BlockTypes[name] = schema.coreConfigSchemaBlock() |
47 | default: | 62 | default: // SchemaConfigModeAuto, or any other invalid value |
48 | // Should never happen for a valid schema | 63 | if schema.Computed && !schema.Optional { |
49 | panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", schema.Elem)) | 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 | } | ||
50 | } | 78 | } |
51 | } | 79 | } |
52 | 80 | ||
@@ -58,12 +86,42 @@ func (m schemaMap) CoreConfigSchema() *configschema.Block { | |||
58 | // Elem is an instance of Schema. Use coreConfigSchemaBlock for collections | 86 | // Elem is an instance of Schema. Use coreConfigSchemaBlock for collections |
59 | // whose elem is a whole resource. | 87 | // whose elem is a whole resource. |
60 | func (s *Schema) coreConfigSchemaAttribute() *configschema.Attribute { | 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 | |||
61 | return &configschema.Attribute{ | 118 | return &configschema.Attribute{ |
62 | Type: s.coreConfigSchemaType(), | 119 | Type: s.coreConfigSchemaType(), |
63 | Optional: s.Optional, | 120 | Optional: opt, |
64 | Required: s.Required, | 121 | Required: reqd, |
65 | Computed: s.Computed, | 122 | Computed: s.Computed, |
66 | Sensitive: s.Sensitive, | 123 | Sensitive: s.Sensitive, |
124 | Description: s.Description, | ||
67 | } | 125 | } |
68 | } | 126 | } |
69 | 127 | ||
@@ -72,7 +130,7 @@ func (s *Schema) coreConfigSchemaAttribute() *configschema.Attribute { | |||
72 | // of Resource, and will panic otherwise. | 130 | // of Resource, and will panic otherwise. |
73 | func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock { | 131 | func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock { |
74 | ret := &configschema.NestedBlock{} | 132 | ret := &configschema.NestedBlock{} |
75 | if nested := s.Elem.(*Resource).CoreConfigSchema(); nested != nil { | 133 | if nested := s.Elem.(*Resource).coreConfigSchema(); nested != nil { |
76 | ret.Block = *nested | 134 | ret.Block = *nested |
77 | } | 135 | } |
78 | switch s.Type { | 136 | switch s.Type { |
@@ -95,6 +153,20 @@ func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock { | |||
95 | // blocks, but we can fake it by requiring at least one item. | 153 | // blocks, but we can fake it by requiring at least one item. |
96 | ret.MinItems = 1 | 154 | ret.MinItems = 1 |
97 | } | 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 | } | ||
98 | 170 | ||
99 | return ret | 171 | return ret |
100 | } | 172 | } |
@@ -117,11 +189,16 @@ func (s *Schema) coreConfigSchemaType() cty.Type { | |||
117 | switch set := s.Elem.(type) { | 189 | switch set := s.Elem.(type) { |
118 | case *Schema: | 190 | case *Schema: |
119 | elemType = set.coreConfigSchemaType() | 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() | ||
120 | case *Resource: | 196 | case *Resource: |
121 | // In practice we don't actually use this for normal schema | 197 | // By default we construct a NestedBlock in this case, but this |
122 | // construction because we construct a NestedBlock in that | 198 | // behavior is selected either for computed-only schemas or |
123 | // case instead. See schemaMap.CoreConfigSchema. | 199 | // when ConfigMode is explicitly SchemaConfigModeBlock. |
124 | elemType = set.CoreConfigSchema().ImpliedType() | 200 | // See schemaMap.CoreConfigSchema for the exact rules. |
201 | elemType = set.coreConfigSchema().ImpliedType() | ||
125 | default: | 202 | default: |
126 | if set != nil { | 203 | if set != nil { |
127 | // Should never happen for a valid schema | 204 | // Should never happen for a valid schema |
@@ -148,8 +225,85 @@ func (s *Schema) coreConfigSchemaType() cty.Type { | |||
148 | } | 225 | } |
149 | } | 226 | } |
150 | 227 | ||
151 | // CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema | 228 | // CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema on |
152 | // on the resource's schema. | 229 | // the resource's schema. CoreConfigSchema adds the implicitly required "id" |
230 | // attribute for top level resources if it doesn't exist. | ||
153 | func (r *Resource) CoreConfigSchema() *configschema.Block { | 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 { | ||
154 | return schemaMap(r.Schema).CoreConfigSchema() | 308 | return schemaMap(r.Schema).CoreConfigSchema() |
155 | } | 309 | } |