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  }