safer, more idiomatic proxy helper (#32732)

Co-authored-by: Sofia Papagiannaki <sofia@grafana.com>
This commit is contained in:
Owen Diehl 2021-04-07 15:36:50 -04:00 committed by GitHub
parent 4527f712e0
commit 8b8fc293b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 171 additions and 158 deletions

View File

@ -3,8 +3,6 @@ package api
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
@ -40,14 +38,14 @@ func (r *LotexRuler) RouteDeleteNamespaceRulesConfig(ctx *models.ReqContext) res
} }
return r.withReq( return r.withReq(
ctx, ctx,
&http.Request{ http.MethodDelete,
Method: "DELETE", withPath(
URL: withPath( *ctx.Req.URL,
*ctx.Req.URL, fmt.Sprintf("%s/%s", legacyRulerPrefix, ctx.Params("Namespace")),
fmt.Sprintf("%s/%s", legacyRulerPrefix, ctx.Params("Namespace")), ),
), nil,
},
messageExtractor, messageExtractor,
nil,
) )
} }
@ -58,19 +56,19 @@ func (r *LotexRuler) RouteDeleteRuleGroupConfig(ctx *models.ReqContext) response
} }
return r.withReq( return r.withReq(
ctx, ctx,
&http.Request{ http.MethodDelete,
Method: "DELETE", withPath(
URL: withPath( *ctx.Req.URL,
*ctx.Req.URL, fmt.Sprintf(
fmt.Sprintf( "%s/%s/%s",
"%s/%s/%s", legacyRulerPrefix,
legacyRulerPrefix, ctx.Params("Namespace"),
ctx.Params("Namespace"), ctx.Params("Groupname"),
ctx.Params("Groupname"),
),
), ),
}, ),
nil,
messageExtractor, messageExtractor,
nil,
) )
} }
@ -80,17 +78,19 @@ func (r *LotexRuler) RouteGetNamespaceRulesConfig(ctx *models.ReqContext) respon
return response.Error(500, err.Error(), nil) return response.Error(500, err.Error(), nil)
} }
return r.withReq( return r.withReq(
ctx, &http.Request{ ctx,
URL: withPath( http.MethodGet,
*ctx.Req.URL, withPath(
fmt.Sprintf( *ctx.Req.URL,
"%s/%s", fmt.Sprintf(
legacyRulerPrefix, "%s/%s",
ctx.Params("Namespace"), legacyRulerPrefix,
), ctx.Params("Namespace"),
), ),
}, ),
nil,
yamlExtractor(apimodels.NamespaceConfigResponse{}), yamlExtractor(apimodels.NamespaceConfigResponse{}),
nil,
) )
} }
@ -101,18 +101,19 @@ func (r *LotexRuler) RouteGetRulegGroupConfig(ctx *models.ReqContext) response.R
} }
return r.withReq( return r.withReq(
ctx, ctx,
&http.Request{ http.MethodGet,
URL: withPath( withPath(
*ctx.Req.URL, *ctx.Req.URL,
fmt.Sprintf( fmt.Sprintf(
"%s/%s/%s", "%s/%s/%s",
legacyRulerPrefix, legacyRulerPrefix,
ctx.Params("Namespace"), ctx.Params("Namespace"),
ctx.Params("Groupname"), ctx.Params("Groupname"),
),
), ),
}, ),
nil,
yamlExtractor(apimodels.RuleGroupConfigResponse{}), yamlExtractor(apimodels.RuleGroupConfigResponse{}),
nil,
) )
} }
@ -123,13 +124,14 @@ func (r *LotexRuler) RouteGetRulesConfig(ctx *models.ReqContext) response.Respon
} }
return r.withReq( return r.withReq(
ctx, ctx,
&http.Request{ http.MethodGet,
URL: withPath( withPath(
*ctx.Req.URL, *ctx.Req.URL,
legacyRulerPrefix, legacyRulerPrefix,
), ),
}, nil,
yamlExtractor(apimodels.NamespaceConfigResponse{}), yamlExtractor(apimodels.NamespaceConfigResponse{}),
nil,
) )
} }
@ -142,18 +144,9 @@ func (r *LotexRuler) RoutePostNameRulesConfig(ctx *models.ReqContext, conf apimo
if err != nil { if err != nil {
return response.Error(500, "Failed marshal rule group", err) return response.Error(500, "Failed marshal rule group", err)
} }
body, ln := payload(yml)
ns := ctx.Params("Namespace") ns := ctx.Params("Namespace")
u := withPath(*ctx.Req.URL, fmt.Sprintf("%s/%s", legacyRulerPrefix, ns)) u := withPath(*ctx.Req.URL, fmt.Sprintf("%s/%s", legacyRulerPrefix, ns))
req := &http.Request{ return r.withReq(ctx, http.MethodPost, u, bytes.NewBuffer(yml), jsonExtractor(nil), nil)
Method: "POST",
URL: u,
Body: body,
ContentLength: ln,
}
return r.withReq(ctx, req, jsonExtractor(nil))
} }
func (r *LotexRuler) getPrefix(ctx *models.ReqContext) (string, error) { func (r *LotexRuler) getPrefix(ctx *models.ReqContext) (string, error) {
@ -173,7 +166,3 @@ func withPath(u url.URL, newPath string) *url.URL {
u.Path = newPath u.Path = newPath
return &u return &u
} }
func payload(b []byte) (io.ReadCloser, int64) {
return ioutil.NopCloser(bytes.NewBuffer(b)), int64(len(b))
}

View File

@ -1,6 +1,7 @@
package api package api
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
@ -37,102 +38,111 @@ func (am *LotexAM) RouteCreateSilence(ctx *models.ReqContext, silenceBody apimod
if err != nil { if err != nil {
return response.Error(500, "Failed marshal silence", err) return response.Error(500, "Failed marshal silence", err)
} }
body, ln := payload(blob)
return am.withReq( return am.withReq(
ctx, &http.Request{ ctx,
Method: "POST", http.MethodPost,
URL: withPath(*ctx.Req.URL, amSilencesPath), withPath(*ctx.Req.URL, amSilencesPath),
Body: body, bytes.NewBuffer(blob),
ContentLength: ln, jsonExtractor(&apimodels.GettableSilence{}),
Header: map[string][]string{"Content-Type": {"application/json"}}, map[string]string{"Content-Type": "application/json"},
},
jsonExtractor(nil),
) )
} }
func (am *LotexAM) RouteDeleteAlertingConfig(ctx *models.ReqContext) response.Response { func (am *LotexAM) RouteDeleteAlertingConfig(ctx *models.ReqContext) response.Response {
return am.withReq( return am.withReq(
ctx, &http.Request{ ctx,
Method: "DELETE", http.MethodDelete,
URL: withPath( withPath(
*ctx.Req.URL, *ctx.Req.URL,
amConfigPath, amConfigPath,
), ),
}, nil,
messageExtractor, messageExtractor,
nil,
) )
} }
func (am *LotexAM) RouteDeleteSilence(ctx *models.ReqContext) response.Response { func (am *LotexAM) RouteDeleteSilence(ctx *models.ReqContext) response.Response {
return am.withReq( return am.withReq(
ctx, &http.Request{ ctx,
Method: "DELETE", http.MethodDelete,
URL: withPath( withPath(
*ctx.Req.URL, *ctx.Req.URL,
fmt.Sprintf(amSilencePath, ctx.Params(":SilenceId")), fmt.Sprintf(amSilencePath, ctx.Params(":SilenceId")),
), ),
}, nil,
messageExtractor, messageExtractor,
nil,
) )
} }
func (am *LotexAM) RouteGetAlertingConfig(ctx *models.ReqContext) response.Response { func (am *LotexAM) RouteGetAlertingConfig(ctx *models.ReqContext) response.Response {
return am.withReq( return am.withReq(
ctx, &http.Request{ ctx,
URL: withPath( http.MethodGet,
*ctx.Req.URL, withPath(
amConfigPath, *ctx.Req.URL,
), amConfigPath,
}, ),
nil,
yamlExtractor(&apimodels.GettableUserConfig{}), yamlExtractor(&apimodels.GettableUserConfig{}),
nil,
) )
} }
func (am *LotexAM) RouteGetAMAlertGroups(ctx *models.ReqContext) response.Response { func (am *LotexAM) RouteGetAMAlertGroups(ctx *models.ReqContext) response.Response {
return am.withReq( return am.withReq(
ctx, &http.Request{ ctx,
URL: withPath( http.MethodGet,
*ctx.Req.URL, withPath(
amAlertGroupsPath, *ctx.Req.URL,
), amAlertGroupsPath,
}, ),
nil,
jsonExtractor(&apimodels.AlertGroups{}), jsonExtractor(&apimodels.AlertGroups{}),
nil,
) )
} }
func (am *LotexAM) RouteGetAMAlerts(ctx *models.ReqContext) response.Response { func (am *LotexAM) RouteGetAMAlerts(ctx *models.ReqContext) response.Response {
return am.withReq( return am.withReq(
ctx, &http.Request{ ctx,
URL: withPath( http.MethodGet,
*ctx.Req.URL, withPath(
amAlertsPath, *ctx.Req.URL,
), amAlertsPath,
}, ),
nil,
jsonExtractor(&apimodels.GettableAlerts{}), jsonExtractor(&apimodels.GettableAlerts{}),
nil,
) )
} }
func (am *LotexAM) RouteGetSilence(ctx *models.ReqContext) response.Response { func (am *LotexAM) RouteGetSilence(ctx *models.ReqContext) response.Response {
return am.withReq( return am.withReq(
ctx, &http.Request{ ctx,
URL: withPath( http.MethodGet,
*ctx.Req.URL, withPath(
fmt.Sprintf(amSilencePath, ctx.Params(":SilenceId")), *ctx.Req.URL,
), fmt.Sprintf(amSilencePath, ctx.Params(":SilenceId")),
}, ),
nil,
jsonExtractor(&apimodels.GettableSilence{}), jsonExtractor(&apimodels.GettableSilence{}),
nil,
) )
} }
func (am *LotexAM) RouteGetSilences(ctx *models.ReqContext) response.Response { func (am *LotexAM) RouteGetSilences(ctx *models.ReqContext) response.Response {
return am.withReq( return am.withReq(
ctx, &http.Request{ ctx,
URL: withPath( http.MethodGet,
*ctx.Req.URL, withPath(
amSilencesPath, *ctx.Req.URL,
), amSilencesPath,
}, ),
nil,
jsonExtractor(&apimodels.GettableSilences{}), jsonExtractor(&apimodels.GettableSilences{}),
nil,
) )
} }
@ -141,16 +151,15 @@ func (am *LotexAM) RoutePostAlertingConfig(ctx *models.ReqContext, config apimod
if err != nil { if err != nil {
return response.Error(500, "Failed marshal alert manager configuration ", err) return response.Error(500, "Failed marshal alert manager configuration ", err)
} }
body, ln := payload(yml)
u := withPath(*ctx.Req.URL, amConfigPath) return am.withReq(
req := &http.Request{ ctx,
Method: "POST", http.MethodPost,
URL: u, withPath(*ctx.Req.URL, amConfigPath),
Body: body, bytes.NewBuffer(yml),
ContentLength: ln, messageExtractor,
} nil,
return am.withReq(ctx, req, messageExtractor) )
} }
func (am *LotexAM) RoutePostAMAlerts(ctx *models.ReqContext, alerts apimodels.PostableAlerts) response.Response { func (am *LotexAM) RoutePostAMAlerts(ctx *models.ReqContext, alerts apimodels.PostableAlerts) response.Response {
@ -158,14 +167,13 @@ func (am *LotexAM) RoutePostAMAlerts(ctx *models.ReqContext, alerts apimodels.Po
if err != nil { if err != nil {
return response.Error(500, "Failed marshal postable alerts", err) return response.Error(500, "Failed marshal postable alerts", err)
} }
body, ln := payload(yml)
u := withPath(*ctx.Req.URL, amAlertsPath) return am.withReq(
req := &http.Request{ ctx,
Method: "POST", http.MethodPost,
URL: u, withPath(*ctx.Req.URL, amAlertsPath),
Body: body, bytes.NewBuffer(yml),
ContentLength: ln, messageExtractor,
} nil,
return am.withReq(ctx, req, messageExtractor) )
} }

View File

@ -44,13 +44,15 @@ func (p *LotexProm) RouteGetAlertStatuses(ctx *models.ReqContext) response.Respo
} }
return p.withReq( return p.withReq(
ctx, &http.Request{ ctx,
URL: withPath( http.MethodGet,
*ctx.Req.URL, withPath(
endpoints.alerts, *ctx.Req.URL,
), endpoints.alerts,
}, ),
nil,
jsonExtractor(&apimodels.AlertResponse{}), jsonExtractor(&apimodels.AlertResponse{}),
nil,
) )
} }
@ -61,13 +63,15 @@ func (p *LotexProm) RouteGetRuleStatuses(ctx *models.ReqContext) response.Respon
} }
return p.withReq( return p.withReq(
ctx, &http.Request{ ctx,
URL: withPath( http.MethodGet,
*ctx.Req.URL, withPath(
endpoints.rules, *ctx.Req.URL,
), endpoints.rules,
}, ),
nil,
jsonExtractor(&apimodels.RuleResponse{}), jsonExtractor(&apimodels.RuleResponse{}),
nil,
) )
} }

View File

@ -2,7 +2,7 @@
### ###
# create AM configuration # create AM configuration
POST http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/config/api/v1/alerts POST http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/config/api/v1/alerts
content-type: application/json content-type: application/json
{ {
@ -63,16 +63,16 @@ content-type: application/json
### ###
# get latest AM configuration # get latest AM configuration
GET http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/config/api/v1/alerts GET http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/config/api/v1/alerts
content-type: application/json content-type: application/json
### ###
# delete AM configuration # delete AM configuration
DELETE http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/config/api/v1/alerts DELETE http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/config/api/v1/alerts
### ###
# create AM alerts # create AM alerts
POST http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/alerts POST http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/alerts
content-type: application/json content-type: application/json
[ [
@ -95,15 +95,15 @@ content-type: application/json
### ###
# get AM alerts # get AM alerts
GET http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/alerts GET http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/alerts
### ###
# get silences - no silences # get silences - no silences
GET http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences?Filter=foo="bar"&Filter=bar="foo" GET http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences?Filter=foo="bar"&Filter=bar="foo"
### ###
# create silence # create silence
POST http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences POST http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences
content-type: application/json content-type: application/json
{ {
@ -117,12 +117,12 @@ content-type: application/json
"createdBy": "spapagian", "createdBy": "spapagian",
"comment": "a comment", "comment": "a comment",
"startsAt": "2021-04-05T14:45:09.885Z", "startsAt": "2021-04-05T14:45:09.885Z",
"endsAt": "2021-04-05T16:45:09.885Z" "endsAt": "2021-04-09T16:45:09.885Z"
} }
### ###
# update silence - does not exist # update silence - does not exist
POST http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences POST http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences
content-type: application/json content-type: application/json
{ {
@ -143,25 +143,25 @@ content-type: application/json
### ###
# get silences # get silences
# @name getSilences # @name getSilences
GET http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences GET http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silences
### ###
@silenceID = {{getSilences.response.body.$.[3].id}} @silenceID = {{getSilences.response.body.$.[0].id}}
### ###
# get silence # get silence
GET http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/{{silenceID}} GET http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/{{silenceID}}
### ###
# get silence - unknown # get silence - unknown
GET http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/unknown GET http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/unknown
### ###
# delete silence # delete silence
DELETE http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/{{silenceID}} DELETE http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/{{silenceID}}
### ###
# delete silence - unknown # delete silence - unknown
DELETE http://admin:admin@localhost:3000/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/unknown DELETE http://admin:admin@localhost:3000/api/alertmanager/{{alertManagerDatasourceID}}/api/v2/silence/unknown

View File

@ -4,7 +4,9 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"net/http" "net/http"
"net/url"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -86,9 +88,19 @@ type AlertingProxy struct {
// withReq proxies a different request // withReq proxies a different request
func (p *AlertingProxy) withReq( func (p *AlertingProxy) withReq(
ctx *models.ReqContext, ctx *models.ReqContext,
req *http.Request, method string,
u *url.URL,
body io.Reader,
extractor func([]byte) (interface{}, error), extractor func([]byte) (interface{}, error),
headers map[string]string,
) response.Response { ) response.Response {
req, err := http.NewRequest(method, u.String(), body)
if err != nil {
return response.Error(400, err.Error(), nil)
}
for h, v := range headers {
req.Header.Add(h, v)
}
newCtx, resp := replacedResponseWriter(ctx) newCtx, resp := replacedResponseWriter(ctx)
newCtx.Req.Request = req newCtx.Req.Request = req
p.DataProxy.ProxyDatasourceRequestWithID(newCtx, ctx.ParamsInt64("Recipient")) p.DataProxy.ProxyDatasourceRequestWithID(newCtx, ctx.ParamsInt64("Recipient"))