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  }