builtin: Refactor resource.Retry to clarify return

Change the `RetryFunc` from a plain `error` return type to a
specialized `RetryError` which must decide whether it is
retryable or not.

Add `RetryableError` / `NonRetryableError` factory functions that
callers are meant to use to build up these errors.

This makes it eminently clear whether or not a given error is
retryable from inside the client code.

Goal here is to _not_ change any behavior, simply reflect the
existing behavior with the new, clearer, API.
This commit is contained in:
Paul Hinze 2016-03-09 16:53:32 -06:00
parent a9fd7b4f1a
commit 108ccf0007
52 changed files with 326 additions and 300 deletions

View File

@ -148,7 +148,7 @@ func resourceAwsApiGatewayApiKeyDelete(d *schema.ResourceData, meta interface{})
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Deleting API Gateway API Key: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteApiKey(&apigateway.DeleteApiKeyInput{
ApiKey: aws.String(d.Id()),
})
@ -161,6 +161,6 @@ func resourceAwsApiGatewayApiKeyDelete(d *schema.ResourceData, meta interface{})
return nil
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
}

View File

@ -138,7 +138,7 @@ func resourceAwsApiGatewayDeploymentDelete(d *schema.ResourceData, meta interfac
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Deleting API Gateway Deployment: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
log.Printf("[DEBUG] schema is %#v", d)
if _, err := conn.DeleteStage(&apigateway.DeleteStageInput{
StageName: aws.String(d.Get("stage_name").(string)),
@ -161,9 +161,9 @@ func resourceAwsApiGatewayDeploymentDelete(d *schema.ResourceData, meta interfac
}
if !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
}

View File

@ -161,7 +161,7 @@ func resourceAwsApiGatewayIntegrationDelete(d *schema.ResourceData, meta interfa
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Deleting API Gateway Integration: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteIntegration(&apigateway.DeleteIntegrationInput{
HttpMethod: aws.String(d.Get("http_method").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)),
@ -177,9 +177,9 @@ func resourceAwsApiGatewayIntegrationDelete(d *schema.ResourceData, meta interfa
}
if !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
}

View File

@ -116,7 +116,7 @@ func resourceAwsApiGatewayIntegrationResponseDelete(d *schema.ResourceData, meta
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Deleting API Gateway Integration Response: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteIntegrationResponse(&apigateway.DeleteIntegrationResponseInput{
HttpMethod: aws.String(d.Get("http_method").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)),
@ -133,9 +133,9 @@ func resourceAwsApiGatewayIntegrationResponseDelete(d *schema.ResourceData, meta
}
if !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
}

View File

@ -154,7 +154,7 @@ func resourceAwsApiGatewayMethodDelete(d *schema.ResourceData, meta interface{})
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Deleting API Gateway Method: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteMethod(&apigateway.DeleteMethodInput{
HttpMethod: aws.String(d.Get("http_method").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)),
@ -170,9 +170,9 @@ func resourceAwsApiGatewayMethodDelete(d *schema.ResourceData, meta interface{})
}
if !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
}

View File

@ -136,7 +136,7 @@ func resourceAwsApiGatewayMethodResponseDelete(d *schema.ResourceData, meta inte
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Deleting API Gateway Method Response: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteMethodResponse(&apigateway.DeleteMethodResponseInput{
HttpMethod: aws.String(d.Get("http_method").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)),
@ -153,9 +153,9 @@ func resourceAwsApiGatewayMethodResponseDelete(d *schema.ResourceData, meta inte
}
if !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
}

View File

@ -144,7 +144,7 @@ func resourceAwsApiGatewayModelDelete(d *schema.ResourceData, meta interface{})
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Deleting API Gateway Model: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
log.Printf("[DEBUG] schema is %#v", d)
_, err := conn.DeleteModel(&apigateway.DeleteModelInput{
ModelName: aws.String(d.Get("name").(string)),
@ -160,9 +160,9 @@ func resourceAwsApiGatewayModelDelete(d *schema.ResourceData, meta interface{})
}
if !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
}

View File

@ -129,7 +129,7 @@ func resourceAwsApiGatewayResourceDelete(d *schema.ResourceData, meta interface{
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Deleting API Gateway Resource: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
log.Printf("[DEBUG] schema is %#v", d)
_, err := conn.DeleteResource(&apigateway.DeleteResourceInput{
ResourceId: aws.String(d.Id()),
@ -143,6 +143,6 @@ func resourceAwsApiGatewayResourceDelete(d *schema.ResourceData, meta interface{
return nil
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
}

View File

@ -144,7 +144,7 @@ func resourceAwsApiGatewayRestApiDelete(d *schema.ResourceData, meta interface{}
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Deleting API Gateway: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteRestApi(&apigateway.DeleteRestApiInput{
RestApiId: aws.String(d.Id()),
})
@ -156,6 +156,6 @@ func resourceAwsApiGatewayRestApiDelete(d *schema.ResourceData, meta interface{}
return nil
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
}

View File

@ -450,7 +450,7 @@ func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{})
// We retry the delete operation to handle InUse/InProgress errors coming
// from scaling operations. We should be able to sneak in a delete in between
// scaling operations within 5m.
err = resource.Retry(5*time.Minute, func() error {
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
if _, err := conn.DeleteAutoScalingGroup(&deleteopts); err != nil {
if awserr, ok := err.(awserr.Error); ok {
switch awserr.Code() {
@ -459,11 +459,11 @@ func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{})
return nil
case "ResourceInUse", "ScalingActivityInProgress":
// These are retryable
return awserr
return resource.RetryableError(awserr)
}
}
// Didn't recognize the error, so shouldn't retry.
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
// Successful delete
return nil
@ -472,9 +472,10 @@ func resourceAwsAutoscalingGroupDelete(d *schema.ResourceData, meta interface{})
return err
}
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
if g, _ = getAwsAutoscalingGroup(d.Id(), conn); g != nil {
return fmt.Errorf("Auto Scaling Group still exists")
return resource.RetryableError(
fmt.Errorf("Auto Scaling Group still exists"))
}
return nil
})
@ -531,10 +532,10 @@ func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{})
// Next, wait for the autoscale group to drain
log.Printf("[DEBUG] Waiting for group to have zero instances")
return resource.Retry(10*time.Minute, func() error {
return resource.Retry(10*time.Minute, func() *resource.RetryError {
g, err := getAwsAutoscalingGroup(d.Id(), conn)
if err != nil {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if g == nil {
log.Printf("[INFO] Autoscaling Group %q not found", d.Id())
@ -546,7 +547,8 @@ func resourceAwsAutoscalingGroupDrain(d *schema.ResourceData, meta interface{})
return nil
}
return fmt.Errorf("group still has %d instances", len(g.Instances))
return resource.RetryableError(
fmt.Errorf("group still has %d instances", len(g.Instances)))
})
}

View File

@ -32,10 +32,10 @@ func waitForASGCapacity(
log.Printf("[DEBUG] Waiting on %s for capacity...", d.Id())
return resource.Retry(wait, func() error {
return resource.Retry(wait, func() *resource.RetryError {
g, err := getAwsAutoscalingGroup(d.Id(), meta.(*AWSClient).autoscalingconn)
if err != nil {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if g == nil {
log.Printf("[INFO] Autoscaling Group %q not found", d.Id())
@ -44,7 +44,7 @@ func waitForASGCapacity(
}
lbis, err := getLBInstanceStates(g, meta)
if err != nil {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
haveASG := 0
@ -86,7 +86,8 @@ func waitForASGCapacity(
return nil
}
return fmt.Errorf("%q: Waiting up to %s: %s", d.Id(), wait, reason)
return resource.RetryableError(
fmt.Errorf("%q: Waiting up to %s: %s", d.Id(), wait, reason))
})
}

View File

@ -64,16 +64,16 @@ func resourceAwsAutoscalingLifecycleHookPut(d *schema.ResourceData, meta interfa
params := getAwsAutoscalingPutLifecycleHookInput(d)
log.Printf("[DEBUG] AutoScaling PutLifecyleHook: %s", params)
err := resource.Retry(5*time.Minute, func() error {
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.PutLifecycleHook(&params)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if strings.Contains(awsErr.Message(), "Unable to publish test message to notification target") {
return fmt.Errorf("[DEBUG] Retrying AWS AutoScaling Lifecycle Hook: %s", params)
return resource.RetryableError(fmt.Errorf("[DEBUG] Retrying AWS AutoScaling Lifecycle Hook: %s", params))
}
}
return resource.RetryError{Err: fmt.Errorf("Error putting lifecycle hook: %s", err)}
return resource.NonRetryableError(fmt.Errorf("Error putting lifecycle hook: %s", err))
}
return nil
})

View File

@ -70,7 +70,7 @@ func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface
// IAM Roles take some time to propagate
var out *events.PutRuleOutput
err := resource.Retry(30*time.Second, func() error {
err := resource.Retry(30*time.Second, func() *resource.RetryError {
var err error
out, err = conn.PutRule(input)
pattern := regexp.MustCompile("cannot be assumed by principal '[a-z]+\\.amazonaws\\.com'\\.$")
@ -78,12 +78,10 @@ func resourceAwsCloudWatchEventRuleCreate(d *schema.ResourceData, meta interface
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "ValidationException" && pattern.MatchString(awsErr.Message()) {
log.Printf("[DEBUG] Retrying creation of CloudWatch Event Rule %q", *input.Name)
return err
return resource.RetryableError(err)
}
}
return resource.RetryError{
Err: err,
}
return resource.NonRetryableError(err)
}
return nil
})
@ -157,7 +155,7 @@ func resourceAwsCloudWatchEventRuleUpdate(d *schema.ResourceData, meta interface
// IAM Roles take some time to propagate
var out *events.PutRuleOutput
err := resource.Retry(30*time.Second, func() error {
err := resource.Retry(30*time.Second, func() *resource.RetryError {
var err error
out, err = conn.PutRule(input)
pattern := regexp.MustCompile("cannot be assumed by principal '[a-z]+\\.amazonaws\\.com'\\.$")
@ -165,12 +163,10 @@ func resourceAwsCloudWatchEventRuleUpdate(d *schema.ResourceData, meta interface
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "ValidationException" && pattern.MatchString(awsErr.Message()) {
log.Printf("[DEBUG] Retrying update of CloudWatch Event Rule %q", *input.Name)
return err
return resource.RetryableError(err)
}
}
return resource.RetryError{
Err: err,
}
return resource.NonRetryableError(err)
}
return nil
})

View File

@ -158,20 +158,20 @@ func resourceAwsCodeDeployDeploymentGroupCreate(d *schema.ResourceData, meta int
// Retry to handle IAM role eventual consistency.
var resp *codedeploy.CreateDeploymentGroupOutput
var err error
err = resource.Retry(2*time.Minute, func() error {
err = resource.Retry(2*time.Minute, func() *resource.RetryError {
resp, err = conn.CreateDeploymentGroup(&input)
if err != nil {
codedeployErr, ok := err.(awserr.Error)
if !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if codedeployErr.Code() == "InvalidRoleException" {
log.Printf("[DEBUG] Trying to create deployment group again: %q",
codedeployErr.Message())
return err
return resource.RetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return nil
})

View File

@ -534,15 +534,15 @@ func resourceAwsDbInstanceCreate(d *schema.ResourceData, meta interface{}) error
log.Printf("[DEBUG] DB Instance create configuration: %#v", opts)
var err error
err = resource.Retry(5*time.Minute, func() error {
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err = conn.CreateDBInstance(&opts)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "InvalidParameterValue" && strings.Contains(awsErr.Message(), "ENHANCED_MONITORING") {
return awsErr
return resource.RetryableError(awsErr)
}
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return nil
})

View File

@ -676,25 +676,25 @@ func resourceAwsDynamoDbTableDelete(d *schema.ResourceData, meta interface{}) er
TableName: aws.String(d.Id()),
}
err = resource.Retry(10*time.Minute, func() error {
err = resource.Retry(10*time.Minute, func() *resource.RetryError {
t, err := dynamodbconn.DescribeTable(params)
if err != nil {
if awserr, ok := err.(awserr.Error); ok && awserr.Code() == "ResourceNotFoundException" {
return nil
}
// Didn't recognize the error, so shouldn't retry.
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if t != nil {
if t.Table.TableStatus != nil && strings.ToLower(*t.Table.TableStatus) == "deleting" {
log.Printf("[DEBUG] AWS Dynamo DB table (%s) is still deleting", d.Id())
return fmt.Errorf("still deleting")
return resource.RetryableError(fmt.Errorf("still deleting"))
}
}
// we should be not found or deleting, so error here
return resource.RetryError{Err: fmt.Errorf("[ERR] Error deleting Dynamo DB table, unexpected state: %s", t)}
return resource.NonRetryableError(err)
})
// check error from retry

View File

@ -85,7 +85,7 @@ func resourceAwsEcsClusterDelete(d *schema.ResourceData, meta interface{}) error
log.Printf("[DEBUG] Deleting ECS cluster %s", d.Id())
err := resource.Retry(10*time.Minute, func() error {
err := resource.Retry(10*time.Minute, func() *resource.RetryError {
out, err := conn.DeleteCluster(&ecs.DeleteClusterInput{
Cluster: aws.String(d.Id()),
})
@ -97,27 +97,27 @@ func resourceAwsEcsClusterDelete(d *schema.ResourceData, meta interface{}) error
awsErr, ok := err.(awserr.Error)
if !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if awsErr.Code() == "ClusterContainsContainerInstancesException" {
log.Printf("[TRACE] Retrying ECS cluster %q deletion after %q", d.Id(), awsErr.Code())
return err
return resource.RetryableError(err)
}
if awsErr.Code() == "ClusterContainsServicesException" {
log.Printf("[TRACE] Retrying ECS cluster %q deletion after %q", d.Id(), awsErr.Code())
return err
return resource.RetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
if err != nil {
return err
}
clusterName := d.Get("name").(string)
err = resource.Retry(5*time.Minute, func() error {
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
log.Printf("[DEBUG] Checking if ECS Cluster %q is INACTIVE", d.Id())
out, err := conn.DescribeClusters(&ecs.DescribeClustersInput{
Clusters: []*string{aws.String(clusterName)},
@ -129,12 +129,13 @@ func resourceAwsEcsClusterDelete(d *schema.ResourceData, meta interface{}) error
return nil
}
return fmt.Errorf("ECS Cluster %q is still %q", clusterName, *c.Status)
return resource.RetryableError(
fmt.Errorf("ECS Cluster %q is still %q", clusterName, *c.Status))
}
}
if err != nil {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return nil

View File

@ -131,21 +131,21 @@ func resourceAwsEcsServiceCreate(d *schema.ResourceData, meta interface{}) error
// See https://github.com/hashicorp/terraform/issues/2869
var out *ecs.CreateServiceOutput
var err error
err = resource.Retry(2*time.Minute, func() error {
err = resource.Retry(2*time.Minute, func() *resource.RetryError {
out, err = conn.CreateService(&input)
if err != nil {
ec2err, ok := err.(awserr.Error)
if !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if ec2err.Code() == "InvalidParameterException" {
log.Printf("[DEBUG] Trying to create ECS service again: %q",
ec2err.Message())
return err
return resource.RetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return nil
@ -308,7 +308,7 @@ func resourceAwsEcsServiceDelete(d *schema.ResourceData, meta interface{}) error
}
// Wait until the ECS service is drained
err = resource.Retry(5*time.Minute, func() error {
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
input := ecs.DeleteServiceInput{
Service: aws.String(d.Id()),
Cluster: aws.String(d.Get("cluster").(string)),
@ -322,16 +322,16 @@ func resourceAwsEcsServiceDelete(d *schema.ResourceData, meta interface{}) error
ec2err, ok := err.(awserr.Error)
if !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if ec2err.Code() == "InvalidParameterException" {
// Prevent "The service cannot be stopped while deployments are active."
log.Printf("[DEBUG] Trying to delete ECS service again: %q",
ec2err.Message())
return err
return resource.RetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
if err != nil {

View File

@ -243,7 +243,7 @@ func resourceAwsEipDelete(d *schema.ResourceData, meta interface{}) error {
}
domain := resourceAwsEipDomain(d)
return resource.Retry(3*time.Minute, func() error {
return resource.Retry(3*time.Minute, func() *resource.RetryError {
var err error
switch domain {
case "vpc":
@ -264,10 +264,10 @@ func resourceAwsEipDelete(d *schema.ResourceData, meta interface{}) error {
return nil
}
if _, ok := err.(awserr.Error); !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return err
return resource.RetryableError(err)
})
}

View File

@ -109,9 +109,10 @@ func resourceAwsElasticBeanstalkApplicationDelete(d *schema.ResourceData, meta i
ApplicationName: aws.String(d.Id()),
})
return resource.Retry(10*time.Second, func() error {
return resource.Retry(10*time.Second, func() *resource.RetryError {
if a, _ = getBeanstalkApplication(d, meta); a != nil {
return fmt.Errorf("Beanstalk Application still exists")
return resource.RetryableError(
fmt.Errorf("Beanstalk Application still exists"))
}
return nil
})

View File

@ -118,24 +118,24 @@ func resourceAwsElasticacheSecurityGroupDelete(d *schema.ResourceData, meta inte
log.Printf("[DEBUG] Cache security group delete: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteCacheSecurityGroup(&elasticache.DeleteCacheSecurityGroupInput{
CacheSecurityGroupName: aws.String(d.Id()),
})
if err != nil {
apierr, ok := err.(awserr.Error)
if !ok {
return err
return resource.RetryableError(err)
}
log.Printf("[DEBUG] APIError.Code: %v", apierr.Code())
switch apierr.Code() {
case "InvalidCacheSecurityGroupState":
return err
return resource.RetryableError(err)
case "DependencyViolation":
// If it is a dependency violation, we want to retry
return err
return resource.RetryableError(err)
default:
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
}
return nil

View File

@ -143,22 +143,22 @@ func resourceAwsElasticacheSubnetGroupDelete(d *schema.ResourceData, meta interf
log.Printf("[DEBUG] Cache subnet group delete: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteCacheSubnetGroup(&elasticache.DeleteCacheSubnetGroupInput{
CacheSubnetGroupName: aws.String(d.Id()),
})
if err != nil {
apierr, ok := err.(awserr.Error)
if !ok {
return err
return resource.RetryableError(err)
}
log.Printf("[DEBUG] APIError.Code: %v", apierr.Code())
switch apierr.Code() {
case "DependencyViolation":
// If it is a dependency violation, we want to retry
return err
return resource.RetryableError(err)
default:
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
}
return nil

View File

@ -207,19 +207,20 @@ func resourceAwsElasticSearchDomainCreate(d *schema.ResourceData, meta interface
d.SetId(*out.DomainStatus.ARN)
log.Printf("[DEBUG] Waiting for ElasticSearch domain %q to be created", d.Id())
err = resource.Retry(15*time.Minute, func() error {
err = resource.Retry(15*time.Minute, func() *resource.RetryError {
out, err := conn.DescribeElasticsearchDomain(&elasticsearch.DescribeElasticsearchDomainInput{
DomainName: aws.String(d.Get("domain_name").(string)),
})
if err != nil {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if !*out.DomainStatus.Processing && out.DomainStatus.Endpoint != nil {
return nil
}
return fmt.Errorf("%q: Timeout while waiting for the domain to be created", d.Id())
return resource.RetryableError(
fmt.Errorf("%q: Timeout while waiting for the domain to be created", d.Id()))
})
if err != nil {
return err
@ -366,19 +367,20 @@ func resourceAwsElasticSearchDomainUpdate(d *schema.ResourceData, meta interface
return err
}
err = resource.Retry(25*time.Minute, func() error {
err = resource.Retry(25*time.Minute, func() *resource.RetryError {
out, err := conn.DescribeElasticsearchDomain(&elasticsearch.DescribeElasticsearchDomainInput{
DomainName: aws.String(d.Get("domain_name").(string)),
})
if err != nil {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if *out.DomainStatus.Processing == false {
return nil
}
return fmt.Errorf("%q: Timeout while waiting for changes to be processed", d.Id())
return resource.RetryableError(
fmt.Errorf("%q: Timeout while waiting for changes to be processed", d.Id()))
})
if err != nil {
return err
@ -401,7 +403,7 @@ func resourceAwsElasticSearchDomainDelete(d *schema.ResourceData, meta interface
}
log.Printf("[DEBUG] Waiting for ElasticSearch domain %q to be deleted", d.Get("domain_name").(string))
err = resource.Retry(15*time.Minute, func() error {
err = resource.Retry(15*time.Minute, func() *resource.RetryError {
out, err := conn.DescribeElasticsearchDomain(&elasticsearch.DescribeElasticsearchDomainInput{
DomainName: aws.String(d.Get("domain_name").(string)),
})
@ -409,21 +411,22 @@ func resourceAwsElasticSearchDomainDelete(d *schema.ResourceData, meta interface
if err != nil {
awsErr, ok := err.(awserr.Error)
if !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if awsErr.Code() == "ResourceNotFoundException" {
return nil
}
return resource.RetryError{Err: awsErr}
return resource.NonRetryableError(err)
}
if !*out.DomainStatus.Processing {
return nil
}
return fmt.Errorf("%q: Timeout while waiting for the domain to be deleted", d.Id())
return resource.RetryableError(
fmt.Errorf("%q: Timeout while waiting for the domain to be deleted", d.Id()))
})
d.SetId("")

View File

@ -253,17 +253,18 @@ func resourceAwsElbCreate(d *schema.ResourceData, meta interface{}) error {
}
log.Printf("[DEBUG] ELB create configuration: %#v", elbOpts)
err = resource.Retry(1*time.Minute, func() error {
err = resource.Retry(1*time.Minute, func() *resource.RetryError {
_, err := elbconn.CreateLoadBalancer(elbOpts)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
// Check for IAM SSL Cert error, eventual consistancy issue
if awsErr.Code() == "CertificateNotFound" {
return fmt.Errorf("[WARN] Error creating ELB Listener with SSL Cert, retrying: %s", err)
return resource.RetryableError(
fmt.Errorf("[WARN] Error creating ELB Listener with SSL Cert, retrying: %s", err))
}
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return nil
})
@ -422,22 +423,22 @@ func resourceAwsElbUpdate(d *schema.ResourceData, meta interface{}) error {
// Occasionally AWS will error with a 'duplicate listener', without any
// other listeners on the ELB. Retry here to eliminate that.
err := resource.Retry(1*time.Minute, func() error {
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
log.Printf("[DEBUG] ELB Create Listeners opts: %s", createListenersOpts)
if _, err := elbconn.CreateLoadBalancerListeners(createListenersOpts); err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "DuplicateListener" {
log.Printf("[DEBUG] Duplicate listener found for ELB (%s), retrying", d.Id())
return awsErr
return resource.RetryableError(awsErr)
}
if awsErr.Code() == "CertificateNotFound" && strings.Contains(awsErr.Message(), "Server Certificate not found for the key: arn") {
log.Printf("[DEBUG] SSL Cert not found for given ARN, retrying")
return awsErr
return resource.RetryableError(awsErr)
}
}
// Didn't recognize the error, so shouldn't retry.
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
// Successful creation
return nil

View File

@ -215,7 +215,7 @@ func attachPolicyToRoles(conn *iam.IAM, roles []*string, arn string) error {
}
var attachmentErr error
attachmentErr = resource.Retry(2*time.Minute, func() error {
attachmentErr = resource.Retry(2*time.Minute, func() *resource.RetryError {
input := iam.ListRolePoliciesInput{
RoleName: r,
@ -223,7 +223,7 @@ func attachPolicyToRoles(conn *iam.IAM, roles []*string, arn string) error {
attachedPolicies, err := conn.ListRolePolicies(&input)
if err != nil {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if len(attachedPolicies.PolicyNames) > 0 {
@ -236,7 +236,7 @@ func attachPolicyToRoles(conn *iam.IAM, roles []*string, arn string) error {
}
if !foundPolicy {
return resource.RetryError{Err: fmt.Errorf("Policy (%q) not yet found", arn)}
return resource.NonRetryableError(err)
}
}

View File

@ -161,7 +161,7 @@ func resourceAwsIAMServerCertificateRead(d *schema.ResourceData, meta interface{
func resourceAwsIAMServerCertificateDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).iamconn
log.Printf("[INFO] Deleting IAM Server Certificate: %s", d.Id())
err := resource.Retry(1*time.Minute, func() error {
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteServerCertificate(&iam.DeleteServerCertificateInput{
ServerCertificateName: aws.String(d.Get("name").(string)),
})
@ -170,10 +170,10 @@ func resourceAwsIAMServerCertificateDelete(d *schema.ResourceData, meta interfac
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "DeleteConflict" && strings.Contains(awsErr.Message(), "currently in use by arn") {
log.Printf("[WARN] Conflict deleting server certificate: %s, retrying", awsErr.Message())
return err
return resource.RetryableError(err)
}
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return nil
})

View File

@ -114,7 +114,7 @@ func resourceAwsInternetGatewayDelete(d *schema.ResourceData, meta interface{})
log.Printf("[INFO] Deleting Internet Gateway: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteInternetGateway(&ec2.DeleteInternetGatewayInput{
InternetGatewayId: aws.String(d.Id()),
})
@ -124,17 +124,17 @@ func resourceAwsInternetGatewayDelete(d *schema.ResourceData, meta interface{})
ec2err, ok := err.(awserr.Error)
if !ok {
return err
return resource.RetryableError(err)
}
switch ec2err.Code() {
case "InvalidInternetGatewayID.NotFound":
return nil
case "DependencyViolation":
return err // retry
return resource.RetryableError(err) // retry
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
}

View File

@ -98,17 +98,15 @@ func resourceAwsLambdaEventSourceMappingCreate(d *schema.ResourceData, meta inte
//
// The role may exist, but the permissions may not have propagated, so we
// retry
err := resource.Retry(1*time.Minute, func() error {
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
eventSourceMappingConfiguration, err := conn.CreateEventSourceMapping(params)
if err != nil {
if awserr, ok := err.(awserr.Error); ok {
if awserr.Code() == "InvalidParameterValueException" {
// Retryable
return awserr
return resource.RetryableError(awserr)
}
}
// Not retryable
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
// No error
d.Set("uuid", eventSourceMappingConfiguration.UUID)
@ -186,19 +184,16 @@ func resourceAwsLambdaEventSourceMappingUpdate(d *schema.ResourceData, meta inte
Enabled: aws.Bool(d.Get("enabled").(bool)),
}
err := resource.Retry(1*time.Minute, func() error {
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
_, err := conn.UpdateEventSourceMapping(params)
if err != nil {
if awserr, ok := err.(awserr.Error); ok {
if awserr.Code() == "InvalidParameterValueException" {
// Retryable
return awserr
return resource.RetryableError(awserr)
}
}
// Not retryable
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
// No error
return nil
})

View File

@ -199,21 +199,18 @@ func resourceAwsLambdaFunctionCreate(d *schema.ResourceData, meta interface{}) e
// IAM profiles can take ~10 seconds to propagate in AWS:
// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console
// Error creating Lambda function: InvalidParameterValueException: The role defined for the task cannot be assumed by Lambda.
err := resource.Retry(1*time.Minute, func() error {
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
_, err := conn.CreateFunction(params)
if err != nil {
if awserr, ok := err.(awserr.Error); ok {
if awserr.Code() == "InvalidParameterValueException" {
log.Printf("[DEBUG] InvalidParameterValueException creating Lambda Function: %s", awserr)
// Retryable
return awserr
return resource.RetryableError(awserr)
}
}
log.Printf("[DEBUG] Error creating Lambda Function: %s", err)
// Not retryable
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
// No error
return nil
})
if err != nil {

View File

@ -99,7 +99,7 @@ func resourceAwsLambdaPermissionCreate(d *schema.ResourceData, meta interface{})
log.Printf("[DEBUG] Adding new Lambda permission: %s", input)
var out *lambda.AddPermissionOutput
err := resource.Retry(1*time.Minute, func() error {
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
var err error
out, err = conn.AddPermission(&input)
@ -107,11 +107,12 @@ func resourceAwsLambdaPermissionCreate(d *schema.ResourceData, meta interface{})
if awsErr, ok := err.(awserr.Error); ok {
// IAM is eventually consistent :/
if awsErr.Code() == "ResourceConflictException" {
return fmt.Errorf("[WARN] Error adding new Lambda Permission for %s, retrying: %s",
*input.FunctionName, err)
return resource.RetryableError(
fmt.Errorf("[WARN] Error adding new Lambda Permission for %s, retrying: %s",
*input.FunctionName, err))
}
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return nil
})
@ -124,21 +125,23 @@ func resourceAwsLambdaPermissionCreate(d *schema.ResourceData, meta interface{})
d.SetId(d.Get("statement_id").(string))
err = resource.Retry(5*time.Minute, func() error {
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
// IAM is eventually cosistent :/
err := resourceAwsLambdaPermissionRead(d, meta)
if err != nil {
if strings.HasPrefix(err.Error(), "Error reading Lambda policy: ResourceNotFoundException") {
return fmt.Errorf("[WARN] Error reading newly created Lambda Permission for %s, retrying: %s",
*input.FunctionName, err)
return resource.RetryableError(
fmt.Errorf("[WARN] Error reading newly created Lambda Permission for %s, retrying: %s",
*input.FunctionName, err))
}
if strings.HasPrefix(err.Error(), "Failed to find statement \""+d.Id()) {
return fmt.Errorf("[WARN] Error reading newly created Lambda Permission statement for %s, retrying: %s",
*input.FunctionName, err)
return resource.RetryableError(
fmt.Errorf("[WARN] Error reading newly created Lambda Permission statement for %s, retrying: %s",
*input.FunctionName, err))
}
log.Printf("[ERROR] An actual error occured when expecting Lambda policy to be there: %s", err)
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return nil
})
@ -159,28 +162,28 @@ func resourceAwsLambdaPermissionRead(d *schema.ResourceData, meta interface{}) e
log.Printf("[DEBUG] Looking for Lambda permission: %s", input)
var out *lambda.GetPolicyOutput
var statement *LambdaPolicyStatement
err := resource.Retry(1*time.Minute, func() error {
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
// IAM is eventually cosistent :/
var err error
out, err = conn.GetPolicy(&input)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "ResourceNotFoundException" {
return err
return resource.RetryableError(err)
}
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
policyInBytes := []byte(*out.Policy)
policy := LambdaPolicy{}
err = json.Unmarshal(policyInBytes, &policy)
if err != nil {
return resource.RetryError{Err: fmt.Errorf("Error unmarshalling Lambda policy: %s", err)}
return resource.NonRetryableError(err)
}
statement, err = findLambdaPolicyStatementById(&policy, d.Id())
return err
return resource.RetryableError(err)
})
if err != nil {
return err
@ -244,7 +247,7 @@ func resourceAwsLambdaPermissionDelete(d *schema.ResourceData, meta interface{})
return err
}
err = resource.Retry(5*time.Minute, func() error {
err = resource.Retry(5*time.Minute, func() *resource.RetryError {
log.Printf("[DEBUG] Checking if Lambda permission %q is deleted", d.Id())
params := &lambda.GetPolicyInput{
@ -262,7 +265,7 @@ func resourceAwsLambdaPermissionDelete(d *schema.ResourceData, meta interface{})
return nil
}
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if resp.Policy == nil {
@ -273,7 +276,8 @@ func resourceAwsLambdaPermissionDelete(d *schema.ResourceData, meta interface{})
policy := LambdaPolicy{}
err = json.Unmarshal(policyInBytes, &policy)
if err != nil {
return fmt.Errorf("Error unmarshalling Lambda policy: %s", err)
return resource.RetryableError(
fmt.Errorf("Error unmarshalling Lambda policy: %s", err))
}
_, err = findLambdaPolicyStatementById(&policy, d.Id())

View File

@ -329,20 +329,20 @@ func testAccCheckLambdaPermissionExists(n string, statement *LambdaPolicyStateme
// IAM is eventually consistent
var foundStatement *LambdaPolicyStatement
err := resource.Retry(5*time.Minute, func() error {
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
var err error
foundStatement, err = lambdaPermissionExists(rs, conn)
if err != nil {
if strings.HasPrefix(err.Error(), "ResourceNotFoundException") {
return err
return resource.RetryableError(err)
}
if strings.HasPrefix(err.Error(), "Lambda policy not found") {
return err
return resource.RetryableError(err)
}
if strings.HasPrefix(err.Error(), "Failed to find statement") {
return err
return resource.RetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return nil
})
@ -365,13 +365,13 @@ func testAccCheckAWSLambdaPermissionDestroy(s *terraform.State) error {
}
// IAM is eventually consistent
err := resource.Retry(5*time.Minute, func() error {
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
err := isLambdaPermissionGone(rs, conn)
if err != nil {
if !strings.HasPrefix(err.Error(), "Error unmarshalling Lambda policy") {
return err
return resource.RetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return nil
})

View File

@ -428,17 +428,15 @@ func resourceAwsLaunchConfigurationCreate(d *schema.ResourceData, meta interface
// IAM profiles can take ~10 seconds to propagate in AWS:
// http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#launch-instance-with-role-console
err := resource.Retry(30*time.Second, func() error {
err := resource.Retry(30*time.Second, func() *resource.RetryError {
_, err := autoscalingconn.CreateLaunchConfiguration(&createLaunchConfigurationOpts)
if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Message() == "Invalid IamInstanceProfile" {
return err
return resource.RetryableError(err)
}
}
return resource.RetryError{
Err: err,
}
return resource.NonRetryableError(err)
}
return nil
})
@ -452,8 +450,12 @@ func resourceAwsLaunchConfigurationCreate(d *schema.ResourceData, meta interface
// We put a Retry here since sometimes eventual consistency bites
// us and we need to retry a few times to get the LC to load properly
return resource.Retry(30*time.Second, func() error {
return resourceAwsLaunchConfigurationRead(d, meta)
return resource.Retry(30*time.Second, func() *resource.RetryError {
err := resourceAwsLaunchConfigurationRead(d, meta)
if err != nil {
return resource.RetryableError(err)
}
return nil
})
}

View File

@ -410,7 +410,7 @@ func resourceAwsNetworkAclDelete(d *schema.ResourceData, meta interface{}) error
conn := meta.(*AWSClient).ec2conn
log.Printf("[INFO] Deleting Network Acl: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteNetworkAcl(&ec2.DeleteNetworkAclInput{
NetworkAclId: aws.String(d.Id()),
})
@ -427,7 +427,7 @@ func resourceAwsNetworkAclDelete(d *schema.ResourceData, meta interface{}) error
a, err := findNetworkAclAssociation(v.(string), conn)
if err != nil {
return resource.RetryError{Err: fmt.Errorf("Dependency violation: Cannot find ACL %s: %s", d.Id(), err)}
return resource.NonRetryableError(err)
}
associations = append(associations, a)
} else if v, ok := d.GetOk("subnet_ids"); ok {
@ -435,14 +435,14 @@ func resourceAwsNetworkAclDelete(d *schema.ResourceData, meta interface{}) error
for _, i := range ids {
a, err := findNetworkAclAssociation(i.(string), conn)
if err != nil {
return resource.RetryError{Err: fmt.Errorf("Dependency violation: Cannot delete acl %s: %s", d.Id(), err)}
return resource.NonRetryableError(err)
}
associations = append(associations, a)
}
}
defaultAcl, err := getDefaultNetworkAcl(d.Get("vpc_id").(string), conn)
if err != nil {
return resource.RetryError{Err: fmt.Errorf("Dependency violation: Cannot delete acl %s: %s", d.Id(), err)}
return resource.NonRetryableError(err)
}
for _, a := range associations {
@ -451,10 +451,10 @@ func resourceAwsNetworkAclDelete(d *schema.ResourceData, meta interface{}) error
NetworkAclId: defaultAcl.NetworkAclId,
})
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
default:
// Any other error, we want to quit the retry loop immediately
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
}
log.Printf("[Info] Deleted network ACL %s successfully", d.Id())

View File

@ -124,10 +124,10 @@ func resourceAwsNetworkAclRuleCreate(d *schema.ResourceData, meta interface{}) e
// It appears it might be a while until the newly created rule is visible via the
// API (see issue GH-4721). Retry the `findNetworkAclRule` function until it is
// visible (which in most cases is likely immediately).
err = resource.Retry(3*time.Minute, func() error {
err = resource.Retry(3*time.Minute, func() *resource.RetryError {
_, findErr := findNetworkAclRule(d, meta)
if findErr != nil {
return findErr
return resource.RetryableError(findErr)
}
return nil

View File

@ -324,7 +324,7 @@ func resourceAwsOpsworksStackCreate(d *schema.ResourceData, meta interface{}) er
log.Printf("[DEBUG] Creating OpsWorks stack: %s", req)
var resp *opsworks.CreateStackOutput
err = resource.Retry(20*time.Minute, func() error {
err = resource.Retry(20*time.Minute, func() *resource.RetryError {
var cerr error
resp, cerr = client.CreateStack(req)
if cerr != nil {
@ -342,10 +342,10 @@ func resourceAwsOpsworksStackCreate(d *schema.ResourceData, meta interface{}) er
trustErr := "not the necessary trust relationship"
if opserr.Code() == "ValidationException" && (strings.Contains(opserr.Message(), trustErr) || strings.Contains(opserr.Message(), propErr)) {
log.Printf("[INFO] Waiting for service IAM role to propagate")
return cerr
return resource.RetryableError(cerr)
}
}
return resource.RetryError{Err: cerr}
return resource.NonRetryableError(cerr)
}
return nil
})

View File

@ -217,18 +217,19 @@ func resourceAwsS3BucketCreate(d *schema.ResourceData, meta interface{}) error {
}
}
err := resource.Retry(5*time.Minute, func() error {
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
log.Printf("[DEBUG] Trying to create new S3 bucket: %q", bucket)
_, err := s3conn.CreateBucket(req)
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "OperationAborted" {
log.Printf("[WARN] Got an error while trying to create S3 bucket %s: %s", bucket, err)
return fmt.Errorf("[WARN] Error creating S3 bucket %s, retrying: %s",
bucket, err)
return resource.RetryableError(
fmt.Errorf("[WARN] Error creating S3 bucket %s, retrying: %s",
bucket, err))
}
}
if err != nil {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return nil
@ -565,18 +566,15 @@ func resourceAwsS3BucketPolicyUpdate(s3conn *s3.S3, d *schema.ResourceData) erro
Policy: aws.String(policy),
}
err := resource.Retry(1*time.Minute, func() error {
err := resource.Retry(1*time.Minute, func() *resource.RetryError {
if _, err := s3conn.PutBucketPolicy(params); err != nil {
if awserr, ok := err.(awserr.Error); ok {
if awserr.Code() == "MalformedPolicy" {
// Retryable
return awserr
return resource.RetryableError(awserr)
}
}
// Not retryable
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
// No error
return nil
})

View File

@ -345,14 +345,14 @@ func resourceAwsSecurityGroupDelete(d *schema.ResourceData, meta interface{}) er
log.Printf("[DEBUG] Security Group destroy: %v", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteSecurityGroup(&ec2.DeleteSecurityGroupInput{
GroupId: aws.String(d.Id()),
})
if err != nil {
ec2err, ok := err.(awserr.Error)
if !ok {
return err
return resource.RetryableError(err)
}
switch ec2err.Code() {
@ -360,10 +360,10 @@ func resourceAwsSecurityGroupDelete(d *schema.ResourceData, meta interface{}) er
return nil
case "DependencyViolation":
// If it is a dependency violation, we want to retry
return err
return resource.RetryableError(err)
default:
// Any other error, we want to quit the retry loop immediately
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
}

View File

@ -214,7 +214,7 @@ func subscribeToSNSTopic(d *schema.ResourceData, snsconn *sns.SNS) (output *sns.
log.Printf("[DEBUG] SNS create topic subscription is pending so fetching the subscription list for topic : %s (%s) @ '%s'", endpoint, protocol, topic_arn)
err = resource.Retry(time.Duration(confirmation_timeout_in_minutes)*time.Minute, func() error {
err = resource.Retry(time.Duration(confirmation_timeout_in_minutes)*time.Minute, func() *resource.RetryError {
subscription, err := findSubscriptionByNonID(d, snsconn)
@ -224,10 +224,12 @@ func subscribeToSNSTopic(d *schema.ResourceData, snsconn *sns.SNS) (output *sns.
}
if err != nil {
return fmt.Errorf("Error fetching subscriptions for SNS topic %s: %s", topic_arn, err)
return resource.RetryableError(
fmt.Errorf("Error fetching subscriptions for SNS topic %s: %s", topic_arn, err))
}
return fmt.Errorf("Endpoint (%s) did not autoconfirm the subscription for topic %s", endpoint, topic_arn)
return resource.RetryableError(
fmt.Errorf("Endpoint (%s) did not autoconfirm the subscription for topic %s", endpoint, topic_arn))
})
if err != nil {

View File

@ -308,7 +308,7 @@ func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error {
}
log.Printf("[INFO] Deleting VPC: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteVpc(DeleteVpcOpts)
if err == nil {
return nil
@ -316,19 +316,17 @@ func resourceAwsVpcDelete(d *schema.ResourceData, meta interface{}) error {
ec2err, ok := err.(awserr.Error)
if !ok {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
switch ec2err.Code() {
case "InvalidVpcID.NotFound":
return nil
case "DependencyViolation":
return err
return resource.RetryableError(err)
}
return resource.RetryError{
Err: fmt.Errorf("Error deleting VPC: %s", err),
}
return resource.NonRetryableError(fmt.Errorf("Error deleting VPC: %s", err))
})
}

View File

@ -180,7 +180,7 @@ func resourceAwsVpcDhcpOptionsUpdate(d *schema.ResourceData, meta interface{}) e
func resourceAwsVpcDhcpOptionsDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn
return resource.Retry(3*time.Minute, func() error {
return resource.Retry(3*time.Minute, func() *resource.RetryError {
log.Printf("[INFO] Deleting DHCP Options ID %s...", d.Id())
_, err := conn.DeleteDhcpOptions(&ec2.DeleteDhcpOptionsInput{
DhcpOptionsId: aws.String(d.Id()),
@ -194,7 +194,7 @@ func resourceAwsVpcDhcpOptionsDelete(d *schema.ResourceData, meta interface{}) e
ec2err, ok := err.(awserr.Error)
if !ok {
return err
return resource.RetryableError(err)
}
switch ec2err.Code() {
@ -206,7 +206,7 @@ func resourceAwsVpcDhcpOptionsDelete(d *schema.ResourceData, meta interface{}) e
vpcs, err2 := findVPCsByDHCPOptionsID(conn, d.Id())
if err2 != nil {
log.Printf("[ERROR] %s", err2)
return err2
return resource.RetryableError(err2)
}
for _, vpc := range vpcs {
@ -215,13 +215,12 @@ func resourceAwsVpcDhcpOptionsDelete(d *schema.ResourceData, meta interface{}) e
DhcpOptionsId: aws.String("default"),
VpcId: vpc.VpcId,
}); err != nil {
return err
return resource.RetryableError(err)
}
}
return err //retry
return resource.RetryableError(err)
default:
// Any other error, we want to quit the retry loop immediately
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
})
}

View File

@ -129,7 +129,7 @@ func resourceAwsVpnGatewayDelete(d *schema.ResourceData, meta interface{}) error
log.Printf("[INFO] Deleting VPN gateway: %s", d.Id())
return resource.Retry(5*time.Minute, func() error {
return resource.Retry(5*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteVpnGateway(&ec2.DeleteVpnGatewayInput{
VpnGatewayId: aws.String(d.Id()),
})
@ -139,17 +139,17 @@ func resourceAwsVpnGatewayDelete(d *schema.ResourceData, meta interface{}) error
ec2err, ok := err.(awserr.Error)
if !ok {
return err
return resource.RetryableError(err)
}
switch ec2err.Code() {
case "InvalidVpnGatewayID.NotFound":
return nil
case "IncorrectState":
return err // retry
return resource.RetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
})
}
@ -173,16 +173,16 @@ func resourceAwsVpnGatewayAttach(d *schema.ResourceData, meta interface{}) error
VpcId: aws.String(d.Get("vpc_id").(string)),
}
err := resource.Retry(30*time.Second, func() error {
err := resource.Retry(30*time.Second, func() *resource.RetryError {
_, err := conn.AttachVpnGateway(req)
if err != nil {
if ec2err, ok := err.(awserr.Error); ok {
if "InvalidVpnGatewayID.NotFound" == ec2err.Code() {
//retry
return fmt.Errorf("Gateway not found, retry for eventual consistancy")
return resource.RetryableError(
fmt.Errorf("Gateway not found, retry for eventual consistancy"))
}
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
return nil
})

View File

@ -622,16 +622,17 @@ func resourceAzureInstanceDelete(d *schema.ResourceData, meta interface{}) error
return err
}
err = resource.Retry(15*time.Minute, func() error {
err = resource.Retry(15*time.Minute, func() *resource.RetryError {
exists, err := blobClient.BlobExists(
storageContainterName, fmt.Sprintf(osDiskBlobNameFormat, name),
)
if err != nil {
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
if exists {
return fmt.Errorf("Instance '%s''s disk storage blob still exists.", name)
return resource.RetryableError(
fmt.Errorf("Instance '%s''s disk storage blob still exists.", name))
}
return nil

View File

@ -117,11 +117,12 @@ func testAccAzureDatabaseServerFirewallRuleExists(name string, servers []string)
for _, server := range servers {
var rules sql.ListFirewallRulesResponse
err := resource.Retry(15*time.Minute, func() error {
err := resource.Retry(15*time.Minute, func() *resource.RetryError {
var erri error
rules, erri = sqlClient.ListFirewallRules(server)
if erri != nil {
return fmt.Errorf("Error listing Azure Database Server Firewall Rules for Server %q: %s", server, erri)
return resource.RetryableError(
fmt.Errorf("Error listing Azure Database Server Firewall Rules for Server %q: %s", server, erri))
}
return nil

View File

@ -49,13 +49,13 @@ func resourceHerokuDrainCreate(d *schema.ResourceData, meta interface{}) error {
log.Printf("[DEBUG] Drain create configuration: %#v, %#v", app, url)
var dr *heroku.LogDrain
err := resource.Retry(2*time.Minute, func() error {
err := resource.Retry(2*time.Minute, func() *resource.RetryError {
d, err := client.LogDrainCreate(app, heroku.LogDrainCreateOpts{URL: url})
if err != nil {
if strings.Contains(err.Error(), retryableError) {
return err
return resource.RetryableError(err)
}
return resource.RetryError{Err: err}
return resource.NonRetryableError(err)
}
dr = d
return nil

View File

@ -146,11 +146,12 @@ func resourceMailgunDomainDelete(d *schema.ResourceData, meta interface{}) error
}
// Give the destroy a chance to take effect
return resource.Retry(1*time.Minute, func() error {
return resource.Retry(1*time.Minute, func() *resource.RetryError {
_, err = client.RetrieveDomain(d.Id())
if err == nil {
log.Printf("[INFO] Retrying until domain disappears...")
return fmt.Errorf("Domain seems to still exist; will check again.")
return resource.RetryableError(
fmt.Errorf("Domain seems to still exist; will check again."))
}
log.Printf("[INFO] Got error looking for domain, seems gone: %s", err)
return nil

View File

@ -3,6 +3,7 @@ package vcd
import (
"fmt"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)
@ -60,15 +61,16 @@ func resourceVcdDNATCreate(d *schema.ResourceData, meta interface{}) error {
// constrained by out lock. If the edge gateway reurns with a busy error, wait
// 3 seconds and then try again. Continue until a non-busy error or success
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := edgeGateway.AddNATMapping("DNAT", d.Get("external_ip").(string),
d.Get("internal_ip").(string),
portString)
if err != nil {
return fmt.Errorf("Error setting DNAT rules: %#v", err)
return resource.RetryableError(
fmt.Errorf("Error setting DNAT rules: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
if err != nil {
@ -119,15 +121,16 @@ func resourceVcdDNATDelete(d *schema.ResourceData, meta interface{}) error {
if err != nil {
return fmt.Errorf("Unable to find edge gateway: %#v", err)
}
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := edgeGateway.RemoveNATMapping("DNAT", d.Get("external_ip").(string),
d.Get("internal_ip").(string),
portString)
if err != nil {
return fmt.Errorf("Error setting DNAT rules: %#v", err)
return resource.RetryableError(
fmt.Errorf("Error setting DNAT rules: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
if err != nil {
return fmt.Errorf("Error completing tasks: %#v", err)

View File

@ -5,6 +5,7 @@ import (
"log"
"strings"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
types "github.com/hmrc/vmware-govcd/types/v56"
)
@ -91,16 +92,17 @@ func resourceVcdFirewallRulesCreate(d *schema.ResourceData, meta interface{}) er
return fmt.Errorf("Unable to find edge gateway: %s", err)
}
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
edgeGateway.Refresh()
firewallRules, _ := expandFirewallRules(d, edgeGateway.EdgeGateway)
task, err := edgeGateway.CreateFirewallRules(d.Get("default_action").(string), firewallRules)
if err != nil {
log.Printf("[INFO] Error setting firewall rules: %s", err)
return fmt.Errorf("Error setting firewall rules: %#v", err)
return resource.RetryableError(
fmt.Errorf("Error setting firewall rules: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
if err != nil {
return fmt.Errorf("Error completing tasks: %#v", err)

View File

@ -8,6 +8,7 @@ import (
"strings"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
types "github.com/hmrc/vmware-govcd/types/v56"
)
@ -156,8 +157,8 @@ func resourceVcdNetworkCreate(d *schema.ResourceData, meta interface{}) error {
log.Printf("[INFO] NETWORK: %#v", newnetwork)
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
return vcdClient.OrgVdc.CreateOrgVDCNetwork(newnetwork)
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
return resource.RetryableError(vcdClient.OrgVdc.CreateOrgVDCNetwork(newnetwork))
})
if err != nil {
return fmt.Errorf("Error: %#v", err)
@ -174,13 +175,13 @@ func resourceVcdNetworkCreate(d *schema.ResourceData, meta interface{}) error {
}
if dhcp, ok := d.GetOk("dhcp_pool"); ok {
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := edgeGateway.AddDhcpPool(network.OrgVDCNetwork, dhcp.(*schema.Set).List())
if err != nil {
return fmt.Errorf("Error adding DHCP pool: %#v", err)
return resource.RetryableError(fmt.Errorf("Error adding DHCP pool: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
if err != nil {
return fmt.Errorf("Error completing tasks: %#v", err)
@ -239,12 +240,13 @@ func resourceVcdNetworkDelete(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Error finding network: %#v", err)
}
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := network.Delete()
if err != nil {
return fmt.Errorf("Error Deleting Network: %#v", err)
return resource.RetryableError(
fmt.Errorf("Error Deleting Network: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
if err != nil {
return err

View File

@ -3,6 +3,7 @@ package vcd
import (
"fmt"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)
@ -51,14 +52,14 @@ func resourceVcdSNATCreate(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Unable to find edge gateway: %#v", err)
}
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := edgeGateway.AddNATMapping("SNAT", d.Get("internal_ip").(string),
d.Get("external_ip").(string),
"any")
if err != nil {
return fmt.Errorf("Error setting SNAT rules: %#v", err)
return resource.RetryableError(fmt.Errorf("Error setting SNAT rules: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
if err != nil {
return err
@ -106,14 +107,14 @@ func resourceVcdSNATDelete(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Unable to find edge gateway: %#v", err)
}
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := edgeGateway.RemoveNATMapping("SNAT", d.Get("internal_ip").(string),
d.Get("external_ip").(string),
"")
if err != nil {
return fmt.Errorf("Error setting SNAT rules: %#v", err)
return resource.RetryableError(fmt.Errorf("Error setting SNAT rules: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
if err != nil {
return err

View File

@ -4,6 +4,7 @@ import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
types "github.com/hmrc/vmware-govcd/types/v56"
)
@ -133,16 +134,16 @@ func resourceVcdVAppCreate(d *schema.ResourceData, meta interface{}) error {
},
}
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
e := vcdClient.OrgVdc.InstantiateVAppTemplate(createvapp)
if e != nil {
return fmt.Errorf("Error: %#v", e)
return resource.RetryableError(fmt.Errorf("Error: %#v", e))
}
e = vcdClient.OrgVdc.Refresh()
if e != nil {
return fmt.Errorf("Error: %#v", e)
return resource.RetryableError(fmt.Errorf("Error: %#v", e))
}
return nil
})
@ -152,36 +153,36 @@ func resourceVcdVAppCreate(d *schema.ResourceData, meta interface{}) error {
vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Get("name").(string))
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := vapp.ChangeVMName(d.Get("name").(string))
if err != nil {
return fmt.Errorf("Error with vm name change: %#v", err)
return resource.RetryableError(fmt.Errorf("Error with vm name change: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
if err != nil {
return fmt.Errorf("Error changing vmname: %#v", err)
}
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := vapp.ChangeNetworkConfig(d.Get("network_name").(string), d.Get("ip").(string))
if err != nil {
return fmt.Errorf("Error with Networking change: %#v", err)
return resource.RetryableError(fmt.Errorf("Error with Networking change: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
if err != nil {
return fmt.Errorf("Error changing network: %#v", err)
}
if initscript, ok := d.GetOk("initscript"); ok {
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := vapp.RunCustomizationScript(d.Get("name").(string), initscript.(string))
if err != nil {
return fmt.Errorf("Error with setting init script: %#v", err)
return resource.RetryableError(fmt.Errorf("Error with setting init script: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
if err != nil {
return fmt.Errorf("Error completing tasks: %#v", err)
@ -246,13 +247,13 @@ func resourceVcdVAppUpdate(d *schema.ResourceData, meta interface{}) error {
}
if d.HasChange("memory") {
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := vapp.ChangeMemorySize(d.Get("memory").(int))
if err != nil {
return fmt.Errorf("Error changing memory size: %#v", err)
return resource.RetryableError(fmt.Errorf("Error changing memory size: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
if err != nil {
return err
@ -260,13 +261,13 @@ func resourceVcdVAppUpdate(d *schema.ResourceData, meta interface{}) error {
}
if d.HasChange("cpus") {
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := vapp.ChangeCPUcount(d.Get("cpus").(int))
if err != nil {
return fmt.Errorf("Error changing cpu count: %#v", err)
return resource.RetryableError(fmt.Errorf("Error changing cpu count: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
if err != nil {
return fmt.Errorf("Error completing task: %#v", err)
@ -317,18 +318,18 @@ func getVAppIPAddress(d *schema.ResourceData, meta interface{}) (string, error)
vcdClient := meta.(*VCDClient)
var ip string
err := retryCall(vcdClient.MaxRetryTimeout, func() error {
err := retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
err := vcdClient.OrgVdc.Refresh()
if err != nil {
return fmt.Errorf("Error refreshing vdc: %#v", err)
return resource.RetryableError(fmt.Errorf("Error refreshing vdc: %#v", err))
}
vapp, err := vcdClient.OrgVdc.FindVAppByName(d.Id())
if err != nil {
return fmt.Errorf("Unable to find vapp.")
return resource.RetryableError(fmt.Errorf("Unable to find vapp."))
}
ip = vapp.VApp.Children.VM[0].NetworkConnectionSection.NetworkConnection.IPAddress
if ip == "" {
return fmt.Errorf("Timeout: VM did not aquire IP address")
return resource.RetryableError(fmt.Errorf("Timeout: VM did not aquire IP address"))
}
return nil
})
@ -348,22 +349,22 @@ func resourceVcdVAppDelete(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Error getting VApp status: %#v", err)
}
_ = retryCall(vcdClient.MaxRetryTimeout, func() error {
_ = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := vapp.Undeploy()
if err != nil {
return fmt.Errorf("Error undeploying: %#v", err)
return resource.RetryableError(fmt.Errorf("Error undeploying: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
err = retryCall(vcdClient.MaxRetryTimeout, func() error {
err = retryCall(vcdClient.MaxRetryTimeout, func() *resource.RetryError {
task, err := vapp.Delete()
if err != nil {
return fmt.Errorf("Error deleting: %#v", err)
return resource.RetryableError(fmt.Errorf("Error deleting: %#v", err))
}
return task.WaitTaskCompletion()
return resource.RetryableError(task.WaitTaskCompletion())
})
return err

View File

@ -5,9 +5,6 @@ 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 {
@ -17,25 +14,24 @@ func Retry(timeout time.Duration, f RetryFunc) error {
var resultErrMu sync.Mutex
c := &StateChangeConf{
Pending: []string{"error"},
Pending: []string{"retryableerror"},
Target: []string{"success"},
Timeout: timeout,
MinTimeout: 500 * time.Millisecond,
Refresh: func() (interface{}, string, error) {
err := f()
if err == nil {
rerr := f()
if rerr == nil {
return 42, "success", nil
}
resultErrMu.Lock()
defer resultErrMu.Unlock()
resultErr = err
if rerr, ok := err.(RetryError); ok {
resultErr = rerr.Err
return nil, "quit", rerr.Err
}
resultErr = rerr.Err
return 42, "error", nil
if rerr.Retryable {
return 42, "retryableerror", nil
}
return nil, "quit", rerr.Err
},
}
@ -48,12 +44,30 @@ func Retry(timeout time.Duration, f RetryFunc) error {
return resultErr
}
// RetryError, if returned, will quit the retry immediately with the
// Err.
// RetryFunc is the function retried until it succeeds.
type RetryFunc func() *RetryError
// RetryError is the required return type of RetryFunc. It forces client code
// to choose whether or not a given error is retryable.
type RetryError struct {
Err error
Err error
Retryable bool
}
func (e RetryError) Error() string {
return e.Err.Error()
// RetryableError is a helper to create a RetryError that's retryable from a
// given error.
func RetryableError(err error) *RetryError {
if err == nil {
return nil
}
return &RetryError{Err: err, Retryable: true}
}
// NonRetryableError is a helper to create a RetryError that's _not)_ retryable
// from a given error.
func NonRetryableError(err error) *RetryError {
if err == nil {
return nil
}
return &RetryError{Err: err, Retryable: false}
}

View File

@ -10,13 +10,13 @@ func TestRetry(t *testing.T) {
t.Parallel()
tries := 0
f := func() error {
f := func() *RetryError {
tries++
if tries == 1 {
return nil
}
return fmt.Errorf("error")
return RetryableError(fmt.Errorf("error"))
}
err := Retry(2*time.Second, f)
@ -28,8 +28,8 @@ func TestRetry(t *testing.T) {
func TestRetry_timeout(t *testing.T) {
t.Parallel()
f := func() error {
return fmt.Errorf("always")
f := func() *RetryError {
return RetryableError(fmt.Errorf("always"))
}
err := Retry(1*time.Second, f)
@ -42,8 +42,8 @@ func TestRetry_error(t *testing.T) {
t.Parallel()
expected := fmt.Errorf("nope")
f := func() error {
return RetryError{expected}
f := func() *RetryError {
return NonRetryableError(expected)
}
errCh := make(chan error)