diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper/schema')
18 files changed, 1158 insertions, 128 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/backend.go b/vendor/github.com/hashicorp/terraform/helper/schema/backend.go index a0729c0..57fbba7 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/backend.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/backend.go | |||
@@ -65,7 +65,7 @@ func (b *Backend) Configure(c *terraform.ResourceConfig) error { | |||
65 | 65 | ||
66 | // Get a ResourceData for this configuration. To do this, we actually | 66 | // Get a ResourceData for this configuration. To do this, we actually |
67 | // generate an intermediary "diff" although that is never exposed. | 67 | // generate an intermediary "diff" although that is never exposed. |
68 | diff, err := sm.Diff(nil, c) | 68 | diff, err := sm.Diff(nil, c, nil, nil) |
69 | if err != nil { | 69 | if err != nil { |
70 | return err | 70 | return err |
71 | } | 71 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go new file mode 100644 index 0000000..bf952f6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go | |||
@@ -0,0 +1,155 @@ | |||
1 | package schema | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/terraform/config/configschema" | ||
7 | "github.com/zclconf/go-cty/cty" | ||
8 | ) | ||
9 | |||
10 | // The functions and methods in this file are concerned with the conversion | ||
11 | // of this package's schema model into the slightly-lower-level schema model | ||
12 | // used by Terraform core for configuration parsing. | ||
13 | |||
14 | // CoreConfigSchema lowers the receiver to the schema model expected by | ||
15 | // Terraform core. | ||
16 | // | ||
17 | // This lower-level model has fewer features than the schema in this package, | ||
18 | // describing only the basic structure of configuration and state values we | ||
19 | // expect. The full schemaMap from this package is still required for full | ||
20 | // validation, handling of default values, etc. | ||
21 | // | ||
22 | // This method presumes a schema that passes InternalValidate, and so may | ||
23 | // panic or produce an invalid result if given an invalid schemaMap. | ||
24 | func (m schemaMap) CoreConfigSchema() *configschema.Block { | ||
25 | if len(m) == 0 { | ||
26 | // We return an actual (empty) object here, rather than a nil, | ||
27 | // because a nil result would mean that we don't have a schema at | ||
28 | // all, rather than that we have an empty one. | ||
29 | return &configschema.Block{} | ||
30 | } | ||
31 | |||
32 | ret := &configschema.Block{ | ||
33 | Attributes: map[string]*configschema.Attribute{}, | ||
34 | BlockTypes: map[string]*configschema.NestedBlock{}, | ||
35 | } | ||
36 | |||
37 | for name, schema := range m { | ||
38 | if schema.Elem == nil { | ||
39 | ret.Attributes[name] = schema.coreConfigSchemaAttribute() | ||
40 | continue | ||
41 | } | ||
42 | switch schema.Elem.(type) { | ||
43 | case *Schema: | ||
44 | ret.Attributes[name] = schema.coreConfigSchemaAttribute() | ||
45 | case *Resource: | ||
46 | ret.BlockTypes[name] = schema.coreConfigSchemaBlock() | ||
47 | default: | ||
48 | // Should never happen for a valid schema | ||
49 | panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", schema.Elem)) | ||
50 | } | ||
51 | } | ||
52 | |||
53 | return ret | ||
54 | } | ||
55 | |||
56 | // coreConfigSchemaAttribute prepares a configschema.Attribute representation | ||
57 | // of a schema. This is appropriate only for primitives or collections whose | ||
58 | // Elem is an instance of Schema. Use coreConfigSchemaBlock for collections | ||
59 | // whose elem is a whole resource. | ||
60 | func (s *Schema) coreConfigSchemaAttribute() *configschema.Attribute { | ||
61 | return &configschema.Attribute{ | ||
62 | Type: s.coreConfigSchemaType(), | ||
63 | Optional: s.Optional, | ||
64 | Required: s.Required, | ||
65 | Computed: s.Computed, | ||
66 | Sensitive: s.Sensitive, | ||
67 | } | ||
68 | } | ||
69 | |||
70 | // coreConfigSchemaBlock prepares a configschema.NestedBlock representation of | ||
71 | // a schema. This is appropriate only for collections whose Elem is an instance | ||
72 | // of Resource, and will panic otherwise. | ||
73 | func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock { | ||
74 | ret := &configschema.NestedBlock{} | ||
75 | if nested := s.Elem.(*Resource).CoreConfigSchema(); nested != nil { | ||
76 | ret.Block = *nested | ||
77 | } | ||
78 | switch s.Type { | ||
79 | case TypeList: | ||
80 | ret.Nesting = configschema.NestingList | ||
81 | case TypeSet: | ||
82 | ret.Nesting = configschema.NestingSet | ||
83 | case TypeMap: | ||
84 | ret.Nesting = configschema.NestingMap | ||
85 | default: | ||
86 | // Should never happen for a valid schema | ||
87 | panic(fmt.Errorf("invalid s.Type %s for s.Elem being resource", s.Type)) | ||
88 | } | ||
89 | |||
90 | ret.MinItems = s.MinItems | ||
91 | ret.MaxItems = s.MaxItems | ||
92 | |||
93 | if s.Required && s.MinItems == 0 { | ||
94 | // configschema doesn't have a "required" representation for nested | ||
95 | // blocks, but we can fake it by requiring at least one item. | ||
96 | ret.MinItems = 1 | ||
97 | } | ||
98 | |||
99 | return ret | ||
100 | } | ||
101 | |||
102 | // coreConfigSchemaType determines the core config schema type that corresponds | ||
103 | // to a particular schema's type. | ||
104 | func (s *Schema) coreConfigSchemaType() cty.Type { | ||
105 | switch s.Type { | ||
106 | case TypeString: | ||
107 | return cty.String | ||
108 | case TypeBool: | ||
109 | return cty.Bool | ||
110 | case TypeInt, TypeFloat: | ||
111 | // configschema doesn't distinguish int and float, so helper/schema | ||
112 | // will deal with this as an additional validation step after | ||
113 | // configuration has been parsed and decoded. | ||
114 | return cty.Number | ||
115 | case TypeList, TypeSet, TypeMap: | ||
116 | var elemType cty.Type | ||
117 | switch set := s.Elem.(type) { | ||
118 | case *Schema: | ||
119 | elemType = set.coreConfigSchemaType() | ||
120 | case *Resource: | ||
121 | // In practice we don't actually use this for normal schema | ||
122 | // construction because we construct a NestedBlock in that | ||
123 | // case instead. See schemaMap.CoreConfigSchema. | ||
124 | elemType = set.CoreConfigSchema().ImpliedType() | ||
125 | default: | ||
126 | if set != nil { | ||
127 | // Should never happen for a valid schema | ||
128 | panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", s.Elem)) | ||
129 | } | ||
130 | // Some pre-existing schemas assume string as default, so we need | ||
131 | // to be compatible with them. | ||
132 | elemType = cty.String | ||
133 | } | ||
134 | switch s.Type { | ||
135 | case TypeList: | ||
136 | return cty.List(elemType) | ||
137 | case TypeSet: | ||
138 | return cty.Set(elemType) | ||
139 | case TypeMap: | ||
140 | return cty.Map(elemType) | ||
141 | default: | ||
142 | // can never get here in practice, due to the case we're inside | ||
143 | panic("invalid collection type") | ||
144 | } | ||
145 | default: | ||
146 | // should never happen for a valid schema | ||
147 | panic(fmt.Errorf("invalid Schema.Type %s", s.Type)) | ||
148 | } | ||
149 | } | ||
150 | |||
151 | // CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema | ||
152 | // on the resource's schema. | ||
153 | func (r *Resource) CoreConfigSchema() *configschema.Block { | ||
154 | return schemaMap(r.Schema).CoreConfigSchema() | ||
155 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go b/vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go index 5a03d2d..8d93750 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go | |||
@@ -32,7 +32,7 @@ func DataSourceResourceShim(name string, dataSource *Resource) *Resource { | |||
32 | 32 | ||
33 | // FIXME: Link to some further docs either on the website or in the | 33 | // FIXME: Link to some further docs either on the website or in the |
34 | // changelog, once such a thing exists. | 34 | // changelog, once such a thing exists. |
35 | dataSource.deprecationMessage = fmt.Sprintf( | 35 | dataSource.DeprecationMessage = fmt.Sprintf( |
36 | "using %s as a resource is deprecated; consider using the data source instead", | 36 | "using %s as a resource is deprecated; consider using the data source instead", |
37 | name, | 37 | name, |
38 | ) | 38 | ) |
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 1660a67..b80b223 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go | |||
@@ -126,6 +126,8 @@ func addrToSchema(addr []string, schemaMap map[string]*Schema) []*Schema { | |||
126 | switch v := current.Elem.(type) { | 126 | switch v := current.Elem.(type) { |
127 | case ValueType: | 127 | case ValueType: |
128 | current = &Schema{Type: v} | 128 | current = &Schema{Type: v} |
129 | case *Schema: | ||
130 | current, _ = current.Elem.(*Schema) | ||
129 | default: | 131 | default: |
130 | // maps default to string values. This is all we can have | 132 | // maps default to string values. This is all we can have |
131 | // if this is nested in another list or map. | 133 | // if this is nested in another list or map. |
@@ -249,11 +251,10 @@ func readObjectField( | |||
249 | } | 251 | } |
250 | 252 | ||
251 | // convert map values to the proper primitive type based on schema.Elem | 253 | // convert map values to the proper primitive type based on schema.Elem |
252 | func mapValuesToPrimitive(m map[string]interface{}, schema *Schema) error { | 254 | func mapValuesToPrimitive(k string, m map[string]interface{}, schema *Schema) error { |
253 | 255 | elemType, err := getValueType(k, schema) | |
254 | elemType := TypeString | 256 | if err != nil { |
255 | if et, ok := schema.Elem.(ValueType); ok { | 257 | return err |
256 | elemType = et | ||
257 | } | 258 | } |
258 | 259 | ||
259 | switch elemType { | 260 | switch elemType { |
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 f958bbc..55a301d 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 | |||
@@ -206,7 +206,7 @@ func (r *ConfigFieldReader) readMap(k string, schema *Schema) (FieldReadResult, | |||
206 | panic(fmt.Sprintf("unknown type: %#v", mraw)) | 206 | panic(fmt.Sprintf("unknown type: %#v", mraw)) |
207 | } | 207 | } |
208 | 208 | ||
209 | err := mapValuesToPrimitive(result, schema) | 209 | err := mapValuesToPrimitive(k, result, schema) |
210 | if err != nil { | 210 | if err != nil { |
211 | return FieldReadResult{}, nil | 211 | return FieldReadResult{}, nil |
212 | } | 212 | } |
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 16bbae2..d558a5b 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 | |||
@@ -29,29 +29,59 @@ type DiffFieldReader struct { | |||
29 | Diff *terraform.InstanceDiff | 29 | Diff *terraform.InstanceDiff |
30 | Source FieldReader | 30 | Source FieldReader |
31 | Schema map[string]*Schema | 31 | Schema map[string]*Schema |
32 | |||
33 | // cache for memoizing ReadField calls. | ||
34 | cache map[string]cachedFieldReadResult | ||
35 | } | ||
36 | |||
37 | type cachedFieldReadResult struct { | ||
38 | val FieldReadResult | ||
39 | err error | ||
32 | } | 40 | } |
33 | 41 | ||
34 | func (r *DiffFieldReader) ReadField(address []string) (FieldReadResult, error) { | 42 | func (r *DiffFieldReader) ReadField(address []string) (FieldReadResult, error) { |
43 | if r.cache == nil { | ||
44 | r.cache = make(map[string]cachedFieldReadResult) | ||
45 | } | ||
46 | |||
47 | // Create the cache key by joining around a value that isn't a valid part | ||
48 | // of an address. This assumes that the Source and Schema are not changed | ||
49 | // for the life of this DiffFieldReader. | ||
50 | cacheKey := strings.Join(address, "|") | ||
51 | if cached, ok := r.cache[cacheKey]; ok { | ||
52 | return cached.val, cached.err | ||
53 | } | ||
54 | |||
35 | schemaList := addrToSchema(address, r.Schema) | 55 | schemaList := addrToSchema(address, r.Schema) |
36 | if len(schemaList) == 0 { | 56 | if len(schemaList) == 0 { |
57 | r.cache[cacheKey] = cachedFieldReadResult{} | ||
37 | return FieldReadResult{}, nil | 58 | return FieldReadResult{}, nil |
38 | } | 59 | } |
39 | 60 | ||
61 | var res FieldReadResult | ||
62 | var err error | ||
63 | |||
40 | schema := schemaList[len(schemaList)-1] | 64 | schema := schemaList[len(schemaList)-1] |
41 | switch schema.Type { | 65 | switch schema.Type { |
42 | case TypeBool, TypeInt, TypeFloat, TypeString: | 66 | case TypeBool, TypeInt, TypeFloat, TypeString: |
43 | return r.readPrimitive(address, schema) | 67 | res, err = r.readPrimitive(address, schema) |
44 | case TypeList: | 68 | case TypeList: |
45 | return readListField(r, address, schema) | 69 | res, err = readListField(r, address, schema) |
46 | case TypeMap: | 70 | case TypeMap: |
47 | return r.readMap(address, schema) | 71 | res, err = r.readMap(address, schema) |
48 | case TypeSet: | 72 | case TypeSet: |
49 | return r.readSet(address, schema) | 73 | res, err = r.readSet(address, schema) |
50 | case typeObject: | 74 | case typeObject: |
51 | return readObjectField(r, address, schema.Elem.(map[string]*Schema)) | 75 | res, err = readObjectField(r, address, schema.Elem.(map[string]*Schema)) |
52 | default: | 76 | default: |
53 | panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) | 77 | panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) |
54 | } | 78 | } |
79 | |||
80 | r.cache[cacheKey] = cachedFieldReadResult{ | ||
81 | val: res, | ||
82 | err: err, | ||
83 | } | ||
84 | return res, err | ||
55 | } | 85 | } |
56 | 86 | ||
57 | func (r *DiffFieldReader) readMap( | 87 | func (r *DiffFieldReader) readMap( |
@@ -92,7 +122,8 @@ func (r *DiffFieldReader) readMap( | |||
92 | result[k] = v.New | 122 | result[k] = v.New |
93 | } | 123 | } |
94 | 124 | ||
95 | err = mapValuesToPrimitive(result, schema) | 125 | key := address[len(address)-1] |
126 | err = mapValuesToPrimitive(key, result, schema) | ||
96 | if err != nil { | 127 | if err != nil { |
97 | return FieldReadResult{}, nil | 128 | return FieldReadResult{}, nil |
98 | } | 129 | } |
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 9533981..054efe0 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 | |||
@@ -61,7 +61,7 @@ func (r *MapFieldReader) readMap(k string, schema *Schema) (FieldReadResult, err | |||
61 | return true | 61 | return true |
62 | }) | 62 | }) |
63 | 63 | ||
64 | err := mapValuesToPrimitive(result, schema) | 64 | err := mapValuesToPrimitive(k, result, schema) |
65 | if err != nil { | 65 | if err != nil { |
66 | return FieldReadResult{}, nil | 66 | return FieldReadResult{}, nil |
67 | } | 67 | } |
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 689ed8d..814c7ba 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 | |||
@@ -39,6 +39,19 @@ func (w *MapFieldWriter) unsafeWriteField(addr string, value string) { | |||
39 | w.result[addr] = value | 39 | w.result[addr] = value |
40 | } | 40 | } |
41 | 41 | ||
42 | // clearTree clears a field and any sub-fields of the given address out of the | ||
43 | // map. This should be used to reset some kind of complex structures (namely | ||
44 | // sets) before writing to make sure that any conflicting data is removed (for | ||
45 | // example, if the set was previously written to the writer's layer). | ||
46 | func (w *MapFieldWriter) clearTree(addr []string) { | ||
47 | prefix := strings.Join(addr, ".") + "." | ||
48 | for k := range w.result { | ||
49 | if strings.HasPrefix(k, prefix) { | ||
50 | delete(w.result, k) | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
42 | func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error { | 55 | func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error { |
43 | w.lock.Lock() | 56 | w.lock.Lock() |
44 | defer w.lock.Unlock() | 57 | defer w.lock.Unlock() |
@@ -115,6 +128,14 @@ func (w *MapFieldWriter) setList( | |||
115 | return fmt.Errorf("%s: %s", k, err) | 128 | return fmt.Errorf("%s: %s", k, err) |
116 | } | 129 | } |
117 | 130 | ||
131 | // Wipe the set from the current writer prior to writing if it exists. | ||
132 | // Multiple writes to the same layer is a lot safer for lists than sets due | ||
133 | // to the fact that indexes are always deterministic and the length will | ||
134 | // always be updated with the current length on the last write, but making | ||
135 | // sure we have a clean namespace removes any chance for edge cases to pop up | ||
136 | // and ensures that the last write to the set is the correct value. | ||
137 | w.clearTree(addr) | ||
138 | |||
118 | // Set the entire list. | 139 | // Set the entire list. |
119 | var err error | 140 | var err error |
120 | for i, elem := range vs { | 141 | for i, elem := range vs { |
@@ -162,6 +183,10 @@ func (w *MapFieldWriter) setMap( | |||
162 | vs[mk.String()] = mv.Interface() | 183 | vs[mk.String()] = mv.Interface() |
163 | } | 184 | } |
164 | 185 | ||
186 | // Wipe this address tree. The contents of the map should always reflect the | ||
187 | // last write made to it. | ||
188 | w.clearTree(addr) | ||
189 | |||
165 | // Remove the pure key since we're setting the full map value | 190 | // Remove the pure key since we're setting the full map value |
166 | delete(w.result, k) | 191 | delete(w.result, k) |
167 | 192 | ||
@@ -308,6 +333,13 @@ func (w *MapFieldWriter) setSet( | |||
308 | value = s | 333 | value = s |
309 | } | 334 | } |
310 | 335 | ||
336 | // Clear any keys that match the set address first. This is necessary because | ||
337 | // it's always possible and sometimes may be necessary to write to a certain | ||
338 | // writer layer more than once with different set data each time, which will | ||
339 | // lead to different keys being inserted, which can lead to determinism | ||
340 | // problems when the old data isn't wiped first. | ||
341 | w.clearTree(addr) | ||
342 | |||
311 | for code, elem := range value.(*Set).m { | 343 | for code, elem := range value.(*Set).m { |
312 | if err := w.set(append(addrCopy, code), elem); err != nil { | 344 | if err := w.set(append(addrCopy, code), elem); err != nil { |
313 | return err | 345 | 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 3a97629..38cd8c7 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | package schema | 3 | package schema |
4 | 4 | ||
5 | import "fmt" | 5 | import "strconv" |
6 | 6 | ||
7 | const ( | 7 | const ( |
8 | _getSource_name_0 = "getSourceStategetSourceConfig" | 8 | _getSource_name_0 = "getSourceStategetSourceConfig" |
@@ -13,8 +13,6 @@ const ( | |||
13 | 13 | ||
14 | var ( | 14 | var ( |
15 | _getSource_index_0 = [...]uint8{0, 14, 29} | 15 | _getSource_index_0 = [...]uint8{0, 14, 29} |
16 | _getSource_index_1 = [...]uint8{0, 13} | ||
17 | _getSource_index_2 = [...]uint8{0, 12} | ||
18 | _getSource_index_3 = [...]uint8{0, 18, 32} | 16 | _getSource_index_3 = [...]uint8{0, 18, 32} |
19 | ) | 17 | ) |
20 | 18 | ||
@@ -31,6 +29,6 @@ func (i getSource) String() string { | |||
31 | i -= 15 | 29 | i -= 15 |
32 | return _getSource_name_3[_getSource_index_3[i]:_getSource_index_3[i+1]] | 30 | return _getSource_name_3[_getSource_index_3[i]:_getSource_index_3[i+1]] |
33 | default: | 31 | default: |
34 | return fmt.Sprintf("getSource(%d)", i) | 32 | return "getSource(" + strconv.FormatInt(int64(i), 10) + ")" |
35 | } | 33 | } |
36 | } | 34 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provider.go b/vendor/github.com/hashicorp/terraform/helper/schema/provider.go index fb28b41..6cd325d 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/provider.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/provider.go | |||
@@ -9,6 +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/terraform" | 13 | "github.com/hashicorp/terraform/terraform" |
13 | ) | 14 | ) |
14 | 15 | ||
@@ -58,7 +59,7 @@ type Provider struct { | |||
58 | 59 | ||
59 | meta interface{} | 60 | meta interface{} |
60 | 61 | ||
61 | // a mutex is required because TestReset can directly repalce the stopCtx | 62 | // a mutex is required because TestReset can directly replace the stopCtx |
62 | stopMu sync.Mutex | 63 | stopMu sync.Mutex |
63 | stopCtx context.Context | 64 | stopCtx context.Context |
64 | stopCtxCancel context.CancelFunc | 65 | stopCtxCancel context.CancelFunc |
@@ -185,6 +186,29 @@ func (p *Provider) TestReset() error { | |||
185 | return nil | 186 | return nil |
186 | } | 187 | } |
187 | 188 | ||
189 | // GetSchema implementation of terraform.ResourceProvider interface | ||
190 | func (p *Provider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) { | ||
191 | resourceTypes := map[string]*configschema.Block{} | ||
192 | dataSources := map[string]*configschema.Block{} | ||
193 | |||
194 | for _, name := range req.ResourceTypes { | ||
195 | if r, exists := p.ResourcesMap[name]; exists { | ||
196 | resourceTypes[name] = r.CoreConfigSchema() | ||
197 | } | ||
198 | } | ||
199 | for _, name := range req.DataSources { | ||
200 | if r, exists := p.DataSourcesMap[name]; exists { | ||
201 | dataSources[name] = r.CoreConfigSchema() | ||
202 | } | ||
203 | } | ||
204 | |||
205 | return &terraform.ProviderSchema{ | ||
206 | Provider: schemaMap(p.Schema).CoreConfigSchema(), | ||
207 | ResourceTypes: resourceTypes, | ||
208 | DataSources: dataSources, | ||
209 | }, nil | ||
210 | } | ||
211 | |||
188 | // Input implementation of terraform.ResourceProvider interface. | 212 | // Input implementation of terraform.ResourceProvider interface. |
189 | func (p *Provider) Input( | 213 | func (p *Provider) Input( |
190 | input terraform.UIInput, | 214 | input terraform.UIInput, |
@@ -227,7 +251,7 @@ func (p *Provider) Configure(c *terraform.ResourceConfig) error { | |||
227 | 251 | ||
228 | // Get a ResourceData for this configuration. To do this, we actually | 252 | // Get a ResourceData for this configuration. To do this, we actually |
229 | // generate an intermediary "diff" although that is never exposed. | 253 | // generate an intermediary "diff" although that is never exposed. |
230 | diff, err := sm.Diff(nil, c) | 254 | diff, err := sm.Diff(nil, c, nil, p.meta) |
231 | if err != nil { | 255 | if err != nil { |
232 | return err | 256 | return err |
233 | } | 257 | } |
@@ -269,7 +293,7 @@ func (p *Provider) Diff( | |||
269 | return nil, fmt.Errorf("unknown resource type: %s", info.Type) | 293 | return nil, fmt.Errorf("unknown resource type: %s", info.Type) |
270 | } | 294 | } |
271 | 295 | ||
272 | return r.Diff(s, c) | 296 | return r.Diff(s, c, p.meta) |
273 | } | 297 | } |
274 | 298 | ||
275 | // Refresh implementation of terraform.ResourceProvider interface. | 299 | // Refresh implementation of terraform.ResourceProvider interface. |
@@ -305,6 +329,10 @@ func (p *Provider) Resources() []terraform.ResourceType { | |||
305 | result = append(result, terraform.ResourceType{ | 329 | result = append(result, terraform.ResourceType{ |
306 | Name: k, | 330 | Name: k, |
307 | Importable: resource.Importer != nil, | 331 | Importable: resource.Importer != nil, |
332 | |||
333 | // Indicates that a provider is compiled against a new enough | ||
334 | // version of core to support the GetSchema method. | ||
335 | SchemaAvailable: true, | ||
308 | }) | 336 | }) |
309 | } | 337 | } |
310 | 338 | ||
@@ -382,7 +410,7 @@ func (p *Provider) ReadDataDiff( | |||
382 | return nil, fmt.Errorf("unknown data source: %s", info.Type) | 410 | return nil, fmt.Errorf("unknown data source: %s", info.Type) |
383 | } | 411 | } |
384 | 412 | ||
385 | return r.Diff(nil, c) | 413 | return r.Diff(nil, c, p.meta) |
386 | } | 414 | } |
387 | 415 | ||
388 | // RefreshData implementation of terraform.ResourceProvider interface. | 416 | // RefreshData implementation of terraform.ResourceProvider interface. |
@@ -410,6 +438,10 @@ func (p *Provider) DataSources() []terraform.DataSource { | |||
410 | for _, k := range keys { | 438 | for _, k := range keys { |
411 | result = append(result, terraform.DataSource{ | 439 | result = append(result, terraform.DataSource{ |
412 | Name: k, | 440 | Name: k, |
441 | |||
442 | // Indicates that a provider is compiled against a new enough | ||
443 | // version of core to support the GetSchema method. | ||
444 | SchemaAvailable: true, | ||
413 | }) | 445 | }) |
414 | } | 446 | } |
415 | 447 | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go index 476192e..a8d42db 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go | |||
@@ -146,7 +146,7 @@ func (p *Provisioner) Apply( | |||
146 | } | 146 | } |
147 | 147 | ||
148 | sm := schemaMap(p.ConnSchema) | 148 | sm := schemaMap(p.ConnSchema) |
149 | diff, err := sm.Diff(nil, terraform.NewResourceConfig(c)) | 149 | diff, err := sm.Diff(nil, terraform.NewResourceConfig(c), nil, nil) |
150 | if err != nil { | 150 | if err != nil { |
151 | return err | 151 | return err |
152 | } | 152 | } |
@@ -160,7 +160,7 @@ func (p *Provisioner) Apply( | |||
160 | // Build the configuration data. Doing this requires making a "diff" | 160 | // 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. | 161 | // even though that's never used. We use that just to get the correct types. |
162 | configMap := schemaMap(p.Schema) | 162 | configMap := schemaMap(p.Schema) |
163 | diff, err := configMap.Diff(nil, c) | 163 | diff, err := configMap.Diff(nil, c, nil, nil) |
164 | if err != nil { | 164 | if err != nil { |
165 | return err | 165 | return err |
166 | } | 166 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go index ddba109..d3be2d6 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go | |||
@@ -85,6 +85,37 @@ type Resource struct { | |||
85 | Delete DeleteFunc | 85 | Delete DeleteFunc |
86 | Exists ExistsFunc | 86 | Exists ExistsFunc |
87 | 87 | ||
88 | // CustomizeDiff is a custom function for working with the diff that | ||
89 | // Terraform has created for this resource - it can be used to customize the | ||
90 | // diff that has been created, diff values not controlled by configuration, | ||
91 | // or even veto the diff altogether and abort the plan. It is passed a | ||
92 | // *ResourceDiff, a structure similar to ResourceData but lacking most write | ||
93 | // functions like Set, while introducing new functions that work with the | ||
94 | // diff such as SetNew, SetNewComputed, and ForceNew. | ||
95 | // | ||
96 | // The phases Terraform runs this in, and the state available via functions | ||
97 | // like Get and GetChange, are as follows: | ||
98 | // | ||
99 | // * New resource: One run with no state | ||
100 | // * Existing resource: One run with state | ||
101 | // * Existing resource, forced new: One run with state (before ForceNew), | ||
102 | // then one run without state (as if new resource) | ||
103 | // * Tainted resource: No runs (custom diff logic is skipped) | ||
104 | // * Destroy: No runs (standard diff logic is skipped on destroy diffs) | ||
105 | // | ||
106 | // This function needs to be resilient to support all scenarios. | ||
107 | // | ||
108 | // If this function needs to access external API resources, remember to flag | ||
109 | // the RequiresRefresh attribute mentioned below to ensure that | ||
110 | // -refresh=false is blocked when running plan or apply, as this means that | ||
111 | // this resource requires refresh-like behaviour to work effectively. | ||
112 | // | ||
113 | // For the most part, only computed fields can be customized by this | ||
114 | // function. | ||
115 | // | ||
116 | // This function is only allowed on regular resources (not data sources). | ||
117 | CustomizeDiff CustomizeDiffFunc | ||
118 | |||
88 | // Importer is the ResourceImporter implementation for this resource. | 119 | // Importer is the ResourceImporter implementation for this resource. |
89 | // If this is nil, then this resource does not support importing. If | 120 | // If this is nil, then this resource does not support importing. If |
90 | // this is non-nil, then it supports importing and ResourceImporter | 121 | // this is non-nil, then it supports importing and ResourceImporter |
@@ -93,9 +124,7 @@ type Resource struct { | |||
93 | Importer *ResourceImporter | 124 | Importer *ResourceImporter |
94 | 125 | ||
95 | // If non-empty, this string is emitted as a warning during Validate. | 126 | // If non-empty, this string is emitted as a warning during Validate. |
96 | // This is a private interface for now, for use by DataSourceResourceShim, | 127 | DeprecationMessage string |
97 | // and not for general use. (But maybe later...) | ||
98 | deprecationMessage string | ||
99 | 128 | ||
100 | // Timeouts allow users to specify specific time durations in which an | 129 | // Timeouts allow users to specify specific time durations in which an |
101 | // operation should time out, to allow them to extend an action to suit their | 130 | // operation should time out, to allow them to extend an action to suit their |
@@ -126,6 +155,9 @@ type ExistsFunc func(*ResourceData, interface{}) (bool, error) | |||
126 | type StateMigrateFunc func( | 155 | type StateMigrateFunc func( |
127 | int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error) | 156 | int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error) |
128 | 157 | ||
158 | // See Resource documentation. | ||
159 | type CustomizeDiffFunc func(*ResourceDiff, interface{}) error | ||
160 | |||
129 | // Apply creates, updates, and/or deletes a resource. | 161 | // Apply creates, updates, and/or deletes a resource. |
130 | func (r *Resource) Apply( | 162 | func (r *Resource) Apply( |
131 | s *terraform.InstanceState, | 163 | s *terraform.InstanceState, |
@@ -202,11 +234,11 @@ func (r *Resource) Apply( | |||
202 | return r.recordCurrentSchemaVersion(data.State()), err | 234 | return r.recordCurrentSchemaVersion(data.State()), err |
203 | } | 235 | } |
204 | 236 | ||
205 | // Diff returns a diff of this resource and is API compatible with the | 237 | // Diff returns a diff of this resource. |
206 | // ResourceProvider interface. | ||
207 | func (r *Resource) Diff( | 238 | func (r *Resource) Diff( |
208 | s *terraform.InstanceState, | 239 | s *terraform.InstanceState, |
209 | c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { | 240 | c *terraform.ResourceConfig, |
241 | meta interface{}) (*terraform.InstanceDiff, error) { | ||
210 | 242 | ||
211 | t := &ResourceTimeout{} | 243 | t := &ResourceTimeout{} |
212 | err := t.ConfigDecode(r, c) | 244 | err := t.ConfigDecode(r, c) |
@@ -215,7 +247,7 @@ func (r *Resource) Diff( | |||
215 | return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err) | 247 | return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err) |
216 | } | 248 | } |
217 | 249 | ||
218 | instanceDiff, err := schemaMap(r.Schema).Diff(s, c) | 250 | instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta) |
219 | if err != nil { | 251 | if err != nil { |
220 | return instanceDiff, err | 252 | return instanceDiff, err |
221 | } | 253 | } |
@@ -235,8 +267,8 @@ func (r *Resource) Diff( | |||
235 | func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) { | 267 | func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) { |
236 | warns, errs := schemaMap(r.Schema).Validate(c) | 268 | warns, errs := schemaMap(r.Schema).Validate(c) |
237 | 269 | ||
238 | if r.deprecationMessage != "" { | 270 | if r.DeprecationMessage != "" { |
239 | warns = append(warns, r.deprecationMessage) | 271 | warns = append(warns, r.DeprecationMessage) |
240 | } | 272 | } |
241 | 273 | ||
242 | return warns, errs | 274 | return warns, errs |
@@ -248,7 +280,6 @@ func (r *Resource) ReadDataApply( | |||
248 | d *terraform.InstanceDiff, | 280 | d *terraform.InstanceDiff, |
249 | meta interface{}, | 281 | meta interface{}, |
250 | ) (*terraform.InstanceState, error) { | 282 | ) (*terraform.InstanceState, error) { |
251 | |||
252 | // Data sources are always built completely from scratch | 283 | // Data sources are always built completely from scratch |
253 | // on each read, so the source state is always nil. | 284 | // on each read, so the source state is always nil. |
254 | data, err := schemaMap(r.Schema).Data(nil, d) | 285 | data, err := schemaMap(r.Schema).Data(nil, d) |
@@ -346,6 +377,11 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error | |||
346 | if r.Create != nil || r.Update != nil || r.Delete != nil { | 377 | if r.Create != nil || r.Update != nil || r.Delete != nil { |
347 | return fmt.Errorf("must not implement Create, Update or Delete") | 378 | return fmt.Errorf("must not implement Create, Update or Delete") |
348 | } | 379 | } |
380 | |||
381 | // CustomizeDiff cannot be defined for read-only resources | ||
382 | if r.CustomizeDiff != nil { | ||
383 | return fmt.Errorf("cannot implement CustomizeDiff") | ||
384 | } | ||
349 | } | 385 | } |
350 | 386 | ||
351 | tsm := topSchemaMap | 387 | tsm := topSchemaMap |
@@ -393,19 +429,43 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error | |||
393 | return err | 429 | return err |
394 | } | 430 | } |
395 | } | 431 | } |
432 | |||
433 | for k, f := range tsm { | ||
434 | if isReservedResourceFieldName(k, f) { | ||
435 | return fmt.Errorf("%s is a reserved field name", k) | ||
436 | } | ||
437 | } | ||
396 | } | 438 | } |
397 | 439 | ||
398 | // Resource-specific checks | 440 | // Data source |
399 | for k, _ := range tsm { | 441 | if r.isTopLevel() && !writable { |
400 | if isReservedResourceFieldName(k) { | 442 | tsm = schemaMap(r.Schema) |
401 | return fmt.Errorf("%s is a reserved field name for a resource", k) | 443 | for k, _ := range tsm { |
444 | if isReservedDataSourceFieldName(k) { | ||
445 | return fmt.Errorf("%s is a reserved field name", k) | ||
446 | } | ||
402 | } | 447 | } |
403 | } | 448 | } |
404 | 449 | ||
405 | return schemaMap(r.Schema).InternalValidate(tsm) | 450 | return schemaMap(r.Schema).InternalValidate(tsm) |
406 | } | 451 | } |
407 | 452 | ||
408 | func isReservedResourceFieldName(name string) bool { | 453 | func isReservedDataSourceFieldName(name string) bool { |
454 | for _, reservedName := range config.ReservedDataSourceFields { | ||
455 | if name == reservedName { | ||
456 | return true | ||
457 | } | ||
458 | } | ||
459 | return false | ||
460 | } | ||
461 | |||
462 | func isReservedResourceFieldName(name string, s *Schema) bool { | ||
463 | // Allow phasing out "id" | ||
464 | // See https://github.com/terraform-providers/terraform-provider-aws/pull/1626#issuecomment-328881415 | ||
465 | if name == "id" && (s.Deprecated != "" || s.Removed != "") { | ||
466 | return false | ||
467 | } | ||
468 | |||
409 | for _, reservedName := range config.ReservedResourceFields { | 469 | for _, reservedName := range config.ReservedResourceFields { |
410 | if name == reservedName { | 470 | if name == reservedName { |
411 | return true | 471 | return true |
@@ -430,6 +490,12 @@ func (r *Resource) Data(s *terraform.InstanceState) *ResourceData { | |||
430 | panic(err) | 490 | panic(err) |
431 | } | 491 | } |
432 | 492 | ||
493 | // load the Resource timeouts | ||
494 | result.timeouts = r.Timeouts | ||
495 | if result.timeouts == nil { | ||
496 | result.timeouts = &ResourceTimeout{} | ||
497 | } | ||
498 | |||
433 | // Set the schema version to latest by default | 499 | // Set the schema version to latest by default |
434 | result.meta = map[string]interface{}{ | 500 | result.meta = map[string]interface{}{ |
435 | "schema_version": strconv.Itoa(r.SchemaVersion), | 501 | "schema_version": strconv.Itoa(r.SchemaVersion), |
@@ -450,7 +516,7 @@ func (r *Resource) TestResourceData() *ResourceData { | |||
450 | // Returns true if the resource is "top level" i.e. not a sub-resource. | 516 | // Returns true if the resource is "top level" i.e. not a sub-resource. |
451 | func (r *Resource) isTopLevel() bool { | 517 | func (r *Resource) isTopLevel() bool { |
452 | // TODO: This is a heuristic; replace with a definitive attribute? | 518 | // TODO: This is a heuristic; replace with a definitive attribute? |
453 | return r.Create != nil | 519 | return (r.Create != nil || r.Read != nil) |
454 | } | 520 | } |
455 | 521 | ||
456 | // Determines if a given InstanceState needs to be migrated by checking the | 522 | // Determines if a given InstanceState needs to be migrated by checking the |
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 b2bc8f6..6cc01ee 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go | |||
@@ -35,6 +35,8 @@ type ResourceData struct { | |||
35 | partialMap map[string]struct{} | 35 | partialMap map[string]struct{} |
36 | once sync.Once | 36 | once sync.Once |
37 | isNew bool | 37 | isNew bool |
38 | |||
39 | panicOnError bool | ||
38 | } | 40 | } |
39 | 41 | ||
40 | // getResult is the internal structure that is generated when a Get | 42 | // getResult is the internal structure that is generated when a Get |
@@ -104,6 +106,22 @@ func (d *ResourceData) GetOk(key string) (interface{}, bool) { | |||
104 | return r.Value, exists | 106 | return r.Value, exists |
105 | } | 107 | } |
106 | 108 | ||
109 | // GetOkExists returns the data for a given key and whether or not the key | ||
110 | // has been set to a non-zero value. This is only useful for determining | ||
111 | // if boolean attributes have been set, if they are Optional but do not | ||
112 | // have a Default value. | ||
113 | // | ||
114 | // This is nearly the same function as GetOk, yet it does not check | ||
115 | // for the zero value of the attribute's type. This allows for attributes | ||
116 | // without a default, to fully check for a literal assignment, regardless | ||
117 | // of the zero-value for that type. | ||
118 | // This should only be used if absolutely required/needed. | ||
119 | func (d *ResourceData) GetOkExists(key string) (interface{}, bool) { | ||
120 | r := d.getRaw(key, getSourceSet) | ||
121 | exists := r.Exists && !r.Computed | ||
122 | return r.Value, exists | ||
123 | } | ||
124 | |||
107 | func (d *ResourceData) getRaw(key string, level getSource) getResult { | 125 | func (d *ResourceData) getRaw(key string, level getSource) getResult { |
108 | var parts []string | 126 | var parts []string |
109 | if key != "" { | 127 | if key != "" { |
@@ -168,7 +186,11 @@ func (d *ResourceData) Set(key string, value interface{}) error { | |||
168 | } | 186 | } |
169 | } | 187 | } |
170 | 188 | ||
171 | return d.setWriter.WriteField(strings.Split(key, "."), value) | 189 | err := d.setWriter.WriteField(strings.Split(key, "."), value) |
190 | if err != nil && d.panicOnError { | ||
191 | panic(err) | ||
192 | } | ||
193 | return err | ||
172 | } | 194 | } |
173 | 195 | ||
174 | // SetPartial adds the key to the final state output while | 196 | // SetPartial adds the key to the final state output while |
@@ -293,6 +315,7 @@ func (d *ResourceData) State() *terraform.InstanceState { | |||
293 | 315 | ||
294 | mapW := &MapFieldWriter{Schema: d.schema} | 316 | mapW := &MapFieldWriter{Schema: d.schema} |
295 | if err := mapW.WriteField(nil, rawMap); err != nil { | 317 | if err := mapW.WriteField(nil, rawMap); err != nil { |
318 | log.Printf("[ERR] Error writing fields: %s", err) | ||
296 | return nil | 319 | return nil |
297 | } | 320 | } |
298 | 321 | ||
@@ -344,6 +367,13 @@ func (d *ResourceData) State() *terraform.InstanceState { | |||
344 | func (d *ResourceData) Timeout(key string) time.Duration { | 367 | func (d *ResourceData) Timeout(key string) time.Duration { |
345 | key = strings.ToLower(key) | 368 | key = strings.ToLower(key) |
346 | 369 | ||
370 | // System default of 20 minutes | ||
371 | defaultTimeout := 20 * time.Minute | ||
372 | |||
373 | if d.timeouts == nil { | ||
374 | return defaultTimeout | ||
375 | } | ||
376 | |||
347 | var timeout *time.Duration | 377 | var timeout *time.Duration |
348 | switch key { | 378 | switch key { |
349 | case TimeoutCreate: | 379 | case TimeoutCreate: |
@@ -364,8 +394,7 @@ func (d *ResourceData) Timeout(key string) time.Duration { | |||
364 | return *d.timeouts.Default | 394 | return *d.timeouts.Default |
365 | } | 395 | } |
366 | 396 | ||
367 | // Return system default of 20 minutes | 397 | return defaultTimeout |
368 | return 20 * time.Minute | ||
369 | } | 398 | } |
370 | 399 | ||
371 | func (d *ResourceData) init() { | 400 | func (d *ResourceData) init() { |
@@ -423,7 +452,7 @@ func (d *ResourceData) init() { | |||
423 | } | 452 | } |
424 | 453 | ||
425 | func (d *ResourceData) diffChange( | 454 | func (d *ResourceData) diffChange( |
426 | k string) (interface{}, interface{}, bool, bool) { | 455 | k string) (interface{}, interface{}, bool, bool, bool) { |
427 | // Get the change between the state and the config. | 456 | // Get the change between the state and the config. |
428 | o, n := d.getChange(k, getSourceState, getSourceConfig|getSourceExact) | 457 | o, n := d.getChange(k, getSourceState, getSourceConfig|getSourceExact) |
429 | if !o.Exists { | 458 | if !o.Exists { |
@@ -434,7 +463,7 @@ func (d *ResourceData) diffChange( | |||
434 | } | 463 | } |
435 | 464 | ||
436 | // Return the old, new, and whether there is a change | 465 | // Return the old, new, and whether there is a change |
437 | return o.Value, n.Value, !reflect.DeepEqual(o.Value, n.Value), n.Computed | 466 | return o.Value, n.Value, !reflect.DeepEqual(o.Value, n.Value), n.Computed, false |
438 | } | 467 | } |
439 | 468 | ||
440 | func (d *ResourceData) getChange( | 469 | func (d *ResourceData) getChange( |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go new file mode 100644 index 0000000..7db3dec --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go | |||
@@ -0,0 +1,559 @@ | |||
1 | package schema | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "fmt" | ||
6 | "reflect" | ||
7 | "strings" | ||
8 | "sync" | ||
9 | |||
10 | "github.com/hashicorp/terraform/terraform" | ||
11 | ) | ||
12 | |||
13 | // newValueWriter is a minor re-implementation of MapFieldWriter to include | ||
14 | // keys that should be marked as computed, to represent the new part of a | ||
15 | // pseudo-diff. | ||
16 | type newValueWriter struct { | ||
17 | *MapFieldWriter | ||
18 | |||
19 | // A list of keys that should be marked as computed. | ||
20 | computedKeys map[string]bool | ||
21 | |||
22 | // A lock to prevent races on writes. The underlying writer will have one as | ||
23 | // well - this is for computed keys. | ||
24 | lock sync.Mutex | ||
25 | |||
26 | // To be used with init. | ||
27 | once sync.Once | ||
28 | } | ||
29 | |||
30 | // init performs any initialization tasks for the newValueWriter. | ||
31 | func (w *newValueWriter) init() { | ||
32 | if w.computedKeys == nil { | ||
33 | w.computedKeys = make(map[string]bool) | ||
34 | } | ||
35 | } | ||
36 | |||
37 | // WriteField overrides MapValueWriter's WriteField, adding the ability to flag | ||
38 | // the address as computed. | ||
39 | func (w *newValueWriter) WriteField(address []string, value interface{}, computed bool) error { | ||
40 | // Fail the write if we have a non-nil value and computed is true. | ||
41 | // NewComputed values should not have a value when written. | ||
42 | if value != nil && computed { | ||
43 | return errors.New("Non-nil value with computed set") | ||
44 | } | ||
45 | |||
46 | if err := w.MapFieldWriter.WriteField(address, value); err != nil { | ||
47 | return err | ||
48 | } | ||
49 | |||
50 | w.once.Do(w.init) | ||
51 | |||
52 | w.lock.Lock() | ||
53 | defer w.lock.Unlock() | ||
54 | if computed { | ||
55 | w.computedKeys[strings.Join(address, ".")] = true | ||
56 | } | ||
57 | return nil | ||
58 | } | ||
59 | |||
60 | // ComputedKeysMap returns the underlying computed keys map. | ||
61 | func (w *newValueWriter) ComputedKeysMap() map[string]bool { | ||
62 | w.once.Do(w.init) | ||
63 | return w.computedKeys | ||
64 | } | ||
65 | |||
66 | // newValueReader is a minor re-implementation of MapFieldReader and is the | ||
67 | // read counterpart to MapValueWriter, allowing the read of keys flagged as | ||
68 | // computed to accommodate the diff override logic in ResourceDiff. | ||
69 | type newValueReader struct { | ||
70 | *MapFieldReader | ||
71 | |||
72 | // The list of computed keys from a newValueWriter. | ||
73 | computedKeys map[string]bool | ||
74 | } | ||
75 | |||
76 | // ReadField reads the values from the underlying writer, returning the | ||
77 | // computed value if it is found as well. | ||
78 | func (r *newValueReader) ReadField(address []string) (FieldReadResult, error) { | ||
79 | addrKey := strings.Join(address, ".") | ||
80 | v, err := r.MapFieldReader.ReadField(address) | ||
81 | if err != nil { | ||
82 | return FieldReadResult{}, err | ||
83 | } | ||
84 | for computedKey := range r.computedKeys { | ||
85 | if childAddrOf(addrKey, computedKey) { | ||
86 | if strings.HasSuffix(addrKey, ".#") { | ||
87 | // This is a count value for a list or set that has been marked as | ||
88 | // computed, or a sub-list/sub-set of a complex resource that has | ||
89 | // been marked as computed. We need to pass through to other readers | ||
90 | // so that an accurate previous count can be fetched for the diff. | ||
91 | v.Exists = false | ||
92 | } | ||
93 | v.Computed = true | ||
94 | } | ||
95 | } | ||
96 | |||
97 | return v, nil | ||
98 | } | ||
99 | |||
100 | // ResourceDiff is used to query and make custom changes to an in-flight diff. | ||
101 | // It can be used to veto particular changes in the diff, customize the diff | ||
102 | // that has been created, or diff values not controlled by config. | ||
103 | // | ||
104 | // The object functions similar to ResourceData, however most notably lacks | ||
105 | // Set, SetPartial, and Partial, as it should be used to change diff values | ||
106 | // only. Most other first-class ResourceData functions exist, namely Get, | ||
107 | // GetOk, HasChange, and GetChange exist. | ||
108 | // | ||
109 | // All functions in ResourceDiff, save for ForceNew, can only be used on | ||
110 | // computed fields. | ||
111 | type ResourceDiff struct { | ||
112 | // The schema for the resource being worked on. | ||
113 | schema map[string]*Schema | ||
114 | |||
115 | // The current config for this resource. | ||
116 | config *terraform.ResourceConfig | ||
117 | |||
118 | // The state for this resource as it exists post-refresh, after the initial | ||
119 | // diff. | ||
120 | state *terraform.InstanceState | ||
121 | |||
122 | // The diff created by Terraform. This diff is used, along with state, | ||
123 | // config, and custom-set diff data, to provide a multi-level reader | ||
124 | // experience similar to ResourceData. | ||
125 | diff *terraform.InstanceDiff | ||
126 | |||
127 | // The internal reader structure that contains the state, config, the default | ||
128 | // diff, and the new diff. | ||
129 | multiReader *MultiLevelFieldReader | ||
130 | |||
131 | // A writer that writes overridden new fields. | ||
132 | newWriter *newValueWriter | ||
133 | |||
134 | // Tracks which keys have been updated by ResourceDiff to ensure that the | ||
135 | // diff does not get re-run on keys that were not touched, or diffs that were | ||
136 | // just removed (re-running on the latter would just roll back the removal). | ||
137 | updatedKeys map[string]bool | ||
138 | |||
139 | // Tracks which keys were flagged as forceNew. These keys are not saved in | ||
140 | // newWriter, but we need to track them so that they can be re-diffed later. | ||
141 | forcedNewKeys map[string]bool | ||
142 | } | ||
143 | |||
144 | // newResourceDiff creates a new ResourceDiff instance. | ||
145 | func newResourceDiff(schema map[string]*Schema, config *terraform.ResourceConfig, state *terraform.InstanceState, diff *terraform.InstanceDiff) *ResourceDiff { | ||
146 | d := &ResourceDiff{ | ||
147 | config: config, | ||
148 | state: state, | ||
149 | diff: diff, | ||
150 | schema: schema, | ||
151 | } | ||
152 | |||
153 | d.newWriter = &newValueWriter{ | ||
154 | MapFieldWriter: &MapFieldWriter{Schema: d.schema}, | ||
155 | } | ||
156 | readers := make(map[string]FieldReader) | ||
157 | var stateAttributes map[string]string | ||
158 | if d.state != nil { | ||
159 | stateAttributes = d.state.Attributes | ||
160 | readers["state"] = &MapFieldReader{ | ||
161 | Schema: d.schema, | ||
162 | Map: BasicMapReader(stateAttributes), | ||
163 | } | ||
164 | } | ||
165 | if d.config != nil { | ||
166 | readers["config"] = &ConfigFieldReader{ | ||
167 | Schema: d.schema, | ||
168 | Config: d.config, | ||
169 | } | ||
170 | } | ||
171 | if d.diff != nil { | ||
172 | readers["diff"] = &DiffFieldReader{ | ||
173 | Schema: d.schema, | ||
174 | Diff: d.diff, | ||
175 | Source: &MultiLevelFieldReader{ | ||
176 | Levels: []string{"state", "config"}, | ||
177 | Readers: readers, | ||
178 | }, | ||
179 | } | ||
180 | } | ||
181 | readers["newDiff"] = &newValueReader{ | ||
182 | MapFieldReader: &MapFieldReader{ | ||
183 | Schema: d.schema, | ||
184 | Map: BasicMapReader(d.newWriter.Map()), | ||
185 | }, | ||
186 | computedKeys: d.newWriter.ComputedKeysMap(), | ||
187 | } | ||
188 | d.multiReader = &MultiLevelFieldReader{ | ||
189 | Levels: []string{ | ||
190 | "state", | ||
191 | "config", | ||
192 | "diff", | ||
193 | "newDiff", | ||
194 | }, | ||
195 | |||
196 | Readers: readers, | ||
197 | } | ||
198 | |||
199 | d.updatedKeys = make(map[string]bool) | ||
200 | d.forcedNewKeys = make(map[string]bool) | ||
201 | |||
202 | return d | ||
203 | } | ||
204 | |||
205 | // UpdatedKeys returns the keys that were updated by this ResourceDiff run. | ||
206 | // These are the only keys that a diff should be re-calculated for. | ||
207 | // | ||
208 | // This is the combined result of both keys for which diff values were updated | ||
209 | // for or cleared, and also keys that were flagged to be re-diffed as a result | ||
210 | // of ForceNew. | ||
211 | func (d *ResourceDiff) UpdatedKeys() []string { | ||
212 | var s []string | ||
213 | for k := range d.updatedKeys { | ||
214 | s = append(s, k) | ||
215 | } | ||
216 | for k := range d.forcedNewKeys { | ||
217 | for _, l := range s { | ||
218 | if k == l { | ||
219 | break | ||
220 | } | ||
221 | } | ||
222 | s = append(s, k) | ||
223 | } | ||
224 | return s | ||
225 | } | ||
226 | |||
227 | // Clear wipes the diff for a particular key. It is called by ResourceDiff's | ||
228 | // functionality to remove any possibility of conflicts, but can be called on | ||
229 | // its own to just remove a specific key from the diff completely. | ||
230 | // | ||
231 | // Note that this does not wipe an override. This function is only allowed on | ||
232 | // computed keys. | ||
233 | func (d *ResourceDiff) Clear(key string) error { | ||
234 | if err := d.checkKey(key, "Clear", true); err != nil { | ||
235 | return err | ||
236 | } | ||
237 | |||
238 | return d.clear(key) | ||
239 | } | ||
240 | |||
241 | func (d *ResourceDiff) clear(key string) error { | ||
242 | // Check the schema to make sure that this key exists first. | ||
243 | schemaL := addrToSchema(strings.Split(key, "."), d.schema) | ||
244 | if len(schemaL) == 0 { | ||
245 | return fmt.Errorf("%s is not a valid key", key) | ||
246 | } | ||
247 | |||
248 | for k := range d.diff.Attributes { | ||
249 | if strings.HasPrefix(k, key) { | ||
250 | delete(d.diff.Attributes, k) | ||
251 | } | ||
252 | } | ||
253 | return nil | ||
254 | } | ||
255 | |||
256 | // GetChangedKeysPrefix helps to implement Resource.CustomizeDiff | ||
257 | // where we need to act on all nested fields | ||
258 | // without calling out each one separately | ||
259 | func (d *ResourceDiff) GetChangedKeysPrefix(prefix string) []string { | ||
260 | keys := make([]string, 0) | ||
261 | for k := range d.diff.Attributes { | ||
262 | if strings.HasPrefix(k, prefix) { | ||
263 | keys = append(keys, k) | ||
264 | } | ||
265 | } | ||
266 | return keys | ||
267 | } | ||
268 | |||
269 | // diffChange helps to implement resourceDiffer and derives its change values | ||
270 | // from ResourceDiff's own change data, in addition to existing diff, config, and state. | ||
271 | func (d *ResourceDiff) diffChange(key string) (interface{}, interface{}, bool, bool, bool) { | ||
272 | old, new, customized := d.getChange(key) | ||
273 | |||
274 | if !old.Exists { | ||
275 | old.Value = nil | ||
276 | } | ||
277 | if !new.Exists || d.removed(key) { | ||
278 | new.Value = nil | ||
279 | } | ||
280 | |||
281 | return old.Value, new.Value, !reflect.DeepEqual(old.Value, new.Value), new.Computed, customized | ||
282 | } | ||
283 | |||
284 | // SetNew is used to set a new diff value for the mentioned key. The value must | ||
285 | // be correct for the attribute's schema (mostly relevant for maps, lists, and | ||
286 | // sets). The original value from the state is used as the old value. | ||
287 | // | ||
288 | // This function is only allowed on computed attributes. | ||
289 | func (d *ResourceDiff) SetNew(key string, value interface{}) error { | ||
290 | if err := d.checkKey(key, "SetNew", false); err != nil { | ||
291 | return err | ||
292 | } | ||
293 | |||
294 | return d.setDiff(key, value, false) | ||
295 | } | ||
296 | |||
297 | // SetNewComputed functions like SetNew, except that it blanks out a new value | ||
298 | // and marks it as computed. | ||
299 | // | ||
300 | // This function is only allowed on computed attributes. | ||
301 | func (d *ResourceDiff) SetNewComputed(key string) error { | ||
302 | if err := d.checkKey(key, "SetNewComputed", false); err != nil { | ||
303 | return err | ||
304 | } | ||
305 | |||
306 | return d.setDiff(key, nil, true) | ||
307 | } | ||
308 | |||
309 | // setDiff performs common diff setting behaviour. | ||
310 | func (d *ResourceDiff) setDiff(key string, new interface{}, computed bool) error { | ||
311 | if err := d.clear(key); err != nil { | ||
312 | return err | ||
313 | } | ||
314 | |||
315 | if err := d.newWriter.WriteField(strings.Split(key, "."), new, computed); err != nil { | ||
316 | return fmt.Errorf("Cannot set new diff value for key %s: %s", key, err) | ||
317 | } | ||
318 | |||
319 | d.updatedKeys[key] = true | ||
320 | |||
321 | return nil | ||
322 | } | ||
323 | |||
324 | // ForceNew force-flags ForceNew in the schema for a specific key, and | ||
325 | // re-calculates its diff, effectively causing this attribute to force a new | ||
326 | // resource. | ||
327 | // | ||
328 | // Keep in mind that forcing a new resource will force a second run of the | ||
329 | // resource's CustomizeDiff function (with a new ResourceDiff) once the current | ||
330 | // one has completed. This second run is performed without state. This behavior | ||
331 | // will be the same as if a new resource is being created and is performed to | ||
332 | // ensure that the diff looks like the diff for a new resource as much as | ||
333 | // possible. CustomizeDiff should expect such a scenario and act correctly. | ||
334 | // | ||
335 | // This function is a no-op/error if there is no diff. | ||
336 | // | ||
337 | // Note that the change to schema is permanent for the lifecycle of this | ||
338 | // specific ResourceDiff instance. | ||
339 | func (d *ResourceDiff) ForceNew(key string) error { | ||
340 | if !d.HasChange(key) { | ||
341 | return fmt.Errorf("ForceNew: No changes for %s", key) | ||
342 | } | ||
343 | |||
344 | keyParts := strings.Split(key, ".") | ||
345 | var schema *Schema | ||
346 | schemaL := addrToSchema(keyParts, d.schema) | ||
347 | if len(schemaL) > 0 { | ||
348 | schema = schemaL[len(schemaL)-1] | ||
349 | } else { | ||
350 | return fmt.Errorf("ForceNew: %s is not a valid key", key) | ||
351 | } | ||
352 | |||
353 | schema.ForceNew = true | ||
354 | |||
355 | // Flag this for a re-diff. Don't save any values to guarantee that existing | ||
356 | // diffs aren't messed with, as this gets messy when dealing with complex | ||
357 | // structures, zero values, etc. | ||
358 | d.forcedNewKeys[keyParts[0]] = true | ||
359 | |||
360 | return nil | ||
361 | } | ||
362 | |||
363 | // Get hands off to ResourceData.Get. | ||
364 | func (d *ResourceDiff) Get(key string) interface{} { | ||
365 | r, _ := d.GetOk(key) | ||
366 | return r | ||
367 | } | ||
368 | |||
369 | // GetChange gets the change between the state and diff, checking first to see | ||
370 | // if a overridden diff exists. | ||
371 | // | ||
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 | ||
374 | // per normal. | ||
375 | func (d *ResourceDiff) GetChange(key string) (interface{}, interface{}) { | ||
376 | old, new, _ := d.getChange(key) | ||
377 | return old.Value, new.Value | ||
378 | } | ||
379 | |||
380 | // GetOk functions the same way as ResourceData.GetOk, but it also checks the | ||
381 | // new diff levels to provide data consistent with the current state of the | ||
382 | // customized diff. | ||
383 | func (d *ResourceDiff) GetOk(key string) (interface{}, bool) { | ||
384 | r := d.get(strings.Split(key, "."), "newDiff") | ||
385 | exists := r.Exists && !r.Computed | ||
386 | if exists { | ||
387 | // If it exists, we also want to verify it is not the zero-value. | ||
388 | value := r.Value | ||
389 | zero := r.Schema.Type.Zero() | ||
390 | |||
391 | if eq, ok := value.(Equal); ok { | ||
392 | exists = !eq.Equal(zero) | ||
393 | } else { | ||
394 | exists = !reflect.DeepEqual(value, zero) | ||
395 | } | ||
396 | } | ||
397 | |||
398 | return r.Value, exists | ||
399 | } | ||
400 | |||
401 | // GetOkExists functions the same way as GetOkExists within ResourceData, but | ||
402 | // it also checks the new diff levels to provide data consistent with the | ||
403 | // current state of the customized diff. | ||
404 | // | ||
405 | // This is nearly the same function as GetOk, yet it does not check | ||
406 | // for the zero value of the attribute's type. This allows for attributes | ||
407 | // without a default, to fully check for a literal assignment, regardless | ||
408 | // of the zero-value for that type. | ||
409 | func (d *ResourceDiff) GetOkExists(key string) (interface{}, bool) { | ||
410 | r := d.get(strings.Split(key, "."), "newDiff") | ||
411 | exists := r.Exists && !r.Computed | ||
412 | return r.Value, exists | ||
413 | } | ||
414 | |||
415 | // NewValueKnown returns true if the new value for the given key is available | ||
416 | // as its final value at diff time. If the return value is false, this means | ||
417 | // either the value is based of interpolation that was unavailable at diff | ||
418 | // time, or that the value was explicitly marked as computed by SetNewComputed. | ||
419 | func (d *ResourceDiff) NewValueKnown(key string) bool { | ||
420 | r := d.get(strings.Split(key, "."), "newDiff") | ||
421 | return !r.Computed | ||
422 | } | ||
423 | |||
424 | // HasChange checks to see if there is a change between state and the diff, or | ||
425 | // in the overridden diff. | ||
426 | func (d *ResourceDiff) HasChange(key string) bool { | ||
427 | old, new := d.GetChange(key) | ||
428 | |||
429 | // If the type implements the Equal interface, then call that | ||
430 | // instead of just doing a reflect.DeepEqual. An example where this is | ||
431 | // needed is *Set | ||
432 | if eq, ok := old.(Equal); ok { | ||
433 | return !eq.Equal(new) | ||
434 | } | ||
435 | |||
436 | return !reflect.DeepEqual(old, new) | ||
437 | } | ||
438 | |||
439 | // Id returns the ID of this resource. | ||
440 | // | ||
441 | // Note that technically, ID does not change during diffs (it either has | ||
442 | // already changed in the refresh, or will change on update), hence we do not | ||
443 | // support updating the ID or fetching it from anything else other than state. | ||
444 | func (d *ResourceDiff) Id() string { | ||
445 | var result string | ||
446 | |||
447 | if d.state != nil { | ||
448 | result = d.state.ID | ||
449 | } | ||
450 | return result | ||
451 | } | ||
452 | |||
453 | // getChange gets values from two different levels, designed for use in | ||
454 | // diffChange, HasChange, and GetChange. | ||
455 | // | ||
456 | // This implementation differs from ResourceData's in the way that we first get | ||
457 | // results from the exact levels for the new diff, then from state and diff as | ||
458 | // per normal. | ||
459 | func (d *ResourceDiff) getChange(key string) (getResult, getResult, bool) { | ||
460 | old := d.get(strings.Split(key, "."), "state") | ||
461 | var new getResult | ||
462 | for p := range d.updatedKeys { | ||
463 | if childAddrOf(key, p) { | ||
464 | new = d.getExact(strings.Split(key, "."), "newDiff") | ||
465 | return old, new, true | ||
466 | } | ||
467 | } | ||
468 | new = d.get(strings.Split(key, "."), "newDiff") | ||
469 | return old, new, false | ||
470 | } | ||
471 | |||
472 | // removed checks to see if the key is present in the existing, pre-customized | ||
473 | // diff and if it was marked as NewRemoved. | ||
474 | func (d *ResourceDiff) removed(k string) bool { | ||
475 | diff, ok := d.diff.Attributes[k] | ||
476 | if !ok { | ||
477 | return false | ||
478 | } | ||
479 | return diff.NewRemoved | ||
480 | } | ||
481 | |||
482 | // get performs the appropriate multi-level reader logic for ResourceDiff, | ||
483 | // starting at source. Refer to newResourceDiff for the level order. | ||
484 | func (d *ResourceDiff) get(addr []string, source string) getResult { | ||
485 | result, err := d.multiReader.ReadFieldMerge(addr, source) | ||
486 | if err != nil { | ||
487 | panic(err) | ||
488 | } | ||
489 | |||
490 | return d.finalizeResult(addr, result) | ||
491 | } | ||
492 | |||
493 | // getExact gets an attribute from the exact level referenced by source. | ||
494 | func (d *ResourceDiff) getExact(addr []string, source string) getResult { | ||
495 | result, err := d.multiReader.ReadFieldExact(addr, source) | ||
496 | if err != nil { | ||
497 | panic(err) | ||
498 | } | ||
499 | |||
500 | return d.finalizeResult(addr, result) | ||
501 | } | ||
502 | |||
503 | // finalizeResult does some post-processing of the result produced by get and getExact. | ||
504 | func (d *ResourceDiff) finalizeResult(addr []string, result FieldReadResult) getResult { | ||
505 | // If the result doesn't exist, then we set the value to the zero value | ||
506 | var schema *Schema | ||
507 | if schemaL := addrToSchema(addr, d.schema); len(schemaL) > 0 { | ||
508 | schema = schemaL[len(schemaL)-1] | ||
509 | } | ||
510 | |||
511 | if result.Value == nil && schema != nil { | ||
512 | result.Value = result.ValueOrZero(schema) | ||
513 | } | ||
514 | |||
515 | // Transform the FieldReadResult into a getResult. It might be worth | ||
516 | // merging these two structures one day. | ||
517 | return getResult{ | ||
518 | Value: result.Value, | ||
519 | ValueProcessed: result.ValueProcessed, | ||
520 | Computed: result.Computed, | ||
521 | Exists: result.Exists, | ||
522 | Schema: schema, | ||
523 | } | ||
524 | } | ||
525 | |||
526 | // childAddrOf does a comparison of two addresses to see if one is the child of | ||
527 | // the other. | ||
528 | func childAddrOf(child, parent string) bool { | ||
529 | cs := strings.Split(child, ".") | ||
530 | ps := strings.Split(parent, ".") | ||
531 | if len(ps) > len(cs) { | ||
532 | return false | ||
533 | } | ||
534 | return reflect.DeepEqual(ps, cs[:len(ps)]) | ||
535 | } | ||
536 | |||
537 | // checkKey checks the key to make sure it exists and is computed. | ||
538 | func (d *ResourceDiff) checkKey(key, caller string, nested bool) error { | ||
539 | var schema *Schema | ||
540 | if nested { | ||
541 | keyParts := strings.Split(key, ".") | ||
542 | schemaL := addrToSchema(keyParts, d.schema) | ||
543 | if len(schemaL) > 0 { | ||
544 | schema = schemaL[len(schemaL)-1] | ||
545 | } | ||
546 | } else { | ||
547 | s, ok := d.schema[key] | ||
548 | if ok { | ||
549 | schema = s | ||
550 | } | ||
551 | } | ||
552 | if schema == nil { | ||
553 | return fmt.Errorf("%s: invalid key: %s", caller, key) | ||
554 | } | ||
555 | if !schema.Computed { | ||
556 | return fmt.Errorf("%s only operates on computed keys - %s is not one", caller, key) | ||
557 | } | ||
558 | return nil | ||
559 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go index acb5618..0ea5aad 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go | |||
@@ -21,9 +21,13 @@ import ( | |||
21 | "strings" | 21 | "strings" |
22 | 22 | ||
23 | "github.com/hashicorp/terraform/terraform" | 23 | "github.com/hashicorp/terraform/terraform" |
24 | "github.com/mitchellh/copystructure" | ||
24 | "github.com/mitchellh/mapstructure" | 25 | "github.com/mitchellh/mapstructure" |
25 | ) | 26 | ) |
26 | 27 | ||
28 | // Name of ENV variable which (if not empty) prefers panic over error | ||
29 | const PanicOnErr = "TF_SCHEMA_PANIC_ON_ERROR" | ||
30 | |||
27 | // type used for schema package context keys | 31 | // type used for schema package context keys |
28 | type contextKey string | 32 | type contextKey string |
29 | 33 | ||
@@ -116,12 +120,16 @@ type Schema struct { | |||
116 | ForceNew bool | 120 | ForceNew bool |
117 | StateFunc SchemaStateFunc | 121 | StateFunc SchemaStateFunc |
118 | 122 | ||
119 | // The following fields are only set for a TypeList or TypeSet Type. | 123 | // The following fields are only set for a TypeList, TypeSet, or TypeMap. |
120 | // | 124 | // |
121 | // Elem must be either a *Schema or a *Resource only if the Type is | 125 | // Elem represents the element type. For a TypeMap, it must be a *Schema |
122 | // TypeList, and represents what the element type is. If it is *Schema, | 126 | // with a Type of TypeString, otherwise it may be either a *Schema or a |
123 | // the element type is just a simple value. If it is *Resource, the | 127 | // *Resource. If it is *Schema, the element type is just a simple value. |
124 | // element type is a complex structure, potentially with its own lifecycle. | 128 | // If it is *Resource, the element type is a complex structure, |
129 | // potentially with its own lifecycle. | ||
130 | Elem interface{} | ||
131 | |||
132 | // The following fields are only set for a TypeList or TypeSet. | ||
125 | // | 133 | // |
126 | // MaxItems defines a maximum amount of items that can exist within a | 134 | // MaxItems defines a maximum amount of items that can exist within a |
127 | // TypeSet or TypeList. Specific use cases would be if a TypeSet is being | 135 | // TypeSet or TypeList. Specific use cases would be if a TypeSet is being |
@@ -138,7 +146,6 @@ type Schema struct { | |||
138 | // ["foo"] automatically. This is primarily for legacy reasons and the | 146 | // ["foo"] automatically. This is primarily for legacy reasons and the |
139 | // ambiguity is not recommended for new usage. Promotion is only allowed | 147 | // ambiguity is not recommended for new usage. Promotion is only allowed |
140 | // for primitive element types. | 148 | // for primitive element types. |
141 | Elem interface{} | ||
142 | MaxItems int | 149 | MaxItems int |
143 | MinItems int | 150 | MinItems int |
144 | PromoteSingle bool | 151 | PromoteSingle bool |
@@ -192,7 +199,7 @@ type Schema struct { | |||
192 | Sensitive bool | 199 | Sensitive bool |
193 | } | 200 | } |
194 | 201 | ||
195 | // SchemaDiffSuppresFunc is a function which can be used to determine | 202 | // SchemaDiffSuppressFunc is a function which can be used to determine |
196 | // whether a detected diff on a schema element is "valid" or not, and | 203 | // whether a detected diff on a schema element is "valid" or not, and |
197 | // suppress it from the plan if necessary. | 204 | // suppress it from the plan if necessary. |
198 | // | 205 | // |
@@ -289,8 +296,7 @@ func (s *Schema) ZeroValue() interface{} { | |||
289 | } | 296 | } |
290 | } | 297 | } |
291 | 298 | ||
292 | func (s *Schema) finalizeDiff( | 299 | func (s *Schema) finalizeDiff(d *terraform.ResourceAttrDiff, customized bool) *terraform.ResourceAttrDiff { |
293 | d *terraform.ResourceAttrDiff) *terraform.ResourceAttrDiff { | ||
294 | if d == nil { | 300 | if d == nil { |
295 | return d | 301 | return d |
296 | } | 302 | } |
@@ -331,13 +337,20 @@ func (s *Schema) finalizeDiff( | |||
331 | } | 337 | } |
332 | 338 | ||
333 | if s.Computed { | 339 | if s.Computed { |
334 | if d.Old != "" && d.New == "" { | 340 | // FIXME: This is where the customized bool from getChange finally |
335 | // This is a computed value with an old value set already, | 341 | // comes into play. It allows the previously incorrect behavior |
336 | // just let it go. | 342 | // of an empty string being used as "unset" when the value is |
337 | return nil | 343 | // computed. This should be removed once we can properly |
344 | // represent an unset/nil value from the configuration. | ||
345 | if !customized { | ||
346 | if d.Old != "" && d.New == "" { | ||
347 | // This is a computed value with an old value set already, | ||
348 | // just let it go. | ||
349 | return nil | ||
350 | } | ||
338 | } | 351 | } |
339 | 352 | ||
340 | if d.New == "" { | 353 | if d.New == "" && !d.NewComputed { |
341 | // Computed attribute without a new value set | 354 | // Computed attribute without a new value set |
342 | d.NewComputed = true | 355 | d.NewComputed = true |
343 | } | 356 | } |
@@ -354,6 +367,13 @@ func (s *Schema) finalizeDiff( | |||
354 | // schemaMap is a wrapper that adds nice functions on top of schemas. | 367 | // schemaMap is a wrapper that adds nice functions on top of schemas. |
355 | type schemaMap map[string]*Schema | 368 | type schemaMap map[string]*Schema |
356 | 369 | ||
370 | func (m schemaMap) panicOnError() bool { | ||
371 | if os.Getenv(PanicOnErr) != "" { | ||
372 | return true | ||
373 | } | ||
374 | return false | ||
375 | } | ||
376 | |||
357 | // Data returns a ResourceData for the given schema, state, and diff. | 377 | // Data returns a ResourceData for the given schema, state, and diff. |
358 | // | 378 | // |
359 | // The diff is optional. | 379 | // The diff is optional. |
@@ -361,17 +381,30 @@ func (m schemaMap) Data( | |||
361 | s *terraform.InstanceState, | 381 | s *terraform.InstanceState, |
362 | d *terraform.InstanceDiff) (*ResourceData, error) { | 382 | d *terraform.InstanceDiff) (*ResourceData, error) { |
363 | return &ResourceData{ | 383 | return &ResourceData{ |
364 | schema: m, | 384 | schema: m, |
365 | state: s, | 385 | state: s, |
366 | diff: d, | 386 | diff: d, |
387 | panicOnError: m.panicOnError(), | ||
367 | }, nil | 388 | }, nil |
368 | } | 389 | } |
369 | 390 | ||
391 | // DeepCopy returns a copy of this schemaMap. The copy can be safely modified | ||
392 | // without affecting the original. | ||
393 | func (m *schemaMap) DeepCopy() schemaMap { | ||
394 | copy, err := copystructure.Config{Lock: true}.Copy(m) | ||
395 | if err != nil { | ||
396 | panic(err) | ||
397 | } | ||
398 | return *copy.(*schemaMap) | ||
399 | } | ||
400 | |||
370 | // Diff returns the diff for a resource given the schema map, | 401 | // Diff returns the diff for a resource given the schema map, |
371 | // state, and configuration. | 402 | // state, and configuration. |
372 | func (m schemaMap) Diff( | 403 | func (m schemaMap) Diff( |
373 | s *terraform.InstanceState, | 404 | s *terraform.InstanceState, |
374 | c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { | 405 | c *terraform.ResourceConfig, |
406 | customizeDiff CustomizeDiffFunc, | ||
407 | meta interface{}) (*terraform.InstanceDiff, error) { | ||
375 | result := new(terraform.InstanceDiff) | 408 | result := new(terraform.InstanceDiff) |
376 | result.Attributes = make(map[string]*terraform.ResourceAttrDiff) | 409 | result.Attributes = make(map[string]*terraform.ResourceAttrDiff) |
377 | 410 | ||
@@ -381,9 +414,10 @@ func (m schemaMap) Diff( | |||
381 | } | 414 | } |
382 | 415 | ||
383 | d := &ResourceData{ | 416 | d := &ResourceData{ |
384 | schema: m, | 417 | schema: m, |
385 | state: s, | 418 | state: s, |
386 | config: c, | 419 | config: c, |
420 | panicOnError: m.panicOnError(), | ||
387 | } | 421 | } |
388 | 422 | ||
389 | for k, schema := range m { | 423 | for k, schema := range m { |
@@ -393,6 +427,29 @@ func (m schemaMap) Diff( | |||
393 | } | 427 | } |
394 | } | 428 | } |
395 | 429 | ||
430 | // Remove any nil diffs just to keep things clean | ||
431 | for k, v := range result.Attributes { | ||
432 | if v == nil { | ||
433 | delete(result.Attributes, k) | ||
434 | } | ||
435 | } | ||
436 | |||
437 | // If this is a non-destroy diff, call any custom diff logic that has been | ||
438 | // defined. | ||
439 | if !result.DestroyTainted && customizeDiff != nil { | ||
440 | mc := m.DeepCopy() | ||
441 | rd := newResourceDiff(mc, c, s, result) | ||
442 | if err := customizeDiff(rd, meta); err != nil { | ||
443 | return nil, err | ||
444 | } | ||
445 | for _, k := range rd.UpdatedKeys() { | ||
446 | err := m.diff(k, mc[k], result, rd, false) | ||
447 | if err != nil { | ||
448 | return nil, err | ||
449 | } | ||
450 | } | ||
451 | } | ||
452 | |||
396 | // If the diff requires a new resource, then we recompute the diff | 453 | // If the diff requires a new resource, then we recompute the diff |
397 | // so we have the complete new resource diff, and preserve the | 454 | // so we have the complete new resource diff, and preserve the |
398 | // RequiresNew fields where necessary so the user knows exactly what | 455 | // RequiresNew fields where necessary so the user knows exactly what |
@@ -418,6 +475,21 @@ func (m schemaMap) Diff( | |||
418 | } | 475 | } |
419 | } | 476 | } |
420 | 477 | ||
478 | // Re-run customization | ||
479 | if !result2.DestroyTainted && customizeDiff != nil { | ||
480 | mc := m.DeepCopy() | ||
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 { | ||
488 | return nil, err | ||
489 | } | ||
490 | } | ||
491 | } | ||
492 | |||
421 | // Force all the fields to not force a new since we know what we | 493 | // Force all the fields to not force a new since we know what we |
422 | // want to force new. | 494 | // want to force new. |
423 | for k, attr := range result2.Attributes { | 495 | for k, attr := range result2.Attributes { |
@@ -456,13 +528,6 @@ func (m schemaMap) Diff( | |||
456 | result = result2 | 528 | result = result2 |
457 | } | 529 | } |
458 | 530 | ||
459 | // Remove any nil diffs just to keep things clean | ||
460 | for k, v := range result.Attributes { | ||
461 | if v == nil { | ||
462 | delete(result.Attributes, k) | ||
463 | } | ||
464 | } | ||
465 | |||
466 | // Go through and detect all of the ComputedWhens now that we've | 531 | // Go through and detect all of the ComputedWhens now that we've |
467 | // finished the diff. | 532 | // finished the diff. |
468 | // TODO | 533 | // TODO |
@@ -681,11 +746,23 @@ func isValidFieldName(name string) bool { | |||
681 | return re.MatchString(name) | 746 | return re.MatchString(name) |
682 | } | 747 | } |
683 | 748 | ||
749 | // resourceDiffer is an interface that is used by the private diff functions. | ||
750 | // This helps facilitate diff logic for both ResourceData and ResoureDiff with | ||
751 | // minimal divergence in code. | ||
752 | type resourceDiffer interface { | ||
753 | diffChange(string) (interface{}, interface{}, bool, bool, bool) | ||
754 | Get(string) interface{} | ||
755 | GetChange(string) (interface{}, interface{}) | ||
756 | GetOk(string) (interface{}, bool) | ||
757 | HasChange(string) bool | ||
758 | Id() string | ||
759 | } | ||
760 | |||
684 | func (m schemaMap) diff( | 761 | func (m schemaMap) diff( |
685 | k string, | 762 | k string, |
686 | schema *Schema, | 763 | schema *Schema, |
687 | diff *terraform.InstanceDiff, | 764 | diff *terraform.InstanceDiff, |
688 | d *ResourceData, | 765 | d resourceDiffer, |
689 | all bool) error { | 766 | all bool) error { |
690 | 767 | ||
691 | unsupressedDiff := new(terraform.InstanceDiff) | 768 | unsupressedDiff := new(terraform.InstanceDiff) |
@@ -706,12 +783,14 @@ func (m schemaMap) diff( | |||
706 | } | 783 | } |
707 | 784 | ||
708 | for attrK, attrV := range unsupressedDiff.Attributes { | 785 | for attrK, attrV := range unsupressedDiff.Attributes { |
709 | if schema.DiffSuppressFunc != nil && | 786 | switch rd := d.(type) { |
710 | attrV != nil && | 787 | case *ResourceData: |
711 | schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, d) { | 788 | if schema.DiffSuppressFunc != nil && |
712 | continue | 789 | attrV != nil && |
790 | schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, rd) { | ||
791 | continue | ||
792 | } | ||
713 | } | 793 | } |
714 | |||
715 | diff.Attributes[attrK] = attrV | 794 | diff.Attributes[attrK] = attrV |
716 | } | 795 | } |
717 | 796 | ||
@@ -722,9 +801,9 @@ func (m schemaMap) diffList( | |||
722 | k string, | 801 | k string, |
723 | schema *Schema, | 802 | schema *Schema, |
724 | diff *terraform.InstanceDiff, | 803 | diff *terraform.InstanceDiff, |
725 | d *ResourceData, | 804 | d resourceDiffer, |
726 | all bool) error { | 805 | all bool) error { |
727 | o, n, _, computedList := d.diffChange(k) | 806 | o, n, _, computedList, customized := d.diffChange(k) |
728 | if computedList { | 807 | if computedList { |
729 | n = nil | 808 | n = nil |
730 | } | 809 | } |
@@ -791,10 +870,13 @@ func (m schemaMap) diffList( | |||
791 | oldStr = "" | 870 | oldStr = "" |
792 | } | 871 | } |
793 | 872 | ||
794 | diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{ | 873 | diff.Attributes[k+".#"] = countSchema.finalizeDiff( |
795 | Old: oldStr, | 874 | &terraform.ResourceAttrDiff{ |
796 | New: newStr, | 875 | Old: oldStr, |
797 | }) | 876 | New: newStr, |
877 | }, | ||
878 | customized, | ||
879 | ) | ||
798 | } | 880 | } |
799 | 881 | ||
800 | // Figure out the maximum | 882 | // Figure out the maximum |
@@ -841,13 +923,13 @@ func (m schemaMap) diffMap( | |||
841 | k string, | 923 | k string, |
842 | schema *Schema, | 924 | schema *Schema, |
843 | diff *terraform.InstanceDiff, | 925 | diff *terraform.InstanceDiff, |
844 | d *ResourceData, | 926 | d resourceDiffer, |
845 | all bool) error { | 927 | all bool) error { |
846 | prefix := k + "." | 928 | prefix := k + "." |
847 | 929 | ||
848 | // First get all the values from the state | 930 | // First get all the values from the state |
849 | var stateMap, configMap map[string]string | 931 | var stateMap, configMap map[string]string |
850 | o, n, _, nComputed := d.diffChange(k) | 932 | o, n, _, nComputed, customized := d.diffChange(k) |
851 | if err := mapstructure.WeakDecode(o, &stateMap); err != nil { | 933 | if err := mapstructure.WeakDecode(o, &stateMap); err != nil { |
852 | return fmt.Errorf("%s: %s", k, err) | 934 | return fmt.Errorf("%s: %s", k, err) |
853 | } | 935 | } |
@@ -899,6 +981,7 @@ func (m schemaMap) diffMap( | |||
899 | Old: oldStr, | 981 | Old: oldStr, |
900 | New: newStr, | 982 | New: newStr, |
901 | }, | 983 | }, |
984 | customized, | ||
902 | ) | 985 | ) |
903 | } | 986 | } |
904 | 987 | ||
@@ -916,16 +999,22 @@ func (m schemaMap) diffMap( | |||
916 | continue | 999 | continue |
917 | } | 1000 | } |
918 | 1001 | ||
919 | diff.Attributes[prefix+k] = schema.finalizeDiff(&terraform.ResourceAttrDiff{ | 1002 | diff.Attributes[prefix+k] = schema.finalizeDiff( |
920 | Old: old, | 1003 | &terraform.ResourceAttrDiff{ |
921 | New: v, | 1004 | Old: old, |
922 | }) | 1005 | New: v, |
1006 | }, | ||
1007 | customized, | ||
1008 | ) | ||
923 | } | 1009 | } |
924 | for k, v := range stateMap { | 1010 | for k, v := range stateMap { |
925 | diff.Attributes[prefix+k] = schema.finalizeDiff(&terraform.ResourceAttrDiff{ | 1011 | diff.Attributes[prefix+k] = schema.finalizeDiff( |
926 | Old: v, | 1012 | &terraform.ResourceAttrDiff{ |
927 | NewRemoved: true, | 1013 | Old: v, |
928 | }) | 1014 | NewRemoved: true, |
1015 | }, | ||
1016 | customized, | ||
1017 | ) | ||
929 | } | 1018 | } |
930 | 1019 | ||
931 | return nil | 1020 | return nil |
@@ -935,10 +1024,10 @@ func (m schemaMap) diffSet( | |||
935 | k string, | 1024 | k string, |
936 | schema *Schema, | 1025 | schema *Schema, |
937 | diff *terraform.InstanceDiff, | 1026 | diff *terraform.InstanceDiff, |
938 | d *ResourceData, | 1027 | d resourceDiffer, |
939 | all bool) error { | 1028 | all bool) error { |
940 | 1029 | ||
941 | o, n, _, computedSet := d.diffChange(k) | 1030 | o, n, _, computedSet, customized := d.diffChange(k) |
942 | if computedSet { | 1031 | if computedSet { |
943 | n = nil | 1032 | n = nil |
944 | } | 1033 | } |
@@ -997,20 +1086,26 @@ func (m schemaMap) diffSet( | |||
997 | countStr = "" | 1086 | countStr = "" |
998 | } | 1087 | } |
999 | 1088 | ||
1000 | diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{ | 1089 | diff.Attributes[k+".#"] = countSchema.finalizeDiff( |
1001 | Old: countStr, | 1090 | &terraform.ResourceAttrDiff{ |
1002 | NewComputed: true, | 1091 | Old: countStr, |
1003 | }) | 1092 | NewComputed: true, |
1093 | }, | ||
1094 | customized, | ||
1095 | ) | ||
1004 | return nil | 1096 | return nil |
1005 | } | 1097 | } |
1006 | 1098 | ||
1007 | // If the counts are not the same, then record that diff | 1099 | // If the counts are not the same, then record that diff |
1008 | changed := oldLen != newLen | 1100 | changed := oldLen != newLen |
1009 | if changed || all { | 1101 | if changed || all { |
1010 | diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{ | 1102 | diff.Attributes[k+".#"] = countSchema.finalizeDiff( |
1011 | Old: oldStr, | 1103 | &terraform.ResourceAttrDiff{ |
1012 | New: newStr, | 1104 | Old: oldStr, |
1013 | }) | 1105 | New: newStr, |
1106 | }, | ||
1107 | customized, | ||
1108 | ) | ||
1014 | } | 1109 | } |
1015 | 1110 | ||
1016 | // Build the list of codes that will make up our set. This is the | 1111 | // Build the list of codes that will make up our set. This is the |
@@ -1056,11 +1151,11 @@ func (m schemaMap) diffString( | |||
1056 | k string, | 1151 | k string, |
1057 | schema *Schema, | 1152 | schema *Schema, |
1058 | diff *terraform.InstanceDiff, | 1153 | diff *terraform.InstanceDiff, |
1059 | d *ResourceData, | 1154 | d resourceDiffer, |
1060 | all bool) error { | 1155 | all bool) error { |
1061 | var originalN interface{} | 1156 | var originalN interface{} |
1062 | var os, ns string | 1157 | var os, ns string |
1063 | o, n, _, computed := d.diffChange(k) | 1158 | o, n, _, computed, customized := d.diffChange(k) |
1064 | if schema.StateFunc != nil && n != nil { | 1159 | if schema.StateFunc != nil && n != nil { |
1065 | originalN = n | 1160 | originalN = n |
1066 | n = schema.StateFunc(n) | 1161 | n = schema.StateFunc(n) |
@@ -1090,20 +1185,23 @@ func (m schemaMap) diffString( | |||
1090 | } | 1185 | } |
1091 | 1186 | ||
1092 | removed := false | 1187 | removed := false |
1093 | if o != nil && n == nil { | 1188 | if o != nil && n == nil && !computed { |
1094 | removed = true | 1189 | removed = true |
1095 | } | 1190 | } |
1096 | if removed && schema.Computed { | 1191 | if removed && schema.Computed { |
1097 | return nil | 1192 | return nil |
1098 | } | 1193 | } |
1099 | 1194 | ||
1100 | diff.Attributes[k] = schema.finalizeDiff(&terraform.ResourceAttrDiff{ | 1195 | diff.Attributes[k] = schema.finalizeDiff( |
1101 | Old: os, | 1196 | &terraform.ResourceAttrDiff{ |
1102 | New: ns, | 1197 | Old: os, |
1103 | NewExtra: originalN, | 1198 | New: ns, |
1104 | NewRemoved: removed, | 1199 | NewExtra: originalN, |
1105 | NewComputed: computed, | 1200 | NewRemoved: removed, |
1106 | }) | 1201 | NewComputed: computed, |
1202 | }, | ||
1203 | customized, | ||
1204 | ) | ||
1107 | 1205 | ||
1108 | return nil | 1206 | return nil |
1109 | } | 1207 | } |
@@ -1172,9 +1270,9 @@ func (m schemaMap) validateConflictingAttributes( | |||
1172 | } | 1270 | } |
1173 | 1271 | ||
1174 | for _, conflicting_key := range schema.ConflictsWith { | 1272 | for _, conflicting_key := range schema.ConflictsWith { |
1175 | if value, ok := c.Get(conflicting_key); ok { | 1273 | if _, ok := c.Get(conflicting_key); ok { |
1176 | return fmt.Errorf( | 1274 | return fmt.Errorf( |
1177 | "%q: conflicts with %s (%#v)", k, conflicting_key, value) | 1275 | "%q: conflicts with %s", k, conflicting_key) |
1178 | } | 1276 | } |
1179 | } | 1277 | } |
1180 | 1278 | ||
@@ -1363,13 +1461,10 @@ func getValueType(k string, schema *Schema) (ValueType, error) { | |||
1363 | return vt, nil | 1461 | return vt, nil |
1364 | } | 1462 | } |
1365 | 1463 | ||
1464 | // If a Schema is provided to a Map, we use the Type of that schema | ||
1465 | // as the type for each element in the Map. | ||
1366 | if s, ok := schema.Elem.(*Schema); ok { | 1466 | if s, ok := schema.Elem.(*Schema); ok { |
1367 | if s.Elem == nil { | 1467 | return s.Type, nil |
1368 | return TypeString, nil | ||
1369 | } | ||
1370 | if vt, ok := s.Elem.(ValueType); ok { | ||
1371 | return vt, nil | ||
1372 | } | ||
1373 | } | 1468 | } |
1374 | 1469 | ||
1375 | if _, ok := schema.Elem.(*Resource); ok { | 1470 | if _, ok := schema.Elem.(*Resource); ok { |
@@ -1430,7 +1525,6 @@ func (m schemaMap) validatePrimitive( | |||
1430 | raw interface{}, | 1525 | raw interface{}, |
1431 | schema *Schema, | 1526 | schema *Schema, |
1432 | c *terraform.ResourceConfig) ([]string, []error) { | 1527 | c *terraform.ResourceConfig) ([]string, []error) { |
1433 | |||
1434 | // Catch if the user gave a complex type where a primitive was | 1528 | // Catch if the user gave a complex type where a primitive was |
1435 | // expected, so we can return a friendly error message that | 1529 | // expected, so we can return a friendly error message that |
1436 | // doesn't contain Go type system terminology. | 1530 | // doesn't contain Go type system terminology. |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/set.go b/vendor/github.com/hashicorp/terraform/helper/schema/set.go index de05f40..cba2890 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/set.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/set.go | |||
@@ -17,6 +17,12 @@ func HashString(v interface{}) int { | |||
17 | return hashcode.String(v.(string)) | 17 | return hashcode.String(v.(string)) |
18 | } | 18 | } |
19 | 19 | ||
20 | // HashInt hashes integers. If you want a Set of integers, this is the | ||
21 | // SchemaSetFunc you want. | ||
22 | func HashInt(v interface{}) int { | ||
23 | return hashcode.String(strconv.Itoa(v.(int))) | ||
24 | } | ||
25 | |||
20 | // HashResource hashes complex structures that are described using | 26 | // HashResource hashes complex structures that are described using |
21 | // a *Resource. This is the default set implementation used when a set's | 27 | // a *Resource. This is the default set implementation used when a set's |
22 | // element type is a full resource. | 28 | // element type is a full resource. |
@@ -153,6 +159,31 @@ func (s *Set) Equal(raw interface{}) bool { | |||
153 | return reflect.DeepEqual(s.m, other.m) | 159 | return reflect.DeepEqual(s.m, other.m) |
154 | } | 160 | } |
155 | 161 | ||
162 | // HashEqual simply checks to the keys the top-level map to the keys in the | ||
163 | // other set's top-level map to see if they are equal. This obviously assumes | ||
164 | // you have a properly working hash function - use HashResource if in doubt. | ||
165 | func (s *Set) HashEqual(raw interface{}) bool { | ||
166 | other, ok := raw.(*Set) | ||
167 | if !ok { | ||
168 | return false | ||
169 | } | ||
170 | |||
171 | ks1 := make([]string, 0) | ||
172 | ks2 := make([]string, 0) | ||
173 | |||
174 | for k := range s.m { | ||
175 | ks1 = append(ks1, k) | ||
176 | } | ||
177 | for k := range other.m { | ||
178 | ks2 = append(ks2, k) | ||
179 | } | ||
180 | |||
181 | sort.Strings(ks1) | ||
182 | sort.Strings(ks2) | ||
183 | |||
184 | return reflect.DeepEqual(ks1, ks2) | ||
185 | } | ||
186 | |||
156 | func (s *Set) GoString() string { | 187 | func (s *Set) GoString() string { |
157 | return fmt.Sprintf("*Set(%#v)", s.m) | 188 | return fmt.Sprintf("*Set(%#v)", s.m) |
158 | } | 189 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/testing.go b/vendor/github.com/hashicorp/terraform/helper/schema/testing.go index 9765bdb..da754ac 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/testing.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/testing.go | |||
@@ -10,13 +10,15 @@ import ( | |||
10 | // TestResourceDataRaw creates a ResourceData from a raw configuration map. | 10 | // TestResourceDataRaw creates a ResourceData from a raw configuration map. |
11 | func TestResourceDataRaw( | 11 | func TestResourceDataRaw( |
12 | t *testing.T, schema map[string]*Schema, raw map[string]interface{}) *ResourceData { | 12 | t *testing.T, schema map[string]*Schema, raw map[string]interface{}) *ResourceData { |
13 | t.Helper() | ||
14 | |||
13 | c, err := config.NewRawConfig(raw) | 15 | c, err := config.NewRawConfig(raw) |
14 | if err != nil { | 16 | if err != nil { |
15 | t.Fatalf("err: %s", err) | 17 | t.Fatalf("err: %s", err) |
16 | } | 18 | } |
17 | 19 | ||
18 | sm := schemaMap(schema) | 20 | sm := schemaMap(schema) |
19 | diff, err := sm.Diff(nil, terraform.NewResourceConfig(c)) | 21 | diff, err := sm.Diff(nil, terraform.NewResourceConfig(c), nil, nil) |
20 | if err != nil { | 22 | if err != nil { |
21 | t.Fatalf("err: %s", err) | 23 | t.Fatalf("err: %s", err) |
22 | } | 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 1610cec..3bc3ac4 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | package schema | 3 | package schema |
4 | 4 | ||
5 | import "fmt" | 5 | import "strconv" |
6 | 6 | ||
7 | const _ValueType_name = "TypeInvalidTypeBoolTypeIntTypeFloatTypeStringTypeListTypeMapTypeSettypeObject" | 7 | const _ValueType_name = "TypeInvalidTypeBoolTypeIntTypeFloatTypeStringTypeListTypeMapTypeSettypeObject" |
8 | 8 | ||
@@ -10,7 +10,7 @@ var _ValueType_index = [...]uint8{0, 11, 19, 26, 35, 45, 53, 60, 67, 77} | |||
10 | 10 | ||
11 | func (i ValueType) String() string { | 11 | func (i ValueType) String() string { |
12 | if i < 0 || i >= ValueType(len(_ValueType_index)-1) { | 12 | if i < 0 || i >= ValueType(len(_ValueType_index)-1) { |
13 | return fmt.Sprintf("ValueType(%d)", i) | 13 | return "ValueType(" + strconv.FormatInt(int64(i), 10) + ")" |
14 | } | 14 | } |
15 | return _ValueType_name[_ValueType_index[i]:_ValueType_index[i+1]] | 15 | return _ValueType_name[_ValueType_index[i]:_ValueType_index[i+1]] |
16 | } | 16 | } |