-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathretry.go
72 lines (65 loc) · 1.88 KB
/
retry.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package retryhandler
import (
"context"
"fmt"
"io"
"net"
"syscall"
"time"
"github.com/containerd/containerd/v2/core/images"
remoteserrors "github.com/containerd/containerd/v2/core/remotes/errors"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
// MaxRetryBackoff is the maximum backoff time before giving up. This is a
// variable so that code which embeds BuildKit can override the default value.
var MaxRetryBackoff = 8 * time.Second
func New(f images.HandlerFunc, logger func([]byte)) images.HandlerFunc {
return func(ctx context.Context, desc ocispecs.Descriptor) ([]ocispecs.Descriptor, error) {
backoff := time.Second
for {
descs, err := f(ctx, desc)
if err != nil {
select {
case <-ctx.Done():
return nil, err
default:
if !retryError(err) {
return nil, err
}
}
if logger != nil {
logger([]byte(fmt.Sprintf("error: %v\n", err.Error())))
}
} else {
return descs, nil
}
// backoff logic
if backoff >= MaxRetryBackoff {
return nil, err
}
if logger != nil {
logger([]byte(fmt.Sprintf("retrying in %v\n", backoff)))
}
time.Sleep(backoff)
backoff *= 2
}
}
}
func retryError(err error) bool {
// Retry on 5xx errors
var errUnexpectedStatus remoteserrors.ErrUnexpectedStatus
if errors.As(err, &errUnexpectedStatus) &&
errUnexpectedStatus.StatusCode >= 500 &&
errUnexpectedStatus.StatusCode <= 599 {
return true
}
if errors.Is(err, io.EOF) || errors.Is(err, syscall.ECONNRESET) || errors.Is(err, syscall.EPIPE) || errors.Is(err, net.ErrClosed) {
return true
}
// catches TLS timeout or other network-related temporary errors
if ne := net.Error(nil); errors.As(err, &ne) && ne.Temporary() { //nolint:staticcheck // ignoring "SA1019: Temporary is deprecated", continue to propagate net.Error through the "temporary" status
return true
}
return false
}