github.com/jrperritt/terraform@v0.1.1-0.20170525065507-96f391dafc38/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  
    24  			resultErrMu.Lock()
    25  			defer resultErrMu.Unlock()
    26  
    27  			if rerr == nil {
    28  				resultErr = nil
    29  				return 42, "success", nil
    30  			}
    31  
    32  			resultErr = rerr.Err
    33  
    34  			if rerr.Retryable {
    35  				return 42, "retryableerror", nil
    36  			}
    37  			return nil, "quit", rerr.Err
    38  		},
    39  	}
    40  
    41  	_, waitErr := c.WaitForState()
    42  
    43  	// Need to acquire the lock here to be able to avoid race using resultErr as
    44  	// the return value
    45  	resultErrMu.Lock()
    46  	defer resultErrMu.Unlock()
    47  
    48  	// resultErr may be nil because the wait timed out and resultErr was never
    49  	// set; this is still an error
    50  	if resultErr == nil {
    51  		return waitErr
    52  	}
    53  	// resultErr takes precedence over waitErr if both are set because it is
    54  	// more likely to be useful
    55  	return resultErr
    56  }
    57  
    58  // RetryFunc is the function retried until it succeeds.
    59  type RetryFunc func() *RetryError
    60  
    61  // RetryError is the required return type of RetryFunc. It forces client code
    62  // to choose whether or not a given error is retryable.
    63  type RetryError struct {
    64  	Err       error
    65  	Retryable bool
    66  }
    67  
    68  // RetryableError is a helper to create a RetryError that's retryable from a
    69  // given error.
    70  func RetryableError(err error) *RetryError {
    71  	if err == nil {
    72  		return nil
    73  	}
    74  	return &RetryError{Err: err, Retryable: true}
    75  }
    76  
    77  // NonRetryableError is a helper to create a RetryError that's _not)_ retryable
    78  // from a given error.
    79  func NonRetryableError(err error) *RetryError {
    80  	if err == nil {
    81  		return nil
    82  	}
    83  	return &RetryError{Err: err, Retryable: false}
    84  }