cloudflare/pint
Publicmirrored from https://github.com/cloudflare/pintAvailable
internal/promapi/errors.go
109lines · 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" |
e6a0531fLukasz Mierzwa4 years ago | 8 | "net" |
f393e060Lukasz Mierzwa3 years ago | 9 | "net/http" |
e6a0531fLukasz Mierzwa4 years ago | 10 | "syscall" |
| 11 | | |
| 12 | v1 "github.com/prometheus/client_golang/api/prometheus/v1" | |
2ea75469Lukasz Mierzwa3 years ago | 13 | "github.com/prymitive/current" |
e6a0531fLukasz Mierzwa4 years ago | 14 | ) |
| 15 | | |
| 16 | func IsUnavailableError(err error) bool { | |
f393e060Lukasz Mierzwa3 years ago | 17 | var e1 APIError |
| 18 | if ok := errors.As(err, &e1); ok { | |
| 19 | return e1.ErrorType == v1.ErrServer | |
e6a0531fLukasz Mierzwa4 years ago | 20 | } |
| 21 | | |
| 22 | return true | |
| 23 | } | |
| 24 | | |
0483c1c0Lukasz Mierzwa4 years ago | 25 | type APIError struct { |
| 26 | Status string `json:"status"` | |
| 27 | ErrorType v1.ErrorType `json:"errorType"` | |
f393e060Lukasz Mierzwa3 years ago | 28 | Err string `json:"error"` |
| 29 | } | |
| 30 | | |
| 31 | func (e APIError) Error() string { | |
| 32 | return e.Err | |
| 33 | } | |
| 34 | | |
| 35 | const ( | |
| 36 | ErrUnknown v1.ErrorType = "unknown" | |
| 37 | ErrJSONStream v1.ErrorType = "json_stream" | |
| 38 | ) | |
| 39 | | |
| 40 | func decodeErrorType(s string) v1.ErrorType { | |
| 41 | switch s { | |
| 42 | case string(v1.ErrBadData): | |
| 43 | return v1.ErrBadData | |
| 44 | case string(v1.ErrTimeout): | |
| 45 | return v1.ErrTimeout | |
| 46 | case string(v1.ErrCanceled): | |
| 47 | return v1.ErrCanceled | |
| 48 | case string(v1.ErrExec): | |
| 49 | return v1.ErrExec | |
| 50 | case string(v1.ErrBadResponse): | |
| 51 | return v1.ErrBadResponse | |
| 52 | case string(v1.ErrServer): | |
| 53 | return v1.ErrServer | |
| 54 | case string(v1.ErrClient): | |
| 55 | return v1.ErrClient | |
| 56 | default: | |
| 57 | return ErrUnknown | |
| 58 | } | |
0483c1c0Lukasz Mierzwa4 years ago | 59 | } |
| 60 | | |
4296ae4fLukasz Mierzwa4 years ago | 61 | func decodeError(err error) string { |
| 62 | if errors.Is(err, context.Canceled) { | |
| 63 | return context.Canceled.Error() | |
| 64 | } | |
| 65 | | |
e6a0531fLukasz Mierzwa4 years ago | 66 | if errors.Is(err, syscall.ECONNREFUSED) { |
4296ae4fLukasz Mierzwa4 years ago | 67 | return "connection refused" |
e6a0531fLukasz Mierzwa4 years ago | 68 | } |
| 69 | | |
| 70 | var neterr net.Error | |
| 71 | if ok := errors.As(err, &neterr); ok && neterr.Timeout() { | |
4296ae4fLukasz Mierzwa4 years ago | 72 | return "connection timeout" |
e6a0531fLukasz Mierzwa4 years ago | 73 | } |
| 74 | | |
f393e060Lukasz Mierzwa3 years ago | 75 | var e1 APIError |
| 76 | if ok := errors.As(err, &e1); ok { | |
| 77 | return fmt.Sprintf("%s: %s", e1.ErrorType, e1.Err) | |
e6a0531fLukasz Mierzwa4 years ago | 78 | } |
| 79 | | |
4296ae4fLukasz Mierzwa4 years ago | 80 | return err.Error() |
e6a0531fLukasz Mierzwa4 years ago | 81 | } |
f393e060Lukasz Mierzwa3 years ago | 82 | |
| 83 | func tryDecodingAPIError(resp *http.Response) error { | |
2ea75469Lukasz Mierzwa3 years ago | 84 | var status, errType, errText string |
| 85 | decoder := current.Object( | |
| 86 | current.Key("status", current.Value(func(s string, isNil bool) { | |
| 87 | status = s | |
| 88 | })), | |
| 89 | current.Key("error", current.Value(func(s string, isNil bool) { | |
| 90 | errText = s | |
| 91 | })), | |
| 92 | current.Key("errorType", current.Value(func(s string, isNil bool) { | |
| 93 | errType = s | |
| 94 | })), | |
| 95 | ) | |
| 96 | | |
| 97 | dec := json.NewDecoder(resp.Body) | |
| 98 | if err := decoder.Stream(dec); err != nil { | |
| 99 | switch resp.StatusCode / 100 { | |
| 100 | case 4: | |
| 101 | return APIError{Status: "error", ErrorType: v1.ErrClient, Err: fmt.Sprintf("client error: %d", resp.StatusCode)} | |
| 102 | case 5: | |
| 103 | return APIError{Status: "error", ErrorType: v1.ErrServer, Err: fmt.Sprintf("server error: %d", resp.StatusCode)} | |
| 104 | } | |
| 105 | return APIError{Status: "error", ErrorType: v1.ErrBadResponse, Err: fmt.Sprintf("bad response code: %d", resp.StatusCode)} | |
f393e060Lukasz Mierzwa3 years ago | 106 | } |
| 107 | | |
2ea75469Lukasz Mierzwa3 years ago | 108 | return APIError{Status: status, ErrorType: decodeErrorType(errType), Err: errText} |
f393e060Lukasz Mierzwa3 years ago | 109 | } |