2016-02-25 07:55:31 -06:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2020-04-21 09:16:41 -05:00
|
|
|
"encoding/json"
|
2020-03-13 06:31:44 -05:00
|
|
|
"errors"
|
2020-03-18 06:08:20 -05:00
|
|
|
"net/http"
|
2021-04-21 08:17:23 -05:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2016-03-15 09:52:29 -05:00
|
|
|
"sort"
|
|
|
|
|
2021-04-21 08:17:23 -05:00
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
2016-02-25 07:55:31 -06:00
|
|
|
"github.com/grafana/grafana/pkg/api/dtos"
|
2021-01-15 07:43:20 -06:00
|
|
|
"github.com/grafana/grafana/pkg/api/response"
|
2016-02-25 07:55:31 -06:00
|
|
|
"github.com/grafana/grafana/pkg/bus"
|
2020-02-19 12:17:05 -06:00
|
|
|
"github.com/grafana/grafana/pkg/models"
|
2016-02-25 07:55:31 -06:00
|
|
|
"github.com/grafana/grafana/pkg/plugins"
|
2020-03-03 04:45:16 -06:00
|
|
|
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
2021-05-12 13:05:16 -05:00
|
|
|
"github.com/grafana/grafana/pkg/plugins/manager/installer"
|
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
2021-10-11 07:30:59 -05:00
|
|
|
"github.com/grafana/grafana/pkg/web"
|
2016-02-25 07:55:31 -06:00
|
|
|
)
|
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
|
2016-03-08 11:17:47 -06:00
|
|
|
typeFilter := c.Query("type")
|
|
|
|
enabledFilter := c.Query("enabled")
|
|
|
|
embeddedFilter := c.Query("embedded")
|
2016-04-08 15:42:33 -05:00
|
|
|
coreFilter := c.Query("core")
|
2016-03-08 11:17:47 -06:00
|
|
|
|
2020-03-16 09:40:46 -05:00
|
|
|
// For users with viewer role we only return core plugins
|
|
|
|
if !c.HasRole(models.ROLE_ADMIN) {
|
|
|
|
coreFilter = "1"
|
|
|
|
}
|
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
pluginSettingsMap, err := hs.PluginManager.GetPluginSettings(c.OrgId)
|
2016-02-25 07:55:31 -06:00
|
|
|
if err != nil {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(500, "Failed to get list of plugins", err)
|
2016-02-25 07:55:31 -06:00
|
|
|
}
|
|
|
|
|
2016-03-15 09:52:29 -05:00
|
|
|
result := make(dtos.PluginList, 0)
|
2021-03-17 10:06:10 -05:00
|
|
|
for _, pluginDef := range hs.PluginManager.Plugins() {
|
2016-03-08 11:17:47 -06:00
|
|
|
// filter out app sub plugins
|
|
|
|
if embeddedFilter == "0" && pluginDef.IncludedInAppId != "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-04-08 15:42:33 -05:00
|
|
|
// filter out core plugins
|
2020-03-16 09:40:46 -05:00
|
|
|
if (coreFilter == "0" && pluginDef.IsCorePlugin) || (coreFilter == "1" && !pluginDef.IsCorePlugin) {
|
2016-04-08 15:42:33 -05:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-03-08 11:17:47 -06:00
|
|
|
// filter on type
|
|
|
|
if typeFilter != "" && typeFilter != pluginDef.Type {
|
2016-03-08 04:29:36 -06:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-04-12 06:46:42 -05:00
|
|
|
if pluginDef.State == plugins.PluginStateAlpha && !hs.Cfg.PluginsEnableAlpha {
|
2018-11-15 04:10:47 -06:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-03-15 09:52:29 -05:00
|
|
|
listItem := dtos.PluginListItem{
|
2016-04-11 11:47:04 -05:00
|
|
|
Id: pluginDef.Id,
|
|
|
|
Name: pluginDef.Name,
|
|
|
|
Type: pluginDef.Type,
|
2019-05-09 04:45:39 -05:00
|
|
|
Category: pluginDef.Category,
|
2016-04-11 11:47:04 -05:00
|
|
|
Info: &pluginDef.Info,
|
|
|
|
LatestVersion: pluginDef.GrafanaNetVersion,
|
|
|
|
HasUpdate: pluginDef.GrafanaNetHasUpdate,
|
2016-05-03 12:00:42 -05:00
|
|
|
DefaultNavUrl: pluginDef.DefaultNavUrl,
|
2017-04-07 05:00:03 -05:00
|
|
|
State: pluginDef.State,
|
2020-04-09 02:00:16 -05:00
|
|
|
Signature: pluginDef.Signature,
|
2020-12-11 05:57:57 -06:00
|
|
|
SignatureType: pluginDef.SignatureType,
|
|
|
|
SignatureOrg: pluginDef.SignatureOrg,
|
2016-02-25 07:55:31 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if pluginSetting, exists := pluginSettingsMap[pluginDef.Id]; exists {
|
|
|
|
listItem.Enabled = pluginSetting.Enabled
|
|
|
|
listItem.Pinned = pluginSetting.Pinned
|
|
|
|
}
|
|
|
|
|
2016-05-03 12:00:42 -05:00
|
|
|
if listItem.DefaultNavUrl == "" || !listItem.Enabled {
|
2021-03-10 05:41:29 -06:00
|
|
|
listItem.DefaultNavUrl = hs.Cfg.AppSubURL + "/plugins/" + listItem.Id + "/"
|
2016-05-03 12:00:42 -05:00
|
|
|
}
|
|
|
|
|
2020-10-23 09:45:43 -05:00
|
|
|
// filter out disabled plugins
|
2016-03-08 11:17:47 -06:00
|
|
|
if enabledFilter == "1" && !listItem.Enabled {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2016-03-14 06:00:56 -05:00
|
|
|
// filter out built in data sources
|
2021-03-17 10:06:10 -05:00
|
|
|
if ds := hs.PluginManager.GetDataSource(pluginDef.Id); ds != nil {
|
2016-03-14 06:00:56 -05:00
|
|
|
if ds.BuiltIn {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-25 07:55:31 -06:00
|
|
|
result = append(result, listItem)
|
|
|
|
}
|
|
|
|
|
2016-03-15 09:52:29 -05:00
|
|
|
sort.Sort(result)
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.JSON(200, result)
|
2016-02-25 07:55:31 -06:00
|
|
|
}
|
|
|
|
|
2021-03-17 10:06:10 -05:00
|
|
|
func (hs *HTTPServer) GetPluginSettingByID(c *models.ReqContext) response.Response {
|
2021-10-11 07:30:59 -05:00
|
|
|
pluginID := web.Params(c.Req)[":pluginId"]
|
2016-02-25 07:55:31 -06:00
|
|
|
|
2021-03-17 10:06:10 -05:00
|
|
|
def := hs.PluginManager.GetPlugin(pluginID)
|
|
|
|
if def == nil {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(404, "Plugin not found, no installed plugin with that id", nil)
|
2018-03-22 06:37:35 -05:00
|
|
|
}
|
2016-03-08 11:17:47 -06:00
|
|
|
|
2018-03-22 06:37:35 -05:00
|
|
|
dto := &dtos.PluginSetting{
|
|
|
|
Type: def.Type,
|
|
|
|
Id: def.Id,
|
|
|
|
Name: def.Name,
|
|
|
|
Info: &def.Info,
|
|
|
|
Dependencies: &def.Dependencies,
|
|
|
|
Includes: def.Includes,
|
|
|
|
BaseUrl: def.BaseUrl,
|
|
|
|
Module: def.Module,
|
|
|
|
DefaultNavUrl: def.DefaultNavUrl,
|
|
|
|
LatestVersion: def.GrafanaNetVersion,
|
|
|
|
HasUpdate: def.GrafanaNetHasUpdate,
|
|
|
|
State: def.State,
|
2020-04-09 02:00:16 -05:00
|
|
|
Signature: def.Signature,
|
2020-12-11 05:57:57 -06:00
|
|
|
SignatureType: def.SignatureType,
|
|
|
|
SignatureOrg: def.SignatureOrg,
|
2018-03-22 06:37:35 -05:00
|
|
|
}
|
2016-02-25 08:56:28 -06:00
|
|
|
|
2021-03-18 07:53:01 -05:00
|
|
|
if app := hs.PluginManager.GetApp(def.Id); app != nil {
|
2021-02-25 03:00:21 -06:00
|
|
|
dto.Enabled = app.AutoEnabled
|
|
|
|
dto.Pinned = app.AutoEnabled
|
|
|
|
}
|
|
|
|
|
2020-02-19 12:17:05 -06:00
|
|
|
query := models.GetPluginSettingByIdQuery{PluginId: pluginID, OrgId: c.OrgId}
|
2018-03-22 06:37:35 -05:00
|
|
|
if err := bus.Dispatch(&query); err != nil {
|
2020-11-19 06:34:28 -06:00
|
|
|
if !errors.Is(err, models.ErrPluginSettingNotFound) {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(500, "Failed to get login settings", nil)
|
2016-02-25 07:55:31 -06:00
|
|
|
}
|
2018-03-22 06:37:35 -05:00
|
|
|
} else {
|
|
|
|
dto.Enabled = query.Result.Enabled
|
|
|
|
dto.Pinned = query.Result.Pinned
|
|
|
|
dto.JsonData = query.Result.JsonData
|
2016-02-25 07:55:31 -06:00
|
|
|
}
|
2018-03-22 06:37:35 -05:00
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.JSON(200, dto)
|
2016-02-25 07:55:31 -06:00
|
|
|
}
|
|
|
|
|
2021-03-18 07:53:01 -05:00
|
|
|
func (hs *HTTPServer) UpdatePluginSetting(c *models.ReqContext, cmd models.UpdatePluginSettingCmd) response.Response {
|
2021-10-11 07:30:59 -05:00
|
|
|
pluginID := web.Params(c.Req)[":pluginId"]
|
2016-02-25 07:55:31 -06:00
|
|
|
|
2021-03-18 07:53:01 -05:00
|
|
|
if app := hs.PluginManager.GetApp(pluginID); app == nil {
|
|
|
|
return response.Error(404, "Plugin not installed", nil)
|
2016-02-25 07:55:31 -06:00
|
|
|
}
|
|
|
|
|
2021-03-18 07:53:01 -05:00
|
|
|
cmd.OrgId = c.OrgId
|
|
|
|
cmd.PluginId = pluginID
|
2016-02-25 07:55:31 -06:00
|
|
|
if err := bus.Dispatch(&cmd); err != nil {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(500, "Failed to update plugin setting", err)
|
2016-02-25 07:55:31 -06:00
|
|
|
}
|
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Success("Plugin settings updated")
|
2016-02-25 07:55:31 -06:00
|
|
|
}
|
2016-03-11 02:57:20 -06:00
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
func (hs *HTTPServer) GetPluginDashboards(c *models.ReqContext) response.Response {
|
2021-10-11 07:30:59 -05:00
|
|
|
pluginID := web.Params(c.Req)[":pluginId"]
|
2016-03-11 02:57:20 -06:00
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
list, err := hs.PluginManager.GetPluginDashboards(c.OrgId, pluginID)
|
2018-03-22 06:37:35 -05:00
|
|
|
if err != nil {
|
2020-11-19 06:34:28 -06:00
|
|
|
var notFound plugins.PluginNotFoundError
|
|
|
|
if errors.As(err, ¬Found) {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(404, notFound.Error(), nil)
|
2016-03-11 02:57:20 -06:00
|
|
|
}
|
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(500, "Failed to get plugin dashboards", err)
|
2016-03-11 02:57:20 -06:00
|
|
|
}
|
2018-03-22 06:37:35 -05:00
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.JSON(200, list)
|
2016-03-11 02:57:20 -06:00
|
|
|
}
|
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
func (hs *HTTPServer) GetPluginMarkdown(c *models.ReqContext) response.Response {
|
2021-10-11 07:30:59 -05:00
|
|
|
pluginID := web.Params(c.Req)[":pluginId"]
|
|
|
|
name := web.Params(c.Req)[":name"]
|
2016-03-13 13:21:44 -05:00
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
content, err := hs.PluginManager.GetPluginMarkdown(pluginID, name)
|
2018-03-22 06:37:35 -05:00
|
|
|
if err != nil {
|
2020-11-19 06:34:28 -06:00
|
|
|
var notFound plugins.PluginNotFoundError
|
|
|
|
if errors.As(err, ¬Found) {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(404, notFound.Error(), nil)
|
2016-03-13 13:21:44 -05:00
|
|
|
}
|
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(500, "Could not get markdown file", err)
|
2016-03-13 13:21:44 -05:00
|
|
|
}
|
2018-03-22 06:37:35 -05:00
|
|
|
|
2018-12-19 03:53:14 -06:00
|
|
|
// fallback try readme
|
|
|
|
if len(content) == 0 {
|
2021-03-08 00:02:49 -06:00
|
|
|
content, err = hs.PluginManager.GetPluginMarkdown(pluginID, "readme")
|
2018-12-19 03:53:14 -06:00
|
|
|
if err != nil {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(501, "Could not get markdown file", err)
|
2018-12-19 03:53:14 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
resp := response.Respond(200, content)
|
2021-03-17 12:12:28 -05:00
|
|
|
resp.SetHeader("Content-Type", "text/plain; charset=utf-8")
|
2018-03-22 06:37:35 -05:00
|
|
|
return resp
|
2016-03-13 13:21:44 -05:00
|
|
|
}
|
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
func (hs *HTTPServer) ImportDashboard(c *models.ReqContext, apiCmd dtos.ImportDashboardCommand) response.Response {
|
2021-05-04 02:03:42 -05:00
|
|
|
var err error
|
2020-01-15 05:10:02 -06:00
|
|
|
if apiCmd.PluginId == "" && apiCmd.Dashboard == nil {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(422, "Dashboard must be set", nil)
|
2020-01-15 05:10:02 -06:00
|
|
|
}
|
|
|
|
|
2021-05-04 02:03:42 -05:00
|
|
|
trimDefaults := c.QueryBoolWithDefault("trimdefaults", true)
|
2021-05-28 15:29:30 -05:00
|
|
|
if trimDefaults && !hs.LoadSchemaService.IsDisabled() {
|
2021-05-04 02:03:42 -05:00
|
|
|
apiCmd.Dashboard, err = hs.LoadSchemaService.DashboardApplyDefaults(apiCmd.Dashboard)
|
|
|
|
if err != nil {
|
|
|
|
return response.Error(500, "Error while applying default value to the dashboard json", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-24 02:31:34 -05:00
|
|
|
dashInfo, dash, err := hs.PluginManager.ImportDashboard(apiCmd.PluginId, apiCmd.Path, c.OrgId, apiCmd.FolderId,
|
2021-03-08 00:02:49 -06:00
|
|
|
apiCmd.Dashboard, apiCmd.Overwrite, apiCmd.Inputs, c.SignedInUser, hs.DataService)
|
|
|
|
if err != nil {
|
2021-03-17 10:06:10 -05:00
|
|
|
return hs.dashboardSaveErrorToApiResponse(err)
|
2016-03-11 02:57:20 -06:00
|
|
|
}
|
|
|
|
|
2021-09-27 02:04:36 -05:00
|
|
|
err = hs.LibraryPanelService.ImportLibraryPanelsForDashboard(c.Req.Context(), c.SignedInUser, dash, apiCmd.FolderId)
|
2021-09-20 03:58:24 -05:00
|
|
|
if err != nil {
|
|
|
|
return response.Error(500, "Error while importing library panels", err)
|
|
|
|
}
|
|
|
|
|
2021-09-27 02:04:36 -05:00
|
|
|
err = hs.LibraryPanelService.ConnectLibraryPanelsForDashboard(c.Req.Context(), c.SignedInUser, dash)
|
2021-05-20 02:40:23 -05:00
|
|
|
if err != nil {
|
|
|
|
return response.Error(500, "Error while connecting library panels", err)
|
|
|
|
}
|
|
|
|
|
2021-03-08 00:02:49 -06:00
|
|
|
return response.JSON(200, dashInfo)
|
2016-03-11 02:57:20 -06:00
|
|
|
}
|
2020-01-31 04:15:50 -06:00
|
|
|
|
2020-03-18 06:08:20 -05:00
|
|
|
// CollectPluginMetrics collect metrics from a plugin.
|
|
|
|
//
|
|
|
|
// /api/plugins/:pluginId/metrics
|
2021-01-15 07:43:20 -06:00
|
|
|
func (hs *HTTPServer) CollectPluginMetrics(c *models.ReqContext) response.Response {
|
2021-10-11 07:30:59 -05:00
|
|
|
pluginID := web.Params(c.Req)[":pluginId"]
|
2021-03-17 10:06:10 -05:00
|
|
|
plugin := hs.PluginManager.GetPlugin(pluginID)
|
|
|
|
if plugin == nil {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(404, "Plugin not found", nil)
|
2020-03-18 06:08:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := hs.BackendPluginManager.CollectMetrics(c.Req.Context(), plugin.Id)
|
|
|
|
if err != nil {
|
2020-06-11 09:14:05 -05:00
|
|
|
return translatePluginRequestErrorToAPIError(err)
|
2020-03-18 06:08:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
headers := make(http.Header)
|
|
|
|
headers.Set("Content-Type", "text/plain")
|
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.CreateNormalResponse(headers, resp.PrometheusMetrics, http.StatusOK)
|
2020-03-18 06:08:20 -05:00
|
|
|
}
|
|
|
|
|
2021-09-08 01:49:05 -05:00
|
|
|
// getPluginAssets returns public plugin assets (images, JS, etc.)
|
2021-04-21 08:17:23 -05:00
|
|
|
//
|
|
|
|
// /public/plugins/:pluginId/*
|
2021-09-08 01:49:05 -05:00
|
|
|
func (hs *HTTPServer) getPluginAssets(c *models.ReqContext) {
|
2021-10-11 07:30:59 -05:00
|
|
|
pluginID := web.Params(c.Req)[":pluginId"]
|
2021-04-21 08:17:23 -05:00
|
|
|
plugin := hs.PluginManager.GetPlugin(pluginID)
|
|
|
|
if plugin == nil {
|
2021-07-15 01:56:11 -05:00
|
|
|
c.JsonApiErr(404, "Plugin not found", nil)
|
2021-07-14 02:38:49 -05:00
|
|
|
return
|
2021-04-21 08:17:23 -05:00
|
|
|
}
|
|
|
|
|
2021-10-11 07:30:59 -05:00
|
|
|
requestedFile := filepath.Clean(web.Params(c.Req)["*"])
|
2021-04-21 08:17:23 -05:00
|
|
|
pluginFilePath := filepath.Join(plugin.PluginDir, requestedFile)
|
|
|
|
|
2021-09-08 01:49:05 -05:00
|
|
|
if !plugin.IncludedInSignature(requestedFile) {
|
|
|
|
hs.log.Warn("Access to requested plugin file will be forbidden in upcoming Grafana versions as the file "+
|
|
|
|
"is not included in the plugin signature", "file", requestedFile)
|
|
|
|
}
|
|
|
|
|
2021-04-21 08:17:23 -05:00
|
|
|
// It's safe to ignore gosec warning G304 since we already clean the requested file path and subsequently
|
|
|
|
// use this with a prefix of the plugin's directory, which is set during plugin loading
|
|
|
|
// nolint:gosec
|
|
|
|
f, err := os.Open(pluginFilePath)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
2021-07-15 01:56:11 -05:00
|
|
|
c.JsonApiErr(404, "Plugin file not found", err)
|
2021-07-14 02:38:49 -05:00
|
|
|
return
|
2021-04-21 08:17:23 -05:00
|
|
|
}
|
2021-07-15 01:56:11 -05:00
|
|
|
c.JsonApiErr(500, "Could not open plugin file", err)
|
2021-07-14 02:38:49 -05:00
|
|
|
return
|
2021-04-21 08:17:23 -05:00
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
if err := f.Close(); err != nil {
|
|
|
|
hs.log.Error("Failed to close file", "err", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
fi, err := f.Stat()
|
|
|
|
if err != nil {
|
2021-07-15 01:56:11 -05:00
|
|
|
c.JsonApiErr(500, "Plugin file exists but could not open", err)
|
2021-07-14 02:38:49 -05:00
|
|
|
return
|
2021-04-21 08:17:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if hs.Cfg.Env == setting.Dev {
|
2021-07-14 02:38:49 -05:00
|
|
|
c.Resp.Header().Set("Cache-Control", "max-age=0, must-revalidate, no-cache")
|
|
|
|
} else {
|
|
|
|
c.Resp.Header().Set("Cache-Control", "public, max-age=3600")
|
2021-04-21 08:17:23 -05:00
|
|
|
}
|
|
|
|
|
2021-09-01 04:18:30 -05:00
|
|
|
http.ServeContent(c.Resp, c.Req, pluginFilePath, fi.ModTime(), f)
|
2021-04-21 08:17:23 -05:00
|
|
|
}
|
|
|
|
|
2020-03-06 07:37:36 -06:00
|
|
|
// CheckHealth returns the health of a plugin.
|
2020-01-31 04:15:50 -06:00
|
|
|
// /api/plugins/:pluginId/health
|
2021-01-15 07:43:20 -06:00
|
|
|
func (hs *HTTPServer) CheckHealth(c *models.ReqContext) response.Response {
|
2021-10-11 07:30:59 -05:00
|
|
|
pluginID := web.Params(c.Req)[":pluginId"]
|
2020-03-13 06:31:44 -05:00
|
|
|
|
2021-05-18 13:39:56 -05:00
|
|
|
pCtx, found, err := hs.PluginContextProvider.Get(pluginID, "", c.SignedInUser, false)
|
2020-03-13 06:31:44 -05:00
|
|
|
if err != nil {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(500, "Failed to get plugin settings", err)
|
2020-03-13 06:31:44 -05:00
|
|
|
}
|
2021-03-23 12:24:08 -05:00
|
|
|
if !found {
|
|
|
|
return response.Error(404, "Plugin not found", nil)
|
|
|
|
}
|
2020-03-13 06:31:44 -05:00
|
|
|
|
2020-04-23 13:08:21 -05:00
|
|
|
resp, err := hs.BackendPluginManager.CheckHealth(c.Req.Context(), pCtx)
|
2020-01-31 04:15:50 -06:00
|
|
|
if err != nil {
|
2020-06-11 09:14:05 -05:00
|
|
|
return translatePluginRequestErrorToAPIError(err)
|
2020-01-31 04:15:50 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
payload := map[string]interface{}{
|
2020-04-21 09:16:41 -05:00
|
|
|
"status": resp.Status.String(),
|
|
|
|
"message": resp.Message,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unmarshal JSONDetails if it's not empty.
|
|
|
|
if len(resp.JSONDetails) > 0 {
|
|
|
|
var jsonDetails map[string]interface{}
|
|
|
|
err = json.Unmarshal(resp.JSONDetails, &jsonDetails)
|
|
|
|
if err != nil {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(500, "Failed to unmarshal detailed response from backend plugin", err)
|
2020-04-21 09:16:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
payload["details"] = jsonDetails
|
2020-01-31 04:15:50 -06:00
|
|
|
}
|
|
|
|
|
2020-06-11 09:14:05 -05:00
|
|
|
if resp.Status != backend.HealthStatusOk {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.JSON(503, payload)
|
2020-01-31 04:15:50 -06:00
|
|
|
}
|
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.JSON(200, payload)
|
2020-01-31 04:15:50 -06:00
|
|
|
}
|
|
|
|
|
2020-03-13 06:31:44 -05:00
|
|
|
// CallResource passes a resource call from a plugin to the backend plugin.
|
|
|
|
//
|
2020-01-31 04:15:50 -06:00
|
|
|
// /api/plugins/:pluginId/resources/*
|
2020-03-03 04:45:16 -06:00
|
|
|
func (hs *HTTPServer) CallResource(c *models.ReqContext) {
|
2021-10-11 07:30:59 -05:00
|
|
|
pluginID := web.Params(c.Req)[":pluginId"]
|
2020-02-19 12:17:05 -06:00
|
|
|
|
2021-05-18 13:39:56 -05:00
|
|
|
pCtx, found, err := hs.PluginContextProvider.Get(pluginID, "", c.SignedInUser, false)
|
2020-02-19 12:17:05 -06:00
|
|
|
if err != nil {
|
2020-03-13 06:31:44 -05:00
|
|
|
c.JsonApiErr(500, "Failed to get plugin settings", err)
|
|
|
|
return
|
2020-01-31 04:15:50 -06:00
|
|
|
}
|
2021-03-23 12:24:08 -05:00
|
|
|
if !found {
|
|
|
|
c.JsonApiErr(404, "Plugin not found", nil)
|
|
|
|
return
|
2020-02-19 12:17:05 -06:00
|
|
|
}
|
2021-10-11 07:30:59 -05:00
|
|
|
hs.BackendPluginManager.CallResource(pCtx, c, web.Params(c.Req)["*"])
|
2020-02-19 12:17:05 -06:00
|
|
|
}
|
2020-06-11 09:14:05 -05:00
|
|
|
|
2021-03-23 12:24:08 -05:00
|
|
|
func (hs *HTTPServer) GetPluginErrorsList(_ *models.ReqContext) response.Response {
|
2021-02-10 06:31:47 -06:00
|
|
|
return response.JSON(200, hs.PluginManager.ScanningErrors())
|
2020-10-23 09:45:43 -05:00
|
|
|
}
|
|
|
|
|
2021-05-12 13:05:16 -05:00
|
|
|
func (hs *HTTPServer) InstallPlugin(c *models.ReqContext, dto dtos.InstallPluginCommand) response.Response {
|
2021-10-11 07:30:59 -05:00
|
|
|
pluginID := web.Params(c.Req)[":pluginId"]
|
2021-05-12 13:05:16 -05:00
|
|
|
|
|
|
|
err := hs.PluginManager.Install(c.Req.Context(), pluginID, dto.Version)
|
|
|
|
if err != nil {
|
|
|
|
var dupeErr plugins.DuplicatePluginError
|
|
|
|
if errors.As(err, &dupeErr) {
|
|
|
|
return response.Error(http.StatusConflict, "Plugin already installed", err)
|
|
|
|
}
|
|
|
|
var versionUnsupportedErr installer.ErrVersionUnsupported
|
|
|
|
if errors.As(err, &versionUnsupportedErr) {
|
|
|
|
return response.Error(http.StatusConflict, "Plugin version not supported", err)
|
|
|
|
}
|
|
|
|
var versionNotFoundErr installer.ErrVersionNotFound
|
|
|
|
if errors.As(err, &versionNotFoundErr) {
|
|
|
|
return response.Error(http.StatusNotFound, "Plugin version not found", err)
|
|
|
|
}
|
2021-07-13 02:58:46 -05:00
|
|
|
var clientError installer.Response4xxError
|
|
|
|
if errors.As(err, &clientError) {
|
|
|
|
return response.Error(clientError.StatusCode, clientError.Message, err)
|
2021-05-12 13:05:16 -05:00
|
|
|
}
|
|
|
|
if errors.Is(err, plugins.ErrInstallCorePlugin) {
|
|
|
|
return response.Error(http.StatusForbidden, "Cannot install or change a Core plugin", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return response.Error(http.StatusInternalServerError, "Failed to install plugin", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return response.JSON(http.StatusOK, []byte{})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (hs *HTTPServer) UninstallPlugin(c *models.ReqContext) response.Response {
|
2021-10-11 07:30:59 -05:00
|
|
|
pluginID := web.Params(c.Req)[":pluginId"]
|
2021-05-12 13:05:16 -05:00
|
|
|
|
|
|
|
err := hs.PluginManager.Uninstall(c.Req.Context(), pluginID)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, plugins.ErrPluginNotInstalled) {
|
|
|
|
return response.Error(http.StatusNotFound, "Plugin not installed", err)
|
|
|
|
}
|
|
|
|
if errors.Is(err, plugins.ErrUninstallCorePlugin) {
|
|
|
|
return response.Error(http.StatusForbidden, "Cannot uninstall a Core plugin", err)
|
|
|
|
}
|
|
|
|
if errors.Is(err, plugins.ErrUninstallOutsideOfPluginDir) {
|
|
|
|
return response.Error(http.StatusForbidden, "Cannot uninstall a plugin outside of the plugins directory", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return response.Error(http.StatusInternalServerError, "Failed to uninstall plugin", err)
|
|
|
|
}
|
|
|
|
return response.JSON(http.StatusOK, []byte{})
|
|
|
|
}
|
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
func translatePluginRequestErrorToAPIError(err error) response.Response {
|
2020-06-11 09:14:05 -05:00
|
|
|
if errors.Is(err, backendplugin.ErrPluginNotRegistered) {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(404, "Plugin not found", err)
|
2020-06-11 09:14:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if errors.Is(err, backendplugin.ErrMethodNotImplemented) {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(404, "Not found", err)
|
2020-06-11 09:14:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if errors.Is(err, backendplugin.ErrHealthCheckFailed) {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(500, "Plugin health check failed", err)
|
2020-06-11 09:14:05 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if errors.Is(err, backendplugin.ErrPluginUnavailable) {
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(503, "Plugin unavailable", err)
|
2020-06-11 09:14:05 -05:00
|
|
|
}
|
|
|
|
|
2021-01-15 07:43:20 -06:00
|
|
|
return response.Error(500, "Plugin request failed", err)
|
2020-06-11 09:14:05 -05:00
|
|
|
}
|