Merge pull request #11761 from hashicorp/jbardin/GH-11746

Cannot store multiple types in atomic.Value
This commit is contained in:
James Bardin 2017-02-08 10:18:13 -05:00 committed by GitHub
commit 6a626a2215
2 changed files with 40 additions and 4 deletions

View File

@ -234,11 +234,17 @@ func copyOutput(
} }
// retryFunc is used to retry a function for a given duration // retryFunc is used to retry a function for a given duration
// TODO: this should probably backoff too
func retryFunc(ctx context.Context, timeout time.Duration, f func() error) error { func retryFunc(ctx context.Context, timeout time.Duration, f func() error) error {
// Build a new context with the timeout // Build a new context with the timeout
ctx, done := context.WithTimeout(ctx, timeout) ctx, done := context.WithTimeout(ctx, timeout)
defer done() defer done()
// container for atomic error value
type errWrap struct {
E error
}
// Try the function in a goroutine // Try the function in a goroutine
var errVal atomic.Value var errVal atomic.Value
doneCh := make(chan struct{}) doneCh := make(chan struct{})
@ -255,19 +261,20 @@ func retryFunc(ctx context.Context, timeout time.Duration, f func() error) error
// Try the function call // Try the function call
err := f() err := f()
errVal.Store(&errWrap{err})
if err == nil { if err == nil {
return return
} }
log.Printf("Retryable error: %v", err) log.Printf("Retryable error: %v", err)
errVal.Store(err)
} }
}() }()
// Wait for completion // Wait for completion
select { select {
case <-doneCh:
case <-ctx.Done(): case <-ctx.Done():
case <-doneCh:
} }
// Check if we have a context error to check if we're interrupted or timeout // Check if we have a context error to check if we're interrupted or timeout
@ -279,8 +286,8 @@ func retryFunc(ctx context.Context, timeout time.Duration, f func() error) error
} }
// Check if we got an error executing // Check if we got an error executing
if err, ok := errVal.Load().(error); ok { if ev, ok := errVal.Load().(errWrap); ok {
return err return ev.E
} }
return nil return nil

View File

@ -2,8 +2,12 @@ package remoteexec
import ( import (
"bytes" "bytes"
"context"
"errors"
"io" "io"
"net"
"testing" "testing"
"time"
"github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -158,6 +162,31 @@ func TestResourceProvider_CollectScripts_scripts(t *testing.T) {
} }
} }
func TestRetryFunc(t *testing.T) {
// succeed on the third try
errs := []error{io.EOF, &net.OpError{Err: errors.New("ERROR")}, nil}
count := 0
err := retryFunc(context.Background(), time.Second, func() error {
if count >= len(errs) {
return errors.New("failed to stop after nil error")
}
err := errs[count]
count++
return err
})
if count != 3 {
t.Fatal("retry func should have been called 3 times")
}
if err != nil {
t.Fatal(err)
}
}
func testConfig( func testConfig(
t *testing.T, t *testing.T,
c map[string]interface{}) *terraform.ResourceConfig { c map[string]interface{}) *terraform.ResourceConfig {