github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/legacy/helper/schema/resource_timeout.go (about) 1 package schema 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/hashicorp/terraform/internal/configs/hcl2shim" 9 "github.com/hashicorp/terraform/internal/legacy/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 { 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 == hcl2shim.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 case []interface{}: 82 for _, r := range raw { 83 if rMap, ok := r.(map[string]interface{}); ok { 84 rawTimeouts = append(rawTimeouts, rMap) 85 } else { 86 // Go will not allow a fallthrough 87 log.Printf("[ERROR] Invalid timeout structure: %#v", raw) 88 return fmt.Errorf("Invalid Timeout structure found") 89 } 90 } 91 default: 92 log.Printf("[ERROR] Invalid timeout structure: %#v", raw) 93 return fmt.Errorf("Invalid Timeout structure found") 94 } 95 96 for _, timeoutValues := range rawTimeouts { 97 for timeKey, timeValue := range timeoutValues { 98 // validate that we're dealing with the normal CRUD actions 99 var found bool 100 for _, key := range timeoutKeys() { 101 if timeKey == key { 102 found = true 103 break 104 } 105 } 106 107 if !found { 108 return fmt.Errorf("Unsupported Timeout configuration key found (%s)", timeKey) 109 } 110 111 // Get timeout 112 rt, err := time.ParseDuration(timeValue.(string)) 113 if err != nil { 114 return fmt.Errorf("Error parsing %q timeout: %s", timeKey, err) 115 } 116 117 var timeout *time.Duration 118 switch timeKey { 119 case TimeoutCreate: 120 timeout = t.Create 121 case TimeoutUpdate: 122 timeout = t.Update 123 case TimeoutRead: 124 timeout = t.Read 125 case TimeoutDelete: 126 timeout = t.Delete 127 case TimeoutDefault: 128 timeout = t.Default 129 } 130 131 // If the resource has not delcared this in the definition, then error 132 // with an unsupported message 133 if timeout == nil { 134 return unsupportedTimeoutKeyError(timeKey) 135 } 136 137 *timeout = rt 138 } 139 return nil 140 } 141 } 142 143 return nil 144 } 145 146 func unsupportedTimeoutKeyError(key string) error { 147 return fmt.Errorf("Timeout Key (%s) is not supported", key) 148 } 149 150 // DiffEncode, StateEncode, and MetaDecode are analogous to the Go stdlib JSONEncoder 151 // interface: they encode/decode a timeouts struct from an instance diff, which is 152 // where the timeout data is stored after a diff to pass into Apply. 153 // 154 // StateEncode encodes the timeout into the ResourceData's InstanceState for 155 // saving to state 156 func (t *ResourceTimeout) DiffEncode(id *terraform.InstanceDiff) error { 157 return t.metaEncode(id) 158 } 159 160 func (t *ResourceTimeout) StateEncode(is *terraform.InstanceState) error { 161 return t.metaEncode(is) 162 } 163 164 // metaEncode encodes the ResourceTimeout into a map[string]interface{} format 165 // and stores it in the Meta field of the interface it's given. 166 // Assumes the interface is either *terraform.InstanceState or 167 // *terraform.InstanceDiff, returns an error otherwise 168 func (t *ResourceTimeout) metaEncode(ids interface{}) error { 169 m := make(map[string]interface{}) 170 171 if t.Create != nil { 172 m[TimeoutCreate] = t.Create.Nanoseconds() 173 } 174 if t.Read != nil { 175 m[TimeoutRead] = t.Read.Nanoseconds() 176 } 177 if t.Update != nil { 178 m[TimeoutUpdate] = t.Update.Nanoseconds() 179 } 180 if t.Delete != nil { 181 m[TimeoutDelete] = t.Delete.Nanoseconds() 182 } 183 if t.Default != nil { 184 m[TimeoutDefault] = t.Default.Nanoseconds() 185 // for any key above that is nil, if default is specified, we need to 186 // populate it with the default 187 for _, k := range timeoutKeys() { 188 if _, ok := m[k]; !ok { 189 m[k] = t.Default.Nanoseconds() 190 } 191 } 192 } 193 194 // only add the Timeout to the Meta if we have values 195 if len(m) > 0 { 196 switch instance := ids.(type) { 197 case *terraform.InstanceDiff: 198 if instance.Meta == nil { 199 instance.Meta = make(map[string]interface{}) 200 } 201 instance.Meta[TimeoutKey] = m 202 case *terraform.InstanceState: 203 if instance.Meta == nil { 204 instance.Meta = make(map[string]interface{}) 205 } 206 instance.Meta[TimeoutKey] = m 207 default: 208 return fmt.Errorf("Error matching type for Diff Encode") 209 } 210 } 211 212 return nil 213 } 214 215 func (t *ResourceTimeout) StateDecode(id *terraform.InstanceState) error { 216 return t.metaDecode(id) 217 } 218 func (t *ResourceTimeout) DiffDecode(is *terraform.InstanceDiff) error { 219 return t.metaDecode(is) 220 } 221 222 func (t *ResourceTimeout) metaDecode(ids interface{}) error { 223 var rawMeta interface{} 224 var ok bool 225 switch rawInstance := ids.(type) { 226 case *terraform.InstanceDiff: 227 rawMeta, ok = rawInstance.Meta[TimeoutKey] 228 if !ok { 229 return nil 230 } 231 case *terraform.InstanceState: 232 rawMeta, ok = rawInstance.Meta[TimeoutKey] 233 if !ok { 234 return nil 235 } 236 default: 237 return fmt.Errorf("Unknown or unsupported type in metaDecode: %#v", ids) 238 } 239 240 times := rawMeta.(map[string]interface{}) 241 if len(times) == 0 { 242 return nil 243 } 244 245 if v, ok := times[TimeoutCreate]; ok { 246 t.Create = DefaultTimeout(v) 247 } 248 if v, ok := times[TimeoutRead]; ok { 249 t.Read = DefaultTimeout(v) 250 } 251 if v, ok := times[TimeoutUpdate]; ok { 252 t.Update = DefaultTimeout(v) 253 } 254 if v, ok := times[TimeoutDelete]; ok { 255 t.Delete = DefaultTimeout(v) 256 } 257 if v, ok := times[TimeoutDefault]; ok { 258 t.Default = DefaultTimeout(v) 259 } 260 261 return nil 262 }