github.com/posener/terraform@v0.11.0-beta1.0.20171103235147-645df36af025/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 const TimeoutsConfigKey = "timeouts" 14 15 const ( 16 TimeoutCreate = "create" 17 TimeoutRead = "read" 18 TimeoutUpdate = "update" 19 TimeoutDelete = "delete" 20 TimeoutDefault = "default" 21 ) 22 23 func timeoutKeys() []string { 24 return []string{ 25 TimeoutCreate, 26 TimeoutRead, 27 TimeoutUpdate, 28 TimeoutDelete, 29 TimeoutDefault, 30 } 31 } 32 33 // could be time.Duration, int64 or float64 34 func DefaultTimeout(tx interface{}) *time.Duration { 35 var td time.Duration 36 switch raw := tx.(type) { 37 case time.Duration: 38 return &raw 39 case int64: 40 td = time.Duration(raw) 41 case float64: 42 td = time.Duration(int64(raw)) 43 default: 44 log.Printf("[WARN] Unknown type in DefaultTimeout: %#v", tx) 45 } 46 return &td 47 } 48 49 type ResourceTimeout struct { 50 Create, Read, Update, Delete, Default *time.Duration 51 } 52 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) 58 if err != nil { 59 log.Printf("[DEBUG] Error with deep copy: %s", err) 60 } 61 *t = *raw.(*ResourceTimeout) 62 } 63 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 71 var found bool 72 for _, key := range timeoutKeys() { 73 if timeKey == key { 74 found = true 75 break 76 } 77 } 78 79 if !found { 80 return fmt.Errorf("Unsupported Timeout configuration key found (%s)", timeKey) 81 } 82 83 // Get timeout 84 rt, err := time.ParseDuration(timeValue.(string)) 85 if err != nil { 86 return fmt.Errorf("Error parsing Timeout for (%s): %s", timeKey, err) 87 } 88 89 var timeout *time.Duration 90 switch timeKey { 91 case TimeoutCreate: 92 timeout = t.Create 93 case TimeoutUpdate: 94 timeout = t.Update 95 case TimeoutRead: 96 timeout = t.Read 97 case TimeoutDelete: 98 timeout = t.Delete 99 case TimeoutDefault: 100 timeout = t.Default 101 } 102 103 // If the resource has not delcared this in the definition, then error 104 // with an unsupported message 105 if timeout == nil { 106 return unsupportedTimeoutKeyError(timeKey) 107 } 108 109 *timeout = rt 110 } 111 } 112 } else { 113 log.Printf("[WARN] Invalid Timeout structure found, skipping timeouts") 114 } 115 } 116 117 return nil 118 } 119 120 func unsupportedTimeoutKeyError(key string) error { 121 return fmt.Errorf("Timeout Key (%s) is not supported", key) 122 } 123 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. 127 // 128 // StateEncode encodes the timeout into the ResourceData's InstanceState for 129 // saving to state 130 // 131 func (t *ResourceTimeout) DiffEncode(id *terraform.InstanceDiff) error { 132 return t.metaEncode(id) 133 } 134 135 func (t *ResourceTimeout) StateEncode(is *terraform.InstanceState) error { 136 return t.metaEncode(is) 137 } 138 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{}) 145 146 if t.Create != nil { 147 m[TimeoutCreate] = t.Create.Nanoseconds() 148 } 149 if t.Read != nil { 150 m[TimeoutRead] = t.Read.Nanoseconds() 151 } 152 if t.Update != nil { 153 m[TimeoutUpdate] = t.Update.Nanoseconds() 154 } 155 if t.Delete != nil { 156 m[TimeoutDelete] = t.Delete.Nanoseconds() 157 } 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() 165 } 166 } 167 } 168 169 // only add the Timeout to the Meta if we have values 170 if len(m) > 0 { 171 switch instance := ids.(type) { 172 case *terraform.InstanceDiff: 173 if instance.Meta == nil { 174 instance.Meta = make(map[string]interface{}) 175 } 176 instance.Meta[TimeoutKey] = m 177 case *terraform.InstanceState: 178 if instance.Meta == nil { 179 instance.Meta = make(map[string]interface{}) 180 } 181 instance.Meta[TimeoutKey] = m 182 default: 183 return fmt.Errorf("Error matching type for Diff Encode") 184 } 185 } 186 187 return nil 188 } 189 190 func (t *ResourceTimeout) StateDecode(id *terraform.InstanceState) error { 191 return t.metaDecode(id) 192 } 193 func (t *ResourceTimeout) DiffDecode(is *terraform.InstanceDiff) error { 194 return t.metaDecode(is) 195 } 196 197 func (t *ResourceTimeout) metaDecode(ids interface{}) error { 198 var rawMeta interface{} 199 var ok bool 200 switch rawInstance := ids.(type) { 201 case *terraform.InstanceDiff: 202 rawMeta, ok = rawInstance.Meta[TimeoutKey] 203 if !ok { 204 return nil 205 } 206 case *terraform.InstanceState: 207 rawMeta, ok = rawInstance.Meta[TimeoutKey] 208 if !ok { 209 return nil 210 } 211 default: 212 return fmt.Errorf("Unknown or unsupported type in metaDecode: %#v", ids) 213 } 214 215 times := rawMeta.(map[string]interface{}) 216 if len(times) == 0 { 217 return nil 218 } 219 220 if v, ok := times[TimeoutCreate]; ok { 221 t.Create = DefaultTimeout(v) 222 } 223 if v, ok := times[TimeoutRead]; ok { 224 t.Read = DefaultTimeout(v) 225 } 226 if v, ok := times[TimeoutUpdate]; ok { 227 t.Update = DefaultTimeout(v) 228 } 229 if v, ok := times[TimeoutDelete]; ok { 230 t.Delete = DefaultTimeout(v) 231 } 232 if v, ok := times[TimeoutDefault]; ok { 233 t.Default = DefaultTimeout(v) 234 } 235 236 return nil 237 }