8 "github.com/hashicorp/terraform/terraform"
9 "github.com/mitchellh/copystructure"
12 const TimeoutKey = "e2bfb730-ecaa-11e6-8f88-34363bc7c4c0"
13 const TimeoutsConfigKey = "timeouts"
16 TimeoutCreate = "create"
18 TimeoutUpdate = "update"
19 TimeoutDelete = "delete"
20 TimeoutDefault = "default"
23 func timeoutKeys() []string {
33 // could be time.Duration, int64 or float64
34 func DefaultTimeout(tx interface{}) *time.Duration {
36 switch raw := tx.(type) {
40 td = time.Duration(raw)
42 td = time.Duration(int64(raw))
44 log.Printf("[WARN] Unknown type in DefaultTimeout: %#v", tx)
49 type ResourceTimeout struct {
50 Create, Read, Update, Delete, Default *time.Duration
53 // ConfigDecode takes a schema and the configuration (available in Diff) and
54 // validates, parses the timeouts into `t`
55 func (t *ResourceTimeout) ConfigDecode(s *Resource, c *terraform.ResourceConfig) error {
56 if s.Timeouts != nil {
57 raw, err := copystructure.Copy(s.Timeouts)
59 log.Printf("[DEBUG] Error with deep copy: %s", err)
61 *t = *raw.(*ResourceTimeout)
64 if raw, ok := c.Config[TimeoutsConfigKey]; ok {
65 if configTimeouts, ok := raw.([]map[string]interface{}); ok {
66 for _, timeoutValues := range configTimeouts {
67 // loop through each Timeout given in the configuration and validate they
68 // the Timeout defined in the resource
69 for timeKey, timeValue := range timeoutValues {
70 // validate that we're dealing with the normal CRUD actions
72 for _, key := range timeoutKeys() {
80 return fmt.Errorf("Unsupported Timeout configuration key found (%s)", timeKey)
84 rt, err := time.ParseDuration(timeValue.(string))
86 return fmt.Errorf("Error parsing Timeout for (%s): %s", timeKey, err)
89 var timeout *time.Duration
103 // If the resource has not delcared this in the definition, then error
104 // with an unsupported message
106 return unsupportedTimeoutKeyError(timeKey)
113 log.Printf("[WARN] Invalid Timeout structure found, skipping timeouts")
120 func unsupportedTimeoutKeyError(key string) error {
121 return fmt.Errorf("Timeout Key (%s) is not supported", key)
124 // DiffEncode, StateEncode, and MetaDecode are analogous to the Go stdlib JSONEncoder
125 // interface: they encode/decode a timeouts struct from an instance diff, which is
126 // where the timeout data is stored after a diff to pass into Apply.
128 // StateEncode encodes the timeout into the ResourceData's InstanceState for
131 func (t *ResourceTimeout) DiffEncode(id *terraform.InstanceDiff) error {
132 return t.metaEncode(id)
135 func (t *ResourceTimeout) StateEncode(is *terraform.InstanceState) error {
136 return t.metaEncode(is)
139 // metaEncode encodes the ResourceTimeout into a map[string]interface{} format
140 // and stores it in the Meta field of the interface it's given.
141 // Assumes the interface is either *terraform.InstanceState or
142 // *terraform.InstanceDiff, returns an error otherwise
143 func (t *ResourceTimeout) metaEncode(ids interface{}) error {
144 m := make(map[string]interface{})
147 m[TimeoutCreate] = t.Create.Nanoseconds()
150 m[TimeoutRead] = t.Read.Nanoseconds()
153 m[TimeoutUpdate] = t.Update.Nanoseconds()
156 m[TimeoutDelete] = t.Delete.Nanoseconds()
158 if t.Default != nil {
159 m[TimeoutDefault] = t.Default.Nanoseconds()
160 // for any key above that is nil, if default is specified, we need to
161 // populate it with the default
162 for _, k := range timeoutKeys() {
163 if _, ok := m[k]; !ok {
164 m[k] = t.Default.Nanoseconds()
169 // only add the Timeout to the Meta if we have values
171 switch instance := ids.(type) {
172 case *terraform.InstanceDiff:
173 if instance.Meta == nil {
174 instance.Meta = make(map[string]interface{})
176 instance.Meta[TimeoutKey] = m
177 case *terraform.InstanceState:
178 if instance.Meta == nil {
179 instance.Meta = make(map[string]interface{})
181 instance.Meta[TimeoutKey] = m
183 return fmt.Errorf("Error matching type for Diff Encode")
190 func (t *ResourceTimeout) StateDecode(id *terraform.InstanceState) error {
191 return t.metaDecode(id)
193 func (t *ResourceTimeout) DiffDecode(is *terraform.InstanceDiff) error {
194 return t.metaDecode(is)
197 func (t *ResourceTimeout) metaDecode(ids interface{}) error {
198 var rawMeta interface{}
200 switch rawInstance := ids.(type) {
201 case *terraform.InstanceDiff:
202 rawMeta, ok = rawInstance.Meta[TimeoutKey]
206 case *terraform.InstanceState:
207 rawMeta, ok = rawInstance.Meta[TimeoutKey]
212 return fmt.Errorf("Unknown or unsupported type in metaDecode: %#v", ids)
215 times := rawMeta.(map[string]interface{})
220 if v, ok := times[TimeoutCreate]; ok {
221 t.Create = DefaultTimeout(v)
223 if v, ok := times[TimeoutRead]; ok {
224 t.Read = DefaultTimeout(v)
226 if v, ok := times[TimeoutUpdate]; ok {
227 t.Update = DefaultTimeout(v)
229 if v, ok := times[TimeoutDelete]; ok {
230 t.Delete = DefaultTimeout(v)
232 if v, ok := times[TimeoutDefault]; ok {
233 t.Default = DefaultTimeout(v)