2014-07-01 03:03:27 +02:00
|
|
|
package resource
|
|
|
|
|
|
|
|
import (
|
2016-03-04 18:20:48 +01:00
|
|
|
"sync"
|
2014-07-01 03:03:27 +02:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2014-10-08 06:44:51 +02:00
|
|
|
// RetryFunc is the function retried until it succeeds.
|
|
|
|
type RetryFunc func() error
|
|
|
|
|
|
|
|
// Retry is a basic wrapper around StateChangeConf that will just retry
|
|
|
|
// a function until it no longer returns an error.
|
|
|
|
func Retry(timeout time.Duration, f RetryFunc) error {
|
2016-03-04 18:20:48 +01:00
|
|
|
// These are used to pull the error out of the function; need a mutex to
|
|
|
|
// avoid a data race.
|
|
|
|
var resultErr error
|
|
|
|
var resultErrMu sync.Mutex
|
|
|
|
|
2014-10-08 06:44:51 +02:00
|
|
|
c := &StateChangeConf{
|
|
|
|
Pending: []string{"error"},
|
2016-01-21 02:20:41 +01:00
|
|
|
Target: []string{"success"},
|
2014-10-08 06:44:51 +02:00
|
|
|
Timeout: timeout,
|
|
|
|
MinTimeout: 500 * time.Millisecond,
|
|
|
|
Refresh: func() (interface{}, string, error) {
|
2016-01-16 19:09:01 +01:00
|
|
|
err := f()
|
2014-10-18 03:28:03 +02:00
|
|
|
if err == nil {
|
|
|
|
return 42, "success", nil
|
2014-07-08 00:21:54 +02:00
|
|
|
}
|
|
|
|
|
2016-03-04 18:20:48 +01:00
|
|
|
resultErrMu.Lock()
|
|
|
|
defer resultErrMu.Unlock()
|
|
|
|
resultErr = err
|
2014-10-18 03:28:03 +02:00
|
|
|
if rerr, ok := err.(RetryError); ok {
|
2016-03-04 18:20:48 +01:00
|
|
|
resultErr = rerr.Err
|
|
|
|
return nil, "quit", rerr.Err
|
2014-10-18 03:28:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 42, "error", nil
|
2014-10-08 06:44:51 +02:00
|
|
|
},
|
2014-07-01 03:03:27 +02:00
|
|
|
}
|
2014-10-08 06:44:51 +02:00
|
|
|
|
2016-03-04 18:20:48 +01:00
|
|
|
c.WaitForState()
|
|
|
|
|
|
|
|
// Need to acquire the lock here to be able to avoid race using resultErr as
|
|
|
|
// the return value
|
|
|
|
resultErrMu.Lock()
|
|
|
|
defer resultErrMu.Unlock()
|
|
|
|
return resultErr
|
2014-07-01 03:03:27 +02:00
|
|
|
}
|
2014-10-18 03:28:03 +02:00
|
|
|
|
|
|
|
// RetryError, if returned, will quit the retry immediately with the
|
|
|
|
// Err.
|
|
|
|
type RetryError struct {
|
|
|
|
Err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e RetryError) Error() string {
|
|
|
|
return e.Err.Error()
|
|
|
|
}
|