cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.29.2

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/promapi/errors.go

110lines · modecode

1package promapi
2
3import (
4 "context"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "net"
9 "net/http"
10 "syscall"
11
12 v1 "github.com/prometheus/client_golang/api/prometheus/v1"
13 "github.com/prymitive/current"
14)
15
16func IsUnavailableError(err error) bool {
17 var e1 APIError
18 if ok := errors.As(err, &e1); ok {
19 return e1.ErrorType == v1.ErrServer
20 }
21
22 return true
23}
24
25type APIError struct {
26 Status string `json:"status"`
27 ErrorType v1.ErrorType `json:"errorType"`
28 Err string `json:"error"`
29}
30
31func (e APIError) Error() string {
32 return e.Err
33}
34
35const (
36 ErrUnknown v1.ErrorType = "unknown"
37 ErrJSONStream v1.ErrorType = "json_stream"
38)
39
40func 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 }
59}
60
61func decodeError(err error) string {
62 if errors.Is(err, context.Canceled) {
63 return context.Canceled.Error()
64 }
65
66 if errors.Is(err, syscall.ECONNREFUSED) {
67 return "connection refused"
68 }
69
70 var neterr net.Error
71 if ok := errors.As(err, &neterr); ok && neterr.Timeout() {
72 return "connection timeout"
73 }
74
75 var e1 APIError
76 if ok := errors.As(err, &e1); ok {
77 return fmt.Sprintf("%s: %s", e1.ErrorType, e1.Err)
78 }
79
80 return err.Error()
81}
82
83func tryDecodingAPIError(resp *http.Response) error {
84 var status, errType, errText string
85 decoder := current.Object(
86 func() {},
87 current.Key("status", current.Text(func(s string) {
88 status = s
89 })),
90 current.Key("error", current.Text(func(s string) {
91 errText = s
92 })),
93 current.Key("errorType", current.Text(func(s string) {
94 errType = s
95 })),
96 )
97
98 dec := json.NewDecoder(resp.Body)
99 if err := current.Stream(dec, decoder); err != nil {
100 switch resp.StatusCode / 100 {
101 case 4:
102 return APIError{Status: "error", ErrorType: v1.ErrClient, Err: fmt.Sprintf("client error: %d", resp.StatusCode)}
103 case 5:
104 return APIError{Status: "error", ErrorType: v1.ErrServer, Err: fmt.Sprintf("server error: %d", resp.StatusCode)}
105 }
106 return APIError{Status: "error", ErrorType: v1.ErrBadResponse, Err: fmt.Sprintf("bad response code: %d", resp.StatusCode)}
107 }
108
109 return APIError{Status: status, ErrorType: decodeErrorType(errType), Err: errText}
110}