mirror of
https://github.com/opentofu/opentofu.git
synced 2025-01-02 12:17:39 -06:00
9cf8f48239
The new decoder is more precise, and unpacks the timeout block into a single map, which ResourceTimeout.ConfigDecode was updated to handle. We however still need to work with legacy versions of terraform, with the old decoder.
254 lines
6.2 KiB
Go
254 lines
6.2 KiB
Go
package schema
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"time"
|
|
|
|
"github.com/hashicorp/terraform/config"
|
|
"github.com/hashicorp/terraform/terraform"
|
|
"github.com/mitchellh/copystructure"
|
|
)
|
|
|
|
const TimeoutKey = "e2bfb730-ecaa-11e6-8f88-34363bc7c4c0"
|
|
const TimeoutsConfigKey = "timeouts"
|
|
|
|
const (
|
|
TimeoutCreate = "create"
|
|
TimeoutRead = "read"
|
|
TimeoutUpdate = "update"
|
|
TimeoutDelete = "delete"
|
|
TimeoutDefault = "default"
|
|
)
|
|
|
|
func timeoutKeys() []string {
|
|
return []string{
|
|
TimeoutCreate,
|
|
TimeoutRead,
|
|
TimeoutUpdate,
|
|
TimeoutDelete,
|
|
TimeoutDefault,
|
|
}
|
|
}
|
|
|
|
// could be time.Duration, int64 or float64
|
|
func DefaultTimeout(tx interface{}) *time.Duration {
|
|
var td time.Duration
|
|
switch raw := tx.(type) {
|
|
case time.Duration:
|
|
return &raw
|
|
case int64:
|
|
td = time.Duration(raw)
|
|
case float64:
|
|
td = time.Duration(int64(raw))
|
|
default:
|
|
log.Printf("[WARN] Unknown type in DefaultTimeout: %#v", tx)
|
|
}
|
|
return &td
|
|
}
|
|
|
|
type ResourceTimeout struct {
|
|
Create, Read, Update, Delete, Default *time.Duration
|
|
}
|
|
|
|
// ConfigDecode takes a schema and the configuration (available in Diff) and
|
|
// validates, parses the timeouts into `t`
|
|
func (t *ResourceTimeout) ConfigDecode(s *Resource, c *terraform.ResourceConfig) error {
|
|
if s.Timeouts != nil {
|
|
raw, err := copystructure.Copy(s.Timeouts)
|
|
if err != nil {
|
|
log.Printf("[DEBUG] Error with deep copy: %s", err)
|
|
}
|
|
*t = *raw.(*ResourceTimeout)
|
|
}
|
|
|
|
if raw, ok := c.Config[TimeoutsConfigKey]; ok {
|
|
var rawTimeouts []map[string]interface{}
|
|
switch raw := raw.(type) {
|
|
case map[string]interface{}:
|
|
rawTimeouts = append(rawTimeouts, raw)
|
|
case []map[string]interface{}:
|
|
rawTimeouts = raw
|
|
case string:
|
|
if raw == config.UnknownVariableValue {
|
|
// Timeout is not defined in the config
|
|
// Defaults will be used instead
|
|
return nil
|
|
} else {
|
|
log.Printf("[ERROR] Invalid timeout value: %q", raw)
|
|
return fmt.Errorf("Invalid Timeout value found")
|
|
}
|
|
default:
|
|
log.Printf("[ERROR] Invalid timeout structure: %#v", raw)
|
|
return fmt.Errorf("Invalid Timeout structure found")
|
|
}
|
|
|
|
for _, timeoutValues := range rawTimeouts {
|
|
for timeKey, timeValue := range timeoutValues {
|
|
// validate that we're dealing with the normal CRUD actions
|
|
var found bool
|
|
for _, key := range timeoutKeys() {
|
|
if timeKey == key {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return fmt.Errorf("Unsupported Timeout configuration key found (%s)", timeKey)
|
|
}
|
|
|
|
// Get timeout
|
|
rt, err := time.ParseDuration(timeValue.(string))
|
|
if err != nil {
|
|
return fmt.Errorf("Error parsing %q timeout: %s", timeKey, err)
|
|
}
|
|
|
|
var timeout *time.Duration
|
|
switch timeKey {
|
|
case TimeoutCreate:
|
|
timeout = t.Create
|
|
case TimeoutUpdate:
|
|
timeout = t.Update
|
|
case TimeoutRead:
|
|
timeout = t.Read
|
|
case TimeoutDelete:
|
|
timeout = t.Delete
|
|
case TimeoutDefault:
|
|
timeout = t.Default
|
|
}
|
|
|
|
// If the resource has not delcared this in the definition, then error
|
|
// with an unsupported message
|
|
if timeout == nil {
|
|
return unsupportedTimeoutKeyError(timeKey)
|
|
}
|
|
|
|
*timeout = rt
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func unsupportedTimeoutKeyError(key string) error {
|
|
return fmt.Errorf("Timeout Key (%s) is not supported", key)
|
|
}
|
|
|
|
// DiffEncode, StateEncode, and MetaDecode are analogous to the Go stdlib JSONEncoder
|
|
// interface: they encode/decode a timeouts struct from an instance diff, which is
|
|
// where the timeout data is stored after a diff to pass into Apply.
|
|
//
|
|
// StateEncode encodes the timeout into the ResourceData's InstanceState for
|
|
// saving to state
|
|
//
|
|
func (t *ResourceTimeout) DiffEncode(id *terraform.InstanceDiff) error {
|
|
return t.metaEncode(id)
|
|
}
|
|
|
|
func (t *ResourceTimeout) StateEncode(is *terraform.InstanceState) error {
|
|
return t.metaEncode(is)
|
|
}
|
|
|
|
// metaEncode encodes the ResourceTimeout into a map[string]interface{} format
|
|
// and stores it in the Meta field of the interface it's given.
|
|
// Assumes the interface is either *terraform.InstanceState or
|
|
// *terraform.InstanceDiff, returns an error otherwise
|
|
func (t *ResourceTimeout) metaEncode(ids interface{}) error {
|
|
m := make(map[string]interface{})
|
|
|
|
if t.Create != nil {
|
|
m[TimeoutCreate] = t.Create.Nanoseconds()
|
|
}
|
|
if t.Read != nil {
|
|
m[TimeoutRead] = t.Read.Nanoseconds()
|
|
}
|
|
if t.Update != nil {
|
|
m[TimeoutUpdate] = t.Update.Nanoseconds()
|
|
}
|
|
if t.Delete != nil {
|
|
m[TimeoutDelete] = t.Delete.Nanoseconds()
|
|
}
|
|
if t.Default != nil {
|
|
m[TimeoutDefault] = t.Default.Nanoseconds()
|
|
// for any key above that is nil, if default is specified, we need to
|
|
// populate it with the default
|
|
for _, k := range timeoutKeys() {
|
|
if _, ok := m[k]; !ok {
|
|
m[k] = t.Default.Nanoseconds()
|
|
}
|
|
}
|
|
}
|
|
|
|
// only add the Timeout to the Meta if we have values
|
|
if len(m) > 0 {
|
|
switch instance := ids.(type) {
|
|
case *terraform.InstanceDiff:
|
|
if instance.Meta == nil {
|
|
instance.Meta = make(map[string]interface{})
|
|
}
|
|
instance.Meta[TimeoutKey] = m
|
|
case *terraform.InstanceState:
|
|
if instance.Meta == nil {
|
|
instance.Meta = make(map[string]interface{})
|
|
}
|
|
instance.Meta[TimeoutKey] = m
|
|
default:
|
|
return fmt.Errorf("Error matching type for Diff Encode")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t *ResourceTimeout) StateDecode(id *terraform.InstanceState) error {
|
|
return t.metaDecode(id)
|
|
}
|
|
func (t *ResourceTimeout) DiffDecode(is *terraform.InstanceDiff) error {
|
|
return t.metaDecode(is)
|
|
}
|
|
|
|
func (t *ResourceTimeout) metaDecode(ids interface{}) error {
|
|
var rawMeta interface{}
|
|
var ok bool
|
|
switch rawInstance := ids.(type) {
|
|
case *terraform.InstanceDiff:
|
|
rawMeta, ok = rawInstance.Meta[TimeoutKey]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
case *terraform.InstanceState:
|
|
rawMeta, ok = rawInstance.Meta[TimeoutKey]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
default:
|
|
return fmt.Errorf("Unknown or unsupported type in metaDecode: %#v", ids)
|
|
}
|
|
|
|
times := rawMeta.(map[string]interface{})
|
|
if len(times) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if v, ok := times[TimeoutCreate]; ok {
|
|
t.Create = DefaultTimeout(v)
|
|
}
|
|
if v, ok := times[TimeoutRead]; ok {
|
|
t.Read = DefaultTimeout(v)
|
|
}
|
|
if v, ok := times[TimeoutUpdate]; ok {
|
|
t.Update = DefaultTimeout(v)
|
|
}
|
|
if v, ok := times[TimeoutDelete]; ok {
|
|
t.Delete = DefaultTimeout(v)
|
|
}
|
|
if v, ok := times[TimeoutDefault]; ok {
|
|
t.Default = DefaultTimeout(v)
|
|
}
|
|
|
|
return nil
|
|
}
|