diff options
author | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
---|---|---|
committer | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
commit | 107c1cdb09c575aa2f61d97f48d8587eb6bada4c (patch) | |
tree | ca7d008643efc555c388baeaf1d986e0b6b3e28c /vendor/github.com/hashicorp/terraform/helper/schema | |
parent | 844b5a68d8af4791755b8f0ad293cc99f5959183 (diff) | |
download | terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.gz terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.zst terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.zip |
Upgrade to 0.12
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper/schema')
19 files changed, 1090 insertions, 175 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/backend.go b/vendor/github.com/hashicorp/terraform/helper/schema/backend.go index 57fbba7..c8d8ae2 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/backend.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/backend.go | |||
@@ -2,8 +2,15 @@ package schema | |||
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "context" | 4 | "context" |
5 | "fmt" | ||
5 | 6 | ||
7 | "github.com/hashicorp/terraform/tfdiags" | ||
8 | "github.com/zclconf/go-cty/cty" | ||
9 | |||
10 | "github.com/hashicorp/terraform/config/hcl2shim" | ||
11 | "github.com/hashicorp/terraform/configs/configschema" | ||
6 | "github.com/hashicorp/terraform/terraform" | 12 | "github.com/hashicorp/terraform/terraform" |
13 | ctyconvert "github.com/zclconf/go-cty/cty/convert" | ||
7 | ) | 14 | ) |
8 | 15 | ||
9 | // Backend represents a partial backend.Backend implementation and simplifies | 16 | // Backend represents a partial backend.Backend implementation and simplifies |
@@ -38,41 +45,123 @@ func FromContextBackendConfig(ctx context.Context) *ResourceData { | |||
38 | return ctx.Value(backendConfigKey).(*ResourceData) | 45 | return ctx.Value(backendConfigKey).(*ResourceData) |
39 | } | 46 | } |
40 | 47 | ||
41 | func (b *Backend) Input( | 48 | func (b *Backend) ConfigSchema() *configschema.Block { |
42 | input terraform.UIInput, | 49 | // This is an alias of CoreConfigSchema just to implement the |
43 | c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { | 50 | // backend.Backend interface. |
51 | return b.CoreConfigSchema() | ||
52 | } | ||
53 | |||
54 | func (b *Backend) PrepareConfig(configVal cty.Value) (cty.Value, tfdiags.Diagnostics) { | ||
44 | if b == nil { | 55 | if b == nil { |
45 | return c, nil | 56 | return configVal, nil |
46 | } | 57 | } |
58 | var diags tfdiags.Diagnostics | ||
59 | var err error | ||
47 | 60 | ||
48 | return schemaMap(b.Schema).Input(input, c) | 61 | // In order to use Transform below, this needs to be filled out completely |
49 | } | 62 | // according the schema. |
63 | configVal, err = b.CoreConfigSchema().CoerceValue(configVal) | ||
64 | if err != nil { | ||
65 | return configVal, diags.Append(err) | ||
66 | } | ||
50 | 67 | ||
51 | func (b *Backend) Validate(c *terraform.ResourceConfig) ([]string, []error) { | 68 | // lookup any required, top-level attributes that are Null, and see if we |
52 | if b == nil { | 69 | // have a Default value available. |
53 | return nil, nil | 70 | configVal, err = cty.Transform(configVal, func(path cty.Path, val cty.Value) (cty.Value, error) { |
71 | // we're only looking for top-level attributes | ||
72 | if len(path) != 1 { | ||
73 | return val, nil | ||
74 | } | ||
75 | |||
76 | // nothing to do if we already have a value | ||
77 | if !val.IsNull() { | ||
78 | return val, nil | ||
79 | } | ||
80 | |||
81 | // get the Schema definition for this attribute | ||
82 | getAttr, ok := path[0].(cty.GetAttrStep) | ||
83 | // these should all exist, but just ignore anything strange | ||
84 | if !ok { | ||
85 | return val, nil | ||
86 | } | ||
87 | |||
88 | attrSchema := b.Schema[getAttr.Name] | ||
89 | // continue to ignore anything that doesn't match | ||
90 | if attrSchema == nil { | ||
91 | return val, nil | ||
92 | } | ||
93 | |||
94 | // this is deprecated, so don't set it | ||
95 | if attrSchema.Deprecated != "" || attrSchema.Removed != "" { | ||
96 | return val, nil | ||
97 | } | ||
98 | |||
99 | // find a default value if it exists | ||
100 | def, err := attrSchema.DefaultValue() | ||
101 | if err != nil { | ||
102 | diags = diags.Append(fmt.Errorf("error getting default for %q: %s", getAttr.Name, err)) | ||
103 | return val, err | ||
104 | } | ||
105 | |||
106 | // no default | ||
107 | if def == nil { | ||
108 | return val, nil | ||
109 | } | ||
110 | |||
111 | // create a cty.Value and make sure it's the correct type | ||
112 | tmpVal := hcl2shim.HCL2ValueFromConfigValue(def) | ||
113 | |||
114 | // helper/schema used to allow setting "" to a bool | ||
115 | if val.Type() == cty.Bool && tmpVal.RawEquals(cty.StringVal("")) { | ||
116 | // return a warning about the conversion | ||
117 | diags = diags.Append("provider set empty string as default value for bool " + getAttr.Name) | ||
118 | tmpVal = cty.False | ||
119 | } | ||
120 | |||
121 | val, err = ctyconvert.Convert(tmpVal, val.Type()) | ||
122 | if err != nil { | ||
123 | diags = diags.Append(fmt.Errorf("error setting default for %q: %s", getAttr.Name, err)) | ||
124 | } | ||
125 | |||
126 | return val, err | ||
127 | }) | ||
128 | if err != nil { | ||
129 | // any error here was already added to the diagnostics | ||
130 | return configVal, diags | ||
54 | } | 131 | } |
55 | 132 | ||
56 | return schemaMap(b.Schema).Validate(c) | 133 | shimRC := b.shimConfig(configVal) |
134 | warns, errs := schemaMap(b.Schema).Validate(shimRC) | ||
135 | for _, warn := range warns { | ||
136 | diags = diags.Append(tfdiags.SimpleWarning(warn)) | ||
137 | } | ||
138 | for _, err := range errs { | ||
139 | diags = diags.Append(err) | ||
140 | } | ||
141 | return configVal, diags | ||
57 | } | 142 | } |
58 | 143 | ||
59 | func (b *Backend) Configure(c *terraform.ResourceConfig) error { | 144 | func (b *Backend) Configure(obj cty.Value) tfdiags.Diagnostics { |
60 | if b == nil { | 145 | if b == nil { |
61 | return nil | 146 | return nil |
62 | } | 147 | } |
63 | 148 | ||
149 | var diags tfdiags.Diagnostics | ||
64 | sm := schemaMap(b.Schema) | 150 | sm := schemaMap(b.Schema) |
151 | shimRC := b.shimConfig(obj) | ||
65 | 152 | ||
66 | // Get a ResourceData for this configuration. To do this, we actually | 153 | // Get a ResourceData for this configuration. To do this, we actually |
67 | // generate an intermediary "diff" although that is never exposed. | 154 | // generate an intermediary "diff" although that is never exposed. |
68 | diff, err := sm.Diff(nil, c, nil, nil) | 155 | diff, err := sm.Diff(nil, shimRC, nil, nil, true) |
69 | if err != nil { | 156 | if err != nil { |
70 | return err | 157 | diags = diags.Append(err) |
158 | return diags | ||
71 | } | 159 | } |
72 | 160 | ||
73 | data, err := sm.Data(nil, diff) | 161 | data, err := sm.Data(nil, diff) |
74 | if err != nil { | 162 | if err != nil { |
75 | return err | 163 | diags = diags.Append(err) |
164 | return diags | ||
76 | } | 165 | } |
77 | b.config = data | 166 | b.config = data |
78 | 167 | ||
@@ -80,11 +169,28 @@ func (b *Backend) Configure(c *terraform.ResourceConfig) error { | |||
80 | err = b.ConfigureFunc(context.WithValue( | 169 | err = b.ConfigureFunc(context.WithValue( |
81 | context.Background(), backendConfigKey, data)) | 170 | context.Background(), backendConfigKey, data)) |
82 | if err != nil { | 171 | if err != nil { |
83 | return err | 172 | diags = diags.Append(err) |
173 | return diags | ||
84 | } | 174 | } |
85 | } | 175 | } |
86 | 176 | ||
87 | return nil | 177 | return diags |
178 | } | ||
179 | |||
180 | // shimConfig turns a new-style cty.Value configuration (which must be of | ||
181 | // an object type) into a minimal old-style *terraform.ResourceConfig object | ||
182 | // that should be populated enough to appease the not-yet-updated functionality | ||
183 | // in this package. This should be removed once everything is updated. | ||
184 | func (b *Backend) shimConfig(obj cty.Value) *terraform.ResourceConfig { | ||
185 | shimMap, ok := hcl2shim.ConfigValueFromHCL2(obj).(map[string]interface{}) | ||
186 | if !ok { | ||
187 | // If the configVal was nil, we still want a non-nil map here. | ||
188 | shimMap = map[string]interface{}{} | ||
189 | } | ||
190 | return &terraform.ResourceConfig{ | ||
191 | Config: shimMap, | ||
192 | Raw: shimMap, | ||
193 | } | ||
88 | } | 194 | } |
89 | 195 | ||
90 | // Config returns the configuration. This is available after Configure is | 196 | // Config returns the configuration. This is available after Configure is |
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 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go index b80b223..2a66a06 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go | |||
@@ -3,6 +3,7 @@ package schema | |||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | "strconv" | 5 | "strconv" |
6 | "strings" | ||
6 | ) | 7 | ) |
7 | 8 | ||
8 | // FieldReaders are responsible for decoding fields out of data into | 9 | // FieldReaders are responsible for decoding fields out of data into |
@@ -41,6 +42,13 @@ func (r *FieldReadResult) ValueOrZero(s *Schema) interface{} { | |||
41 | return s.ZeroValue() | 42 | return s.ZeroValue() |
42 | } | 43 | } |
43 | 44 | ||
45 | // SchemasForFlatmapPath tries its best to find a sequence of schemas that | ||
46 | // the given dot-delimited attribute path traverses through. | ||
47 | func SchemasForFlatmapPath(path string, schemaMap map[string]*Schema) []*Schema { | ||
48 | parts := strings.Split(path, ".") | ||
49 | return addrToSchema(parts, schemaMap) | ||
50 | } | ||
51 | |||
44 | // addrToSchema finds the final element schema for the given address | 52 | // addrToSchema finds the final element schema for the given address |
45 | // and the given schema. It returns all the schemas that led to the final | 53 | // and the given schema. It returns all the schemas that led to the final |
46 | // schema. These are in order of the address (out to in). | 54 | // schema. These are in order of the address (out to in). |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go index 55a301d..808375c 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go | |||
@@ -2,6 +2,7 @@ package schema | |||
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | "log" | ||
5 | "strconv" | 6 | "strconv" |
6 | "strings" | 7 | "strings" |
7 | "sync" | 8 | "sync" |
@@ -93,6 +94,22 @@ func (r *ConfigFieldReader) readField( | |||
93 | } | 94 | } |
94 | } | 95 | } |
95 | 96 | ||
97 | if protoVersion5 { | ||
98 | switch schema.Type { | ||
99 | case TypeList, TypeSet, TypeMap, typeObject: | ||
100 | // Check if the value itself is unknown. | ||
101 | // The new protocol shims will add unknown values to this list of | ||
102 | // ComputedKeys. This is the only way we have to indicate that a | ||
103 | // collection is unknown in the config | ||
104 | for _, unknown := range r.Config.ComputedKeys { | ||
105 | if k == unknown { | ||
106 | log.Printf("[DEBUG] setting computed for %q from ComputedKeys", k) | ||
107 | return FieldReadResult{Computed: true, Exists: true}, nil | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
96 | switch schema.Type { | 113 | switch schema.Type { |
97 | case TypeBool, TypeFloat, TypeInt, TypeString: | 114 | case TypeBool, TypeFloat, TypeInt, TypeString: |
98 | return r.readPrimitive(k, schema) | 115 | return r.readPrimitive(k, schema) |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go index d558a5b..ae35b4a 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go | |||
@@ -174,6 +174,9 @@ func (r *DiffFieldReader) readPrimitive( | |||
174 | 174 | ||
175 | func (r *DiffFieldReader) readSet( | 175 | func (r *DiffFieldReader) readSet( |
176 | address []string, schema *Schema) (FieldReadResult, error) { | 176 | address []string, schema *Schema) (FieldReadResult, error) { |
177 | // copy address to ensure we don't modify the argument | ||
178 | address = append([]string(nil), address...) | ||
179 | |||
177 | prefix := strings.Join(address, ".") + "." | 180 | prefix := strings.Join(address, ".") + "." |
178 | 181 | ||
179 | // Create the set that will be our result | 182 | // Create the set that will be our result |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go index 054efe0..53f73b7 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go | |||
@@ -98,6 +98,9 @@ func (r *MapFieldReader) readPrimitive( | |||
98 | 98 | ||
99 | func (r *MapFieldReader) readSet( | 99 | func (r *MapFieldReader) readSet( |
100 | address []string, schema *Schema) (FieldReadResult, error) { | 100 | address []string, schema *Schema) (FieldReadResult, error) { |
101 | // copy address to ensure we don't modify the argument | ||
102 | address = append([]string(nil), address...) | ||
103 | |||
101 | // Get the number of elements in the list | 104 | // Get the number of elements in the list |
102 | countRaw, err := r.readPrimitive( | 105 | countRaw, err := r.readPrimitive( |
103 | append(address, "#"), &Schema{Type: TypeInt}) | 106 | append(address, "#"), &Schema{Type: TypeInt}) |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go index 814c7ba..c09358b 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go | |||
@@ -297,13 +297,14 @@ func (w *MapFieldWriter) setSet( | |||
297 | // we get the proper order back based on the hash code. | 297 | // we get the proper order back based on the hash code. |
298 | if v := reflect.ValueOf(value); v.Kind() == reflect.Slice { | 298 | if v := reflect.ValueOf(value); v.Kind() == reflect.Slice { |
299 | // Build a temp *ResourceData to use for the conversion | 299 | // Build a temp *ResourceData to use for the conversion |
300 | tempAddr := addr[len(addr)-1:] | ||
300 | tempSchema := *schema | 301 | tempSchema := *schema |
301 | tempSchema.Type = TypeList | 302 | tempSchema.Type = TypeList |
302 | tempSchemaMap := map[string]*Schema{addr[0]: &tempSchema} | 303 | tempSchemaMap := map[string]*Schema{tempAddr[0]: &tempSchema} |
303 | tempW := &MapFieldWriter{Schema: tempSchemaMap} | 304 | tempW := &MapFieldWriter{Schema: tempSchemaMap} |
304 | 305 | ||
305 | // Set the entire list, this lets us get sane values out of it | 306 | // Set the entire list, this lets us get sane values out of it |
306 | if err := tempW.WriteField(addr, value); err != nil { | 307 | if err := tempW.WriteField(tempAddr, value); err != nil { |
307 | return err | 308 | return err |
308 | } | 309 | } |
309 | 310 | ||
@@ -319,7 +320,7 @@ func (w *MapFieldWriter) setSet( | |||
319 | } | 320 | } |
320 | for i := 0; i < v.Len(); i++ { | 321 | for i := 0; i < v.Len(); i++ { |
321 | is := strconv.FormatInt(int64(i), 10) | 322 | is := strconv.FormatInt(int64(i), 10) |
322 | result, err := tempR.ReadField(append(addrCopy, is)) | 323 | result, err := tempR.ReadField(append(tempAddr, is)) |
323 | if err != nil { | 324 | if err != nil { |
324 | return err | 325 | return err |
325 | } | 326 | } |
@@ -340,6 +341,11 @@ func (w *MapFieldWriter) setSet( | |||
340 | // problems when the old data isn't wiped first. | 341 | // problems when the old data isn't wiped first. |
341 | w.clearTree(addr) | 342 | w.clearTree(addr) |
342 | 343 | ||
344 | if value.(*Set) == nil { | ||
345 | w.result[k+".#"] = "0" | ||
346 | return nil | ||
347 | } | ||
348 | |||
343 | for code, elem := range value.(*Set).m { | 349 | for code, elem := range value.(*Set).m { |
344 | if err := w.set(append(addrCopy, code), elem); err != nil { | 350 | if err := w.set(append(addrCopy, code), elem); err != nil { |
345 | return err | 351 | return err |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go b/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go index 38cd8c7..0184d7b 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go | |||
@@ -4,6 +4,18 @@ package schema | |||
4 | 4 | ||
5 | import "strconv" | 5 | import "strconv" |
6 | 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[getSourceState-1] | ||
12 | _ = x[getSourceConfig-2] | ||
13 | _ = x[getSourceDiff-4] | ||
14 | _ = x[getSourceSet-8] | ||
15 | _ = x[getSourceExact-16] | ||
16 | _ = x[getSourceLevelMask-15] | ||
17 | } | ||
18 | |||
7 | const ( | 19 | const ( |
8 | _getSource_name_0 = "getSourceStategetSourceConfig" | 20 | _getSource_name_0 = "getSourceStategetSourceConfig" |
9 | _getSource_name_1 = "getSourceDiff" | 21 | _getSource_name_1 = "getSourceDiff" |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provider.go b/vendor/github.com/hashicorp/terraform/helper/schema/provider.go index 6cd325d..9702447 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/provider.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/provider.go | |||
@@ -9,7 +9,7 @@ import ( | |||
9 | 9 | ||
10 | "github.com/hashicorp/go-multierror" | 10 | "github.com/hashicorp/go-multierror" |
11 | "github.com/hashicorp/terraform/config" | 11 | "github.com/hashicorp/terraform/config" |
12 | "github.com/hashicorp/terraform/config/configschema" | 12 | "github.com/hashicorp/terraform/configs/configschema" |
13 | "github.com/hashicorp/terraform/terraform" | 13 | "github.com/hashicorp/terraform/terraform" |
14 | ) | 14 | ) |
15 | 15 | ||
@@ -64,6 +64,8 @@ type Provider struct { | |||
64 | stopCtx context.Context | 64 | stopCtx context.Context |
65 | stopCtxCancel context.CancelFunc | 65 | stopCtxCancel context.CancelFunc |
66 | stopOnce sync.Once | 66 | stopOnce sync.Once |
67 | |||
68 | TerraformVersion string | ||
67 | } | 69 | } |
68 | 70 | ||
69 | // ConfigureFunc is the function used to configure a Provider. | 71 | // ConfigureFunc is the function used to configure a Provider. |
@@ -251,7 +253,7 @@ func (p *Provider) Configure(c *terraform.ResourceConfig) error { | |||
251 | 253 | ||
252 | // Get a ResourceData for this configuration. To do this, we actually | 254 | // Get a ResourceData for this configuration. To do this, we actually |
253 | // generate an intermediary "diff" although that is never exposed. | 255 | // generate an intermediary "diff" although that is never exposed. |
254 | diff, err := sm.Diff(nil, c, nil, p.meta) | 256 | diff, err := sm.Diff(nil, c, nil, p.meta, true) |
255 | if err != nil { | 257 | if err != nil { |
256 | return err | 258 | return err |
257 | } | 259 | } |
@@ -296,6 +298,20 @@ func (p *Provider) Diff( | |||
296 | return r.Diff(s, c, p.meta) | 298 | return r.Diff(s, c, p.meta) |
297 | } | 299 | } |
298 | 300 | ||
301 | // SimpleDiff is used by the new protocol wrappers to get a diff that doesn't | ||
302 | // attempt to calculate ignore_changes. | ||
303 | func (p *Provider) SimpleDiff( | ||
304 | info *terraform.InstanceInfo, | ||
305 | s *terraform.InstanceState, | ||
306 | c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { | ||
307 | r, ok := p.ResourcesMap[info.Type] | ||
308 | if !ok { | ||
309 | return nil, fmt.Errorf("unknown resource type: %s", info.Type) | ||
310 | } | ||
311 | |||
312 | return r.simpleDiff(s, c, p.meta) | ||
313 | } | ||
314 | |||
299 | // Refresh implementation of terraform.ResourceProvider interface. | 315 | // Refresh implementation of terraform.ResourceProvider interface. |
300 | func (p *Provider) Refresh( | 316 | func (p *Provider) Refresh( |
301 | info *terraform.InstanceInfo, | 317 | info *terraform.InstanceInfo, |
@@ -311,7 +327,7 @@ func (p *Provider) Refresh( | |||
311 | // Resources implementation of terraform.ResourceProvider interface. | 327 | // Resources implementation of terraform.ResourceProvider interface. |
312 | func (p *Provider) Resources() []terraform.ResourceType { | 328 | func (p *Provider) Resources() []terraform.ResourceType { |
313 | keys := make([]string, 0, len(p.ResourcesMap)) | 329 | keys := make([]string, 0, len(p.ResourcesMap)) |
314 | for k, _ := range p.ResourcesMap { | 330 | for k := range p.ResourcesMap { |
315 | keys = append(keys, k) | 331 | keys = append(keys, k) |
316 | } | 332 | } |
317 | sort.Strings(keys) | 333 | sort.Strings(keys) |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go index a8d42db..637e221 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go | |||
@@ -8,6 +8,7 @@ import ( | |||
8 | 8 | ||
9 | "github.com/hashicorp/go-multierror" | 9 | "github.com/hashicorp/go-multierror" |
10 | "github.com/hashicorp/terraform/config" | 10 | "github.com/hashicorp/terraform/config" |
11 | "github.com/hashicorp/terraform/configs/configschema" | ||
11 | "github.com/hashicorp/terraform/terraform" | 12 | "github.com/hashicorp/terraform/terraform" |
12 | ) | 13 | ) |
13 | 14 | ||
@@ -121,6 +122,11 @@ func (p *Provisioner) Stop() error { | |||
121 | return nil | 122 | return nil |
122 | } | 123 | } |
123 | 124 | ||
125 | // GetConfigSchema implementation of terraform.ResourceProvisioner interface. | ||
126 | func (p *Provisioner) GetConfigSchema() (*configschema.Block, error) { | ||
127 | return schemaMap(p.Schema).CoreConfigSchema(), nil | ||
128 | } | ||
129 | |||
124 | // Apply implementation of terraform.ResourceProvisioner interface. | 130 | // Apply implementation of terraform.ResourceProvisioner interface. |
125 | func (p *Provisioner) Apply( | 131 | func (p *Provisioner) Apply( |
126 | o terraform.UIOutput, | 132 | o terraform.UIOutput, |
@@ -146,7 +152,7 @@ func (p *Provisioner) Apply( | |||
146 | } | 152 | } |
147 | 153 | ||
148 | sm := schemaMap(p.ConnSchema) | 154 | sm := schemaMap(p.ConnSchema) |
149 | diff, err := sm.Diff(nil, terraform.NewResourceConfig(c), nil, nil) | 155 | diff, err := sm.Diff(nil, terraform.NewResourceConfig(c), nil, nil, true) |
150 | if err != nil { | 156 | if err != nil { |
151 | return err | 157 | return err |
152 | } | 158 | } |
@@ -160,7 +166,7 @@ func (p *Provisioner) Apply( | |||
160 | // Build the configuration data. Doing this requires making a "diff" | 166 | // Build the configuration data. Doing this requires making a "diff" |
161 | // even though that's never used. We use that just to get the correct types. | 167 | // even though that's never used. We use that just to get the correct types. |
162 | configMap := schemaMap(p.Schema) | 168 | configMap := schemaMap(p.Schema) |
163 | diff, err := configMap.Diff(nil, c, nil, nil) | 169 | diff, err := configMap.Diff(nil, c, nil, nil, true) |
164 | if err != nil { | 170 | if err != nil { |
165 | return err | 171 | return err |
166 | } | 172 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go index d3be2d6..b5e3065 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go | |||
@@ -8,6 +8,7 @@ import ( | |||
8 | 8 | ||
9 | "github.com/hashicorp/terraform/config" | 9 | "github.com/hashicorp/terraform/config" |
10 | "github.com/hashicorp/terraform/terraform" | 10 | "github.com/hashicorp/terraform/terraform" |
11 | "github.com/zclconf/go-cty/cty" | ||
11 | ) | 12 | ) |
12 | 13 | ||
13 | // Resource represents a thing in Terraform that has a set of configurable | 14 | // Resource represents a thing in Terraform that has a set of configurable |
@@ -44,6 +45,12 @@ type Resource struct { | |||
44 | // their Versioning at any integer >= 1 | 45 | // their Versioning at any integer >= 1 |
45 | SchemaVersion int | 46 | SchemaVersion int |
46 | 47 | ||
48 | // MigrateState is deprecated and any new changes to a resource's schema | ||
49 | // should be handled by StateUpgraders. Existing MigrateState implementations | ||
50 | // should remain for compatibility with existing state. MigrateState will | ||
51 | // still be called if the stored SchemaVersion is less than the | ||
52 | // first version of the StateUpgraders. | ||
53 | // | ||
47 | // MigrateState is responsible for updating an InstanceState with an old | 54 | // MigrateState is responsible for updating an InstanceState with an old |
48 | // version to the format expected by the current version of the Schema. | 55 | // version to the format expected by the current version of the Schema. |
49 | // | 56 | // |
@@ -56,6 +63,18 @@ type Resource struct { | |||
56 | // needs to make any remote API calls. | 63 | // needs to make any remote API calls. |
57 | MigrateState StateMigrateFunc | 64 | MigrateState StateMigrateFunc |
58 | 65 | ||
66 | // StateUpgraders contains the functions responsible for upgrading an | ||
67 | // existing state with an old schema version to a newer schema. It is | ||
68 | // called specifically by Terraform when the stored schema version is less | ||
69 | // than the current SchemaVersion of the Resource. | ||
70 | // | ||
71 | // StateUpgraders map specific schema versions to a StateUpgrader | ||
72 | // function. The registered versions are expected to be ordered, | ||
73 | // consecutive values. The initial value may be greater than 0 to account | ||
74 | // for legacy schemas that weren't recorded and can be handled by | ||
75 | // MigrateState. | ||
76 | StateUpgraders []StateUpgrader | ||
77 | |||
59 | // The functions below are the CRUD operations for this resource. | 78 | // The functions below are the CRUD operations for this resource. |
60 | // | 79 | // |
61 | // The only optional operation is Update. If Update is not implemented, | 80 | // The only optional operation is Update. If Update is not implemented, |
@@ -136,6 +155,27 @@ type Resource struct { | |||
136 | Timeouts *ResourceTimeout | 155 | Timeouts *ResourceTimeout |
137 | } | 156 | } |
138 | 157 | ||
158 | // ShimInstanceStateFromValue converts a cty.Value to a | ||
159 | // terraform.InstanceState. | ||
160 | func (r *Resource) ShimInstanceStateFromValue(state cty.Value) (*terraform.InstanceState, error) { | ||
161 | // Get the raw shimmed value. While this is correct, the set hashes don't | ||
162 | // match those from the Schema. | ||
163 | s := terraform.NewInstanceStateShimmedFromValue(state, r.SchemaVersion) | ||
164 | |||
165 | // We now rebuild the state through the ResourceData, so that the set indexes | ||
166 | // match what helper/schema expects. | ||
167 | data, err := schemaMap(r.Schema).Data(s, nil) | ||
168 | if err != nil { | ||
169 | return nil, err | ||
170 | } | ||
171 | |||
172 | s = data.State() | ||
173 | if s == nil { | ||
174 | s = &terraform.InstanceState{} | ||
175 | } | ||
176 | return s, nil | ||
177 | } | ||
178 | |||
139 | // See Resource documentation. | 179 | // See Resource documentation. |
140 | type CreateFunc func(*ResourceData, interface{}) error | 180 | type CreateFunc func(*ResourceData, interface{}) error |
141 | 181 | ||
@@ -155,6 +195,27 @@ type ExistsFunc func(*ResourceData, interface{}) (bool, error) | |||
155 | type StateMigrateFunc func( | 195 | type StateMigrateFunc func( |
156 | int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error) | 196 | int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error) |
157 | 197 | ||
198 | type StateUpgrader struct { | ||
199 | // Version is the version schema that this Upgrader will handle, converting | ||
200 | // it to Version+1. | ||
201 | Version int | ||
202 | |||
203 | // Type describes the schema that this function can upgrade. Type is | ||
204 | // required to decode the schema if the state was stored in a legacy | ||
205 | // flatmap format. | ||
206 | Type cty.Type | ||
207 | |||
208 | // Upgrade takes the JSON encoded state and the provider meta value, and | ||
209 | // upgrades the state one single schema version. The provided state is | ||
210 | // deocded into the default json types using a map[string]interface{}. It | ||
211 | // is up to the StateUpgradeFunc to ensure that the returned value can be | ||
212 | // encoded using the new schema. | ||
213 | Upgrade StateUpgradeFunc | ||
214 | } | ||
215 | |||
216 | // See StateUpgrader | ||
217 | type StateUpgradeFunc func(rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) | ||
218 | |||
158 | // See Resource documentation. | 219 | // See Resource documentation. |
159 | type CustomizeDiffFunc func(*ResourceDiff, interface{}) error | 220 | type CustomizeDiffFunc func(*ResourceDiff, interface{}) error |
160 | 221 | ||
@@ -247,7 +308,7 @@ func (r *Resource) Diff( | |||
247 | return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err) | 308 | return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err) |
248 | } | 309 | } |
249 | 310 | ||
250 | instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta) | 311 | instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta, true) |
251 | if err != nil { | 312 | if err != nil { |
252 | return instanceDiff, err | 313 | return instanceDiff, err |
253 | } | 314 | } |
@@ -263,6 +324,45 @@ func (r *Resource) Diff( | |||
263 | return instanceDiff, err | 324 | return instanceDiff, err |
264 | } | 325 | } |
265 | 326 | ||
327 | func (r *Resource) simpleDiff( | ||
328 | s *terraform.InstanceState, | ||
329 | c *terraform.ResourceConfig, | ||
330 | meta interface{}) (*terraform.InstanceDiff, error) { | ||
331 | |||
332 | t := &ResourceTimeout{} | ||
333 | err := t.ConfigDecode(r, c) | ||
334 | |||
335 | if err != nil { | ||
336 | return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err) | ||
337 | } | ||
338 | |||
339 | instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta, false) | ||
340 | if err != nil { | ||
341 | return instanceDiff, err | ||
342 | } | ||
343 | |||
344 | if instanceDiff == nil { | ||
345 | log.Printf("[DEBUG] Instance Diff is nil in SimpleDiff()") | ||
346 | return nil, err | ||
347 | } | ||
348 | |||
349 | // Make sure the old value is set in each of the instance diffs. | ||
350 | // This was done by the RequiresNew logic in the full legacy Diff. | ||
351 | for k, attr := range instanceDiff.Attributes { | ||
352 | if attr == nil { | ||
353 | continue | ||
354 | } | ||
355 | if s != nil { | ||
356 | attr.Old = s.Attributes[k] | ||
357 | } | ||
358 | } | ||
359 | |||
360 | if err := t.DiffEncode(instanceDiff); err != nil { | ||
361 | log.Printf("[ERR] Error encoding timeout to instance diff: %s", err) | ||
362 | } | ||
363 | return instanceDiff, err | ||
364 | } | ||
365 | |||
266 | // Validate validates the resource configuration against the schema. | 366 | // Validate validates the resource configuration against the schema. |
267 | func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) { | 367 | func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) { |
268 | warns, errs := schemaMap(r.Schema).Validate(c) | 368 | warns, errs := schemaMap(r.Schema).Validate(c) |
@@ -300,8 +400,11 @@ func (r *Resource) ReadDataApply( | |||
300 | return r.recordCurrentSchemaVersion(state), err | 400 | return r.recordCurrentSchemaVersion(state), err |
301 | } | 401 | } |
302 | 402 | ||
303 | // Refresh refreshes the state of the resource. | 403 | // RefreshWithoutUpgrade reads the instance state, but does not call |
304 | func (r *Resource) Refresh( | 404 | // MigrateState or the StateUpgraders, since those are now invoked in a |
405 | // separate API call. | ||
406 | // RefreshWithoutUpgrade is part of the new plugin shims. | ||
407 | func (r *Resource) RefreshWithoutUpgrade( | ||
305 | s *terraform.InstanceState, | 408 | s *terraform.InstanceState, |
306 | meta interface{}) (*terraform.InstanceState, error) { | 409 | meta interface{}) (*terraform.InstanceState, error) { |
307 | // If the ID is already somehow blank, it doesn't exist | 410 | // If the ID is already somehow blank, it doesn't exist |
@@ -335,12 +438,60 @@ func (r *Resource) Refresh( | |||
335 | } | 438 | } |
336 | } | 439 | } |
337 | 440 | ||
338 | needsMigration, stateSchemaVersion := r.checkSchemaVersion(s) | 441 | data, err := schemaMap(r.Schema).Data(s, nil) |
339 | if needsMigration && r.MigrateState != nil { | 442 | data.timeouts = &rt |
340 | s, err := r.MigrateState(stateSchemaVersion, s, meta) | 443 | if err != nil { |
444 | return s, err | ||
445 | } | ||
446 | |||
447 | err = r.Read(data, meta) | ||
448 | state := data.State() | ||
449 | if state != nil && state.ID == "" { | ||
450 | state = nil | ||
451 | } | ||
452 | |||
453 | return r.recordCurrentSchemaVersion(state), err | ||
454 | } | ||
455 | |||
456 | // Refresh refreshes the state of the resource. | ||
457 | func (r *Resource) Refresh( | ||
458 | s *terraform.InstanceState, | ||
459 | meta interface{}) (*terraform.InstanceState, error) { | ||
460 | // If the ID is already somehow blank, it doesn't exist | ||
461 | if s.ID == "" { | ||
462 | return nil, nil | ||
463 | } | ||
464 | |||
465 | rt := ResourceTimeout{} | ||
466 | if _, ok := s.Meta[TimeoutKey]; ok { | ||
467 | if err := rt.StateDecode(s); err != nil { | ||
468 | log.Printf("[ERR] Error decoding ResourceTimeout: %s", err) | ||
469 | } | ||
470 | } | ||
471 | |||
472 | if r.Exists != nil { | ||
473 | // Make a copy of data so that if it is modified it doesn't | ||
474 | // affect our Read later. | ||
475 | data, err := schemaMap(r.Schema).Data(s, nil) | ||
476 | data.timeouts = &rt | ||
477 | |||
341 | if err != nil { | 478 | if err != nil { |
342 | return s, err | 479 | return s, err |
343 | } | 480 | } |
481 | |||
482 | exists, err := r.Exists(data, meta) | ||
483 | if err != nil { | ||
484 | return s, err | ||
485 | } | ||
486 | if !exists { | ||
487 | return nil, nil | ||
488 | } | ||
489 | } | ||
490 | |||
491 | // there may be new StateUpgraders that need to be run | ||
492 | s, err := r.upgradeState(s, meta) | ||
493 | if err != nil { | ||
494 | return s, err | ||
344 | } | 495 | } |
345 | 496 | ||
346 | data, err := schemaMap(r.Schema).Data(s, nil) | 497 | data, err := schemaMap(r.Schema).Data(s, nil) |
@@ -358,6 +509,71 @@ func (r *Resource) Refresh( | |||
358 | return r.recordCurrentSchemaVersion(state), err | 509 | return r.recordCurrentSchemaVersion(state), err |
359 | } | 510 | } |
360 | 511 | ||
512 | func (r *Resource) upgradeState(s *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) { | ||
513 | var err error | ||
514 | |||
515 | needsMigration, stateSchemaVersion := r.checkSchemaVersion(s) | ||
516 | migrate := needsMigration && r.MigrateState != nil | ||
517 | |||
518 | if migrate { | ||
519 | s, err = r.MigrateState(stateSchemaVersion, s, meta) | ||
520 | if err != nil { | ||
521 | return s, err | ||
522 | } | ||
523 | } | ||
524 | |||
525 | if len(r.StateUpgraders) == 0 { | ||
526 | return s, nil | ||
527 | } | ||
528 | |||
529 | // If we ran MigrateState, then the stateSchemaVersion value is no longer | ||
530 | // correct. We can expect the first upgrade function to be the correct | ||
531 | // schema type version. | ||
532 | if migrate { | ||
533 | stateSchemaVersion = r.StateUpgraders[0].Version | ||
534 | } | ||
535 | |||
536 | schemaType := r.CoreConfigSchema().ImpliedType() | ||
537 | // find the expected type to convert the state | ||
538 | for _, upgrader := range r.StateUpgraders { | ||
539 | if stateSchemaVersion == upgrader.Version { | ||
540 | schemaType = upgrader.Type | ||
541 | } | ||
542 | } | ||
543 | |||
544 | // StateUpgraders only operate on the new JSON format state, so the state | ||
545 | // need to be converted. | ||
546 | stateVal, err := StateValueFromInstanceState(s, schemaType) | ||
547 | if err != nil { | ||
548 | return nil, err | ||
549 | } | ||
550 | |||
551 | jsonState, err := StateValueToJSONMap(stateVal, schemaType) | ||
552 | if err != nil { | ||
553 | return nil, err | ||
554 | } | ||
555 | |||
556 | for _, upgrader := range r.StateUpgraders { | ||
557 | if stateSchemaVersion != upgrader.Version { | ||
558 | continue | ||
559 | } | ||
560 | |||
561 | jsonState, err = upgrader.Upgrade(jsonState, meta) | ||
562 | if err != nil { | ||
563 | return nil, err | ||
564 | } | ||
565 | stateSchemaVersion++ | ||
566 | } | ||
567 | |||
568 | // now we need to re-flatmap the new state | ||
569 | stateVal, err = JSONMapToStateValue(jsonState, r.CoreConfigSchema()) | ||
570 | if err != nil { | ||
571 | return nil, err | ||
572 | } | ||
573 | |||
574 | return r.ShimInstanceStateFromValue(stateVal) | ||
575 | } | ||
576 | |||
361 | // InternalValidate should be called to validate the structure | 577 | // InternalValidate should be called to validate the structure |
362 | // of the resource. | 578 | // of the resource. |
363 | // | 579 | // |
@@ -437,6 +653,31 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error | |||
437 | } | 653 | } |
438 | } | 654 | } |
439 | 655 | ||
656 | lastVersion := -1 | ||
657 | for _, u := range r.StateUpgraders { | ||
658 | if lastVersion >= 0 && u.Version-lastVersion > 1 { | ||
659 | return fmt.Errorf("missing schema version between %d and %d", lastVersion, u.Version) | ||
660 | } | ||
661 | |||
662 | if u.Version >= r.SchemaVersion { | ||
663 | return fmt.Errorf("StateUpgrader version %d is >= current version %d", u.Version, r.SchemaVersion) | ||
664 | } | ||
665 | |||
666 | if !u.Type.IsObjectType() { | ||
667 | return fmt.Errorf("StateUpgrader %d type is not cty.Object", u.Version) | ||
668 | } | ||
669 | |||
670 | if u.Upgrade == nil { | ||
671 | return fmt.Errorf("StateUpgrader %d missing StateUpgradeFunc", u.Version) | ||
672 | } | ||
673 | |||
674 | lastVersion = u.Version | ||
675 | } | ||
676 | |||
677 | if lastVersion >= 0 && lastVersion != r.SchemaVersion-1 { | ||
678 | return fmt.Errorf("missing StateUpgrader between %d and %d", lastVersion, r.SchemaVersion) | ||
679 | } | ||
680 | |||
440 | // Data source | 681 | // Data source |
441 | if r.isTopLevel() && !writable { | 682 | if r.isTopLevel() && !writable { |
442 | tsm = schemaMap(r.Schema) | 683 | tsm = schemaMap(r.Schema) |
@@ -513,6 +754,13 @@ func (r *Resource) TestResourceData() *ResourceData { | |||
513 | } | 754 | } |
514 | } | 755 | } |
515 | 756 | ||
757 | // SchemasForFlatmapPath tries its best to find a sequence of schemas that | ||
758 | // the given dot-delimited attribute path traverses through in the schema | ||
759 | // of the receiving Resource. | ||
760 | func (r *Resource) SchemasForFlatmapPath(path string) []*Schema { | ||
761 | return SchemasForFlatmapPath(path, r.Schema) | ||
762 | } | ||
763 | |||
516 | // Returns true if the resource is "top level" i.e. not a sub-resource. | 764 | // Returns true if the resource is "top level" i.e. not a sub-resource. |
517 | func (r *Resource) isTopLevel() bool { | 765 | func (r *Resource) isTopLevel() bool { |
518 | // TODO: This is a heuristic; replace with a definitive attribute? | 766 | // TODO: This is a heuristic; replace with a definitive attribute? |
@@ -538,7 +786,15 @@ func (r *Resource) checkSchemaVersion(is *terraform.InstanceState) (bool, int) { | |||
538 | } | 786 | } |
539 | 787 | ||
540 | stateSchemaVersion, _ := strconv.Atoi(rawString) | 788 | stateSchemaVersion, _ := strconv.Atoi(rawString) |
541 | return stateSchemaVersion < r.SchemaVersion, stateSchemaVersion | 789 | |
790 | // Don't run MigrateState if the version is handled by a StateUpgrader, | ||
791 | // since StateMigrateFuncs are not required to handle unknown versions | ||
792 | maxVersion := r.SchemaVersion | ||
793 | if len(r.StateUpgraders) > 0 { | ||
794 | maxVersion = r.StateUpgraders[0].Version | ||
795 | } | ||
796 | |||
797 | return stateSchemaVersion < maxVersion, stateSchemaVersion | ||
542 | } | 798 | } |
543 | 799 | ||
544 | func (r *Resource) recordCurrentSchemaVersion( | 800 | func (r *Resource) recordCurrentSchemaVersion( |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go index 6cc01ee..1c39070 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go | |||
@@ -52,6 +52,8 @@ type getResult struct { | |||
52 | // UnsafeSetFieldRaw allows setting arbitrary values in state to arbitrary | 52 | // UnsafeSetFieldRaw allows setting arbitrary values in state to arbitrary |
53 | // values, bypassing schema. This MUST NOT be used in normal circumstances - | 53 | // values, bypassing schema. This MUST NOT be used in normal circumstances - |
54 | // it exists only to support the remote_state data source. | 54 | // it exists only to support the remote_state data source. |
55 | // | ||
56 | // Deprecated: Fully define schema attributes and use Set() instead. | ||
55 | func (d *ResourceData) UnsafeSetFieldRaw(key string, value string) { | 57 | func (d *ResourceData) UnsafeSetFieldRaw(key string, value string) { |
56 | d.once.Do(d.init) | 58 | d.once.Do(d.init) |
57 | 59 | ||
@@ -219,10 +221,16 @@ func (d *ResourceData) Id() string { | |||
219 | 221 | ||
220 | if d.state != nil { | 222 | if d.state != nil { |
221 | result = d.state.ID | 223 | result = d.state.ID |
224 | if result == "" { | ||
225 | result = d.state.Attributes["id"] | ||
226 | } | ||
222 | } | 227 | } |
223 | 228 | ||
224 | if d.newState != nil { | 229 | if d.newState != nil { |
225 | result = d.newState.ID | 230 | result = d.newState.ID |
231 | if result == "" { | ||
232 | result = d.newState.Attributes["id"] | ||
233 | } | ||
226 | } | 234 | } |
227 | 235 | ||
228 | return result | 236 | return result |
@@ -246,6 +254,18 @@ func (d *ResourceData) ConnInfo() map[string]string { | |||
246 | func (d *ResourceData) SetId(v string) { | 254 | func (d *ResourceData) SetId(v string) { |
247 | d.once.Do(d.init) | 255 | d.once.Do(d.init) |
248 | d.newState.ID = v | 256 | d.newState.ID = v |
257 | |||
258 | // once we transition away from the legacy state types, "id" will no longer | ||
259 | // be a special field, and will become a normal attribute. | ||
260 | // set the attribute normally | ||
261 | d.setWriter.unsafeWriteField("id", v) | ||
262 | |||
263 | // Make sure the newState is also set, otherwise the old value | ||
264 | // may get precedence. | ||
265 | if d.newState.Attributes == nil { | ||
266 | d.newState.Attributes = map[string]string{} | ||
267 | } | ||
268 | d.newState.Attributes["id"] = v | ||
249 | } | 269 | } |
250 | 270 | ||
251 | // SetConnInfo sets the connection info for a resource. | 271 | // SetConnInfo sets the connection info for a resource. |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go index 7db3dec..47b5481 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go | |||
@@ -367,7 +367,7 @@ func (d *ResourceDiff) Get(key string) interface{} { | |||
367 | } | 367 | } |
368 | 368 | ||
369 | // GetChange gets the change between the state and diff, checking first to see | 369 | // GetChange gets the change between the state and diff, checking first to see |
370 | // if a overridden diff exists. | 370 | // if an overridden diff exists. |
371 | // | 371 | // |
372 | // This implementation differs from ResourceData's in the way that we first get | 372 | // This implementation differs from ResourceData's in the way that we first get |
373 | // results from the exact levels for the new diff, then from state and diff as | 373 | // results from the exact levels for the new diff, then from state and diff as |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go index 445819f..9e422c1 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go | |||
@@ -5,6 +5,7 @@ import ( | |||
5 | "log" | 5 | "log" |
6 | "time" | 6 | "time" |
7 | 7 | ||
8 | "github.com/hashicorp/terraform/config" | ||
8 | "github.com/hashicorp/terraform/terraform" | 9 | "github.com/hashicorp/terraform/terraform" |
9 | "github.com/mitchellh/copystructure" | 10 | "github.com/mitchellh/copystructure" |
10 | ) | 11 | ) |
@@ -62,55 +63,70 @@ func (t *ResourceTimeout) ConfigDecode(s *Resource, c *terraform.ResourceConfig) | |||
62 | } | 63 | } |
63 | 64 | ||
64 | if raw, ok := c.Config[TimeoutsConfigKey]; ok { | 65 | if raw, ok := c.Config[TimeoutsConfigKey]; ok { |
65 | if configTimeouts, ok := raw.([]map[string]interface{}); ok { | 66 | var rawTimeouts []map[string]interface{} |
66 | for _, timeoutValues := range configTimeouts { | 67 | switch raw := raw.(type) { |
67 | // loop through each Timeout given in the configuration and validate they | 68 | case map[string]interface{}: |
68 | // the Timeout defined in the resource | 69 | rawTimeouts = append(rawTimeouts, raw) |
69 | for timeKey, timeValue := range timeoutValues { | 70 | case []map[string]interface{}: |
70 | // validate that we're dealing with the normal CRUD actions | 71 | rawTimeouts = raw |
71 | var found bool | 72 | case string: |
72 | for _, key := range timeoutKeys() { | 73 | if raw == config.UnknownVariableValue { |
73 | if timeKey == key { | 74 | // Timeout is not defined in the config |
74 | found = true | 75 | // Defaults will be used instead |
75 | break | 76 | return nil |
76 | } | 77 | } else { |
77 | } | 78 | log.Printf("[ERROR] Invalid timeout value: %q", raw) |
79 | return fmt.Errorf("Invalid Timeout value found") | ||
80 | } | ||
81 | default: | ||
82 | log.Printf("[ERROR] Invalid timeout structure: %#v", raw) | ||
83 | return fmt.Errorf("Invalid Timeout structure found") | ||
84 | } | ||
78 | 85 | ||
79 | if !found { | 86 | for _, timeoutValues := range rawTimeouts { |
80 | return fmt.Errorf("Unsupported Timeout configuration key found (%s)", timeKey) | 87 | for timeKey, timeValue := range timeoutValues { |
88 | // validate that we're dealing with the normal CRUD actions | ||
89 | var found bool | ||
90 | for _, key := range timeoutKeys() { | ||
91 | if timeKey == key { | ||
92 | found = true | ||
93 | break | ||
81 | } | 94 | } |
95 | } | ||
82 | 96 | ||
83 | // Get timeout | 97 | if !found { |
84 | rt, err := time.ParseDuration(timeValue.(string)) | 98 | return fmt.Errorf("Unsupported Timeout configuration key found (%s)", timeKey) |
85 | if err != nil { | 99 | } |
86 | return fmt.Errorf("Error parsing Timeout for (%s): %s", timeKey, err) | ||
87 | } | ||
88 | 100 | ||
89 | var timeout *time.Duration | 101 | // Get timeout |
90 | switch timeKey { | 102 | rt, err := time.ParseDuration(timeValue.(string)) |
91 | case TimeoutCreate: | 103 | if err != nil { |
92 | timeout = t.Create | 104 | return fmt.Errorf("Error parsing %q timeout: %s", timeKey, err) |
93 | case TimeoutUpdate: | 105 | } |
94 | timeout = t.Update | ||
95 | case TimeoutRead: | ||
96 | timeout = t.Read | ||
97 | case TimeoutDelete: | ||
98 | timeout = t.Delete | ||
99 | case TimeoutDefault: | ||
100 | timeout = t.Default | ||
101 | } | ||
102 | 106 | ||
103 | // If the resource has not delcared this in the definition, then error | 107 | var timeout *time.Duration |
104 | // with an unsupported message | 108 | switch timeKey { |
105 | if timeout == nil { | 109 | case TimeoutCreate: |
106 | return unsupportedTimeoutKeyError(timeKey) | 110 | timeout = t.Create |
107 | } | 111 | case TimeoutUpdate: |
112 | timeout = t.Update | ||
113 | case TimeoutRead: | ||
114 | timeout = t.Read | ||
115 | case TimeoutDelete: | ||
116 | timeout = t.Delete | ||
117 | case TimeoutDefault: | ||
118 | timeout = t.Default | ||
119 | } | ||
108 | 120 | ||
109 | *timeout = rt | 121 | // If the resource has not delcared this in the definition, then error |
122 | // with an unsupported message | ||
123 | if timeout == nil { | ||
124 | return unsupportedTimeoutKeyError(timeKey) | ||
110 | } | 125 | } |
126 | |||
127 | *timeout = rt | ||
111 | } | 128 | } |
112 | } else { | 129 | return nil |
113 | log.Printf("[WARN] Invalid Timeout structure found, skipping timeouts") | ||
114 | } | 130 | } |
115 | } | 131 | } |
116 | 132 | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go index 0ea5aad..6a3c15a 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go | |||
@@ -12,6 +12,7 @@ | |||
12 | package schema | 12 | package schema |
13 | 13 | ||
14 | import ( | 14 | import ( |
15 | "context" | ||
15 | "fmt" | 16 | "fmt" |
16 | "os" | 17 | "os" |
17 | "reflect" | 18 | "reflect" |
@@ -19,7 +20,9 @@ import ( | |||
19 | "sort" | 20 | "sort" |
20 | "strconv" | 21 | "strconv" |
21 | "strings" | 22 | "strings" |
23 | "sync" | ||
22 | 24 | ||
25 | "github.com/hashicorp/terraform/config" | ||
23 | "github.com/hashicorp/terraform/terraform" | 26 | "github.com/hashicorp/terraform/terraform" |
24 | "github.com/mitchellh/copystructure" | 27 | "github.com/mitchellh/copystructure" |
25 | "github.com/mitchellh/mapstructure" | 28 | "github.com/mitchellh/mapstructure" |
@@ -31,6 +34,27 @@ const PanicOnErr = "TF_SCHEMA_PANIC_ON_ERROR" | |||
31 | // type used for schema package context keys | 34 | // type used for schema package context keys |
32 | type contextKey string | 35 | type contextKey string |
33 | 36 | ||
37 | var ( | ||
38 | protoVersionMu sync.Mutex | ||
39 | protoVersion5 = false | ||
40 | ) | ||
41 | |||
42 | func isProto5() bool { | ||
43 | protoVersionMu.Lock() | ||
44 | defer protoVersionMu.Unlock() | ||
45 | return protoVersion5 | ||
46 | |||
47 | } | ||
48 | |||
49 | // SetProto5 enables a feature flag for any internal changes required required | ||
50 | // to work with the new plugin protocol. This should not be called by | ||
51 | // provider. | ||
52 | func SetProto5() { | ||
53 | protoVersionMu.Lock() | ||
54 | defer protoVersionMu.Unlock() | ||
55 | protoVersion5 = true | ||
56 | } | ||
57 | |||
34 | // Schema is used to describe the structure of a value. | 58 | // Schema is used to describe the structure of a value. |
35 | // | 59 | // |
36 | // Read the documentation of the struct elements for important details. | 60 | // Read the documentation of the struct elements for important details. |
@@ -51,6 +75,26 @@ type Schema struct { | |||
51 | // | 75 | // |
52 | Type ValueType | 76 | Type ValueType |
53 | 77 | ||
78 | // ConfigMode allows for overriding the default behaviors for mapping | ||
79 | // schema entries onto configuration constructs. | ||
80 | // | ||
81 | // By default, the Elem field is used to choose whether a particular | ||
82 | // schema is represented in configuration as an attribute or as a nested | ||
83 | // block; if Elem is a *schema.Resource then it's a block and it's an | ||
84 | // attribute otherwise. | ||
85 | // | ||
86 | // If Elem is *schema.Resource then setting ConfigMode to | ||
87 | // SchemaConfigModeAttr will force it to be represented in configuration | ||
88 | // as an attribute, which means that the Computed flag can be used to | ||
89 | // provide default elements when the argument isn't set at all, while still | ||
90 | // allowing the user to force zero elements by explicitly assigning an | ||
91 | // empty list. | ||
92 | // | ||
93 | // When Computed is set without Optional, the attribute is not settable | ||
94 | // in configuration at all and so SchemaConfigModeAttr is the automatic | ||
95 | // behavior, and SchemaConfigModeBlock is not permitted. | ||
96 | ConfigMode SchemaConfigMode | ||
97 | |||
54 | // If one of these is set, then this item can come from the configuration. | 98 | // If one of these is set, then this item can come from the configuration. |
55 | // Both cannot be set. If Optional is set, the value is optional. If | 99 | // Both cannot be set. If Optional is set, the value is optional. If |
56 | // Required is set, the value is required. | 100 | // Required is set, the value is required. |
@@ -123,7 +167,8 @@ type Schema struct { | |||
123 | // The following fields are only set for a TypeList, TypeSet, or TypeMap. | 167 | // The following fields are only set for a TypeList, TypeSet, or TypeMap. |
124 | // | 168 | // |
125 | // Elem represents the element type. For a TypeMap, it must be a *Schema | 169 | // Elem represents the element type. For a TypeMap, it must be a *Schema |
126 | // with a Type of TypeString, otherwise it may be either a *Schema or a | 170 | // with a Type that is one of the primitives: TypeString, TypeBool, |
171 | // TypeInt, or TypeFloat. Otherwise it may be either a *Schema or a | ||
127 | // *Resource. If it is *Schema, the element type is just a simple value. | 172 | // *Resource. If it is *Schema, the element type is just a simple value. |
128 | // If it is *Resource, the element type is a complex structure, | 173 | // If it is *Resource, the element type is a complex structure, |
129 | // potentially with its own lifecycle. | 174 | // potentially with its own lifecycle. |
@@ -141,13 +186,17 @@ type Schema struct { | |||
141 | // used to wrap a complex structure, however less than one instance would | 186 | // used to wrap a complex structure, however less than one instance would |
142 | // cause instability. | 187 | // cause instability. |
143 | // | 188 | // |
144 | // PromoteSingle, if true, will allow single elements to be standalone | 189 | // If the field Optional is set to true then MinItems is ignored and thus |
145 | // and promote them to a list. For example "foo" would be promoted to | 190 | // effectively zero. |
146 | // ["foo"] automatically. This is primarily for legacy reasons and the | 191 | MaxItems int |
147 | // ambiguity is not recommended for new usage. Promotion is only allowed | 192 | MinItems int |
148 | // for primitive element types. | 193 | |
149 | MaxItems int | 194 | // PromoteSingle originally allowed for a single element to be assigned |
150 | MinItems int | 195 | // where a primitive list was expected, but this no longer works from |
196 | // Terraform v0.12 onwards (Terraform Core will require a list to be set | ||
197 | // regardless of what this is set to) and so only applies to Terraform v0.11 | ||
198 | // and earlier, and so should be used only to retain this functionality | ||
199 | // for those still using v0.11 with a provider that formerly used this. | ||
151 | PromoteSingle bool | 200 | PromoteSingle bool |
152 | 201 | ||
153 | // The following fields are only valid for a TypeSet type. | 202 | // The following fields are only valid for a TypeSet type. |
@@ -189,7 +238,8 @@ type Schema struct { | |||
189 | // guaranteed to be of the proper Schema type, and it can yield warnings or | 238 | // guaranteed to be of the proper Schema type, and it can yield warnings or |
190 | // errors based on inspection of that value. | 239 | // errors based on inspection of that value. |
191 | // | 240 | // |
192 | // ValidateFunc currently only works for primitive types. | 241 | // ValidateFunc is honored only when the schema's Type is set to TypeInt, |
242 | // TypeFloat, TypeString, TypeBool, or TypeMap. It is ignored for all other types. | ||
193 | ValidateFunc SchemaValidateFunc | 243 | ValidateFunc SchemaValidateFunc |
194 | 244 | ||
195 | // Sensitive ensures that the attribute's value does not get displayed in | 245 | // Sensitive ensures that the attribute's value does not get displayed in |
@@ -199,6 +249,17 @@ type Schema struct { | |||
199 | Sensitive bool | 249 | Sensitive bool |
200 | } | 250 | } |
201 | 251 | ||
252 | // SchemaConfigMode is used to influence how a schema item is mapped into a | ||
253 | // corresponding configuration construct, using the ConfigMode field of | ||
254 | // Schema. | ||
255 | type SchemaConfigMode int | ||
256 | |||
257 | const ( | ||
258 | SchemaConfigModeAuto SchemaConfigMode = iota | ||
259 | SchemaConfigModeAttr | ||
260 | SchemaConfigModeBlock | ||
261 | ) | ||
262 | |||
202 | // SchemaDiffSuppressFunc is a function which can be used to determine | 263 | // SchemaDiffSuppressFunc is a function which can be used to determine |
203 | // whether a detected diff on a schema element is "valid" or not, and | 264 | // whether a detected diff on a schema element is "valid" or not, and |
204 | // suppress it from the plan if necessary. | 265 | // suppress it from the plan if necessary. |
@@ -364,6 +425,11 @@ func (s *Schema) finalizeDiff(d *terraform.ResourceAttrDiff, customized bool) *t | |||
364 | return d | 425 | return d |
365 | } | 426 | } |
366 | 427 | ||
428 | // InternalMap is used to aid in the transition to the new schema types and | ||
429 | // protocol. The name is not meant to convey any usefulness, as this is not to | ||
430 | // be used directly by any providers. | ||
431 | type InternalMap = schemaMap | ||
432 | |||
367 | // schemaMap is a wrapper that adds nice functions on top of schemas. | 433 | // schemaMap is a wrapper that adds nice functions on top of schemas. |
368 | type schemaMap map[string]*Schema | 434 | type schemaMap map[string]*Schema |
369 | 435 | ||
@@ -404,7 +470,8 @@ func (m schemaMap) Diff( | |||
404 | s *terraform.InstanceState, | 470 | s *terraform.InstanceState, |
405 | c *terraform.ResourceConfig, | 471 | c *terraform.ResourceConfig, |
406 | customizeDiff CustomizeDiffFunc, | 472 | customizeDiff CustomizeDiffFunc, |
407 | meta interface{}) (*terraform.InstanceDiff, error) { | 473 | meta interface{}, |
474 | handleRequiresNew bool) (*terraform.InstanceDiff, error) { | ||
408 | result := new(terraform.InstanceDiff) | 475 | result := new(terraform.InstanceDiff) |
409 | result.Attributes = make(map[string]*terraform.ResourceAttrDiff) | 476 | result.Attributes = make(map[string]*terraform.ResourceAttrDiff) |
410 | 477 | ||
@@ -450,82 +517,85 @@ func (m schemaMap) Diff( | |||
450 | } | 517 | } |
451 | } | 518 | } |
452 | 519 | ||
453 | // If the diff requires a new resource, then we recompute the diff | 520 | if handleRequiresNew { |
454 | // so we have the complete new resource diff, and preserve the | 521 | // If the diff requires a new resource, then we recompute the diff |
455 | // RequiresNew fields where necessary so the user knows exactly what | 522 | // so we have the complete new resource diff, and preserve the |
456 | // caused that. | 523 | // RequiresNew fields where necessary so the user knows exactly what |
457 | if result.RequiresNew() { | 524 | // caused that. |
458 | // Create the new diff | 525 | if result.RequiresNew() { |
459 | result2 := new(terraform.InstanceDiff) | 526 | // Create the new diff |
460 | result2.Attributes = make(map[string]*terraform.ResourceAttrDiff) | 527 | result2 := new(terraform.InstanceDiff) |
461 | 528 | result2.Attributes = make(map[string]*terraform.ResourceAttrDiff) | |
462 | // Preserve the DestroyTainted flag | ||
463 | result2.DestroyTainted = result.DestroyTainted | ||
464 | 529 | ||
465 | // Reset the data to not contain state. We have to call init() | 530 | // Preserve the DestroyTainted flag |
466 | // again in order to reset the FieldReaders. | 531 | result2.DestroyTainted = result.DestroyTainted |
467 | d.state = nil | ||
468 | d.init() | ||
469 | 532 | ||
470 | // Perform the diff again | 533 | // Reset the data to not contain state. We have to call init() |
471 | for k, schema := range m { | 534 | // again in order to reset the FieldReaders. |
472 | err := m.diff(k, schema, result2, d, false) | 535 | d.state = nil |
473 | if err != nil { | 536 | d.init() |
474 | return nil, err | ||
475 | } | ||
476 | } | ||
477 | 537 | ||
478 | // Re-run customization | 538 | // Perform the diff again |
479 | if !result2.DestroyTainted && customizeDiff != nil { | 539 | for k, schema := range m { |
480 | mc := m.DeepCopy() | 540 | err := m.diff(k, schema, result2, d, false) |
481 | rd := newResourceDiff(mc, c, d.state, result2) | ||
482 | if err := customizeDiff(rd, meta); err != nil { | ||
483 | return nil, err | ||
484 | } | ||
485 | for _, k := range rd.UpdatedKeys() { | ||
486 | err := m.diff(k, mc[k], result2, rd, false) | ||
487 | if err != nil { | 541 | if err != nil { |
488 | return nil, err | 542 | return nil, err |
489 | } | 543 | } |
490 | } | 544 | } |
491 | } | ||
492 | 545 | ||
493 | // Force all the fields to not force a new since we know what we | 546 | // Re-run customization |
494 | // want to force new. | 547 | if !result2.DestroyTainted && customizeDiff != nil { |
495 | for k, attr := range result2.Attributes { | 548 | mc := m.DeepCopy() |
496 | if attr == nil { | 549 | rd := newResourceDiff(mc, c, d.state, result2) |
497 | continue | 550 | if err := customizeDiff(rd, meta); err != nil { |
551 | return nil, err | ||
552 | } | ||
553 | for _, k := range rd.UpdatedKeys() { | ||
554 | err := m.diff(k, mc[k], result2, rd, false) | ||
555 | if err != nil { | ||
556 | return nil, err | ||
557 | } | ||
558 | } | ||
498 | } | 559 | } |
499 | 560 | ||
500 | if attr.RequiresNew { | 561 | // Force all the fields to not force a new since we know what we |
501 | attr.RequiresNew = false | 562 | // want to force new. |
502 | } | 563 | for k, attr := range result2.Attributes { |
564 | if attr == nil { | ||
565 | continue | ||
566 | } | ||
503 | 567 | ||
504 | if s != nil { | 568 | if attr.RequiresNew { |
505 | attr.Old = s.Attributes[k] | 569 | attr.RequiresNew = false |
506 | } | 570 | } |
507 | } | ||
508 | 571 | ||
509 | // Now copy in all the requires new diffs... | 572 | if s != nil { |
510 | for k, attr := range result.Attributes { | 573 | attr.Old = s.Attributes[k] |
511 | if attr == nil { | 574 | } |
512 | continue | ||
513 | } | 575 | } |
514 | 576 | ||
515 | newAttr, ok := result2.Attributes[k] | 577 | // Now copy in all the requires new diffs... |
516 | if !ok { | 578 | for k, attr := range result.Attributes { |
517 | newAttr = attr | 579 | if attr == nil { |
518 | } | 580 | continue |
581 | } | ||
519 | 582 | ||
520 | if attr.RequiresNew { | 583 | newAttr, ok := result2.Attributes[k] |
521 | newAttr.RequiresNew = true | 584 | if !ok { |
585 | newAttr = attr | ||
586 | } | ||
587 | |||
588 | if attr.RequiresNew { | ||
589 | newAttr.RequiresNew = true | ||
590 | } | ||
591 | |||
592 | result2.Attributes[k] = newAttr | ||
522 | } | 593 | } |
523 | 594 | ||
524 | result2.Attributes[k] = newAttr | 595 | // And set the diff! |
596 | result = result2 | ||
525 | } | 597 | } |
526 | 598 | ||
527 | // And set the diff! | ||
528 | result = result2 | ||
529 | } | 599 | } |
530 | 600 | ||
531 | // Go through and detect all of the ComputedWhens now that we've | 601 | // Go through and detect all of the ComputedWhens now that we've |
@@ -611,6 +681,10 @@ func (m schemaMap) Validate(c *terraform.ResourceConfig) ([]string, []error) { | |||
611 | // from a unit test (and not in user-path code) to verify that a schema | 681 | // from a unit test (and not in user-path code) to verify that a schema |
612 | // is properly built. | 682 | // is properly built. |
613 | func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error { | 683 | func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error { |
684 | return m.internalValidate(topSchemaMap, false) | ||
685 | } | ||
686 | |||
687 | func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) error { | ||
614 | if topSchemaMap == nil { | 688 | if topSchemaMap == nil { |
615 | topSchemaMap = m | 689 | topSchemaMap = m |
616 | } | 690 | } |
@@ -631,6 +705,34 @@ func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error { | |||
631 | return fmt.Errorf("%s: One of optional, required, or computed must be set", k) | 705 | return fmt.Errorf("%s: One of optional, required, or computed must be set", k) |
632 | } | 706 | } |
633 | 707 | ||
708 | computedOnly := v.Computed && !v.Optional | ||
709 | |||
710 | switch v.ConfigMode { | ||
711 | case SchemaConfigModeBlock: | ||
712 | if _, ok := v.Elem.(*Resource); !ok { | ||
713 | return fmt.Errorf("%s: ConfigMode of block is allowed only when Elem is *schema.Resource", k) | ||
714 | } | ||
715 | if attrsOnly { | ||
716 | return fmt.Errorf("%s: ConfigMode of block cannot be used in child of schema with ConfigMode of attribute", k) | ||
717 | } | ||
718 | if computedOnly { | ||
719 | return fmt.Errorf("%s: ConfigMode of block cannot be used for computed schema", k) | ||
720 | } | ||
721 | case SchemaConfigModeAttr: | ||
722 | // anything goes | ||
723 | case SchemaConfigModeAuto: | ||
724 | // Since "Auto" for Elem: *Resource would create a nested block, | ||
725 | // and that's impossible inside an attribute, we require it to be | ||
726 | // explicitly overridden as mode "Attr" for clarity. | ||
727 | if _, ok := v.Elem.(*Resource); ok { | ||
728 | if attrsOnly { | ||
729 | return fmt.Errorf("%s: in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute", k) | ||
730 | } | ||
731 | } | ||
732 | default: | ||
733 | return fmt.Errorf("%s: invalid ConfigMode value", k) | ||
734 | } | ||
735 | |||
634 | if v.Computed && v.Default != nil { | 736 | if v.Computed && v.Default != nil { |
635 | return fmt.Errorf("%s: Default must be nil if computed", k) | 737 | return fmt.Errorf("%s: Default must be nil if computed", k) |
636 | } | 738 | } |
@@ -695,7 +797,9 @@ func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error { | |||
695 | 797 | ||
696 | switch t := v.Elem.(type) { | 798 | switch t := v.Elem.(type) { |
697 | case *Resource: | 799 | case *Resource: |
698 | if err := t.InternalValidate(topSchemaMap, true); err != nil { | 800 | attrsOnly := attrsOnly || v.ConfigMode == SchemaConfigModeAttr |
801 | |||
802 | if err := schemaMap(t.Schema).internalValidate(topSchemaMap, attrsOnly); err != nil { | ||
699 | return err | 803 | return err |
700 | } | 804 | } |
701 | case *Schema: | 805 | case *Schema: |
@@ -785,10 +889,19 @@ func (m schemaMap) diff( | |||
785 | for attrK, attrV := range unsupressedDiff.Attributes { | 889 | for attrK, attrV := range unsupressedDiff.Attributes { |
786 | switch rd := d.(type) { | 890 | switch rd := d.(type) { |
787 | case *ResourceData: | 891 | case *ResourceData: |
788 | if schema.DiffSuppressFunc != nil && | 892 | if schema.DiffSuppressFunc != nil && attrV != nil && |
789 | attrV != nil && | ||
790 | schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, rd) { | 893 | schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, rd) { |
791 | continue | 894 | // If this attr diff is suppressed, we may still need it in the |
895 | // overall diff if it's contained within a set. Rather than | ||
896 | // dropping the diff, make it a NOOP. | ||
897 | if !all { | ||
898 | continue | ||
899 | } | ||
900 | |||
901 | attrV = &terraform.ResourceAttrDiff{ | ||
902 | Old: attrV.Old, | ||
903 | New: attrV.Old, | ||
904 | } | ||
792 | } | 905 | } |
793 | } | 906 | } |
794 | diff.Attributes[attrK] = attrV | 907 | diff.Attributes[attrK] = attrV |
@@ -1171,7 +1284,7 @@ func (m schemaMap) diffString( | |||
1171 | return fmt.Errorf("%s: %s", k, err) | 1284 | return fmt.Errorf("%s: %s", k, err) |
1172 | } | 1285 | } |
1173 | 1286 | ||
1174 | if os == ns && !all { | 1287 | if os == ns && !all && !computed { |
1175 | // They're the same value. If there old value is not blank or we | 1288 | // They're the same value. If there old value is not blank or we |
1176 | // have an ID, then return right away since we're already setup. | 1289 | // have an ID, then return right away since we're already setup. |
1177 | if os != "" || d.Id() != "" { | 1290 | if os != "" || d.Id() != "" { |
@@ -1179,7 +1292,7 @@ func (m schemaMap) diffString( | |||
1179 | } | 1292 | } |
1180 | 1293 | ||
1181 | // Otherwise, only continue if we're computed | 1294 | // Otherwise, only continue if we're computed |
1182 | if !schema.Computed && !computed { | 1295 | if !schema.Computed { |
1183 | return nil | 1296 | return nil |
1184 | } | 1297 | } |
1185 | } | 1298 | } |
@@ -1210,7 +1323,7 @@ func (m schemaMap) inputString( | |||
1210 | input terraform.UIInput, | 1323 | input terraform.UIInput, |
1211 | k string, | 1324 | k string, |
1212 | schema *Schema) (interface{}, error) { | 1325 | schema *Schema) (interface{}, error) { |
1213 | result, err := input.Input(&terraform.InputOpts{ | 1326 | result, err := input.Input(context.Background(), &terraform.InputOpts{ |
1214 | Id: k, | 1327 | Id: k, |
1215 | Query: k, | 1328 | Query: k, |
1216 | Description: schema.Description, | 1329 | Description: schema.Description, |
@@ -1252,6 +1365,13 @@ func (m schemaMap) validate( | |||
1252 | "%q: this field cannot be set", k)} | 1365 | "%q: this field cannot be set", k)} |
1253 | } | 1366 | } |
1254 | 1367 | ||
1368 | if raw == config.UnknownVariableValue { | ||
1369 | // If the value is unknown then we can't validate it yet. | ||
1370 | // In particular, this avoids spurious type errors where downstream | ||
1371 | // validation code sees UnknownVariableValue as being just a string. | ||
1372 | return nil, nil | ||
1373 | } | ||
1374 | |||
1255 | err := m.validateConflictingAttributes(k, schema, c) | 1375 | err := m.validateConflictingAttributes(k, schema, c) |
1256 | if err != nil { | 1376 | if err != nil { |
1257 | return nil, []error{err} | 1377 | return nil, []error{err} |
@@ -1269,10 +1389,15 @@ func (m schemaMap) validateConflictingAttributes( | |||
1269 | return nil | 1389 | return nil |
1270 | } | 1390 | } |
1271 | 1391 | ||
1272 | for _, conflicting_key := range schema.ConflictsWith { | 1392 | for _, conflictingKey := range schema.ConflictsWith { |
1273 | if _, ok := c.Get(conflicting_key); ok { | 1393 | if raw, ok := c.Get(conflictingKey); ok { |
1394 | if raw == config.UnknownVariableValue { | ||
1395 | // An unknown value might become unset (null) once known, so | ||
1396 | // we must defer validation until it's known. | ||
1397 | continue | ||
1398 | } | ||
1274 | return fmt.Errorf( | 1399 | return fmt.Errorf( |
1275 | "%q: conflicts with %s", k, conflicting_key) | 1400 | "%q: conflicts with %s", k, conflictingKey) |
1276 | } | 1401 | } |
1277 | } | 1402 | } |
1278 | 1403 | ||
@@ -1284,6 +1409,13 @@ func (m schemaMap) validateList( | |||
1284 | raw interface{}, | 1409 | raw interface{}, |
1285 | schema *Schema, | 1410 | schema *Schema, |
1286 | c *terraform.ResourceConfig) ([]string, []error) { | 1411 | c *terraform.ResourceConfig) ([]string, []error) { |
1412 | // first check if the list is wholly unknown | ||
1413 | if s, ok := raw.(string); ok { | ||
1414 | if s == config.UnknownVariableValue { | ||
1415 | return nil, nil | ||
1416 | } | ||
1417 | } | ||
1418 | |||
1287 | // We use reflection to verify the slice because you can't | 1419 | // We use reflection to verify the slice because you can't |
1288 | // case to []interface{} unless the slice is exactly that type. | 1420 | // case to []interface{} unless the slice is exactly that type. |
1289 | rawV := reflect.ValueOf(raw) | 1421 | rawV := reflect.ValueOf(raw) |
@@ -1355,6 +1487,13 @@ func (m schemaMap) validateMap( | |||
1355 | raw interface{}, | 1487 | raw interface{}, |
1356 | schema *Schema, | 1488 | schema *Schema, |
1357 | c *terraform.ResourceConfig) ([]string, []error) { | 1489 | c *terraform.ResourceConfig) ([]string, []error) { |
1490 | // first check if the list is wholly unknown | ||
1491 | if s, ok := raw.(string); ok { | ||
1492 | if s == config.UnknownVariableValue { | ||
1493 | return nil, nil | ||
1494 | } | ||
1495 | } | ||
1496 | |||
1358 | // We use reflection to verify the slice because you can't | 1497 | // We use reflection to verify the slice because you can't |
1359 | // case to []interface{} unless the slice is exactly that type. | 1498 | // case to []interface{} unless the slice is exactly that type. |
1360 | rawV := reflect.ValueOf(raw) | 1499 | rawV := reflect.ValueOf(raw) |
@@ -1556,12 +1695,25 @@ func (m schemaMap) validatePrimitive( | |||
1556 | } | 1695 | } |
1557 | decoded = n | 1696 | decoded = n |
1558 | case TypeInt: | 1697 | case TypeInt: |
1559 | // Verify that we can parse this as an int | 1698 | switch { |
1560 | var n int | 1699 | case isProto5(): |
1561 | if err := mapstructure.WeakDecode(raw, &n); err != nil { | 1700 | // We need to verify the type precisely, because WeakDecode will |
1562 | return nil, []error{fmt.Errorf("%s: %s", k, err)} | 1701 | // decode a float as an integer. |
1702 | |||
1703 | // the config shims only use int for integral number values | ||
1704 | if v, ok := raw.(int); ok { | ||
1705 | decoded = v | ||
1706 | } else { | ||
1707 | return nil, []error{fmt.Errorf("%s: must be a whole number, got %v", k, raw)} | ||
1708 | } | ||
1709 | default: | ||
1710 | // Verify that we can parse this as an int | ||
1711 | var n int | ||
1712 | if err := mapstructure.WeakDecode(raw, &n); err != nil { | ||
1713 | return nil, []error{fmt.Errorf("%s: %s", k, err)} | ||
1714 | } | ||
1715 | decoded = n | ||
1563 | } | 1716 | } |
1564 | decoded = n | ||
1565 | case TypeFloat: | 1717 | case TypeFloat: |
1566 | // Verify that we can parse this as an int | 1718 | // Verify that we can parse this as an int |
1567 | var n float64 | 1719 | var n float64 |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/set.go b/vendor/github.com/hashicorp/terraform/helper/schema/set.go index cba2890..8ee89e4 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/set.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/set.go | |||
@@ -198,6 +198,16 @@ func (s *Set) add(item interface{}, computed bool) string { | |||
198 | code := s.hash(item) | 198 | code := s.hash(item) |
199 | if computed { | 199 | if computed { |
200 | code = "~" + code | 200 | code = "~" + code |
201 | |||
202 | if isProto5() { | ||
203 | tmpCode := code | ||
204 | count := 0 | ||
205 | for _, exists := s.m[tmpCode]; exists; _, exists = s.m[tmpCode] { | ||
206 | count++ | ||
207 | tmpCode = fmt.Sprintf("%s%d", code, count) | ||
208 | } | ||
209 | code = tmpCode | ||
210 | } | ||
201 | } | 211 | } |
202 | 212 | ||
203 | if _, ok := s.m[code]; !ok { | 213 | if _, ok := s.m[code]; !ok { |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/shims.go b/vendor/github.com/hashicorp/terraform/helper/schema/shims.go new file mode 100644 index 0000000..203d017 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/schema/shims.go | |||
@@ -0,0 +1,115 @@ | |||
1 | package schema | ||
2 | |||
3 | import ( | ||
4 | "encoding/json" | ||
5 | |||
6 | "github.com/zclconf/go-cty/cty" | ||
7 | ctyjson "github.com/zclconf/go-cty/cty/json" | ||
8 | |||
9 | "github.com/hashicorp/terraform/config" | ||
10 | "github.com/hashicorp/terraform/configs/configschema" | ||
11 | "github.com/hashicorp/terraform/terraform" | ||
12 | ) | ||
13 | |||
14 | // DiffFromValues takes the current state and desired state as cty.Values and | ||
15 | // derives a terraform.InstanceDiff to give to the legacy providers. This is | ||
16 | // used to take the states provided by the new ApplyResourceChange method and | ||
17 | // convert them to a state+diff required for the legacy Apply method. | ||
18 | func DiffFromValues(prior, planned cty.Value, res *Resource) (*terraform.InstanceDiff, error) { | ||
19 | return diffFromValues(prior, planned, res, nil) | ||
20 | } | ||
21 | |||
22 | // diffFromValues takes an additional CustomizeDiffFunc, so we can generate our | ||
23 | // test fixtures from the legacy tests. In the new provider protocol the diff | ||
24 | // only needs to be created for the apply operation, and any customizations | ||
25 | // have already been done. | ||
26 | func diffFromValues(prior, planned cty.Value, res *Resource, cust CustomizeDiffFunc) (*terraform.InstanceDiff, error) { | ||
27 | instanceState, err := res.ShimInstanceStateFromValue(prior) | ||
28 | if err != nil { | ||
29 | return nil, err | ||
30 | } | ||
31 | |||
32 | configSchema := res.CoreConfigSchema() | ||
33 | |||
34 | cfg := terraform.NewResourceConfigShimmed(planned, configSchema) | ||
35 | removeConfigUnknowns(cfg.Config) | ||
36 | removeConfigUnknowns(cfg.Raw) | ||
37 | |||
38 | diff, err := schemaMap(res.Schema).Diff(instanceState, cfg, cust, nil, false) | ||
39 | if err != nil { | ||
40 | return nil, err | ||
41 | } | ||
42 | |||
43 | return diff, err | ||
44 | } | ||
45 | |||
46 | // During apply the only unknown values are those which are to be computed by | ||
47 | // the resource itself. These may have been marked as unknown config values, and | ||
48 | // need to be removed to prevent the UnknownVariableValue from appearing the diff. | ||
49 | func removeConfigUnknowns(cfg map[string]interface{}) { | ||
50 | for k, v := range cfg { | ||
51 | switch v := v.(type) { | ||
52 | case string: | ||
53 | if v == config.UnknownVariableValue { | ||
54 | delete(cfg, k) | ||
55 | } | ||
56 | case []interface{}: | ||
57 | for _, i := range v { | ||
58 | if m, ok := i.(map[string]interface{}); ok { | ||
59 | removeConfigUnknowns(m) | ||
60 | } | ||
61 | } | ||
62 | case map[string]interface{}: | ||
63 | removeConfigUnknowns(v) | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
68 | // ApplyDiff takes a cty.Value state and applies a terraform.InstanceDiff to | ||
69 | // get a new cty.Value state. This is used to convert the diff returned from | ||
70 | // the legacy provider Diff method to the state required for the new | ||
71 | // PlanResourceChange method. | ||
72 | func ApplyDiff(base cty.Value, d *terraform.InstanceDiff, schema *configschema.Block) (cty.Value, error) { | ||
73 | return d.ApplyToValue(base, schema) | ||
74 | } | ||
75 | |||
76 | // StateValueToJSONMap converts a cty.Value to generic JSON map via the cty JSON | ||
77 | // encoding. | ||
78 | func StateValueToJSONMap(val cty.Value, ty cty.Type) (map[string]interface{}, error) { | ||
79 | js, err := ctyjson.Marshal(val, ty) | ||
80 | if err != nil { | ||
81 | return nil, err | ||
82 | } | ||
83 | |||
84 | var m map[string]interface{} | ||
85 | if err := json.Unmarshal(js, &m); err != nil { | ||
86 | return nil, err | ||
87 | } | ||
88 | |||
89 | return m, nil | ||
90 | } | ||
91 | |||
92 | // JSONMapToStateValue takes a generic json map[string]interface{} and converts it | ||
93 | // to the specific type, ensuring that the values conform to the schema. | ||
94 | func JSONMapToStateValue(m map[string]interface{}, block *configschema.Block) (cty.Value, error) { | ||
95 | var val cty.Value | ||
96 | |||
97 | js, err := json.Marshal(m) | ||
98 | if err != nil { | ||
99 | return val, err | ||
100 | } | ||
101 | |||
102 | val, err = ctyjson.Unmarshal(js, block.ImpliedType()) | ||
103 | if err != nil { | ||
104 | return val, err | ||
105 | } | ||
106 | |||
107 | return block.CoerceValue(val) | ||
108 | } | ||
109 | |||
110 | // StateValueFromInstanceState converts a terraform.InstanceState to a | ||
111 | // cty.Value as described by the provided cty.Type, and maintains the resource | ||
112 | // ID as the "id" attribute. | ||
113 | func StateValueFromInstanceState(is *terraform.InstanceState, ty cty.Type) (cty.Value, error) { | ||
114 | return is.AttrsAsObjectValue(ty) | ||
115 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/testing.go b/vendor/github.com/hashicorp/terraform/helper/schema/testing.go index da754ac..a367a1f 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/testing.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/testing.go | |||
@@ -18,7 +18,7 @@ func TestResourceDataRaw( | |||
18 | } | 18 | } |
19 | 19 | ||
20 | sm := schemaMap(schema) | 20 | sm := schemaMap(schema) |
21 | diff, err := sm.Diff(nil, terraform.NewResourceConfig(c), nil, nil) | 21 | diff, err := sm.Diff(nil, terraform.NewResourceConfig(c), nil, nil, true) |
22 | if err != nil { | 22 | if err != nil { |
23 | t.Fatalf("err: %s", err) | 23 | t.Fatalf("err: %s", err) |
24 | } | 24 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go b/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go index 3bc3ac4..914ca32 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go | |||
@@ -4,6 +4,21 @@ package schema | |||
4 | 4 | ||
5 | import "strconv" | 5 | import "strconv" |
6 | 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[TypeInvalid-0] | ||
12 | _ = x[TypeBool-1] | ||
13 | _ = x[TypeInt-2] | ||
14 | _ = x[TypeFloat-3] | ||
15 | _ = x[TypeString-4] | ||
16 | _ = x[TypeList-5] | ||
17 | _ = x[TypeMap-6] | ||
18 | _ = x[TypeSet-7] | ||
19 | _ = x[typeObject-8] | ||
20 | } | ||
21 | |||
7 | const _ValueType_name = "TypeInvalidTypeBoolTypeIntTypeFloatTypeStringTypeListTypeMapTypeSettypeObject" | 22 | const _ValueType_name = "TypeInvalidTypeBoolTypeIntTypeFloatTypeStringTypeListTypeMapTypeSettypeObject" |
8 | 23 | ||
9 | var _ValueType_index = [...]uint8{0, 11, 19, 26, 35, 45, 53, 60, 67, 77} | 24 | var _ValueType_index = [...]uint8{0, 11, 19, 26, 35, 45, 53, 60, 67, 77} |