mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
	
	
		
			155 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
		
		
			
		
	
	
			155 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
|  | package promlib | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"context" | ||
|  | 	"errors" | ||
|  | 	"fmt" | ||
|  | 	"strings" | ||
|  | 	"time" | ||
|  | 
 | ||
|  | 	"github.com/grafana/grafana-plugin-sdk-go/backend" | ||
|  | 	"github.com/grafana/grafana-plugin-sdk-go/backend/datasource" | ||
|  | 	sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" | ||
|  | 	"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" | ||
|  | 	"github.com/grafana/grafana-plugin-sdk-go/backend/log" | ||
|  | 	"github.com/patrickmn/go-cache" | ||
|  | 	apiv1 "github.com/prometheus/client_golang/api/prometheus/v1" | ||
|  | 
 | ||
|  | 	"github.com/grafana/grafana/pkg/promlib/client" | ||
|  | 	"github.com/grafana/grafana/pkg/promlib/instrumentation" | ||
|  | 	"github.com/grafana/grafana/pkg/promlib/querydata" | ||
|  | 	"github.com/grafana/grafana/pkg/promlib/resource" | ||
|  | ) | ||
|  | 
 | ||
|  | type Service struct { | ||
|  | 	im     instancemgmt.InstanceManager | ||
|  | 	logger log.Logger | ||
|  | } | ||
|  | 
 | ||
|  | type instance struct { | ||
|  | 	queryData    *querydata.QueryData | ||
|  | 	resource     *resource.Resource | ||
|  | 	versionCache *cache.Cache | ||
|  | } | ||
|  | 
 | ||
|  | type ExtendOptions func(ctx context.Context, settings backend.DataSourceInstanceSettings, clientOpts *sdkhttpclient.Options) error | ||
|  | 
 | ||
|  | func NewService(httpClientProvider *sdkhttpclient.Provider, plog log.Logger, extendOptions ExtendOptions) *Service { | ||
|  | 	if httpClientProvider == nil { | ||
|  | 		httpClientProvider = sdkhttpclient.NewProvider() | ||
|  | 	} | ||
|  | 	return &Service{ | ||
|  | 		im:     datasource.NewInstanceManager(newInstanceSettings(httpClientProvider, plog, extendOptions)), | ||
|  | 		logger: plog, | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func newInstanceSettings(httpClientProvider *sdkhttpclient.Provider, log log.Logger, extendOptions ExtendOptions) datasource.InstanceFactoryFunc { | ||
|  | 	return func(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { | ||
|  | 		// Creates a http roundTripper.
 | ||
|  | 		opts, err := client.CreateTransportOptions(ctx, settings, log) | ||
|  | 		if err != nil { | ||
|  | 			return nil, fmt.Errorf("error creating transport options: %v", err) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		err = extendOptions(ctx, settings, opts) | ||
|  | 		if err != nil { | ||
|  | 			return nil, fmt.Errorf("error extending transport options: %v", err) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		httpClient, err := httpClientProvider.New(*opts) | ||
|  | 		if err != nil { | ||
|  | 			return nil, fmt.Errorf("error creating http client: %v", err) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// New version using custom client and better response parsing
 | ||
|  | 		qd, err := querydata.New(httpClient, settings, log) | ||
|  | 		if err != nil { | ||
|  | 			return nil, err | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Resource call management using new custom client same as querydata
 | ||
|  | 		r, err := resource.New(httpClient, settings, log) | ||
|  | 		if err != nil { | ||
|  | 			return nil, err | ||
|  | 		} | ||
|  | 
 | ||
|  | 		return instance{ | ||
|  | 			queryData:    qd, | ||
|  | 			resource:     r, | ||
|  | 			versionCache: cache.New(time.Minute*1, time.Minute*5), | ||
|  | 		}, nil | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { | ||
|  | 	if len(req.Queries) == 0 { | ||
|  | 		err := fmt.Errorf("query contains no queries") | ||
|  | 		instrumentation.UpdateQueryDataMetrics(err, nil) | ||
|  | 		return &backend.QueryDataResponse{}, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	i, err := s.getInstance(ctx, req.PluginContext) | ||
|  | 	if err != nil { | ||
|  | 		instrumentation.UpdateQueryDataMetrics(err, nil) | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	qd, err := i.queryData.Execute(ctx, req) | ||
|  | 	instrumentation.UpdateQueryDataMetrics(err, qd) | ||
|  | 
 | ||
|  | 	return qd, err | ||
|  | } | ||
|  | 
 | ||
|  | func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { | ||
|  | 	i, err := s.getInstance(ctx, req.PluginContext) | ||
|  | 	if err != nil { | ||
|  | 		return err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if strings.EqualFold(req.Path, "version-detect") { | ||
|  | 		versionObj, found := i.versionCache.Get("version") | ||
|  | 		if found { | ||
|  | 			return sender.Send(versionObj.(*backend.CallResourceResponse)) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		vResp, err := i.resource.DetectVersion(ctx, req) | ||
|  | 		if err != nil { | ||
|  | 			return err | ||
|  | 		} | ||
|  | 		i.versionCache.Set("version", vResp, cache.DefaultExpiration) | ||
|  | 		return sender.Send(vResp) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	resp, err := i.resource.Execute(ctx, req) | ||
|  | 	if err != nil { | ||
|  | 		return err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return sender.Send(resp) | ||
|  | } | ||
|  | 
 | ||
|  | func (s *Service) getInstance(ctx context.Context, pluginCtx backend.PluginContext) (*instance, error) { | ||
|  | 	i, err := s.im.Get(ctx, pluginCtx) | ||
|  | 	if err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 	in := i.(instance) | ||
|  | 	return &in, nil | ||
|  | } | ||
|  | 
 | ||
|  | // IsAPIError returns whether err is or wraps a Prometheus error.
 | ||
|  | func IsAPIError(err error) bool { | ||
|  | 	// Check if the right error type is in err's chain.
 | ||
|  | 	var e *apiv1.Error | ||
|  | 	return errors.As(err, &e) | ||
|  | } | ||
|  | 
 | ||
|  | func ConvertAPIError(err error) error { | ||
|  | 	var e *apiv1.Error | ||
|  | 	if errors.As(err, &e) { | ||
|  | 		return fmt.Errorf("%s: %s", e.Msg, e.Detail) | ||
|  | 	} | ||
|  | 	return err | ||
|  | } |