package resource

import (
	"time"
)

// 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 {
	var err error
	c := &StateChangeConf{
		Pending:    []string{"error"},
		Target:     "success",
		Timeout:    timeout,
		MinTimeout: 500 * time.Millisecond,
		Refresh: func() (interface{}, string, error) {
			err = f()
			if err == nil {
				return 42, "success", nil
			}

			if rerr, ok := err.(RetryError); ok {
				err = rerr.Err
				return nil, "quit", err
			}

			return 42, "error", nil
		},
	}

	c.WaitForState()
	return err
}

// 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()
}

package resource

import (
	"time"
)

// 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 {
	c := &StateChangeConf{
		Pending:    []string{"error"},
		Target:     "success",
		Timeout:    timeout,
		MinTimeout: 500 * time.Millisecond,
		Refresh: func() (interface{}, string, error) {
			err := f()
			if err == nil {
				return 42, "success", nil
			}

			if rerr, ok := err.(RetryError); ok {
				err = rerr.Err
				return nil, "quit", err
			}

			return 42, "error", nil
		},
	}

	_, err := c.WaitForState()
	return err
}

// 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()
}

Fix data race in resource.Retry
helper/resource: Fix data race in resource.Retry

The implementation was attempting to capture the error using a scoped
variable reference, but the error value is already exposed via the
return value of `WaitForState()`. Using that instead fixes the data
race.

Fixes the following data race:

```
==================
WARNING: DATA RACE
Read by goroutine 74:
  github.com/hashicorp/terraform/helper/resource.Retry()
      /Users/phinze/go/src/github.com/hashicorp/terraform/helper/resource/wait.go:35 +0x284
  github.com/hashicorp/terraform/helper/resource.TestRetry_timeout()
      /Users/phinze/go/src/github.com/hashicorp/terraform/helper/resource/wait_test.go:35 +0x60
  testing.tRunner()
      /private/var/folders/vd/7l9ys5k57l91x63sh28wl_kc0000gn/T/workdir/go/src/testing/testing.go:456 +0xdc

Previous write by goroutine 90:
  github.com/hashicorp/terraform/helper/resource.Retry.func1()
      /Users/phinze/go/src/github.com/hashicorp/terraform/helper/resource/wait.go:20 +0x87
  github.com/hashicorp/terraform/helper/resource.(*StateChangeConf).WaitForState.func1()
      /Users/phinze/go/src/github.com/hashicorp/terraform/helper/resource/state.go:83 +0x284

Goroutine 74 (running) created at:
  testing.RunTests()
      /private/var/folders/vd/7l9ys5k57l91x63sh28wl_kc0000gn/T/workdir/go/src/testing/testing.go:561 +0xaa3
  testing.(*M).Run()
      /private/var/folders/vd/7l9ys5k57l91x63sh28wl_kc0000gn/T/workdir/go/src/testing/testing.go:494 +0xe4
  main.main()
      github.com/hashicorp/terraform/helper/resource/_test/_testmain.go:84 +0x20f

Goroutine 90 (running) created at:
  github.com/hashicorp/terraform/helper/resource.(*StateChangeConf).WaitForState()
      /Users/phinze/go/src/github.com/hashicorp/terraform/helper/resource/state.go:127 +0x283
  github.com/hashicorp/terraform/helper/resource.Retry()
      /Users/phinze/go/src/github.com/hashicorp/terraform/helper/resource/wait.go:34 +0x276
  github.com/hashicorp/terraform/helper/resource.TestRetry_timeout()
      /Users/phinze/go/src/github.com/hashicorp/terraform/helper/resource/wait_test.go:35 +0x60
  testing.tRunner()
      /private/var/folders/vd/7l9ys5k57l91x63sh28wl_kc0000gn/T/workdir/go/src/testing/testing.go:456 +0xdc
==================
```
