github.com/htcondor/osdf-client/v6@v6.13.0-rc1.0.20231009141709-766e7b4d1dc8/errorAccum.go (about) 1 package stashcp 2 3 import ( 4 "errors" 5 "fmt" 6 "net/http" 7 "sync" 8 "time" 9 10 grab "github.com/cavaliercoder/grab" 11 ) 12 13 type TimestampedError struct { 14 err error 15 timestamp time.Time 16 } 17 18 var ( 19 bunchOfErrors []TimestampedError 20 mu sync.Mutex 21 // We will generate an error string including the time since startup 22 startup time.Time = time.Now() 23 ) 24 25 // AddError will add an accumulated error to the error stack 26 func AddError(err error) bool { 27 mu.Lock() 28 defer mu.Unlock() 29 bunchOfErrors = append(bunchOfErrors, TimestampedError{err, time.Now()}) 30 return true 31 } 32 33 func ClearErrors() { 34 mu.Lock() 35 defer mu.Unlock() 36 37 bunchOfErrors = make([]TimestampedError, 0) 38 } 39 40 func GetErrors() string { 41 mu.Lock() 42 defer mu.Unlock() 43 first := true 44 lastError := startup 45 var errorsFormatted []string 46 for idx, theError := range bunchOfErrors { 47 errFmt := fmt.Sprintf("Attempt #%v: %s", idx + 1, theError.err.Error()) 48 timeElapsed := theError.timestamp.Sub(lastError) 49 timeFormat := timeElapsed.Truncate(100*time.Millisecond).String() 50 errFmt += " (" + timeFormat 51 if first { 52 errFmt += " since start)" 53 } else { 54 timeSinceStart := theError.timestamp.Sub(startup) 55 timeSinceStartFormat := timeSinceStart.Truncate(100*time.Millisecond).String() 56 errFmt += " elapsed, " + timeSinceStartFormat + " since start)" 57 } 58 lastError = theError.timestamp 59 errorsFormatted = append(errorsFormatted, errFmt) 60 first = false 61 } 62 var toReturn string 63 first = true 64 for idx := len(errorsFormatted)-1; idx >= 0; idx-- { 65 if !first { 66 toReturn += "; " 67 } 68 toReturn += errorsFormatted[idx] 69 first = false 70 } 71 return toReturn 72 } 73 74 // IsRetryable will return true if the error is retryable 75 func IsRetryable(err error) bool { 76 if errors.Is(err, &SlowTransferError{}) { 77 return true 78 } 79 var cse *ConnectionSetupError 80 if errors.As(err, &cse) { 81 if sce, ok := cse.Unwrap().(grab.StatusCodeError); ok { 82 switch int(sce) { 83 case http.StatusInternalServerError: 84 case http.StatusBadGateway: 85 case http.StatusServiceUnavailable: 86 case http.StatusGatewayTimeout: 87 return true 88 default: 89 return false 90 } 91 } 92 return true 93 } 94 return false 95 } 96 97 // ErrorsRetryable returns if the errors in the stack are retryable later 98 func ErrorsRetryable() bool { 99 mu.Lock() 100 defer mu.Unlock() 101 // Loop through the errors and see if all of them are retryable 102 for _, theError := range bunchOfErrors { 103 if !IsRetryable(theError.err) { 104 return false 105 } 106 } 107 return true 108 }