grafana/pkg/services/plugindashboards/service/dashboard_updater.go
Will Browne bda3f860a8
Plugins: Add plugin settings DTO (#46283)
* add clearer service layer

* re-order frontend settings for clarity

* fix fetch fail

* fix API response

* fix mockstore

* in -> where
2022-03-18 20:49:13 +01:00

182 lines
6.3 KiB
Go

package service
import (
"context"
"fmt"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/dashboardimport"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/plugindashboards"
"github.com/grafana/grafana/pkg/services/pluginsettings"
)
func ProvideDashboardUpdater(bus bus.Bus, pluginStore plugins.Store, pluginDashboardService plugindashboards.Service,
dashboardImportService dashboardimport.Service, pluginSettingsService pluginsettings.Service,
dashboardPluginService dashboards.PluginService, dashboardService dashboards.DashboardService) *DashboardUpdater {
du := newDashboardUpdater(bus, pluginStore, pluginDashboardService, dashboardImportService,
pluginSettingsService, dashboardPluginService, dashboardService)
du.updateAppDashboards()
return du
}
func newDashboardUpdater(bus bus.Bus, pluginStore plugins.Store,
pluginDashboardService plugindashboards.Service, dashboardImportService dashboardimport.Service,
pluginSettingsService pluginsettings.Service, dashboardPluginService dashboards.PluginService,
dashboardService dashboards.DashboardService) *DashboardUpdater {
s := &DashboardUpdater{
pluginStore: pluginStore,
pluginDashboardService: pluginDashboardService,
dashboardImportService: dashboardImportService,
pluginSettingsService: pluginSettingsService,
dashboardPluginService: dashboardPluginService,
dashboardService: dashboardService,
logger: log.New("plugindashboards"),
}
bus.AddEventListener(s.handlePluginStateChanged)
return s
}
type DashboardUpdater struct {
pluginStore plugins.Store
pluginDashboardService plugindashboards.Service
dashboardImportService dashboardimport.Service
pluginSettingsService pluginsettings.Service
dashboardPluginService dashboards.PluginService
dashboardService dashboards.DashboardService
logger log.Logger
}
func (du *DashboardUpdater) updateAppDashboards() {
du.logger.Debug("Looking for app dashboard updates")
pluginSettings, err := du.pluginSettingsService.GetPluginSettings(context.Background(), &pluginsettings.GetArgs{OrgID: 0})
if err != nil {
du.logger.Error("Failed to get all plugin settings", "error", err)
return
}
for _, pluginSetting := range pluginSettings {
// ignore disabled plugins
if !pluginSetting.Enabled {
continue
}
if pluginDef, exists := du.pluginStore.Plugin(context.Background(), pluginSetting.PluginID); exists {
if pluginDef.Info.Version != pluginSetting.PluginVersion {
du.syncPluginDashboards(context.Background(), pluginDef, pluginSetting.OrgID)
}
}
}
}
func (du *DashboardUpdater) syncPluginDashboards(ctx context.Context, plugin plugins.PluginDTO, orgID int64) {
du.logger.Info("Syncing plugin dashboards to DB", "pluginId", plugin.ID)
// Get plugin dashboards
req := &plugindashboards.ListPluginDashboardsRequest{
OrgID: orgID,
PluginID: plugin.ID,
}
resp, err := du.pluginDashboardService.ListPluginDashboards(ctx, req)
if err != nil {
du.logger.Error("Failed to load app dashboards", "error", err)
return
}
// Update dashboards with updated revisions
for _, dash := range resp.Items {
// remove removed ones
if dash.Removed {
du.logger.Info("Deleting plugin dashboard", "pluginId", plugin.ID, "dashboard", dash.Slug)
if err := du.dashboardService.DeleteDashboard(ctx, dash.DashboardId, orgID); err != nil {
du.logger.Error("Failed to auto update app dashboard", "pluginId", plugin.ID, "error", err)
return
}
continue
}
// update updated ones
if dash.ImportedRevision != dash.Revision {
if err := du.autoUpdateAppDashboard(ctx, dash, orgID); err != nil {
du.logger.Error("Failed to auto update app dashboard", "pluginId", plugin.ID, "error", err)
return
}
}
}
// update version in plugin_setting table to mark that we have processed the update
query := pluginsettings.GetByPluginIDArgs{PluginID: plugin.ID, OrgID: orgID}
ps, err := du.pluginSettingsService.GetPluginSettingByPluginID(ctx, &query)
if err != nil {
du.logger.Error("Failed to read plugin setting by ID", "error", err)
return
}
cmd := pluginsettings.UpdatePluginVersionArgs{
OrgID: ps.OrgID,
PluginID: ps.PluginID,
PluginVersion: plugin.Info.Version,
}
if err := du.pluginSettingsService.UpdatePluginSettingPluginVersion(ctx, &cmd); err != nil {
du.logger.Error("Failed to update plugin setting version", "error", err)
}
}
func (du *DashboardUpdater) handlePluginStateChanged(ctx context.Context, event *models.PluginStateChangedEvent) error {
du.logger.Info("Plugin state changed", "pluginId", event.PluginId, "enabled", event.Enabled)
if event.Enabled {
p, exists := du.pluginStore.Plugin(ctx, event.PluginId)
if !exists {
return fmt.Errorf("plugin %s not found. Could not sync plugin dashboards", event.PluginId)
}
du.syncPluginDashboards(ctx, p, event.OrgId)
} else {
query := models.GetDashboardsByPluginIdQuery{PluginId: event.PluginId, OrgId: event.OrgId}
if err := du.dashboardPluginService.GetDashboardsByPluginID(ctx, &query); err != nil {
return err
}
for _, dash := range query.Result {
du.logger.Info("Deleting plugin dashboard", "pluginId", event.PluginId, "dashboard", dash.Slug)
if err := du.dashboardService.DeleteDashboard(ctx, dash.Id, dash.OrgId); err != nil {
return err
}
}
}
return nil
}
func (du *DashboardUpdater) autoUpdateAppDashboard(ctx context.Context, pluginDashInfo *plugindashboards.PluginDashboard, orgID int64) error {
req := &plugindashboards.LoadPluginDashboardRequest{
PluginID: pluginDashInfo.PluginId,
Reference: pluginDashInfo.Reference,
}
resp, err := du.pluginDashboardService.LoadPluginDashboard(ctx, req)
if err != nil {
return err
}
du.logger.Info("Auto updating App dashboard", "dashboard", resp.Dashboard.Title, "newRev",
pluginDashInfo.Revision, "oldRev", pluginDashInfo.ImportedRevision)
_, err = du.dashboardImportService.ImportDashboard(ctx, &dashboardimport.ImportDashboardRequest{
PluginId: pluginDashInfo.PluginId,
User: &models.SignedInUser{UserId: 0, OrgRole: models.ROLE_ADMIN, OrgId: orgID},
Path: pluginDashInfo.Reference,
FolderId: 0,
Dashboard: resp.Dashboard.Data,
Overwrite: true,
Inputs: nil,
})
return err
}