cloudflare/pint
Publicmirrored from https://github.com/cloudflare/pintAvailable
internal/promapi/errors.go
133lines · modeblame
e6a0531fLukasz Mierzwa4 years ago | 1 | package promapi |
| 2 | | |
| 3 | import ( | |
4296ae4fLukasz Mierzwa4 years ago | 4 | "context" |
2ea75469Lukasz Mierzwa3 years ago | 5 | "encoding/json" |
e6a0531fLukasz Mierzwa4 years ago | 6 | "errors" |
4296ae4fLukasz Mierzwa4 years ago | 7 | "fmt" |
a795cccaLukasz Mierzwa2 years ago | 8 | "log/slog" |
e6a0531fLukasz Mierzwa4 years ago | 9 | "net" |
f393e060Lukasz Mierzwa3 years ago | 10 | "net/http" |
552e44c1Lukasz Mierzwa3 years ago | 11 | "strings" |
e6a0531fLukasz Mierzwa4 years ago | 12 | "syscall" |
| 13 | | |
| 14 | v1 "github.com/prometheus/client_golang/api/prometheus/v1" | |
2ea75469Lukasz Mierzwa3 years ago | 15 | "github.com/prymitive/current" |
e6a0531fLukasz Mierzwa4 years ago | 16 | ) |
| 17 | | |
| 18 | func IsUnavailableError(err error) bool { | |
f393e060Lukasz Mierzwa3 years ago | 19 | var e1 APIError |
| 20 | if ok := errors.As(err, &e1); ok { | |
| 21 | return e1.ErrorType == v1.ErrServer | |
e6a0531fLukasz Mierzwa4 years ago | 22 | } |
| 23 | return true | |
| 24 | } | |
| 25 | | |
552e44c1Lukasz Mierzwa3 years ago | 26 | func IsQueryTooExpensive(err error) bool { |
| 27 | var e1 APIError | |
| 28 | if ok := errors.As(err, &e1); ok { | |
| 29 | if e1.ErrorType != v1.ErrExec { | |
| 30 | return false | |
| 31 | } | |
| 32 | if strings.HasPrefix(e1.Err, "query processing would load too many samples into memory in ") { | |
| 33 | return true | |
| 34 | } | |
| 35 | if strings.HasSuffix(e1.Err, "expanding series: context deadline exceeded") { | |
| 36 | return true | |
| 37 | } | |
| 38 | } | |
| 39 | return false | |
| 40 | } | |
| 41 | | |
0483c1c0Lukasz Mierzwa4 years ago | 42 | type APIError struct { |
| 43 | Status string `json:"status"` | |
| 44 | ErrorType v1.ErrorType `json:"errorType"` | |
f393e060Lukasz Mierzwa3 years ago | 45 | Err string `json:"error"` |
| 46 | } | |
| 47 | | |
| 48 | func (e APIError) Error() string { | |
| 49 | return e.Err | |
| 50 | } | |
| 51 | | |
| 52 | const ( | |
| 53 | ErrUnknown v1.ErrorType = "unknown" | |
| 54 | ErrJSONStream v1.ErrorType = "json_stream" | |
| 55 | ) | |
| 56 | | |
| 57 | func decodeErrorType(s string) v1.ErrorType { | |
| 58 | switch s { | |
| 59 | case string(v1.ErrBadData): | |
| 60 | return v1.ErrBadData | |
| 61 | case string(v1.ErrTimeout): | |
| 62 | return v1.ErrTimeout | |
| 63 | case string(v1.ErrCanceled): | |
| 64 | return v1.ErrCanceled | |
| 65 | case string(v1.ErrExec): | |
| 66 | return v1.ErrExec | |
| 67 | case string(v1.ErrBadResponse): | |
| 68 | return v1.ErrBadResponse | |
| 69 | case string(v1.ErrServer): | |
| 70 | return v1.ErrServer | |
| 71 | case string(v1.ErrClient): | |
| 72 | return v1.ErrClient | |
| 73 | default: | |
| 74 | return ErrUnknown | |
| 75 | } | |
0483c1c0Lukasz Mierzwa4 years ago | 76 | } |
| 77 | | |
552e44c1Lukasz Mierzwa3 years ago | 78 | const ( |
| 79 | errConnRefused = "connection refused" | |
| 80 | errConnTimeout = "connection timeout" | |
| 81 | ) | |
| 82 | | |
4296ae4fLukasz Mierzwa4 years ago | 83 | func decodeError(err error) string { |
| 84 | if errors.Is(err, context.Canceled) { | |
| 85 | return context.Canceled.Error() | |
| 86 | } | |
| 87 | | |
e6a0531fLukasz Mierzwa4 years ago | 88 | if errors.Is(err, syscall.ECONNREFUSED) { |
552e44c1Lukasz Mierzwa3 years ago | 89 | return errConnRefused |
e6a0531fLukasz Mierzwa4 years ago | 90 | } |
| 91 | | |
| 92 | var neterr net.Error | |
| 93 | if ok := errors.As(err, &neterr); ok && neterr.Timeout() { | |
552e44c1Lukasz Mierzwa3 years ago | 94 | return errConnTimeout |
e6a0531fLukasz Mierzwa4 years ago | 95 | } |
| 96 | | |
f393e060Lukasz Mierzwa3 years ago | 97 | var e1 APIError |
| 98 | if ok := errors.As(err, &e1); ok { | |
| 99 | return fmt.Sprintf("%s: %s", e1.ErrorType, e1.Err) | |
e6a0531fLukasz Mierzwa4 years ago | 100 | } |
| 101 | | |
4296ae4fLukasz Mierzwa4 years ago | 102 | return err.Error() |
e6a0531fLukasz Mierzwa4 years ago | 103 | } |
f393e060Lukasz Mierzwa3 years ago | 104 | |
| 105 | func tryDecodingAPIError(resp *http.Response) error { | |
a795cccaLukasz Mierzwa2 years ago | 106 | slog.Debug("Trying to parse Prometheus error response", slog.Int("code", resp.StatusCode)) |
552e44c1Lukasz Mierzwa3 years ago | 107 | |
2ea75469Lukasz Mierzwa3 years ago | 108 | var status, errType, errText string |
| 109 | decoder := current.Object( | |
faf2c006Lukasz Mierzwa2 years ago | 110 | current.Key("status", current.Value(func(s string, _ bool) { |
2ea75469Lukasz Mierzwa3 years ago | 111 | status = s |
| 112 | })), | |
faf2c006Lukasz Mierzwa2 years ago | 113 | current.Key("error", current.Value(func(s string, _ bool) { |
2ea75469Lukasz Mierzwa3 years ago | 114 | errText = s |
| 115 | })), | |
faf2c006Lukasz Mierzwa2 years ago | 116 | current.Key("errorType", current.Value(func(s string, _ bool) { |
2ea75469Lukasz Mierzwa3 years ago | 117 | errType = s |
| 118 | })), | |
| 119 | ) | |
| 120 | | |
| 121 | dec := json.NewDecoder(resp.Body) | |
| 122 | if err := decoder.Stream(dec); err != nil { | |
| 123 | switch resp.StatusCode / 100 { | |
| 124 | case 4: | |
| 125 | return APIError{Status: "error", ErrorType: v1.ErrClient, Err: fmt.Sprintf("client error: %d", resp.StatusCode)} | |
| 126 | case 5: | |
| 127 | return APIError{Status: "error", ErrorType: v1.ErrServer, Err: fmt.Sprintf("server error: %d", resp.StatusCode)} | |
| 128 | } | |
| 129 | return APIError{Status: "error", ErrorType: v1.ErrBadResponse, Err: fmt.Sprintf("bad response code: %d", resp.StatusCode)} | |
f393e060Lukasz Mierzwa3 years ago | 130 | } |
| 131 | | |
2ea75469Lukasz Mierzwa3 years ago | 132 | return APIError{Status: status, ErrorType: decodeErrorType(errType), Err: errText} |
f393e060Lukasz Mierzwa3 years ago | 133 | } |