Backend plugins: Refactor to allow shared contract between core and external backend plugins (#25472)
Refactor to allow shared contract between core and external backend plugins allowing core backend data sources in Grafana to be implemented in same way as an external backend plugin. Use v0.67.0 of sdk. Add tests for verifying plugin is restarted when process is killed. Enable strict linting for backendplugin packages
This commit is contained in:
+1
-1
@@ -266,7 +266,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
apiRoute.Any("/datasources/proxy/:id", reqSignedIn, hs.ProxyDataSourceRequest)
|
||||
apiRoute.Any("/datasources/:id/resources", hs.CallDatasourceResource)
|
||||
apiRoute.Any("/datasources/:id/resources/*", hs.CallDatasourceResource)
|
||||
apiRoute.Any("/datasources/:id/health", hs.CheckDatasourceHealth)
|
||||
apiRoute.Any("/datasources/:id/health", Wrap(hs.CheckDatasourceHealth))
|
||||
|
||||
// Folders
|
||||
apiRoute.Group("/folders", func(folderRoute routing.RouteRegister) {
|
||||
|
||||
+10
-34
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/datasource/wrapper"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
@@ -342,28 +341,25 @@ func convertModelToDtos(ds *models.DataSource) dtos.DataSource {
|
||||
|
||||
// CheckDatasourceHealth sends a health check request to the plugin datasource
|
||||
// /api/datasource/:id/health
|
||||
func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) {
|
||||
func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) Response {
|
||||
datasourceID := c.ParamsInt64("id")
|
||||
|
||||
ds, err := hs.DatasourceCache.GetDatasource(datasourceID, c.SignedInUser, c.SkipCache)
|
||||
if err != nil {
|
||||
if err == models.ErrDataSourceAccessDenied {
|
||||
c.JsonApiErr(403, "Access denied to datasource", err)
|
||||
return
|
||||
return Error(403, "Access denied to datasource", err)
|
||||
}
|
||||
c.JsonApiErr(500, "Unable to load datasource metadata", err)
|
||||
return
|
||||
return Error(500, "Unable to load datasource metadata", err)
|
||||
}
|
||||
|
||||
plugin, ok := hs.PluginManager.GetDatasource(ds.Type)
|
||||
if !ok {
|
||||
c.JsonApiErr(500, "Unable to find datasource plugin", err)
|
||||
return
|
||||
return Error(500, "Unable to find datasource plugin", err)
|
||||
}
|
||||
|
||||
dsInstanceSettings, err := wrapper.ModelToInstanceSettings(ds)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Unable to get datasource model", err)
|
||||
return Error(500, "Unable to get datasource model", err)
|
||||
}
|
||||
pCtx := backend.PluginContext{
|
||||
User: wrapper.BackendUserFromSignedInUser(c.SignedInUser),
|
||||
@@ -374,25 +370,7 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) {
|
||||
|
||||
resp, err := hs.BackendPluginManager.CheckHealth(c.Req.Context(), pCtx)
|
||||
if err != nil {
|
||||
if err == backendplugin.ErrPluginNotRegistered {
|
||||
c.JsonApiErr(404, "Plugin not found", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Return status unknown instead?
|
||||
if err == backendplugin.ErrDiagnosticsNotSupported {
|
||||
c.JsonApiErr(404, "Health check not implemented", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Return status unknown or error instead?
|
||||
if err == backendplugin.ErrHealthCheckFailed {
|
||||
c.JsonApiErr(500, "Plugin health check failed", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JsonApiErr(500, "Plugin healthcheck returned an unknown error", err)
|
||||
return
|
||||
return translatePluginRequestErrorToAPIError(err)
|
||||
}
|
||||
|
||||
payload := map[string]interface{}{
|
||||
@@ -405,17 +383,15 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) {
|
||||
var jsonDetails map[string]interface{}
|
||||
err = json.Unmarshal(resp.JSONDetails, &jsonDetails)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Failed to unmarshal detailed response from backend plugin", err)
|
||||
return
|
||||
return Error(500, "Failed to unmarshal detailed response from backend plugin", err)
|
||||
}
|
||||
|
||||
payload["details"] = jsonDetails
|
||||
}
|
||||
|
||||
if resp.Status != backendplugin.HealthStatusOk {
|
||||
c.JSON(503, payload)
|
||||
return
|
||||
if resp.Status != backend.HealthStatusOk {
|
||||
return JSON(503, payload)
|
||||
}
|
||||
|
||||
c.JSON(200, payload)
|
||||
return JSON(200, payload)
|
||||
}
|
||||
|
||||
+26
-28
@@ -266,20 +266,12 @@ func (hs *HTTPServer) CollectPluginMetrics(c *models.ReqContext) Response {
|
||||
pluginID := c.Params("pluginId")
|
||||
plugin, exists := plugins.Plugins[pluginID]
|
||||
if !exists {
|
||||
return Error(404, "Plugin not found, no installed plugin with that id", nil)
|
||||
return Error(404, "Plugin not found", nil)
|
||||
}
|
||||
|
||||
resp, err := hs.BackendPluginManager.CollectMetrics(c.Req.Context(), plugin.Id)
|
||||
if err != nil {
|
||||
if err == backendplugin.ErrPluginNotRegistered {
|
||||
return Error(404, "Plugin not found", err)
|
||||
}
|
||||
|
||||
if err == backendplugin.ErrDiagnosticsNotSupported {
|
||||
return Error(404, "Health check not implemented", err)
|
||||
}
|
||||
|
||||
return Error(500, "Collect plugin metrics failed", err)
|
||||
return translatePluginRequestErrorToAPIError(err)
|
||||
}
|
||||
|
||||
headers := make(http.Header)
|
||||
@@ -300,7 +292,7 @@ func (hs *HTTPServer) CheckHealth(c *models.ReqContext) Response {
|
||||
pCtx, err := hs.getPluginContext(pluginID, c.SignedInUser)
|
||||
if err != nil {
|
||||
if err == ErrPluginNotFound {
|
||||
return Error(404, "Plugin not found, no installed plugin with that id", nil)
|
||||
return Error(404, "Plugin not found", nil)
|
||||
}
|
||||
|
||||
return Error(500, "Failed to get plugin settings", err)
|
||||
@@ -308,21 +300,7 @@ func (hs *HTTPServer) CheckHealth(c *models.ReqContext) Response {
|
||||
|
||||
resp, err := hs.BackendPluginManager.CheckHealth(c.Req.Context(), pCtx)
|
||||
if err != nil {
|
||||
if err == backendplugin.ErrPluginNotRegistered {
|
||||
return Error(404, "Plugin not found", err)
|
||||
}
|
||||
|
||||
// Return status unknown instead?
|
||||
if err == backendplugin.ErrDiagnosticsNotSupported {
|
||||
return Error(404, "Health check not implemented", err)
|
||||
}
|
||||
|
||||
// Return status unknown or error instead?
|
||||
if err == backendplugin.ErrHealthCheckFailed {
|
||||
return Error(500, "Plugin health check failed", err)
|
||||
}
|
||||
|
||||
return Error(500, "Plugin healthcheck returned an unknown error", err)
|
||||
return translatePluginRequestErrorToAPIError(err)
|
||||
}
|
||||
|
||||
payload := map[string]interface{}{
|
||||
@@ -341,7 +319,7 @@ func (hs *HTTPServer) CheckHealth(c *models.ReqContext) Response {
|
||||
payload["details"] = jsonDetails
|
||||
}
|
||||
|
||||
if resp.Status != backendplugin.HealthStatusOk {
|
||||
if resp.Status != backend.HealthStatusOk {
|
||||
return JSON(503, payload)
|
||||
}
|
||||
|
||||
@@ -357,7 +335,7 @@ func (hs *HTTPServer) CallResource(c *models.ReqContext) {
|
||||
pCtx, err := hs.getPluginContext(pluginID, c.SignedInUser)
|
||||
if err != nil {
|
||||
if err == ErrPluginNotFound {
|
||||
c.JsonApiErr(404, "Plugin not found, no installed plugin with that id", nil)
|
||||
c.JsonApiErr(404, "Plugin not found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -385,3 +363,23 @@ func (hs *HTTPServer) getCachedPluginSettings(pluginID string, user *models.Sign
|
||||
hs.CacheService.Set(cacheKey, query.Result, time.Second*5)
|
||||
return query.Result, nil
|
||||
}
|
||||
|
||||
func translatePluginRequestErrorToAPIError(err error) Response {
|
||||
if errors.Is(err, backendplugin.ErrPluginNotRegistered) {
|
||||
return Error(404, "Plugin not found", err)
|
||||
}
|
||||
|
||||
if errors.Is(err, backendplugin.ErrMethodNotImplemented) {
|
||||
return Error(404, "Not found", err)
|
||||
}
|
||||
|
||||
if errors.Is(err, backendplugin.ErrHealthCheckFailed) {
|
||||
return Error(500, "Plugin health check failed", err)
|
||||
}
|
||||
|
||||
if errors.Is(err, backendplugin.ErrPluginUnavailable) {
|
||||
return Error(503, "Plugin unavailable", err)
|
||||
}
|
||||
|
||||
return Error(500, "Plugin request failed", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user