Skip to content

Commit 2139d3e

Browse files
authored
Return errors in Prometheus HTTP API format (observatorium#326)
* Return errors in Prometheus HTTP API format Signed-off-by: Saswata Mukherjee <[email protected]> * Change to httperr, address suggestions Signed-off-by: Saswata Mukherjee <[email protected]>
1 parent 71dda84 commit 2139d3e

File tree

12 files changed

+104
-73
lines changed

12 files changed

+104
-73
lines changed

Diff for: api/logs/v1/labels_enforcer.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/url"
88

99
"github.com/observatorium/api/authorization"
10+
"github.com/observatorium/api/httperr"
1011
logqlv2 "github.com/observatorium/api/logql/v2"
1112
"github.com/prometheus/prometheus/pkg/labels"
1213
)
@@ -18,7 +19,7 @@ func WithEnforceAuthorizationLabels() func(http.Handler) http.Handler {
1819
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1920
data, ok := authorization.GetData(r.Context())
2021
if !ok {
21-
http.Error(w, "error finding authorization label matcher", http.StatusInternalServerError)
22+
httperr.PrometheusAPIError(w, "error finding authorization label matcher", http.StatusInternalServerError)
2223

2324
return
2425
}
@@ -33,14 +34,14 @@ func WithEnforceAuthorizationLabels() func(http.Handler) http.Handler {
3334

3435
var lm []*labels.Matcher
3536
if err := json.Unmarshal([]byte(data), &lm); err != nil {
36-
http.Error(w, "error parsing authorization label matchers", http.StatusInternalServerError)
37+
httperr.PrometheusAPIError(w, "error parsing authorization label matchers", http.StatusInternalServerError)
3738

3839
return
3940
}
4041

4142
q, err := enforceValues(lm, r.URL.Query())
4243
if err != nil {
43-
http.Error(w, fmt.Sprintf("could not enforce authorization label matchers: %v", err), http.StatusInternalServerError)
44+
httperr.PrometheusAPIError(w, fmt.Sprintf("could not enforce authorization label matchers: %v", err), http.StatusInternalServerError)
4445

4546
return
4647
}

Diff for: api/metrics/v1/labels_enforcer.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/observatorium/api/authentication"
1212
"github.com/observatorium/api/authorization"
13+
"github.com/observatorium/api/httperr"
1314
"github.com/prometheus-community/prom-label-proxy/injectproxy"
1415
"github.com/prometheus/prometheus/pkg/labels"
1516
"github.com/prometheus/prometheus/promql/parser"
@@ -28,7 +29,7 @@ func WithEnforceTenancyOnQuery(label string) func(http.Handler) http.Handler {
2829
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2930
id, ok := authentication.GetTenantID(r.Context())
3031
if !ok {
31-
http.Error(w, "error finding tenant ID", http.StatusInternalServerError)
32+
httperr.PrometheusAPIError(w, "error finding tenant ID", http.StatusInternalServerError)
3233

3334
return
3435
}
@@ -59,7 +60,7 @@ func WithEnforceTenancyOnMatchers(label string) func(http.Handler) http.Handler
5960
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
6061
id, ok := authentication.GetTenantID(r.Context())
6162
if !ok {
62-
http.Error(w, "error finding tenant ID", http.StatusInternalServerError)
63+
httperr.PrometheusAPIError(w, "error finding tenant ID", http.StatusInternalServerError)
6364

6465
return
6566
}
@@ -100,7 +101,7 @@ func WithEnforceAuthorizationLabels() func(http.Handler) http.Handler {
100101
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
101102
data, ok := authorization.GetData(r.Context())
102103
if !ok {
103-
http.Error(w, "error finding authorization label matcher", http.StatusInternalServerError)
104+
httperr.PrometheusAPIError(w, "error finding authorization label matcher", http.StatusInternalServerError)
104105

105106
return
106107
}
@@ -115,7 +116,7 @@ func WithEnforceAuthorizationLabels() func(http.Handler) http.Handler {
115116

116117
var lm []*labels.Matcher
117118
if err := json.Unmarshal([]byte(data), &lm); err != nil {
118-
http.Error(w, "error parsing authorization label matcher", http.StatusInternalServerError)
119+
httperr.PrometheusAPIError(w, "error parsing authorization label matcher", http.StatusInternalServerError)
119120

120121
return
121122
}
@@ -138,7 +139,7 @@ func enforceRequestQueryLabels(e *injectproxy.Enforcer, w http.ResponseWriter, r
138139
// enforce in both places.
139140
q, found1, err := enforceQueryValues(e, r.URL.Query())
140141
if err != nil {
141-
http.Error(w, fmt.Sprintf("could not enforce labels: %v", err), http.StatusBadRequest)
142+
httperr.PrometheusAPIError(w, fmt.Sprintf("could not enforce labels: %v", err), http.StatusBadRequest)
142143

143144
return false
144145
}
@@ -150,14 +151,14 @@ func enforceRequestQueryLabels(e *injectproxy.Enforcer, w http.ResponseWriter, r
150151
if r.Method == http.MethodPost {
151152
if err := r.ParseForm(); err != nil {
152153
// We're returning server error here because we cannot ensure this is a bad request.
153-
http.Error(w, fmt.Sprintf("could not parse form: %v", err), http.StatusInternalServerError)
154+
httperr.PrometheusAPIError(w, fmt.Sprintf("could not parse form: %v", err), http.StatusInternalServerError)
154155

155156
return false
156157
}
157158

158159
q, found2, err = enforceQueryValues(e, r.PostForm)
159160
if err != nil {
160-
http.Error(w, fmt.Sprintf("could not enforce labels: %v", err), http.StatusBadRequest)
161+
httperr.PrometheusAPIError(w, fmt.Sprintf("could not enforce labels: %v", err), http.StatusBadRequest)
161162

162163
return false
163164
}
@@ -169,7 +170,7 @@ func enforceRequestQueryLabels(e *injectproxy.Enforcer, w http.ResponseWriter, r
169170

170171
// If no query was found, return early.
171172
if !found1 && !found2 {
172-
http.Error(w, "no query found", http.StatusBadRequest)
173+
httperr.PrometheusAPIError(w, "no query found", http.StatusBadRequest)
173174

174175
return false
175176
}

Diff for: api/metrics/v1/rules.go

+16-15
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/go-kit/log"
1212
"github.com/go-kit/log/level"
1313
"github.com/observatorium/api/authentication"
14+
"github.com/observatorium/api/httperr"
1415
"github.com/observatorium/api/rules"
1516
"github.com/prometheus-community/prom-label-proxy/injectproxy"
1617
"github.com/prometheus/prometheus/pkg/labels"
@@ -99,13 +100,13 @@ type rulesHandler struct {
99100
func (rh *rulesHandler) get(w http.ResponseWriter, r *http.Request) {
100101
tenant, ok := authentication.GetTenant(r.Context())
101102
if !ok {
102-
http.Error(w, "error finding tenant", http.StatusUnauthorized)
103+
httperr.PrometheusAPIError(w, "error finding tenant", http.StatusUnauthorized)
103104
return
104105
}
105106

106107
id, ok := authentication.GetTenantID(r.Context())
107108
if !ok {
108-
http.Error(w, "error finding tenant ID", http.StatusUnauthorized)
109+
httperr.PrometheusAPIError(w, "error finding tenant ID", http.StatusUnauthorized)
109110
return
110111
}
111112

@@ -118,7 +119,7 @@ func (rh *rulesHandler) get(w http.ResponseWriter, r *http.Request) {
118119
sc = resp.StatusCode
119120
}
120121

121-
http.Error(w, "error listing rules", sc)
122+
httperr.PrometheusAPIError(w, "error listing rules", sc)
122123

123124
return
124125
}
@@ -128,9 +129,9 @@ func (rh *rulesHandler) get(w http.ResponseWriter, r *http.Request) {
128129
if resp.StatusCode/100 != 2 {
129130
switch resp.StatusCode {
130131
case http.StatusNotFound:
131-
http.Error(w, "no rules found", resp.StatusCode)
132+
httperr.PrometheusAPIError(w, "no rules found", resp.StatusCode)
132133
default:
133-
http.Error(w, "error listing rules", resp.StatusCode)
134+
httperr.PrometheusAPIError(w, "error listing rules", resp.StatusCode)
134135
}
135136

136137
return
@@ -139,23 +140,23 @@ func (rh *rulesHandler) get(w http.ResponseWriter, r *http.Request) {
139140
rawRules, err := unmarshalRules(resp.Body)
140141
if err != nil {
141142
level.Error(rh.logger).Log("msg", "could not unmarshal rules", "err", err.Error())
142-
http.Error(w, "error unmarshaling rules", http.StatusInternalServerError)
143+
httperr.PrometheusAPIError(w, "error unmarshaling rules", http.StatusInternalServerError)
143144

144145
return
145146
}
146147

147148
err = enforceLabelsInRules(rawRules, rh.tenantLabel, id)
148149
if err != nil {
149150
level.Error(rh.logger).Log("msg", "could not enforce labels in rules", "err", err.Error())
150-
http.Error(w, "failed to process rules", http.StatusInternalServerError)
151+
httperr.PrometheusAPIError(w, "failed to process rules", http.StatusInternalServerError)
151152

152153
return
153154
}
154155

155156
body, err := yaml.Marshal(rawRules)
156157
if err != nil {
157158
level.Error(rh.logger).Log("msg", "could not marshal rules YAML", "err", err.Error())
158-
http.Error(w, "error marshaling rules YAML", http.StatusInternalServerError)
159+
httperr.PrometheusAPIError(w, "error marshaling rules YAML", http.StatusInternalServerError)
159160

160161
return
161162
}
@@ -169,35 +170,35 @@ func (rh *rulesHandler) get(w http.ResponseWriter, r *http.Request) {
169170
func (rh *rulesHandler) put(w http.ResponseWriter, r *http.Request) {
170171
tenant, ok := authentication.GetTenant(r.Context())
171172
if !ok {
172-
http.Error(w, "error finding tenant", http.StatusUnauthorized)
173+
httperr.PrometheusAPIError(w, "error finding tenant", http.StatusUnauthorized)
173174
}
174175

175176
id, ok := authentication.GetTenantID(r.Context())
176177
if !ok {
177-
http.Error(w, "error finding tenant ID", http.StatusUnauthorized)
178+
httperr.PrometheusAPIError(w, "error finding tenant ID", http.StatusUnauthorized)
178179
return
179180
}
180181

181182
rawRules, err := unmarshalRules(r.Body)
182183
if err != nil {
183184
level.Error(rh.logger).Log("msg", "could not unmarshal rules", "err", err.Error())
184-
http.Error(w, "error unmarshaling rules", http.StatusInternalServerError)
185+
httperr.PrometheusAPIError(w, "error unmarshaling rules", http.StatusInternalServerError)
185186

186187
return
187188
}
188189

189190
err = enforceLabelsInRules(rawRules, rh.tenantLabel, id)
190191
if err != nil {
191192
level.Error(rh.logger).Log("msg", "could not enforce labels in rules", "err", err.Error())
192-
http.Error(w, "failed to process rules", http.StatusInternalServerError)
193+
httperr.PrometheusAPIError(w, "failed to process rules", http.StatusInternalServerError)
193194

194195
return
195196
}
196197

197198
body, err := yaml.Marshal(rawRules)
198199
if err != nil {
199200
level.Error(rh.logger).Log("msg", "could not marshal rules YAML", "err", err.Error())
200-
http.Error(w, "error marshaling rules YAML", http.StatusInternalServerError)
201+
httperr.PrometheusAPIError(w, "error marshaling rules YAML", http.StatusInternalServerError)
201202

202203
return
203204
}
@@ -210,7 +211,7 @@ func (rh *rulesHandler) put(w http.ResponseWriter, r *http.Request) {
210211
}
211212

212213
level.Error(rh.logger).Log("msg", "could not set rules", "err", err.Error())
213-
http.Error(w, "error creating rules", sc)
214+
httperr.PrometheusAPIError(w, "error creating rules", sc)
214215

215216
return
216217
}
@@ -220,7 +221,7 @@ func (rh *rulesHandler) put(w http.ResponseWriter, r *http.Request) {
220221
w.WriteHeader(resp.StatusCode)
221222

222223
if _, err := io.Copy(w, resp.Body); err != nil {
223-
http.Error(w, "error writing rules response", http.StatusInternalServerError)
224+
httperr.PrometheusAPIError(w, "error writing rules response", http.StatusInternalServerError)
224225
return
225226
}
226227
}

Diff for: authentication/authentication.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/go-kit/log"
99
"github.com/go-kit/log/level"
10+
"github.com/observatorium/api/httperr"
1011
"github.com/prometheus/client_golang/prometheus"
1112
"google.golang.org/grpc"
1213
)
@@ -132,7 +133,7 @@ func (pm *ProviderManager) PatternHandler(pattern string) http.HandlerFunc {
132133
const msg = "error finding tenant"
133134
if !ok {
134135
level.Warn(pm.logger).Log("msg", msg, "tenant", tenant)
135-
http.Error(w, msg, http.StatusInternalServerError)
136+
httperr.PrometheusAPIError(w, msg, http.StatusInternalServerError)
136137
return
137138
}
138139

@@ -141,7 +142,7 @@ func (pm *ProviderManager) PatternHandler(pattern string) http.HandlerFunc {
141142
pm.mtx.RUnlock()
142143
if !ok {
143144
level.Debug(pm.logger).Log("msg", msg, "tenant", tenant)
144-
http.Error(w, msg, http.StatusUnauthorized)
145+
httperr.PrometheusAPIError(w, msg, http.StatusUnauthorized)
145146
return
146147
}
147148
h.ServeHTTP(w, r)

Diff for: authentication/http.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88

99
"github.com/go-chi/chi"
10+
"github.com/observatorium/api/httperr"
1011
)
1112

1213
// contextKey to use when setting context values in the HTTP package.
@@ -143,7 +144,7 @@ func WithTenantMiddlewares(mwFns ...MiddlewareFunc) Middleware {
143144
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
144145
tenant, ok := GetTenant(r.Context())
145146
if !ok {
146-
http.Error(w, "error finding tenant", http.StatusBadRequest)
147+
httperr.PrometheusAPIError(w, "error finding tenant", http.StatusBadRequest)
147148
return
148149
}
149150

@@ -154,7 +155,7 @@ func WithTenantMiddlewares(mwFns ...MiddlewareFunc) Middleware {
154155
}
155156
}
156157

157-
http.Error(w, "tenant not found, have you registered it?", http.StatusUnauthorized)
158+
httperr.PrometheusAPIError(w, "tenant not found, have you registered it?", http.StatusUnauthorized)
158159
})
159160
}
160161
}
@@ -182,7 +183,7 @@ func EnforceAccessTokenPresentOnSignalWrite(oidcTenants map[string]struct{}) fun
182183

183184
rawToken := r.Header.Get("Authorization")
184185
if rawToken == "" {
185-
http.Error(w, "couldn't find the authorization header", http.StatusBadRequest)
186+
httperr.PrometheusAPIError(w, "couldn't find the authorization header", http.StatusBadRequest)
186187
return
187188
}
188189

Diff for: authentication/mtls.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/go-kit/log"
1313
grpc_middleware_auth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
1414
"github.com/mitchellh/mapstructure"
15+
"github.com/observatorium/api/httperr"
1516
"github.com/prometheus/client_golang/prometheus"
1617
"google.golang.org/grpc"
1718
"google.golang.org/grpc/codes"
@@ -98,7 +99,7 @@ func (a MTLSAuthenticator) Middleware() Middleware {
9899
}
99100

100101
if len(r.TLS.PeerCertificates) == 0 {
101-
http.Error(w, "no client certificate presented", http.StatusUnauthorized)
102+
httperr.PrometheusAPIError(w, "no client certificate presented", http.StatusUnauthorized)
102103
return
103104
}
104105

@@ -116,10 +117,10 @@ func (a MTLSAuthenticator) Middleware() Middleware {
116117

117118
if _, err := r.TLS.PeerCertificates[0].Verify(opts); err != nil {
118119
if errors.Is(err, x509.CertificateInvalidError{}) {
119-
http.Error(w, err.Error(), http.StatusUnauthorized)
120+
httperr.PrometheusAPIError(w, err.Error(), http.StatusUnauthorized)
120121
return
121122
}
122-
http.Error(w, err.Error(), http.StatusInternalServerError)
123+
httperr.PrometheusAPIError(w, err.Error(), http.StatusInternalServerError)
123124
return
124125
}
125126

@@ -134,7 +135,7 @@ func (a MTLSAuthenticator) Middleware() Middleware {
134135
case len(r.TLS.PeerCertificates[0].IPAddresses) > 0:
135136
sub = r.TLS.PeerCertificates[0].IPAddresses[0].String()
136137
default:
137-
http.Error(w, "could not determine subject", http.StatusBadRequest)
138+
httperr.PrometheusAPIError(w, "could not determine subject", http.StatusBadRequest)
138139
return
139140
}
140141
ctx := context.WithValue(r.Context(), subjectKey, sub)

0 commit comments

Comments
 (0)