github.com/memsql/terraform@v0.7.0-rc2.0.20160706152241-21e2173e0a32/helper/resource/wait.go (about)

     1  package resource
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  )
     7  
     8  // Retry is a basic wrapper around StateChangeConf that will just retry
     9  // a function until it no longer returns an error.
    10  func Retry(timeout time.Duration, f RetryFunc) error {
    11  	// These are used to pull the error out of the function; need a mutex to
    12  	// avoid a data race.
    13  	var resultErr error
    14  	var resultErrMu sync.Mutex
    15  
    16  	c := &StateChangeConf{
    17  		Pending:    []string{"retryableerror"},
    18  		Target:     []string{"success"},
    19  		Timeout:    timeout,
    20  		MinTimeout: 500 * time.Millisecond,
    21  		Refresh: func() (interface{}, string, error) {
    22  			rerr := f()
    23  			if rerr == nil {
    24  				resultErr = nil
    25  				return 42, "success", nil
    26  			}
    27  
    28  			resultErrMu.Lock()
    29  			defer resultErrMu.Unlock()
    30  			resultErr = rerr.Err
    31  
    32  			if rerr.Retryable {
    33  				return 42, "retryableerror", nil
    34  			}
    35  			return nil, "quit", rerr.Err
    36  		},
    37  	}
    38  
    39  	_, waitErr := c.WaitForState()
    40  
    41  	// Need to acquire the lock here to be able to avoid race using resultErr as
    42  	// the return value
    43  	resultErrMu.Lock()
    44  	defer resultErrMu.Unlock()
    45  
    46  	// resultErr may be nil because the wait timed out and resultErr was never
    47  	// set; this is still an error
    48  	if resultErr == nil {
    49  		return waitErr
    50  	}
    51  	// resultErr takes precedence over waitErr if both are set because it is
    52  	// more likely to be useful
    53  	return resultErr
    54  }
    55  
    56  // RetryFunc is the function retried until it succeeds.
    57  type RetryFunc func() *RetryError
    58  
    59  // RetryError is the required return type of RetryFunc. It forces client code
    60  // to choose whether or not a given error is retryable.
    61  type RetryError struct {
    62  	Err       error
    63  	Retryable bool
    64  }
    65  
    66  // RetryableError is a helper to create a RetryError that's retryable from a
    67  // given error.
    68  func RetryableError(err error) *RetryError {
    69  	if err == nil {
    70  		return nil
    71  	}
    72  	return &RetryError{Err: err, Retryable: true}
    73  }
    74  
    75  // NonRetryableError is a helper to create a RetryError that's _not)_ retryable
    76  // from a given error.
    77  func NonRetryableError(err error) *RetryError {
    78  	if err == nil {
    79  		return nil
    80  	}
    81  	return &RetryError{Err: err, Retryable: false}
    82  }