Skip to content

Commit 3f56145

Browse files
authored
Merge pull request #4766 from jupdec/release-2.17
fixed#4680 wrapping last retryable err with timeout err
2 parents 3b9bdb1 + e8444f0 commit 3f56145

2 files changed

Lines changed: 44 additions & 24 deletions

File tree

pkg/runtime/retry_utils.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
package runtime
22

33
import (
4-
"k8s.io/apimachinery/pkg/util/wait"
4+
"fmt"
55
"time"
6+
7+
"k8s.io/apimachinery/pkg/util/wait"
68
)
79

810
// RetryImmediateOnError tries to run fn every interval until it succeeds, a non-retryable error occurs or the timeout is reached.
11+
// If the timeout is reached while retrying, the returned error wraps both the timeout and the last retryable error.
912
func RetryImmediateOnError(interval time.Duration, timeout time.Duration, retryable func(error) bool, fn func() error) error {
10-
return wait.PollImmediate(interval, timeout, func() (bool, error) {
13+
var lastRetryableErr error
14+
err := wait.PollImmediate(interval, timeout, func() (bool, error) {
1115
err := fn()
1216
if err != nil {
1317
if retryable(err) {
18+
lastRetryableErr = err
1419
return false, nil
1520
}
1621
return false, err
1722
}
1823
return true, nil
1924
})
25+
if wait.Interrupted(err) && lastRetryableErr != nil {
26+
return fmt.Errorf("%w: %v", err, lastRetryableErr)
27+
}
28+
return err
2029
}

pkg/runtime/retry_utils_test.go

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ package runtime
22

33
import (
44
"errors"
5-
"github.com/stretchr/testify/assert"
65
"testing"
76
"time"
7+
8+
"github.com/stretchr/testify/assert"
89
)
910

1011
func Test_RetryImmediateOnError(t *testing.T) {
@@ -38,54 +39,63 @@ func Test_RetryImmediateOnError(t *testing.T) {
3839
fn func() error
3940
}
4041
tests := []struct {
41-
name string
42-
args args
43-
wantCount int
44-
wantErr error
42+
name string
43+
args args
44+
wantMinCount int
45+
wantMaxCount int
46+
wantErr error
4547
}{
4648
{
4749
name: "retry 4 times before failure",
4850
args: args{
49-
interval: 10 * time.Millisecond,
50-
timeout: 100 * time.Millisecond,
51+
interval: 50 * time.Millisecond,
52+
timeout: 500 * time.Millisecond,
5153
retryable: retryable,
5254
fn: failureAfterRetryCountFnGen(4),
5355
},
54-
wantCount: 5,
55-
wantErr: errors.New("failure"),
56+
wantMinCount: 5,
57+
wantMaxCount: 5,
58+
wantErr: errors.New("failure"),
5659
},
5760
{
5861
name: "retry 4 times before failure - but timeout after 2nd retry",
5962
args: args{
60-
interval: 10 * time.Millisecond,
61-
timeout: 19 * time.Millisecond,
63+
interval: 50 * time.Millisecond,
64+
timeout: 120 * time.Millisecond,
6265
retryable: retryable,
6366
fn: failureAfterRetryCountFnGen(4),
6467
},
65-
wantCount: 3,
66-
wantErr: errors.New("timed out waiting for the condition"),
68+
// PollImmediate calls fn at t=0, t=50ms, t=100ms. Timeout at 120ms
69+
// may race with the next tick, so count can be 3 or 4.
70+
wantMinCount: 3,
71+
wantMaxCount: 4,
72+
wantErr: errors.New("timed out waiting for the condition: retryable"),
6773
},
6874
{
6975
name: "retry 4 times before success",
7076
args: args{
71-
interval: 10 * time.Millisecond,
72-
timeout: 100 * time.Millisecond,
77+
interval: 50 * time.Millisecond,
78+
timeout: 500 * time.Millisecond,
7379
retryable: retryable,
7480
fn: successAfterRetryCountFnGen(4),
7581
},
76-
wantCount: 5,
77-
wantErr: nil,
82+
wantMinCount: 5,
83+
wantMaxCount: 5,
84+
wantErr: nil,
7885
},
7986
{
8087
name: "retry 4 times before success - but timeout after 2nd retry",
8188
args: args{
82-
interval: 10 * time.Millisecond,
83-
timeout: 19 * time.Millisecond,
89+
interval: 50 * time.Millisecond,
90+
timeout: 120 * time.Millisecond,
8491
retryable: retryable,
8592
fn: successAfterRetryCountFnGen(4),
8693
},
87-
wantCount: 3,
88-
wantErr: errors.New("timed out waiting for the condition"),
94+
// PollImmediate calls fn at t=0, t=50ms, t=100ms. Timeout at 120ms
95+
// may race with the next tick, so count can be 3 or 4.
96+
wantMinCount: 3,
97+
wantMaxCount: 4,
98+
wantErr: errors.New("timed out waiting for the condition: retryable"),
8999
},
90100
}
91101
for _, tt := range tests {
@@ -100,7 +110,8 @@ func Test_RetryImmediateOnError(t *testing.T) {
100110
} else {
101111
assert.NoError(t, err)
102112
}
103-
assert.Equal(t, tt.wantCount, count)
113+
assert.GreaterOrEqual(t, count, tt.wantMinCount, "retry count below minimum")
114+
assert.LessOrEqual(t, count, tt.wantMaxCount, "retry count above maximum")
104115
})
105116
}
106117
}

0 commit comments

Comments
 (0)