Prometheus: Migrate metadata queries to use resource calls (#49921)

* Sent resource calls for metadata to the backend

* moved resource calls to the backend

* code review feedback

* fixed post with body

* statuscode >= 300

* cleanup

* fixed tests

* fixed datasource tests

* code review feedback

* force some other endpoints to only GET

* fix linting errors

* fixed tests

* was able to remove section of redundant code

* cleanup and code review feedback

* moved query_exemplars to get request

* fixed return on error

* went back to resource calls, but using the backendsrv directly

* moved to a resource call with fallback

* fixed tests

* check for proper messages

* proper check for invalid calls

* code review changes
This commit is contained in:
Stephanie Closson
2022-06-03 15:56:13 -03:00
committed by GitHub
parent d2f3631a47
commit 53ee72d15d
6 changed files with 181 additions and 28 deletions

View File

@@ -42,7 +42,7 @@ func (c *Client) QueryRange(ctx context.Context, q *models.Query) (*http.Respons
qs.Set("end", formatTime(tr.End))
qs.Set("step", strconv.FormatFloat(tr.Step.Seconds(), 'f', -1, 64))
return c.fetch(ctx, u, qs)
return c.fetch(ctx, c.method, u, qs)
}
func (c *Client) QueryInstant(ctx context.Context, q *models.Query) (*http.Response, error) {
@@ -60,7 +60,7 @@ func (c *Client) QueryInstant(ctx context.Context, q *models.Query) (*http.Respo
qs.Set("time", formatTime(tr.End))
}
return c.fetch(ctx, u, qs)
return c.fetch(ctx, c.method, u, qs)
}
func (c *Client) QueryExemplars(ctx context.Context, q *models.Query) (*http.Response, error) {
@@ -77,20 +77,31 @@ func (c *Client) QueryExemplars(ctx context.Context, q *models.Query) (*http.Res
qs.Set("start", formatTime(tr.Start))
qs.Set("end", formatTime(tr.End))
return c.fetch(ctx, u, qs)
return c.fetch(ctx, c.method, u, qs)
}
func (c *Client) fetch(ctx context.Context, u *url.URL, qs url.Values) (*http.Response, error) {
if strings.ToUpper(c.method) == http.MethodGet {
u.RawQuery = qs.Encode()
}
r, err := http.NewRequestWithContext(ctx, c.method, u.String(), nil)
func (c *Client) QueryResource(ctx context.Context, method string, p string, qs url.Values) (*http.Response, error) {
u, err := url.ParseRequestURI(c.baseUrl)
if err != nil {
return nil, err
}
if strings.ToUpper(c.method) == http.MethodPost {
u.Path = path.Join(u.Path, p)
return c.fetch(ctx, method, u, qs)
}
func (c *Client) fetch(ctx context.Context, method string, u *url.URL, qs url.Values) (*http.Response, error) {
if strings.ToUpper(method) == http.MethodGet {
u.RawQuery = qs.Encode()
}
r, err := http.NewRequestWithContext(ctx, method, u.String(), nil)
if err != nil {
return nil, err
}
if strings.ToUpper(method) == http.MethodPost {
r.Body = ioutil.NopCloser(strings.NewReader(qs.Encode()))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}

View File

@@ -16,6 +16,7 @@ import (
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb/prometheus/buffered"
"github.com/grafana/grafana/pkg/tsdb/prometheus/querydata"
"github.com/grafana/grafana/pkg/tsdb/prometheus/resource"
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
)
@@ -29,6 +30,7 @@ type Service struct {
type instance struct {
buffered *buffered.Buffered
queryData *querydata.QueryData
resource *resource.Resource
}
func ProvideService(httpClientProvider httpclient.Provider, cfg *setting.Cfg, features featuremgmt.FeatureToggles, tracer tracing.Tracer) *Service {
@@ -57,9 +59,15 @@ func newInstanceSettings(httpClientProvider httpclient.Provider, cfg *setting.Cf
return nil, err
}
r, err := resource.New(httpClientProvider, cfg, features, settings, plog)
if err != nil {
return nil, err
}
return instance{
buffered: b,
queryData: qd,
resource: r,
}, nil
}
}
@@ -81,6 +89,27 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
return i.buffered.ExecuteTimeSeriesQuery(ctx, req)
}
func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
i, err := s.getInstance(req.PluginContext)
if err != nil {
return err
}
statusCode, bytes, err := i.resource.Execute(ctx, req)
body := bytes
if err != nil {
body = []byte(err.Error())
}
return sender.Send(&backend.CallResourceResponse{
Status: statusCode,
Headers: map[string][]string{
"content-type": {"application/json"},
},
Body: body,
})
}
func (s *Service) getInstance(pluginCtx backend.PluginContext) (*instance, error) {
i, err := s.im.Get(pluginCtx)
if err != nil {

View File

@@ -0,0 +1,85 @@
package resource
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/url"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tsdb/prometheus/client"
)
type Resource struct {
provider *client.Provider
log log.Logger
}
func New(
httpClientProvider httpclient.Provider,
cfg *setting.Cfg,
features featuremgmt.FeatureToggles,
settings backend.DataSourceInstanceSettings,
plog log.Logger,
) (*Resource, error) {
var jsonData map[string]interface{}
if err := json.Unmarshal(settings.JSONData, &jsonData); err != nil {
return nil, fmt.Errorf("error reading settings: %w", err)
}
p := client.NewProvider(settings, jsonData, httpClientProvider, cfg, features, plog)
return &Resource{
log: plog,
provider: p,
}, nil
}
func (r *Resource) Execute(ctx context.Context, req *backend.CallResourceRequest) (int, []byte, error) {
client, err := r.provider.GetClient(reqHeaders(req.Headers))
if err != nil {
return 500, nil, err
}
return r.fetch(ctx, client, req)
}
func (r *Resource) fetch(ctx context.Context, client *client.Client, req *backend.CallResourceRequest) (int, []byte, error) {
r.log.Debug("Sending resource query", "URL", req.URL)
u, err := url.Parse(req.URL)
if err != nil {
return 500, nil, err
}
resp, err := client.QueryResource(ctx, req.Method, u.Path, u.Query())
if err != nil {
return resp.StatusCode, nil, err
}
defer resp.Body.Close() //nolint (we don't care about the error being returned by resp.Body.Close())
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return 500, nil, err
}
return resp.StatusCode, data, err
}
func reqHeaders(headers map[string][]string) map[string]string {
// Keep only the authorization header, incase downstream the authorization header is required.
// Strip all the others out as appropriate headers will be applied to speak with prometheus.
h := make(map[string]string)
accessValues := headers["Authorization"]
if len(accessValues) > 0 {
h["Authorization"] = accessValues[0]
}
return h
}