Plugins: Enhanced plugin instrumentation (#90199)
* Plugins: Enhanced plugin instrumentation * use backend.CallResourceResponseSenderFunc * sdk v0.237.0 * support admission control * cover all handlers in log and metrics middlewares * fix after review
This commit is contained in:
@@ -160,7 +160,7 @@ func (m *CachingMiddleware) CallResource(ctx context.Context, req *backend.CallR
|
||||
return m.next.CallResource(ctx, req, sender)
|
||||
}
|
||||
// Otherwise, intercept the responses in a wrapped sender so we can cache them first
|
||||
cacheSender := callResourceResponseSenderFunc(func(res *backend.CallResourceResponse) error {
|
||||
cacheSender := backend.CallResourceResponseSenderFunc(func(res *backend.CallResourceResponse) error {
|
||||
cr.UpdateCacheFn(ctx, res)
|
||||
return sender.Send(res)
|
||||
})
|
||||
|
||||
@@ -221,7 +221,7 @@ func TestCachingMiddleware(t *testing.T) {
|
||||
}
|
||||
|
||||
var sentResponse *backend.CallResourceResponse
|
||||
var storeOneResponseCallResourceSender = callResourceResponseSenderFunc(func(res *backend.CallResourceResponse) error {
|
||||
var storeOneResponseCallResourceSender = backend.CallResourceResponseSenderFunc(func(res *backend.CallResourceResponse) error {
|
||||
sentResponse = res
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -24,8 +24,15 @@ type ContextualLoggerMiddleware struct {
|
||||
}
|
||||
|
||||
// instrumentContext adds a contextual logger with plugin and request details to the given context.
|
||||
func instrumentContext(ctx context.Context, endpoint string, pCtx backend.PluginContext) context.Context {
|
||||
p := []any{"endpoint", endpoint, "pluginId", pCtx.PluginID}
|
||||
func instrumentContext(ctx context.Context, pCtx backend.PluginContext) context.Context {
|
||||
p := []any{}
|
||||
|
||||
if ep := backend.EndpointFromContext(ctx); !ep.IsEmpty() {
|
||||
p = append(p, "endpoint", string(ep))
|
||||
}
|
||||
|
||||
p = append(p, "pluginId", pCtx.PluginID)
|
||||
|
||||
if pCtx.DataSourceInstanceSettings != nil {
|
||||
p = append(p, "dsName", pCtx.DataSourceInstanceSettings.Name)
|
||||
p = append(p, "dsUID", pCtx.DataSourceInstanceSettings.UID)
|
||||
@@ -37,54 +44,54 @@ func instrumentContext(ctx context.Context, endpoint string, pCtx backend.Plugin
|
||||
}
|
||||
|
||||
func (m *ContextualLoggerMiddleware) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
ctx = instrumentContext(ctx, endpointQueryData, req.PluginContext)
|
||||
ctx = instrumentContext(ctx, req.PluginContext)
|
||||
return m.next.QueryData(ctx, req)
|
||||
}
|
||||
|
||||
func (m *ContextualLoggerMiddleware) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||
ctx = instrumentContext(ctx, endpointCallResource, req.PluginContext)
|
||||
ctx = instrumentContext(ctx, req.PluginContext)
|
||||
return m.next.CallResource(ctx, req, sender)
|
||||
}
|
||||
|
||||
func (m *ContextualLoggerMiddleware) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
ctx = instrumentContext(ctx, endpointCheckHealth, req.PluginContext)
|
||||
ctx = instrumentContext(ctx, req.PluginContext)
|
||||
return m.next.CheckHealth(ctx, req)
|
||||
}
|
||||
|
||||
func (m *ContextualLoggerMiddleware) CollectMetrics(ctx context.Context, req *backend.CollectMetricsRequest) (*backend.CollectMetricsResult, error) {
|
||||
ctx = instrumentContext(ctx, endpointCollectMetrics, req.PluginContext)
|
||||
ctx = instrumentContext(ctx, req.PluginContext)
|
||||
return m.next.CollectMetrics(ctx, req)
|
||||
}
|
||||
|
||||
func (m *ContextualLoggerMiddleware) SubscribeStream(ctx context.Context, req *backend.SubscribeStreamRequest) (*backend.SubscribeStreamResponse, error) {
|
||||
ctx = instrumentContext(ctx, endpointSubscribeStream, req.PluginContext)
|
||||
ctx = instrumentContext(ctx, req.PluginContext)
|
||||
return m.next.SubscribeStream(ctx, req)
|
||||
}
|
||||
|
||||
func (m *ContextualLoggerMiddleware) PublishStream(ctx context.Context, req *backend.PublishStreamRequest) (*backend.PublishStreamResponse, error) {
|
||||
ctx = instrumentContext(ctx, endpointPublishStream, req.PluginContext)
|
||||
ctx = instrumentContext(ctx, req.PluginContext)
|
||||
return m.next.PublishStream(ctx, req)
|
||||
}
|
||||
|
||||
func (m *ContextualLoggerMiddleware) RunStream(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
ctx = instrumentContext(ctx, endpointRunStream, req.PluginContext)
|
||||
ctx = instrumentContext(ctx, req.PluginContext)
|
||||
return m.next.RunStream(ctx, req, sender)
|
||||
}
|
||||
|
||||
// ValidateAdmission implements backend.AdmissionHandler.
|
||||
func (m *ContextualLoggerMiddleware) ValidateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.ValidationResponse, error) {
|
||||
ctx = instrumentContext(ctx, endpointValidateAdmission, req.PluginContext)
|
||||
ctx = instrumentContext(ctx, req.PluginContext)
|
||||
return m.next.ValidateAdmission(ctx, req)
|
||||
}
|
||||
|
||||
// MutateAdmission implements backend.AdmissionHandler.
|
||||
func (m *ContextualLoggerMiddleware) MutateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.MutationResponse, error) {
|
||||
ctx = instrumentContext(ctx, endpointMutateAdmission, req.PluginContext)
|
||||
ctx = instrumentContext(ctx, req.PluginContext)
|
||||
return m.next.MutateAdmission(ctx, req)
|
||||
}
|
||||
|
||||
// ConvertObject implements backend.AdmissionHandler.
|
||||
func (m *ContextualLoggerMiddleware) ConvertObject(ctx context.Context, req *backend.ConversionRequest) (*backend.ConversionResponse, error) {
|
||||
ctx = instrumentContext(ctx, endpointConvertObject, req.PluginContext)
|
||||
ctx = instrumentContext(ctx, req.PluginContext)
|
||||
return m.next.ConvertObject(ctx, req)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/instrumentationutils"
|
||||
plog "github.com/grafana/grafana/pkg/plugins/log"
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginrequestmeta"
|
||||
)
|
||||
@@ -30,13 +31,13 @@ type LoggerMiddleware struct {
|
||||
logger plog.Logger
|
||||
}
|
||||
|
||||
func (m *LoggerMiddleware) logRequest(ctx context.Context, fn func(ctx context.Context) (requestStatus, error)) error {
|
||||
func (m *LoggerMiddleware) logRequest(ctx context.Context, fn func(ctx context.Context) (instrumentationutils.RequestStatus, error)) error {
|
||||
start := time.Now()
|
||||
timeBeforePluginRequest := log.TimeSinceStart(ctx, start)
|
||||
|
||||
status, err := fn(ctx)
|
||||
logParams := []any{
|
||||
"status", status,
|
||||
"status", status.String(),
|
||||
"duration", time.Since(start),
|
||||
"eventName", "grafana-data-egress",
|
||||
"time_before_plugin_request", timeBeforePluginRequest,
|
||||
@@ -48,7 +49,7 @@ func (m *LoggerMiddleware) logRequest(ctx context.Context, fn func(ctx context.C
|
||||
|
||||
ctxLogger := m.logger.FromContext(ctx)
|
||||
logFunc := ctxLogger.Info
|
||||
if status > requestStatusOK {
|
||||
if status > instrumentationutils.RequestStatusOK {
|
||||
logFunc = ctxLogger.Error
|
||||
}
|
||||
|
||||
@@ -63,11 +64,12 @@ func (m *LoggerMiddleware) QueryData(ctx context.Context, req *backend.QueryData
|
||||
}
|
||||
|
||||
var resp *backend.QueryDataResponse
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (status requestStatus, innerErr error) {
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.QueryData(ctx, req)
|
||||
|
||||
if innerErr != nil {
|
||||
return requestStatusFromError(innerErr), innerErr
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
}
|
||||
|
||||
ctxLogger := m.logger.FromContext(ctx)
|
||||
@@ -83,7 +85,7 @@ func (m *LoggerMiddleware) QueryData(ctx context.Context, req *backend.QueryData
|
||||
}
|
||||
}
|
||||
|
||||
return requestStatusFromQueryDataResponse(resp, innerErr), innerErr
|
||||
return instrumentationutils.RequestStatusFromQueryDataResponse(resp, innerErr), innerErr
|
||||
})
|
||||
|
||||
return resp, err
|
||||
@@ -94,9 +96,9 @@ func (m *LoggerMiddleware) CallResource(ctx context.Context, req *backend.CallRe
|
||||
return m.next.CallResource(ctx, req, sender)
|
||||
}
|
||||
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (status requestStatus, innerErr error) {
|
||||
innerErr = m.next.CallResource(ctx, req, sender)
|
||||
return requestStatusFromError(innerErr), innerErr
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
innerErr := m.next.CallResource(ctx, req, sender)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return err
|
||||
@@ -108,9 +110,10 @@ func (m *LoggerMiddleware) CheckHealth(ctx context.Context, req *backend.CheckHe
|
||||
}
|
||||
|
||||
var resp *backend.CheckHealthResult
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (status requestStatus, innerErr error) {
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.CheckHealth(ctx, req)
|
||||
return requestStatusFromError(innerErr), innerErr
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return resp, err
|
||||
@@ -122,9 +125,98 @@ func (m *LoggerMiddleware) CollectMetrics(ctx context.Context, req *backend.Coll
|
||||
}
|
||||
|
||||
var resp *backend.CollectMetricsResult
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (status requestStatus, innerErr error) {
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.CollectMetrics(ctx, req)
|
||||
return requestStatusFromError(innerErr), innerErr
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *LoggerMiddleware) SubscribeStream(ctx context.Context, req *backend.SubscribeStreamRequest) (*backend.SubscribeStreamResponse, error) {
|
||||
if req == nil {
|
||||
return m.next.SubscribeStream(ctx, req)
|
||||
}
|
||||
|
||||
var resp *backend.SubscribeStreamResponse
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.SubscribeStream(ctx, req)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *LoggerMiddleware) PublishStream(ctx context.Context, req *backend.PublishStreamRequest) (*backend.PublishStreamResponse, error) {
|
||||
if req == nil {
|
||||
return m.next.PublishStream(ctx, req)
|
||||
}
|
||||
|
||||
var resp *backend.PublishStreamResponse
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.PublishStream(ctx, req)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *LoggerMiddleware) RunStream(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
if req == nil {
|
||||
return m.next.RunStream(ctx, req, sender)
|
||||
}
|
||||
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
innerErr := m.next.RunStream(ctx, req, sender)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *LoggerMiddleware) ValidateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.ValidationResponse, error) {
|
||||
if req == nil {
|
||||
return m.next.ValidateAdmission(ctx, req)
|
||||
}
|
||||
|
||||
var resp *backend.ValidationResponse
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.ValidateAdmission(ctx, req)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *LoggerMiddleware) MutateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.MutationResponse, error) {
|
||||
if req == nil {
|
||||
return m.next.MutateAdmission(ctx, req)
|
||||
}
|
||||
|
||||
var resp *backend.MutationResponse
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.MutateAdmission(ctx, req)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *LoggerMiddleware) ConvertObject(ctx context.Context, req *backend.ConversionRequest) (*backend.ConversionResponse, error) {
|
||||
if req == nil {
|
||||
return m.next.ConvertObject(ctx, req)
|
||||
}
|
||||
|
||||
var resp *backend.ConversionResponse
|
||||
err := m.logRequest(ctx, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.ConvertObject(ctx, req)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return resp, err
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/instrumentationutils"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/registry"
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginrequestmeta"
|
||||
)
|
||||
@@ -94,17 +95,18 @@ func (m *MetricsMiddleware) pluginTarget(ctx context.Context, pluginID, pluginVe
|
||||
}
|
||||
|
||||
// instrumentPluginRequestSize tracks the size of the given request in the m.pluginRequestSize metric.
|
||||
func (m *MetricsMiddleware) instrumentPluginRequestSize(ctx context.Context, pluginCtx backend.PluginContext, endpoint string, requestSize float64) error {
|
||||
func (m *MetricsMiddleware) instrumentPluginRequestSize(ctx context.Context, pluginCtx backend.PluginContext, requestSize float64) error {
|
||||
target, err := m.pluginTarget(ctx, pluginCtx.PluginID, pluginCtx.PluginVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.pluginRequestSize.WithLabelValues("grafana-backend", pluginCtx.PluginID, endpoint, target).Observe(requestSize)
|
||||
endpoint := backend.EndpointFromContext(ctx)
|
||||
m.pluginRequestSize.WithLabelValues("grafana-backend", pluginCtx.PluginID, string(endpoint), target).Observe(requestSize)
|
||||
return nil
|
||||
}
|
||||
|
||||
// instrumentPluginRequest increments the m.pluginRequestCounter metric and tracks the duration of the given request.
|
||||
func (m *MetricsMiddleware) instrumentPluginRequest(ctx context.Context, pluginCtx backend.PluginContext, endpoint string, fn func(context.Context) (requestStatus, error)) error {
|
||||
func (m *MetricsMiddleware) instrumentPluginRequest(ctx context.Context, pluginCtx backend.PluginContext, fn func(context.Context) (instrumentationutils.RequestStatus, error)) error {
|
||||
target, err := m.pluginTarget(ctx, pluginCtx.PluginID, pluginCtx.PluginVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -116,10 +118,11 @@ func (m *MetricsMiddleware) instrumentPluginRequest(ctx context.Context, pluginC
|
||||
elapsed := time.Since(start)
|
||||
|
||||
statusSource := pluginrequestmeta.StatusSourceFromContext(ctx)
|
||||
endpoint := backend.EndpointFromContext(ctx)
|
||||
|
||||
pluginRequestDurationWithLabels := m.pluginRequestDuration.WithLabelValues(pluginCtx.PluginID, endpoint, target, string(statusSource))
|
||||
pluginRequestCounterWithLabels := m.pluginRequestCounter.WithLabelValues(pluginCtx.PluginID, endpoint, status.String(), target, string(statusSource))
|
||||
pluginRequestDurationSecondsWithLabels := m.pluginRequestDurationSeconds.WithLabelValues("grafana-backend", pluginCtx.PluginID, endpoint, status.String(), target, string(statusSource))
|
||||
pluginRequestDurationWithLabels := m.pluginRequestDuration.WithLabelValues(pluginCtx.PluginID, string(endpoint), target, string(statusSource))
|
||||
pluginRequestCounterWithLabels := m.pluginRequestCounter.WithLabelValues(pluginCtx.PluginID, string(endpoint), status.String(), target, string(statusSource))
|
||||
pluginRequestDurationSecondsWithLabels := m.pluginRequestDurationSeconds.WithLabelValues("grafana-backend", pluginCtx.PluginID, string(endpoint), status.String(), target, string(statusSource))
|
||||
|
||||
if traceID := tracing.TraceIDFromContext(ctx, true); traceID != "" {
|
||||
pluginRequestDurationWithLabels.(prometheus.ExemplarObserver).ObserveWithExemplar(
|
||||
@@ -144,74 +147,108 @@ func (m *MetricsMiddleware) QueryData(ctx context.Context, req *backend.QueryDat
|
||||
requestSize += float64(len(v.JSON))
|
||||
}
|
||||
|
||||
if err := m.instrumentPluginRequestSize(ctx, req.PluginContext, endpointQueryData, requestSize); err != nil {
|
||||
if err := m.instrumentPluginRequestSize(ctx, req.PluginContext, requestSize); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var resp *backend.QueryDataResponse
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, endpointQueryData, func(ctx context.Context) (status requestStatus, innerErr error) {
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.QueryData(ctx, req)
|
||||
return requestStatusFromQueryDataResponse(resp, innerErr), innerErr
|
||||
return instrumentationutils.RequestStatusFromQueryDataResponse(resp, innerErr), innerErr
|
||||
})
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *MetricsMiddleware) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||
if err := m.instrumentPluginRequestSize(ctx, req.PluginContext, endpointCallResource, float64(len(req.Body))); err != nil {
|
||||
if err := m.instrumentPluginRequestSize(ctx, req.PluginContext, float64(len(req.Body))); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.instrumentPluginRequest(ctx, req.PluginContext, endpointCallResource, func(ctx context.Context) (requestStatus, error) {
|
||||
return m.instrumentPluginRequest(ctx, req.PluginContext, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
innerErr := m.next.CallResource(ctx, req, sender)
|
||||
return requestStatusFromError(innerErr), innerErr
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
}
|
||||
|
||||
func (m *MetricsMiddleware) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
var result *backend.CheckHealthResult
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, endpointCheckHealth, func(ctx context.Context) (status requestStatus, innerErr error) {
|
||||
result, innerErr = m.next.CheckHealth(ctx, req)
|
||||
return requestStatusFromError(innerErr), innerErr
|
||||
var resp *backend.CheckHealthResult
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.CheckHealth(ctx, req)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return result, err
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *MetricsMiddleware) CollectMetrics(ctx context.Context, req *backend.CollectMetricsRequest) (*backend.CollectMetricsResult, error) {
|
||||
var result *backend.CollectMetricsResult
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, endpointCollectMetrics, func(ctx context.Context) (status requestStatus, innerErr error) {
|
||||
result, innerErr = m.next.CollectMetrics(ctx, req)
|
||||
return requestStatusFromError(innerErr), innerErr
|
||||
var resp *backend.CollectMetricsResult
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.CollectMetrics(ctx, req)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
return result, err
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *MetricsMiddleware) SubscribeStream(ctx context.Context, req *backend.SubscribeStreamRequest) (*backend.SubscribeStreamResponse, error) {
|
||||
var resp *backend.SubscribeStreamResponse
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.SubscribeStream(ctx, req)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *MetricsMiddleware) PublishStream(ctx context.Context, req *backend.PublishStreamRequest) (*backend.PublishStreamResponse, error) {
|
||||
var resp *backend.PublishStreamResponse
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.PublishStream(ctx, req)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *MetricsMiddleware) RunStream(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
innerErr := m.next.RunStream(ctx, req, sender)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *MetricsMiddleware) ValidateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.ValidationResponse, error) {
|
||||
var result *backend.ValidationResponse
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, endpointMutateAdmission, func(ctx context.Context) (status requestStatus, innerErr error) {
|
||||
result, innerErr = m.next.ValidateAdmission(ctx, req)
|
||||
return requestStatusFromError(innerErr), innerErr
|
||||
var resp *backend.ValidationResponse
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.ValidateAdmission(ctx, req)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return result, err
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *MetricsMiddleware) MutateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.MutationResponse, error) {
|
||||
var result *backend.MutationResponse
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, endpointMutateAdmission, func(ctx context.Context) (status requestStatus, innerErr error) {
|
||||
result, innerErr = m.next.MutateAdmission(ctx, req)
|
||||
return requestStatusFromError(innerErr), innerErr
|
||||
var resp *backend.MutationResponse
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.MutateAdmission(ctx, req)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return result, err
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (m *MetricsMiddleware) ConvertObject(ctx context.Context, req *backend.ConversionRequest) (*backend.ConversionResponse, error) {
|
||||
var result *backend.ConversionResponse
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, endpointMutateAdmission, func(ctx context.Context) (status requestStatus, innerErr error) {
|
||||
result, innerErr = m.next.ConvertObject(ctx, req)
|
||||
return requestStatusFromError(innerErr), innerErr
|
||||
var resp *backend.ConversionResponse
|
||||
err := m.instrumentPluginRequest(ctx, req.PluginContext, func(ctx context.Context) (instrumentationutils.RequestStatus, error) {
|
||||
var innerErr error
|
||||
resp, innerErr = m.next.ConvertObject(ctx, req)
|
||||
return instrumentationutils.RequestStatusFromError(innerErr), innerErr
|
||||
})
|
||||
|
||||
return result, err
|
||||
return resp, err
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/instrumentationutils"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/client/clienttest"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/fakes"
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginrequestmeta"
|
||||
@@ -32,12 +33,12 @@ func TestInstrumentationMiddleware(t *testing.T) {
|
||||
pCtx := backend.PluginContext{PluginID: pluginID}
|
||||
t.Run("should instrument requests", func(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
expEndpoint string
|
||||
expEndpoint backend.Endpoint
|
||||
fn func(cdt *clienttest.ClientDecoratorTest) error
|
||||
shouldInstrumentRequestSize bool
|
||||
}{
|
||||
{
|
||||
expEndpoint: endpointCheckHealth,
|
||||
expEndpoint: backend.EndpointCheckHealth,
|
||||
fn: func(cdt *clienttest.ClientDecoratorTest) error {
|
||||
_, err := cdt.Decorator.CheckHealth(context.Background(), &backend.CheckHealthRequest{PluginContext: pCtx})
|
||||
return err
|
||||
@@ -45,14 +46,14 @@ func TestInstrumentationMiddleware(t *testing.T) {
|
||||
shouldInstrumentRequestSize: false,
|
||||
},
|
||||
{
|
||||
expEndpoint: endpointCallResource,
|
||||
expEndpoint: backend.EndpointCallResource,
|
||||
fn: func(cdt *clienttest.ClientDecoratorTest) error {
|
||||
return cdt.Decorator.CallResource(context.Background(), &backend.CallResourceRequest{PluginContext: pCtx}, nopCallResourceSender)
|
||||
},
|
||||
shouldInstrumentRequestSize: true,
|
||||
},
|
||||
{
|
||||
expEndpoint: endpointQueryData,
|
||||
expEndpoint: backend.EndpointQueryData,
|
||||
fn: func(cdt *clienttest.ClientDecoratorTest) error {
|
||||
_, err := cdt.Decorator.QueryData(context.Background(), &backend.QueryDataRequest{PluginContext: pCtx})
|
||||
return err
|
||||
@@ -60,7 +61,7 @@ func TestInstrumentationMiddleware(t *testing.T) {
|
||||
shouldInstrumentRequestSize: true,
|
||||
},
|
||||
{
|
||||
expEndpoint: endpointCollectMetrics,
|
||||
expEndpoint: backend.EndpointCollectMetrics,
|
||||
fn: func(cdt *clienttest.ClientDecoratorTest) error {
|
||||
_, err := cdt.Decorator.CollectMetrics(context.Background(), &backend.CollectMetricsRequest{PluginContext: pCtx})
|
||||
return err
|
||||
@@ -68,7 +69,7 @@ func TestInstrumentationMiddleware(t *testing.T) {
|
||||
shouldInstrumentRequestSize: false,
|
||||
},
|
||||
} {
|
||||
t.Run(tc.expEndpoint, func(t *testing.T) {
|
||||
t.Run(string(tc.expEndpoint), func(t *testing.T) {
|
||||
promRegistry := prometheus.NewRegistry()
|
||||
pluginsRegistry := fakes.NewFakePluginRegistry()
|
||||
require.NoError(t, pluginsRegistry.Add(context.Background(), &plugins.Plugin{
|
||||
@@ -89,12 +90,12 @@ func TestInstrumentationMiddleware(t *testing.T) {
|
||||
require.Equal(t, 1, testutil.CollectAndCount(promRegistry, metricRequestDurationMs))
|
||||
require.Equal(t, 1, testutil.CollectAndCount(promRegistry, metricRequestDurationS))
|
||||
|
||||
counter := mw.pluginMetrics.pluginRequestCounter.WithLabelValues(pluginID, tc.expEndpoint, requestStatusOK.String(), string(backendplugin.TargetUnknown), string(pluginrequestmeta.DefaultStatusSource))
|
||||
counter := mw.pluginMetrics.pluginRequestCounter.WithLabelValues(pluginID, string(tc.expEndpoint), instrumentationutils.RequestStatusOK.String(), string(backendplugin.TargetUnknown), string(pluginrequestmeta.DefaultStatusSource))
|
||||
require.Equal(t, 1.0, testutil.ToFloat64(counter))
|
||||
for _, m := range []string{metricRequestDurationMs, metricRequestDurationS} {
|
||||
require.NoError(t, checkHistogram(promRegistry, m, map[string]string{
|
||||
"plugin_id": pluginID,
|
||||
"endpoint": tc.expEndpoint,
|
||||
"endpoint": string(tc.expEndpoint),
|
||||
"target": string(backendplugin.TargetUnknown),
|
||||
}))
|
||||
}
|
||||
@@ -102,7 +103,7 @@ func TestInstrumentationMiddleware(t *testing.T) {
|
||||
require.Equal(t, 1, testutil.CollectAndCount(promRegistry, metricRequestSize), "request size should have been instrumented")
|
||||
require.NoError(t, checkHistogram(promRegistry, metricRequestSize, map[string]string{
|
||||
"plugin_id": pluginID,
|
||||
"endpoint": tc.expEndpoint,
|
||||
"endpoint": string(tc.expEndpoint),
|
||||
"target": string(backendplugin.TargetUnknown),
|
||||
"source": "grafana-backend",
|
||||
}), "request size should have been instrumented")
|
||||
@@ -116,8 +117,8 @@ func TestInstrumentationMiddlewareStatusSource(t *testing.T) {
|
||||
const labelStatusSource = "status_source"
|
||||
queryDataErrorCounterLabels := prometheus.Labels{
|
||||
"plugin_id": pluginID,
|
||||
"endpoint": endpointQueryData,
|
||||
"status": requestStatusError.String(),
|
||||
"endpoint": string(backend.EndpointQueryData),
|
||||
"status": instrumentationutils.RequestStatusError.String(),
|
||||
"target": string(backendplugin.TargetUnknown),
|
||||
}
|
||||
downstreamErrorResponse := backend.DataResponse{
|
||||
|
||||
@@ -35,7 +35,7 @@ func (m *ResourceResponseMiddleware) CallResource(ctx context.Context, req *back
|
||||
}
|
||||
|
||||
processedStreams := 0
|
||||
wrappedSender := callResourceResponseSenderFunc(func(res *backend.CallResourceResponse) error {
|
||||
wrappedSender := backend.CallResourceResponseSenderFunc(func(res *backend.CallResourceResponse) error {
|
||||
if processedStreams == 0 {
|
||||
if res.Headers == nil {
|
||||
res.Headers = map[string][]string{}
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ func TestResourceResponseMiddleware(t *testing.T) {
|
||||
)
|
||||
|
||||
var sentResponse *backend.CallResourceResponse
|
||||
sender := callResourceResponseSenderFunc(func(res *backend.CallResourceResponse) error {
|
||||
sender := backend.CallResourceResponseSenderFunc(func(res *backend.CallResourceResponse) error {
|
||||
sentResponse = res
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -2,6 +2,6 @@ package clientmiddleware
|
||||
|
||||
import "github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
var nopCallResourceSender = callResourceResponseSenderFunc(func(res *backend.CallResourceResponse) error {
|
||||
var nopCallResourceSender = backend.CallResourceResponseSenderFunc(func(res *backend.CallResourceResponse) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -44,10 +44,10 @@ func setSpanAttributeFromHTTPHeader(headers http.Header, span trace.Span, attrib
|
||||
// plugin id, org id, user login, ds, dashboard and panel info. The second function returned is a cleanup function,
|
||||
// which should be called by the caller (deferred) and will set the span status/error and end the span.
|
||||
func (m *TracingMiddleware) traceWrap(
|
||||
ctx context.Context, pluginContext backend.PluginContext, opName string,
|
||||
ctx context.Context, pluginContext backend.PluginContext,
|
||||
) (context.Context, func(error)) {
|
||||
// Start span
|
||||
ctx, span := m.tracer.Start(ctx, "PluginClient."+opName, trace.WithAttributes(
|
||||
endpoint := backend.EndpointFromContext(ctx)
|
||||
ctx, span := m.tracer.Start(ctx, "PluginClient."+string(endpoint), trace.WithAttributes(
|
||||
// Attach some plugin context information to span
|
||||
attribute.String("plugin_id", pluginContext.PluginID),
|
||||
attribute.Int64("org_id", pluginContext.OrgID),
|
||||
@@ -82,7 +82,7 @@ func (m *TracingMiddleware) traceWrap(
|
||||
|
||||
func (m *TracingMiddleware) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
var err error
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext, "queryData")
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext)
|
||||
defer func() { end(err) }()
|
||||
resp, err := m.next.QueryData(ctx, req)
|
||||
return resp, err
|
||||
@@ -90,7 +90,7 @@ func (m *TracingMiddleware) QueryData(ctx context.Context, req *backend.QueryDat
|
||||
|
||||
func (m *TracingMiddleware) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||
var err error
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext, "callResource")
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext)
|
||||
defer func() { end(err) }()
|
||||
err = m.next.CallResource(ctx, req, sender)
|
||||
return err
|
||||
@@ -98,7 +98,7 @@ func (m *TracingMiddleware) CallResource(ctx context.Context, req *backend.CallR
|
||||
|
||||
func (m *TracingMiddleware) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
var err error
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext, "checkHealth")
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext)
|
||||
defer func() { end(err) }()
|
||||
resp, err := m.next.CheckHealth(ctx, req)
|
||||
return resp, err
|
||||
@@ -106,7 +106,7 @@ func (m *TracingMiddleware) CheckHealth(ctx context.Context, req *backend.CheckH
|
||||
|
||||
func (m *TracingMiddleware) CollectMetrics(ctx context.Context, req *backend.CollectMetricsRequest) (*backend.CollectMetricsResult, error) {
|
||||
var err error
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext, "collectMetrics")
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext)
|
||||
defer func() { end(err) }()
|
||||
resp, err := m.next.CollectMetrics(ctx, req)
|
||||
return resp, err
|
||||
@@ -114,7 +114,7 @@ func (m *TracingMiddleware) CollectMetrics(ctx context.Context, req *backend.Col
|
||||
|
||||
func (m *TracingMiddleware) SubscribeStream(ctx context.Context, req *backend.SubscribeStreamRequest) (*backend.SubscribeStreamResponse, error) {
|
||||
var err error
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext, "subscribeStream")
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext)
|
||||
defer func() { end(err) }()
|
||||
resp, err := m.next.SubscribeStream(ctx, req)
|
||||
return resp, err
|
||||
@@ -122,7 +122,7 @@ func (m *TracingMiddleware) SubscribeStream(ctx context.Context, req *backend.Su
|
||||
|
||||
func (m *TracingMiddleware) PublishStream(ctx context.Context, req *backend.PublishStreamRequest) (*backend.PublishStreamResponse, error) {
|
||||
var err error
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext, "publishStream")
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext)
|
||||
defer func() { end(err) }()
|
||||
resp, err := m.next.PublishStream(ctx, req)
|
||||
return resp, err
|
||||
@@ -130,7 +130,7 @@ func (m *TracingMiddleware) PublishStream(ctx context.Context, req *backend.Publ
|
||||
|
||||
func (m *TracingMiddleware) RunStream(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error {
|
||||
var err error
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext, "runStream")
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext)
|
||||
defer func() { end(err) }()
|
||||
err = m.next.RunStream(ctx, req, sender)
|
||||
return err
|
||||
@@ -139,7 +139,7 @@ func (m *TracingMiddleware) RunStream(ctx context.Context, req *backend.RunStrea
|
||||
// ValidateAdmission implements backend.AdmissionHandler.
|
||||
func (m *TracingMiddleware) ValidateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.ValidationResponse, error) {
|
||||
var err error
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext, endpointValidateAdmission)
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext)
|
||||
defer func() { end(err) }()
|
||||
resp, err := m.next.ValidateAdmission(ctx, req)
|
||||
return resp, err
|
||||
@@ -148,7 +148,7 @@ func (m *TracingMiddleware) ValidateAdmission(ctx context.Context, req *backend.
|
||||
// MutateAdmission implements backend.AdmissionHandler.
|
||||
func (m *TracingMiddleware) MutateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.MutationResponse, error) {
|
||||
var err error
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext, endpointMutateAdmission)
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext)
|
||||
defer func() { end(err) }()
|
||||
resp, err := m.next.MutateAdmission(ctx, req)
|
||||
return resp, err
|
||||
@@ -157,7 +157,7 @@ func (m *TracingMiddleware) MutateAdmission(ctx context.Context, req *backend.Ad
|
||||
// ConvertObject implements backend.AdmissionHandler.
|
||||
func (m *TracingMiddleware) ConvertObject(ctx context.Context, req *backend.ConversionRequest) (*backend.ConversionResponse, error) {
|
||||
var err error
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext, endpointConvertObject)
|
||||
ctx, end := m.traceWrap(ctx, req.PluginContext)
|
||||
defer func() { end(err) }()
|
||||
resp, err := m.next.ConvertObject(ctx, req)
|
||||
return resp, err
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
package clientmiddleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
)
|
||||
|
||||
type requestStatus int
|
||||
|
||||
const (
|
||||
requestStatusOK requestStatus = iota
|
||||
requestStatusCancelled
|
||||
requestStatusError
|
||||
)
|
||||
|
||||
func (status requestStatus) String() string {
|
||||
names := [...]string{"ok", "cancelled", "error"}
|
||||
if status < requestStatusOK || status > requestStatusError {
|
||||
return ""
|
||||
}
|
||||
|
||||
return names[status]
|
||||
}
|
||||
|
||||
const (
|
||||
endpointCallResource = "callResource"
|
||||
endpointCheckHealth = "checkHealth"
|
||||
endpointCollectMetrics = "collectMetrics"
|
||||
endpointQueryData = "queryData"
|
||||
endpointSubscribeStream = "subscribeStream"
|
||||
endpointPublishStream = "publishStream"
|
||||
endpointRunStream = "runStream"
|
||||
endpointValidateAdmission = "validateAdmission"
|
||||
endpointMutateAdmission = "mutateAdmission"
|
||||
endpointConvertObject = "convertObject"
|
||||
)
|
||||
|
||||
type callResourceResponseSenderFunc func(res *backend.CallResourceResponse) error
|
||||
|
||||
func (fn callResourceResponseSenderFunc) Send(res *backend.CallResourceResponse) error {
|
||||
return fn(res)
|
||||
}
|
||||
|
||||
func requestStatusFromError(err error) requestStatus {
|
||||
status := requestStatusOK
|
||||
if err != nil {
|
||||
status = requestStatusError
|
||||
if errors.Is(err, context.Canceled) {
|
||||
status = requestStatusCancelled
|
||||
}
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
func requestStatusFromQueryDataResponse(res *backend.QueryDataResponse, err error) requestStatus {
|
||||
if err != nil {
|
||||
return requestStatusFromError(err)
|
||||
}
|
||||
|
||||
status := requestStatusOK
|
||||
|
||||
if res != nil {
|
||||
for _, dr := range res.Responses {
|
||||
if dr.Error != nil {
|
||||
s := requestStatusFromError(dr.Error)
|
||||
if s > status {
|
||||
status = s
|
||||
}
|
||||
|
||||
if status == requestStatusError {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package clientmiddleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRequestStatus(t *testing.T) {
|
||||
tcs := []struct {
|
||||
s requestStatus
|
||||
expectedLabel string
|
||||
}{
|
||||
{
|
||||
s: requestStatusOK,
|
||||
expectedLabel: "ok",
|
||||
},
|
||||
{
|
||||
s: requestStatusError,
|
||||
expectedLabel: "error",
|
||||
},
|
||||
{
|
||||
s: requestStatusCancelled,
|
||||
expectedLabel: "cancelled",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.s.String(), func(t *testing.T) {
|
||||
require.Equal(t, tc.expectedLabel, tc.s.String())
|
||||
require.Equal(t, tc.expectedLabel, fmt.Sprint(tc.s))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestStatusFromError(t *testing.T) {
|
||||
tcs := []struct {
|
||||
desc string
|
||||
err error
|
||||
expectedStatus requestStatus
|
||||
}{
|
||||
{
|
||||
desc: "no error should be status ok",
|
||||
err: nil,
|
||||
expectedStatus: requestStatusOK,
|
||||
},
|
||||
{
|
||||
desc: "error should be status error",
|
||||
err: errors.New("boom"),
|
||||
expectedStatus: requestStatusError,
|
||||
},
|
||||
{
|
||||
desc: "context canceled should be status cancelled",
|
||||
err: context.Canceled,
|
||||
expectedStatus: requestStatusCancelled,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
status := requestStatusFromError(tc.err)
|
||||
require.Equal(t, tc.expectedStatus, status)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestStatusFromQueryDataResponse(t *testing.T) {
|
||||
responseWithoutError := backend.NewQueryDataResponse()
|
||||
responseWithoutError.Responses["A"] = backend.DataResponse{
|
||||
Frames: data.Frames{data.NewFrame("test")},
|
||||
}
|
||||
|
||||
responseWithError := backend.NewQueryDataResponse()
|
||||
responseWithError.Responses["A"] = backend.DataResponse{
|
||||
Error: errors.New("boom"),
|
||||
}
|
||||
responseWithMultipleErrors := backend.NewQueryDataResponse()
|
||||
responseWithMultipleErrors.Responses["A"] = backend.DataResponse{
|
||||
Error: context.Canceled,
|
||||
}
|
||||
responseWithMultipleErrors.Responses["B"] = backend.DataResponse{
|
||||
Frames: data.Frames{data.NewFrame("test")},
|
||||
}
|
||||
responseWithMultipleErrors.Responses["C"] = backend.DataResponse{
|
||||
Error: errors.New("boom"),
|
||||
}
|
||||
|
||||
tcs := []struct {
|
||||
desc string
|
||||
resp *backend.QueryDataResponse
|
||||
err error
|
||||
expectedStatus requestStatus
|
||||
}{
|
||||
{
|
||||
desc: "no error should be status ok",
|
||||
err: nil,
|
||||
expectedStatus: requestStatusOK,
|
||||
},
|
||||
{
|
||||
desc: "error should be status error",
|
||||
err: errors.New("boom"),
|
||||
expectedStatus: requestStatusError,
|
||||
},
|
||||
{
|
||||
desc: "context canceled should be status cancelled",
|
||||
err: context.Canceled,
|
||||
expectedStatus: requestStatusCancelled,
|
||||
},
|
||||
{
|
||||
desc: "response without error should be status ok",
|
||||
resp: responseWithoutError,
|
||||
expectedStatus: requestStatusOK,
|
||||
},
|
||||
{
|
||||
desc: "response with error should be status error",
|
||||
resp: responseWithError,
|
||||
expectedStatus: requestStatusError,
|
||||
},
|
||||
{
|
||||
desc: "response with multiple error should pick the highest status cancelled",
|
||||
resp: responseWithMultipleErrors,
|
||||
expectedStatus: requestStatusError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
status := requestStatusFromQueryDataResponse(tc.resp, tc.err)
|
||||
require.Equal(t, tc.expectedStatus, status)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user