mirror of
https://github.com/grafana/grafana.git
synced 2025-12-20 16:54:59 +08:00
Compare commits
1 Commits
docs/add-a
...
andreas/pl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb9246b6bd |
@@ -451,6 +451,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
// Deprecated: use /datasources/uid/:uid/health API instead.
|
||||
apiRoute.Any("/datasources/:id/health", requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), authorize(ac.EvalPermission(datasources.ActionQuery)), routing.Wrap(hs.CheckDatasourceHealth))
|
||||
apiRoute.Any("/datasources/uid/:uid/health", requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), authorize(ac.EvalPermission(datasources.ActionQuery)), routing.Wrap(hs.CheckDatasourceHealthWithUID))
|
||||
apiRoute.Any("/datasources/uid/:uid/schema", requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), authorize(ac.EvalPermission(datasources.ActionQuery)), routing.Wrap(hs.GetDatasourceSchemaWithUID))
|
||||
|
||||
// Folders
|
||||
hs.registerFolderAPI(apiRoute, authorize)
|
||||
|
||||
@@ -775,6 +775,32 @@ func (hs *HTTPServer) convertModelToDtos(ctx context.Context, ds *datasources.Da
|
||||
return dto
|
||||
}
|
||||
|
||||
// swagger:route GET /datasources/uid/{uid}/schema datasources schema getDatasourceSchemaWithUID
|
||||
//
|
||||
// Sends a schema request to the plugin datasource identified by the UID.
|
||||
//
|
||||
// Responses:
|
||||
// 200: okResponse
|
||||
// 400: badRequestError
|
||||
// 401: unauthorisedError
|
||||
// 403: forbiddenError
|
||||
// 500: internalServerError
|
||||
func (hs *HTTPServer) GetDatasourceSchemaWithUID(c *contextmodel.ReqContext) response.Response {
|
||||
dsUID := web.Params(c.Req)[":uid"]
|
||||
if !util.IsValidShortUID(dsUID) {
|
||||
return response.Error(http.StatusBadRequest, "UID is invalid", nil)
|
||||
}
|
||||
|
||||
ds, err := hs.DataSourceCache.GetDatasourceByUID(c.Req.Context(), dsUID, c.SignedInUser, c.SkipDSCache)
|
||||
if err != nil {
|
||||
if errors.Is(err, datasources.ErrDataSourceAccessDenied) {
|
||||
return response.Error(http.StatusForbidden, "Access denied to datasource", err)
|
||||
}
|
||||
return response.Error(http.StatusInternalServerError, "Unable to load datasource metadata", err)
|
||||
}
|
||||
return hs.getDatasourceSchema(c, ds)
|
||||
}
|
||||
|
||||
// swagger:route GET /datasources/uid/{uid}/health datasources health checkDatasourceHealthWithUID
|
||||
//
|
||||
// Sends a health check request to the plugin datasource identified by the UID.
|
||||
@@ -831,6 +857,29 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *contextmodel.ReqContext) response
|
||||
return hs.checkDatasourceHealth(c, ds)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) getDatasourceSchema(c *contextmodel.ReqContext, ds *datasources.DataSource) response.Response {
|
||||
pCtx, err := hs.pluginContextProvider.GetWithDataSource(c.Req.Context(), ds.Type, c.SignedInUser, ds)
|
||||
if err != nil {
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "Unable to get plugin context", err)
|
||||
}
|
||||
req := &backend.SchemaRequest{
|
||||
PluginContext: pCtx,
|
||||
Headers: map[string]string{},
|
||||
}
|
||||
|
||||
err = hs.DataSourceRequestValidator.Validate(ds, c.Req)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusForbidden, "Access denied", err)
|
||||
}
|
||||
|
||||
resp, err := hs.pluginClient.Schema(c.Req.Context(), req)
|
||||
if err != nil {
|
||||
return translatePluginRequestErrorToAPIError(err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) checkDatasourceHealth(c *contextmodel.ReqContext, ds *datasources.DataSource) response.Response {
|
||||
pCtx, err := hs.pluginContextProvider.GetWithDataSource(c.Req.Context(), ds.Type, c.SignedInUser, ds)
|
||||
if err != nil {
|
||||
|
||||
@@ -21,6 +21,7 @@ type corePlugin struct {
|
||||
backend.StreamHandler
|
||||
backend.AdmissionHandler
|
||||
backend.ConversionHandler
|
||||
backend.SchemaHandler
|
||||
}
|
||||
|
||||
// New returns a new backendplugin.PluginFactoryFunc for creating a core (built-in) backendplugin.Plugin.
|
||||
@@ -152,3 +153,12 @@ func (cp *corePlugin) ConvertObjects(ctx context.Context, req *backend.Conversio
|
||||
}
|
||||
return nil, plugins.ErrMethodNotImplemented
|
||||
}
|
||||
|
||||
func (cp *corePlugin) Schema(ctx context.Context, req *backend.SchemaRequest) (*backend.SchemaResponse, error) {
|
||||
if cp.SchemaHandler != nil {
|
||||
ctx = backend.WithGrafanaConfig(ctx, req.PluginContext.GrafanaConfig)
|
||||
return cp.SchemaHandler.Schema(ctx, req)
|
||||
}
|
||||
|
||||
return nil, plugins.ErrMethodNotImplemented
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ var pluginSet = map[int]goplugin.PluginSet{
|
||||
"admission": &grpcplugin.AdmissionGRPCPlugin{},
|
||||
"conversion": &grpcplugin.ConversionGRPCPlugin{},
|
||||
"renderer": &pluginextensionv2.RendererGRPCPlugin{},
|
||||
"information": &grpcplugin.InformationGRPCPlugin{},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -189,3 +189,11 @@ func (r *protoClient) ConvertObjects(ctx context.Context, in *pluginv2.Conversio
|
||||
}
|
||||
return c.ConversionClient.ConvertObjects(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (r *protoClient) Schema(ctx context.Context, in *pluginv2.SchemaRequest, opts ...grpc.CallOption) (*pluginv2.SchemaResponse, error) {
|
||||
c, exists := r.client(ctx)
|
||||
if !exists {
|
||||
return nil, errClientNotAvailable
|
||||
}
|
||||
return c.InformationClient.Schema(ctx, in, opts...)
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ type ClientV2 struct {
|
||||
grpcplugin.StreamClient
|
||||
grpcplugin.AdmissionClient
|
||||
grpcplugin.ConversionClient
|
||||
grpcplugin.InformationClient
|
||||
pluginextensionv2.RendererPlugin
|
||||
}
|
||||
|
||||
@@ -69,6 +70,11 @@ func newClientV2(descriptor PluginDescriptor, logger log.Logger, rpcClient plugi
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawInformation, err := rpcClient.Dispense("information")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &ClientV2{}
|
||||
if rawDiagnostics != nil {
|
||||
if diagnosticsClient, ok := rawDiagnostics.(grpcplugin.DiagnosticsClient); ok {
|
||||
@@ -112,6 +118,12 @@ func newClientV2(descriptor PluginDescriptor, logger log.Logger, rpcClient plugi
|
||||
}
|
||||
}
|
||||
|
||||
if rawInformation != nil {
|
||||
if informationClient, ok := rawInformation.(grpcplugin.InformationClient); ok {
|
||||
c.InformationClient = informationClient
|
||||
}
|
||||
}
|
||||
|
||||
if descriptor.startRendererFn != nil {
|
||||
if err := descriptor.startRendererFn(descriptor.pluginID, c.RendererPlugin, logger); err != nil {
|
||||
return nil, err
|
||||
@@ -336,6 +348,36 @@ func (c *ClientV2) ConvertObjects(ctx context.Context, req *backend.ConversionRe
|
||||
return backend.FromProto().ConversionResponse(protoResp), nil
|
||||
}
|
||||
|
||||
func (c *ClientV2) Schema(ctx context.Context, req *backend.SchemaRequest) (*backend.SchemaResponse, error) {
|
||||
if c.InformationClient == nil {
|
||||
return nil, plugins.ErrMethodNotImplemented
|
||||
}
|
||||
|
||||
protoReq := backend.ToProto().SchemaRequest(req)
|
||||
protoResp, err := c.InformationClient.Schema(ctx, protoReq)
|
||||
|
||||
if err != nil {
|
||||
if status.Code(err) == codes.Unimplemented {
|
||||
return nil, plugins.ErrMethodNotImplemented
|
||||
}
|
||||
|
||||
if status.Code(err) == codes.Unavailable {
|
||||
return nil, plugins.ErrPluginGrpcConnectionUnavailableBaseFn(ctx).Errorf("%v", err)
|
||||
}
|
||||
|
||||
if status.Code(err) == codes.ResourceExhausted {
|
||||
return nil, plugins.ErrPluginGrpcResourceExhaustedBase.Errorf("%v", err)
|
||||
}
|
||||
|
||||
if errorSource, ok := backend.ErrorSourceFromGrpcStatusError(ctx, err); ok {
|
||||
return nil, handleGrpcStatusError(ctx, errorSource, err)
|
||||
}
|
||||
return nil, fmt.Errorf("%v: %w", "Failed to request schema", err)
|
||||
}
|
||||
|
||||
return backend.FromProto().SchemaResponse(protoResp), nil
|
||||
}
|
||||
|
||||
// handleGrpcStatusError sets the error source via context based on the error source provided. Regardless of its value,
|
||||
// a plugin downstream error is returned as both plugin and downstream errors are treated the same in Grafana.
|
||||
func handleGrpcStatusError(ctx context.Context, errorSource errstatus.Source, err error) error {
|
||||
|
||||
@@ -252,3 +252,11 @@ func (p *grpcPlugin) ConvertObjects(ctx context.Context, request *backend.Conver
|
||||
}
|
||||
return pc.ConvertObjects(ctx, request)
|
||||
}
|
||||
|
||||
func (p *grpcPlugin) Schema(ctx context.Context, req *backend.SchemaRequest) (*backend.SchemaResponse, error) {
|
||||
pc, ok := p.getPluginClient(ctx)
|
||||
if !ok {
|
||||
return nil, plugins.ErrPluginUnavailable
|
||||
}
|
||||
return pc.Schema(ctx, req)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ type Plugin interface {
|
||||
backend.AdmissionHandler
|
||||
backend.ConversionHandler
|
||||
backend.StreamHandler
|
||||
backend.SchemaHandler
|
||||
}
|
||||
|
||||
type Target string
|
||||
|
||||
@@ -253,6 +253,36 @@ func (s *Service) ConvertObjects(ctx context.Context, req *backend.ConversionReq
|
||||
return plugin.ConvertObjects(ctx, req)
|
||||
}
|
||||
|
||||
func (s *Service) Schema(ctx context.Context, req *backend.SchemaRequest) (*backend.SchemaResponse, error) {
|
||||
if req == nil {
|
||||
return nil, errNilRequest
|
||||
}
|
||||
|
||||
p, exists := s.plugin(ctx, req.PluginContext.PluginID, req.PluginContext.PluginVersion)
|
||||
if !exists {
|
||||
return nil, plugins.ErrPluginNotRegistered
|
||||
}
|
||||
|
||||
resp, err := p.Schema(ctx, req)
|
||||
if err != nil {
|
||||
if errors.Is(err, plugins.ErrMethodNotImplemented) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if errors.Is(err, plugins.ErrPluginUnavailable) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return nil, plugins.ErrPluginRequestCanceledErrorBase.Errorf("client: schema request canceled: %w", err)
|
||||
}
|
||||
|
||||
return nil, plugins.ErrPluginRequestFailureErrorBase.Errorf("client: failed to request schema: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// MutateAdmission implements plugins.Client.
|
||||
func (s *Service) MutateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.MutationResponse, error) {
|
||||
if req == nil {
|
||||
|
||||
@@ -76,6 +76,7 @@ var (
|
||||
_ = backend.StreamHandler(&Plugin{})
|
||||
_ = backend.AdmissionHandler(&Plugin{})
|
||||
_ = backend.ConversionHandler(&Plugin{})
|
||||
_ = backend.SchemaHandler(&Plugin{})
|
||||
)
|
||||
|
||||
type AngularMeta struct {
|
||||
@@ -413,6 +414,14 @@ func (p *Plugin) ConvertObjects(ctx context.Context, req *backend.ConversionRequ
|
||||
return pluginClient.ConvertObjects(ctx, req)
|
||||
}
|
||||
|
||||
func (p *Plugin) Schema(ctx context.Context, req *backend.SchemaRequest) (*backend.SchemaResponse, error) {
|
||||
pluginClient, ok := p.Client()
|
||||
if !ok {
|
||||
return nil, ErrPluginUnavailable
|
||||
}
|
||||
return pluginClient.Schema(ctx, req)
|
||||
}
|
||||
|
||||
func (p *Plugin) File(name string) (fs.File, error) {
|
||||
cleanPath, err := util.CleanRelativePath(name)
|
||||
if err != nil {
|
||||
@@ -470,6 +479,7 @@ type PluginClient interface {
|
||||
backend.AdmissionHandler
|
||||
backend.ConversionHandler
|
||||
backend.StreamHandler
|
||||
backend.SchemaHandler
|
||||
}
|
||||
|
||||
func (p *Plugin) StaticRoute() *StaticRoute {
|
||||
|
||||
@@ -401,6 +401,10 @@ func (m *alwaysErrorFuncMiddleware) ConvertObjects(ctx context.Context, req *bac
|
||||
return nil, m.f()
|
||||
}
|
||||
|
||||
func (m *alwaysErrorFuncMiddleware) Schema(ctx context.Context, req *backend.SchemaRequest) (*backend.SchemaResponse, error) {
|
||||
return nil, m.f()
|
||||
}
|
||||
|
||||
// newAlwaysErrorMiddleware returns a new middleware that always returns the specified error.
|
||||
func newAlwaysErrorMiddleware(err error) backend.HandlerMiddleware {
|
||||
return backend.HandlerMiddlewareFunc(func(next backend.Handler) backend.Handler {
|
||||
|
||||
@@ -849,6 +849,7 @@ type testPlugin struct {
|
||||
backend.StreamHandler
|
||||
backend.AdmissionHandler
|
||||
backend.ConversionHandler
|
||||
backend.SchemaHandler
|
||||
}
|
||||
|
||||
func (tp *testPlugin) PluginID() string {
|
||||
|
||||
Reference in New Issue
Block a user