]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package schema |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | "log" | |
6 | "time" | |
7 | ||
107c1cdb | 8 | "github.com/hashicorp/terraform/config" |
bae9f6d2 JC |
9 | "github.com/hashicorp/terraform/terraform" |
10 | "github.com/mitchellh/copystructure" | |
11 | ) | |
12 | ||
13 | const TimeoutKey = "e2bfb730-ecaa-11e6-8f88-34363bc7c4c0" | |
14 | const TimeoutsConfigKey = "timeouts" | |
15 | ||
16 | const ( | |
17 | TimeoutCreate = "create" | |
18 | TimeoutRead = "read" | |
19 | TimeoutUpdate = "update" | |
20 | TimeoutDelete = "delete" | |
21 | TimeoutDefault = "default" | |
22 | ) | |
23 | ||
24 | func timeoutKeys() []string { | |
25 | return []string{ | |
26 | TimeoutCreate, | |
27 | TimeoutRead, | |
28 | TimeoutUpdate, | |
29 | TimeoutDelete, | |
30 | TimeoutDefault, | |
31 | } | |
32 | } | |
33 | ||
34 | // could be time.Duration, int64 or float64 | |
35 | func DefaultTimeout(tx interface{}) *time.Duration { | |
36 | var td time.Duration | |
37 | switch raw := tx.(type) { | |
38 | case time.Duration: | |
39 | return &raw | |
40 | case int64: | |
41 | td = time.Duration(raw) | |
42 | case float64: | |
43 | td = time.Duration(int64(raw)) | |
44 | default: | |
45 | log.Printf("[WARN] Unknown type in DefaultTimeout: %#v", tx) | |
46 | } | |
47 | return &td | |
48 | } | |
49 | ||
50 | type ResourceTimeout struct { | |
51 | Create, Read, Update, Delete, Default *time.Duration | |
52 | } | |
53 | ||
54 | // ConfigDecode takes a schema and the configuration (available in Diff) and | |
55 | // validates, parses the timeouts into `t` | |
56 | func (t *ResourceTimeout) ConfigDecode(s *Resource, c *terraform.ResourceConfig) error { | |
57 | if s.Timeouts != nil { | |
58 | raw, err := copystructure.Copy(s.Timeouts) | |
59 | if err != nil { | |
60 | log.Printf("[DEBUG] Error with deep copy: %s", err) | |
61 | } | |
62 | *t = *raw.(*ResourceTimeout) | |
63 | } | |
64 | ||
65 | if raw, ok := c.Config[TimeoutsConfigKey]; ok { | |
107c1cdb ND |
66 | var rawTimeouts []map[string]interface{} |
67 | switch raw := raw.(type) { | |
68 | case map[string]interface{}: | |
69 | rawTimeouts = append(rawTimeouts, raw) | |
70 | case []map[string]interface{}: | |
71 | rawTimeouts = raw | |
72 | case string: | |
73 | if raw == config.UnknownVariableValue { | |
74 | // Timeout is not defined in the config | |
75 | // Defaults will be used instead | |
76 | return nil | |
77 | } else { | |
78 | log.Printf("[ERROR] Invalid timeout value: %q", raw) | |
79 | return fmt.Errorf("Invalid Timeout value found") | |
80 | } | |
81 | default: | |
82 | log.Printf("[ERROR] Invalid timeout structure: %#v", raw) | |
83 | return fmt.Errorf("Invalid Timeout structure found") | |
84 | } | |
bae9f6d2 | 85 | |
107c1cdb ND |
86 | for _, timeoutValues := range rawTimeouts { |
87 | for timeKey, timeValue := range timeoutValues { | |
88 | // validate that we're dealing with the normal CRUD actions | |
89 | var found bool | |
90 | for _, key := range timeoutKeys() { | |
91 | if timeKey == key { | |
92 | found = true | |
93 | break | |
bae9f6d2 | 94 | } |
107c1cdb | 95 | } |
bae9f6d2 | 96 | |
107c1cdb ND |
97 | if !found { |
98 | return fmt.Errorf("Unsupported Timeout configuration key found (%s)", timeKey) | |
99 | } | |
bae9f6d2 | 100 | |
107c1cdb ND |
101 | // Get timeout |
102 | rt, err := time.ParseDuration(timeValue.(string)) | |
103 | if err != nil { | |
104 | return fmt.Errorf("Error parsing %q timeout: %s", timeKey, err) | |
105 | } | |
bae9f6d2 | 106 | |
107c1cdb ND |
107 | var timeout *time.Duration |
108 | switch timeKey { | |
109 | case TimeoutCreate: | |
110 | timeout = t.Create | |
111 | case TimeoutUpdate: | |
112 | timeout = t.Update | |
113 | case TimeoutRead: | |
114 | timeout = t.Read | |
115 | case TimeoutDelete: | |
116 | timeout = t.Delete | |
117 | case TimeoutDefault: | |
118 | timeout = t.Default | |
119 | } | |
bae9f6d2 | 120 | |
107c1cdb ND |
121 | // If the resource has not delcared this in the definition, then error |
122 | // with an unsupported message | |
123 | if timeout == nil { | |
124 | return unsupportedTimeoutKeyError(timeKey) | |
bae9f6d2 | 125 | } |
107c1cdb ND |
126 | |
127 | *timeout = rt | |
bae9f6d2 | 128 | } |
107c1cdb | 129 | return nil |
bae9f6d2 JC |
130 | } |
131 | } | |
132 | ||
133 | return nil | |
134 | } | |
135 | ||
136 | func unsupportedTimeoutKeyError(key string) error { | |
137 | return fmt.Errorf("Timeout Key (%s) is not supported", key) | |
138 | } | |
139 | ||
140 | // DiffEncode, StateEncode, and MetaDecode are analogous to the Go stdlib JSONEncoder | |
141 | // interface: they encode/decode a timeouts struct from an instance diff, which is | |
142 | // where the timeout data is stored after a diff to pass into Apply. | |
143 | // | |
144 | // StateEncode encodes the timeout into the ResourceData's InstanceState for | |
145 | // saving to state | |
146 | // | |
147 | func (t *ResourceTimeout) DiffEncode(id *terraform.InstanceDiff) error { | |
148 | return t.metaEncode(id) | |
149 | } | |
150 | ||
151 | func (t *ResourceTimeout) StateEncode(is *terraform.InstanceState) error { | |
152 | return t.metaEncode(is) | |
153 | } | |
154 | ||
155 | // metaEncode encodes the ResourceTimeout into a map[string]interface{} format | |
156 | // and stores it in the Meta field of the interface it's given. | |
157 | // Assumes the interface is either *terraform.InstanceState or | |
158 | // *terraform.InstanceDiff, returns an error otherwise | |
159 | func (t *ResourceTimeout) metaEncode(ids interface{}) error { | |
160 | m := make(map[string]interface{}) | |
161 | ||
162 | if t.Create != nil { | |
163 | m[TimeoutCreate] = t.Create.Nanoseconds() | |
164 | } | |
165 | if t.Read != nil { | |
166 | m[TimeoutRead] = t.Read.Nanoseconds() | |
167 | } | |
168 | if t.Update != nil { | |
169 | m[TimeoutUpdate] = t.Update.Nanoseconds() | |
170 | } | |
171 | if t.Delete != nil { | |
172 | m[TimeoutDelete] = t.Delete.Nanoseconds() | |
173 | } | |
174 | if t.Default != nil { | |
175 | m[TimeoutDefault] = t.Default.Nanoseconds() | |
176 | // for any key above that is nil, if default is specified, we need to | |
177 | // populate it with the default | |
178 | for _, k := range timeoutKeys() { | |
179 | if _, ok := m[k]; !ok { | |
180 | m[k] = t.Default.Nanoseconds() | |
181 | } | |
182 | } | |
183 | } | |
184 | ||
185 | // only add the Timeout to the Meta if we have values | |
186 | if len(m) > 0 { | |
187 | switch instance := ids.(type) { | |
188 | case *terraform.InstanceDiff: | |
189 | if instance.Meta == nil { | |
190 | instance.Meta = make(map[string]interface{}) | |
191 | } | |
192 | instance.Meta[TimeoutKey] = m | |
193 | case *terraform.InstanceState: | |
194 | if instance.Meta == nil { | |
195 | instance.Meta = make(map[string]interface{}) | |
196 | } | |
197 | instance.Meta[TimeoutKey] = m | |
198 | default: | |
199 | return fmt.Errorf("Error matching type for Diff Encode") | |
200 | } | |
201 | } | |
202 | ||
203 | return nil | |
204 | } | |
205 | ||
206 | func (t *ResourceTimeout) StateDecode(id *terraform.InstanceState) error { | |
207 | return t.metaDecode(id) | |
208 | } | |
209 | func (t *ResourceTimeout) DiffDecode(is *terraform.InstanceDiff) error { | |
210 | return t.metaDecode(is) | |
211 | } | |
212 | ||
213 | func (t *ResourceTimeout) metaDecode(ids interface{}) error { | |
214 | var rawMeta interface{} | |
215 | var ok bool | |
216 | switch rawInstance := ids.(type) { | |
217 | case *terraform.InstanceDiff: | |
218 | rawMeta, ok = rawInstance.Meta[TimeoutKey] | |
219 | if !ok { | |
220 | return nil | |
221 | } | |
222 | case *terraform.InstanceState: | |
223 | rawMeta, ok = rawInstance.Meta[TimeoutKey] | |
224 | if !ok { | |
225 | return nil | |
226 | } | |
227 | default: | |
228 | return fmt.Errorf("Unknown or unsupported type in metaDecode: %#v", ids) | |
229 | } | |
230 | ||
231 | times := rawMeta.(map[string]interface{}) | |
232 | if len(times) == 0 { | |
233 | return nil | |
234 | } | |
235 | ||
236 | if v, ok := times[TimeoutCreate]; ok { | |
237 | t.Create = DefaultTimeout(v) | |
238 | } | |
239 | if v, ok := times[TimeoutRead]; ok { | |
240 | t.Read = DefaultTimeout(v) | |
241 | } | |
242 | if v, ok := times[TimeoutUpdate]; ok { | |
243 | t.Update = DefaultTimeout(v) | |
244 | } | |
245 | if v, ok := times[TimeoutDelete]; ok { | |
246 | t.Delete = DefaultTimeout(v) | |
247 | } | |
248 | if v, ok := times[TimeoutDefault]; ok { | |
249 | t.Default = DefaultTimeout(v) | |
250 | } | |
251 | ||
252 | return nil | |
253 | } |