8 "github.com/hashicorp/terraform/config"
9 "github.com/hashicorp/terraform/terraform"
10 "github.com/mitchellh/copystructure"
13 const TimeoutKey = "e2bfb730-ecaa-11e6-8f88-34363bc7c4c0"
14 const TimeoutsConfigKey = "timeouts"
17 TimeoutCreate = "create"
19 TimeoutUpdate = "update"
20 TimeoutDelete = "delete"
21 TimeoutDefault = "default"
24 func timeoutKeys() []string {
34 // could be time.Duration, int64 or float64
35 func DefaultTimeout(tx interface{}) *time.Duration {
37 switch raw := tx.(type) {
41 td = time.Duration(raw)
43 td = time.Duration(int64(raw))
45 log.Printf("[WARN] Unknown type in DefaultTimeout: %#v", tx)
50 type ResourceTimeout struct {
51 Create, Read, Update, Delete, Default *time.Duration
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)
60 log.Printf("[DEBUG] Error with deep copy: %s", err)
62 *t = *raw.(*ResourceTimeout)
65 if raw, ok := c.Config[TimeoutsConfigKey]; ok {
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{}:
73 if raw == config.UnknownVariableValue {
74 // Timeout is not defined in the config
75 // Defaults will be used instead
78 log.Printf("[ERROR] Invalid timeout value: %q", raw)
79 return fmt.Errorf("Invalid Timeout value found")
82 log.Printf("[ERROR] Invalid timeout structure: %#v", raw)
83 return fmt.Errorf("Invalid Timeout structure found")
86 for _, timeoutValues := range rawTimeouts {
87 for timeKey, timeValue := range timeoutValues {
88 // validate that we're dealing with the normal CRUD actions
90 for _, key := range timeoutKeys() {
98 return fmt.Errorf("Unsupported Timeout configuration key found (%s)", timeKey)
102 rt, err := time.ParseDuration(timeValue.(string))
104 return fmt.Errorf("Error parsing %q timeout: %s", timeKey, err)
107 var timeout *time.Duration
121 // If the resource has not delcared this in the definition, then error
122 // with an unsupported message
124 return unsupportedTimeoutKeyError(timeKey)
136 func unsupportedTimeoutKeyError(key string) error {
137 return fmt.Errorf("Timeout Key (%s) is not supported", key)
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.
144 // StateEncode encodes the timeout into the ResourceData's InstanceState for
147 func (t *ResourceTimeout) DiffEncode(id *terraform.InstanceDiff) error {
148 return t.metaEncode(id)
151 func (t *ResourceTimeout) StateEncode(is *terraform.InstanceState) error {
152 return t.metaEncode(is)
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{})
163 m[TimeoutCreate] = t.Create.Nanoseconds()
166 m[TimeoutRead] = t.Read.Nanoseconds()
169 m[TimeoutUpdate] = t.Update.Nanoseconds()
172 m[TimeoutDelete] = t.Delete.Nanoseconds()
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()
185 // only add the Timeout to the Meta if we have values
187 switch instance := ids.(type) {
188 case *terraform.InstanceDiff:
189 if instance.Meta == nil {
190 instance.Meta = make(map[string]interface{})
192 instance.Meta[TimeoutKey] = m
193 case *terraform.InstanceState:
194 if instance.Meta == nil {
195 instance.Meta = make(map[string]interface{})
197 instance.Meta[TimeoutKey] = m
199 return fmt.Errorf("Error matching type for Diff Encode")
206 func (t *ResourceTimeout) StateDecode(id *terraform.InstanceState) error {
207 return t.metaDecode(id)
209 func (t *ResourceTimeout) DiffDecode(is *terraform.InstanceDiff) error {
210 return t.metaDecode(is)
213 func (t *ResourceTimeout) metaDecode(ids interface{}) error {
214 var rawMeta interface{}
216 switch rawInstance := ids.(type) {
217 case *terraform.InstanceDiff:
218 rawMeta, ok = rawInstance.Meta[TimeoutKey]
222 case *terraform.InstanceState:
223 rawMeta, ok = rawInstance.Meta[TimeoutKey]
228 return fmt.Errorf("Unknown or unsupported type in metaDecode: %#v", ids)
231 times := rawMeta.(map[string]interface{})
236 if v, ok := times[TimeoutCreate]; ok {
237 t.Create = DefaultTimeout(v)
239 if v, ok := times[TimeoutRead]; ok {
240 t.Read = DefaultTimeout(v)
242 if v, ok := times[TimeoutUpdate]; ok {
243 t.Update = DefaultTimeout(v)
245 if v, ok := times[TimeoutDelete]; ok {
246 t.Delete = DefaultTimeout(v)
248 if v, ok := times[TimeoutDefault]; ok {
249 t.Default = DefaultTimeout(v)