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