mirror of https://github.com/grafana/grafana.git
Advisor: Check plugin signature (#106044)
This commit is contained in:
parent
27ab895eef
commit
e2e8de29ba
|
|
@ -28,6 +28,7 @@ type Service struct {
|
|||
pluginContextProvider *plugincontext.Provider
|
||||
pluginClient plugins.Client
|
||||
pluginRepo repo.Service
|
||||
pluginErrorResolver plugins.ErrorResolver
|
||||
updateChecker pluginchecker.PluginUpdateChecker
|
||||
pluginPreinstall pluginchecker.Preinstall
|
||||
managedPlugins managedplugins.Manager
|
||||
|
|
@ -42,6 +43,7 @@ func ProvideService(datasourceSvc datasources.DataSourceService, pluginStore plu
|
|||
updateChecker pluginchecker.PluginUpdateChecker,
|
||||
pluginRepo repo.Service, pluginPreinstall pluginchecker.Preinstall, managedPlugins managedplugins.Manager,
|
||||
provisionedPlugins provisionedplugins.Manager, ssoSettingsSvc ssosettings.Service, cfg *setting.Cfg,
|
||||
pluginErrorResolver plugins.ErrorResolver,
|
||||
) *Service {
|
||||
return &Service{
|
||||
datasourceSvc: datasourceSvc,
|
||||
|
|
@ -49,6 +51,7 @@ func ProvideService(datasourceSvc datasources.DataSourceService, pluginStore plu
|
|||
pluginContextProvider: pluginContextProvider,
|
||||
pluginClient: pluginClient,
|
||||
pluginRepo: pluginRepo,
|
||||
pluginErrorResolver: pluginErrorResolver,
|
||||
updateChecker: updateChecker,
|
||||
pluginPreinstall: pluginPreinstall,
|
||||
managedPlugins: managedPlugins,
|
||||
|
|
@ -73,6 +76,7 @@ func (s *Service) Checks() []checks.Check {
|
|||
s.pluginStore,
|
||||
s.pluginRepo,
|
||||
s.updateChecker,
|
||||
s.pluginErrorResolver,
|
||||
s.GrafanaVersion,
|
||||
),
|
||||
authchecks.New(s.ssoSettingsSvc),
|
||||
|
|
|
|||
|
|
@ -2,43 +2,42 @@ package plugincheck
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
sysruntime "runtime"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
)
|
||||
|
||||
const (
|
||||
CheckID = "plugin"
|
||||
DeprecationStepID = "deprecation"
|
||||
UpdateStepID = "update"
|
||||
CheckID = "plugin"
|
||||
)
|
||||
|
||||
func New(
|
||||
pluginStore pluginstore.Store,
|
||||
pluginRepo repo.Service,
|
||||
updateChecker pluginchecker.PluginUpdateChecker,
|
||||
pluginErrorResolver plugins.ErrorResolver,
|
||||
grafanaVersion string,
|
||||
) checks.Check {
|
||||
return &check{
|
||||
PluginStore: pluginStore,
|
||||
PluginRepo: pluginRepo,
|
||||
GrafanaVersion: grafanaVersion,
|
||||
updateChecker: updateChecker,
|
||||
PluginStore: pluginStore,
|
||||
PluginRepo: pluginRepo,
|
||||
GrafanaVersion: grafanaVersion,
|
||||
updateChecker: updateChecker,
|
||||
pluginErrorResolver: pluginErrorResolver,
|
||||
}
|
||||
}
|
||||
|
||||
type check struct {
|
||||
PluginStore pluginstore.Store
|
||||
PluginRepo repo.Service
|
||||
updateChecker pluginchecker.PluginUpdateChecker
|
||||
GrafanaVersion string
|
||||
pluginIndex map[string]repo.PluginInfo
|
||||
PluginStore pluginstore.Store
|
||||
PluginRepo repo.Service
|
||||
updateChecker pluginchecker.PluginUpdateChecker
|
||||
pluginErrorResolver plugins.ErrorResolver
|
||||
GrafanaVersion string
|
||||
pluginIndex map[string]repo.PluginInfo
|
||||
}
|
||||
|
||||
func (c *check) ID() string {
|
||||
|
|
@ -49,12 +48,40 @@ func (c *check) Name() string {
|
|||
return "plugin"
|
||||
}
|
||||
|
||||
type pluginItem struct {
|
||||
Plugin *pluginstore.Plugin
|
||||
Err *plugins.Error
|
||||
}
|
||||
|
||||
func (c *check) Items(ctx context.Context) ([]any, error) {
|
||||
ps := c.PluginStore.Plugins(ctx)
|
||||
res := make([]any, len(ps))
|
||||
for i, p := range ps {
|
||||
res[i] = p
|
||||
resMap := map[string]*pluginItem{}
|
||||
for _, p := range ps {
|
||||
resMap[p.ID] = &pluginItem{
|
||||
Plugin: &p,
|
||||
Err: c.pluginErrorResolver.PluginError(ctx, p.ID),
|
||||
}
|
||||
}
|
||||
|
||||
// Plugins with errors are not added to the plugin store but
|
||||
// we still want to show them in the check results so we add them to the map
|
||||
pluginErrors := c.pluginErrorResolver.PluginErrors(ctx)
|
||||
for _, e := range pluginErrors {
|
||||
if _, exists := resMap[e.PluginID]; exists {
|
||||
resMap[e.PluginID].Err = e
|
||||
} else {
|
||||
resMap[e.PluginID] = &pluginItem{
|
||||
Plugin: nil,
|
||||
Err: e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res := make([]any, 0, len(resMap))
|
||||
for _, p := range resMap {
|
||||
res = append(res, p)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +90,10 @@ func (c *check) Item(ctx context.Context, id string) (any, error) {
|
|||
if !exists {
|
||||
return nil, nil
|
||||
}
|
||||
return p, nil
|
||||
return &pluginItem{
|
||||
Plugin: &p,
|
||||
Err: c.pluginErrorResolver.PluginError(ctx, p.ID),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *check) Init(ctx context.Context) error {
|
||||
|
|
@ -99,117 +129,8 @@ func (c *check) Steps() []checks.Step {
|
|||
updateChecker: c.updateChecker,
|
||||
pluginIndex: c.pluginIndex,
|
||||
},
|
||||
&unsignedStep{
|
||||
pluginIndex: c.pluginIndex,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type deprecationStep struct {
|
||||
GrafanaVersion string
|
||||
updateChecker pluginchecker.PluginUpdateChecker
|
||||
pluginIndex map[string]repo.PluginInfo
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Title() string {
|
||||
return "Deprecation check"
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Description() string {
|
||||
return "Check if any installed plugins are deprecated."
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Resolution() string {
|
||||
return "Check the <a href='https://grafana.com/legal/plugin-deprecation/#a-plugin-i-use-is-deprecated-what-should-i-do'" +
|
||||
"target=_blank>documentation</a> for recommended steps or delete the plugin."
|
||||
}
|
||||
|
||||
func (s *deprecationStep) ID() string {
|
||||
return DeprecationStepID
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) {
|
||||
p, ok := it.(pluginstore.Plugin)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
|
||||
if !s.updateChecker.IsUpdatable(ctx, p) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check if plugin is deprecated
|
||||
i, ok := s.pluginIndex[p.ID]
|
||||
if !ok {
|
||||
// Unable to check deprecation status
|
||||
return nil, nil
|
||||
}
|
||||
if i.Status == "deprecated" {
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
p.Name,
|
||||
p.ID,
|
||||
[]advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "View plugin",
|
||||
Url: fmt.Sprintf("/plugins/%s", p.ID),
|
||||
},
|
||||
},
|
||||
)}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type updateStep struct {
|
||||
GrafanaVersion string
|
||||
updateChecker pluginchecker.PluginUpdateChecker
|
||||
pluginIndex map[string]repo.PluginInfo
|
||||
}
|
||||
|
||||
func (s *updateStep) Title() string {
|
||||
return "Update check"
|
||||
}
|
||||
|
||||
func (s *updateStep) Description() string {
|
||||
return "Checks if an installed plugins has a newer version available."
|
||||
}
|
||||
|
||||
func (s *updateStep) Resolution() string {
|
||||
return "Go to the plugin admin page and upgrade to the latest version."
|
||||
}
|
||||
|
||||
func (s *updateStep) ID() string {
|
||||
return UpdateStepID
|
||||
}
|
||||
|
||||
func (s *updateStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, i any) ([]advisor.CheckReportFailure, error) {
|
||||
p, ok := i.(pluginstore.Plugin)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", i)
|
||||
}
|
||||
|
||||
if !s.updateChecker.IsUpdatable(ctx, p) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check if plugin has a newer version available
|
||||
info, ok := s.pluginIndex[p.ID]
|
||||
if !ok {
|
||||
// Unable to check updates
|
||||
return nil, nil
|
||||
}
|
||||
if s.updateChecker.CanUpdate(p.ID, p.Info.Version, info.Version, false) {
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityLow,
|
||||
s.ID(),
|
||||
p.Name,
|
||||
p.ID,
|
||||
[]advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "Upgrade",
|
||||
Url: fmt.Sprintf("/plugins/%s?page=version-history", p.ID),
|
||||
},
|
||||
},
|
||||
)}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ func TestRun(t *testing.T) {
|
|||
pluginPreinstalled []string
|
||||
pluginManaged []string
|
||||
pluginProvisioned []string
|
||||
pluginErrors []*plugins.Error
|
||||
expectedFailures []advisor.CheckReportFailure
|
||||
}{
|
||||
{
|
||||
|
|
@ -119,6 +120,69 @@ func TestRun(t *testing.T) {
|
|||
pluginProvisioned: []string{"plugin5"},
|
||||
expectedFailures: []advisor.CheckReportFailure{},
|
||||
},
|
||||
{
|
||||
name: "Invalid signatures",
|
||||
plugins: []pluginstore.Plugin{
|
||||
{JSONData: plugins.JSONData{ID: "plugin6", Name: "Plugin 6", Info: plugins.Info{Version: "1.0.0"}}, Signature: plugins.SignatureStatusInvalid},
|
||||
{JSONData: plugins.JSONData{ID: "plugin7", Name: "Plugin 7", Info: plugins.Info{Version: "1.0.0"}}, Signature: plugins.SignatureStatusModified},
|
||||
{JSONData: plugins.JSONData{ID: "plugin8", Name: "Plugin 8", Info: plugins.Info{Version: "1.0.0"}}, Signature: plugins.SignatureStatusUnsigned},
|
||||
},
|
||||
pluginInfo: []repo.PluginInfo{
|
||||
{Status: "active", Slug: "plugin6", Version: "1.0.0"},
|
||||
{Status: "active", Slug: "plugin7", Version: "1.0.0"},
|
||||
{Status: "active", Slug: "plugin8", Version: "1.0.0"},
|
||||
},
|
||||
pluginErrors: []*plugins.Error{
|
||||
{PluginID: "plugin9", ErrorCode: plugins.ErrorCodeSignatureInvalid},
|
||||
{PluginID: "plugin10", ErrorCode: plugins.ErrorCodeSignatureModified},
|
||||
{PluginID: "plugin11", ErrorCode: plugins.ErrorCodeSignatureMissing},
|
||||
{PluginID: "plugin12", ErrorCode: plugins.ErrorCodeFailedBackendStart}, // This should be ignored atm
|
||||
},
|
||||
expectedFailures: []advisor.CheckReportFailure{
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityLow,
|
||||
StepID: UnsignedStepID,
|
||||
Item: "Plugin 6",
|
||||
ItemID: "plugin6",
|
||||
Links: []advisor.CheckErrorLink{{Url: "/plugins/plugin6", Message: "View plugin"}},
|
||||
},
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityLow,
|
||||
StepID: UnsignedStepID,
|
||||
Item: "Plugin 7",
|
||||
ItemID: "plugin7",
|
||||
Links: []advisor.CheckErrorLink{{Url: "/plugins/plugin7", Message: "View plugin"}},
|
||||
},
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityLow,
|
||||
StepID: UnsignedStepID,
|
||||
Item: "Plugin 8",
|
||||
ItemID: "plugin8",
|
||||
Links: []advisor.CheckErrorLink{{Url: "/plugins/plugin8", Message: "View plugin"}},
|
||||
},
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityHigh,
|
||||
StepID: UnsignedStepID,
|
||||
Item: "plugin9",
|
||||
ItemID: "plugin9",
|
||||
Links: []advisor.CheckErrorLink{},
|
||||
},
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityHigh,
|
||||
StepID: UnsignedStepID,
|
||||
Item: "plugin10",
|
||||
ItemID: "plugin10",
|
||||
Links: []advisor.CheckErrorLink{},
|
||||
},
|
||||
{
|
||||
Severity: advisor.CheckReportFailureSeverityHigh,
|
||||
StepID: UnsignedStepID,
|
||||
Item: "plugin11",
|
||||
ItemID: "plugin11",
|
||||
Links: []advisor.CheckErrorLink{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
@ -131,7 +195,8 @@ func TestRun(t *testing.T) {
|
|||
managedPlugins := &mockManagedPlugins{managed: tt.pluginManaged}
|
||||
provisionedPlugins := &mockProvisionedPlugins{provisioned: tt.pluginProvisioned}
|
||||
updateChecker := pluginchecker.ProvideService(managedPlugins, provisionedPlugins, pluginPreinstall)
|
||||
check := New(pluginStore, pluginRepo, updateChecker, "12.0.0")
|
||||
pluginErrorResolver := &mockPluginErrorResolver{pluginErrors: tt.pluginErrors}
|
||||
check := New(pluginStore, pluginRepo, updateChecker, pluginErrorResolver, "12.0.0")
|
||||
|
||||
items, err := check.Items(context.Background())
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -148,8 +213,8 @@ func TestRun(t *testing.T) {
|
|||
}
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, len(tt.plugins), len(items))
|
||||
assert.Equal(t, tt.expectedFailures, failures)
|
||||
assert.Equal(t, len(tt.plugins)+len(tt.pluginErrors), len(items))
|
||||
assert.ElementsMatch(t, tt.expectedFailures, failures)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -222,3 +287,21 @@ type mockProvisionedPlugins struct {
|
|||
func (m *mockProvisionedPlugins) ProvisionedPlugins(ctx context.Context) ([]string, error) {
|
||||
return m.provisioned, nil
|
||||
}
|
||||
|
||||
type mockPluginErrorResolver struct {
|
||||
plugins.ErrorResolver
|
||||
pluginErrors []*plugins.Error
|
||||
}
|
||||
|
||||
func (m *mockPluginErrorResolver) PluginErrors(ctx context.Context) []*plugins.Error {
|
||||
return m.pluginErrors
|
||||
}
|
||||
|
||||
func (m *mockPluginErrorResolver) PluginError(ctx context.Context, id string) *plugins.Error {
|
||||
for _, err := range m.pluginErrors {
|
||||
if err.PluginID == id {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
package plugincheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker"
|
||||
)
|
||||
|
||||
const (
|
||||
DeprecationStepID = "deprecation"
|
||||
)
|
||||
|
||||
type deprecationStep struct {
|
||||
GrafanaVersion string
|
||||
updateChecker pluginchecker.PluginUpdateChecker
|
||||
pluginIndex map[string]repo.PluginInfo
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Title() string {
|
||||
return "Deprecation check"
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Description() string {
|
||||
return "Check if any installed plugins are deprecated."
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Resolution() string {
|
||||
return "Check the <a href='https://grafana.com/legal/plugin-deprecation/#a-plugin-i-use-is-deprecated-what-should-i-do'" +
|
||||
"target=_blank>documentation</a> for recommended steps or delete the plugin."
|
||||
}
|
||||
|
||||
func (s *deprecationStep) ID() string {
|
||||
return DeprecationStepID
|
||||
}
|
||||
|
||||
func (s *deprecationStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) {
|
||||
pi, ok := it.(*pluginItem)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
p := pi.Plugin
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !s.updateChecker.IsUpdatable(ctx, *p) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check if plugin is deprecated
|
||||
i, ok := s.pluginIndex[p.ID]
|
||||
if !ok {
|
||||
// Unable to check deprecation status
|
||||
return nil, nil
|
||||
}
|
||||
if i.Status == "deprecated" {
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
p.Name,
|
||||
p.ID,
|
||||
[]advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "View plugin",
|
||||
Url: fmt.Sprintf("/plugins/%s", p.ID),
|
||||
},
|
||||
},
|
||||
)}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
package plugincheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
)
|
||||
|
||||
const (
|
||||
UnsignedStepID = "unsigned"
|
||||
)
|
||||
|
||||
type unsignedStep struct {
|
||||
pluginIndex map[string]repo.PluginInfo
|
||||
}
|
||||
|
||||
func (s *unsignedStep) Title() string {
|
||||
return "Plugin signature check"
|
||||
}
|
||||
|
||||
func (s *unsignedStep) Description() string {
|
||||
return "Checks has a missing or invalid signature."
|
||||
}
|
||||
|
||||
func (s *unsignedStep) Resolution() string {
|
||||
return "For security, we recommend only installing plugins from the catalog. " +
|
||||
"Review the plugin's status and verify your allowlist if appropriate."
|
||||
}
|
||||
|
||||
func (s *unsignedStep) ID() string {
|
||||
return UnsignedStepID
|
||||
}
|
||||
|
||||
func (s *unsignedStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) {
|
||||
pi, ok := it.(*pluginItem)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
|
||||
p := pi.Plugin
|
||||
invalidSignatureTypes := []plugins.SignatureStatus{
|
||||
plugins.SignatureStatusUnsigned,
|
||||
plugins.SignatureStatusModified,
|
||||
plugins.SignatureStatusInvalid,
|
||||
}
|
||||
if p != nil && slices.Contains(invalidSignatureTypes, p.Signature) {
|
||||
// This will only happen in dev mode or if the plugin is in the unsigned allow list
|
||||
links := []advisor.CheckErrorLink{}
|
||||
if _, ok := s.pluginIndex[p.ID]; ok {
|
||||
links = append(links, advisor.CheckErrorLink{
|
||||
Message: "View plugin",
|
||||
Url: fmt.Sprintf("/plugins/%s", p.ID),
|
||||
})
|
||||
}
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityLow,
|
||||
s.ID(),
|
||||
p.Name,
|
||||
p.ID,
|
||||
links,
|
||||
)}, nil
|
||||
}
|
||||
|
||||
pluginErr := pi.Err
|
||||
invalidErrorCodeTypes := []plugins.ErrorCode{
|
||||
plugins.ErrorCodeSignatureMissing,
|
||||
plugins.ErrorCodeSignatureInvalid,
|
||||
plugins.ErrorCodeSignatureModified,
|
||||
}
|
||||
if pluginErr != nil && slices.Contains(invalidErrorCodeTypes, pluginErr.ErrorCode) {
|
||||
links := []advisor.CheckErrorLink{}
|
||||
if _, ok := s.pluginIndex[pluginErr.PluginID]; ok {
|
||||
links = append(links, advisor.CheckErrorLink{
|
||||
Message: "View plugin",
|
||||
Url: fmt.Sprintf("/plugins/%s", pluginErr.PluginID),
|
||||
})
|
||||
}
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
pluginErr.PluginID,
|
||||
pluginErr.PluginID,
|
||||
links,
|
||||
)}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package plugincheck
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker"
|
||||
)
|
||||
|
||||
const (
|
||||
UpdateStepID = "update"
|
||||
)
|
||||
|
||||
type updateStep struct {
|
||||
GrafanaVersion string
|
||||
updateChecker pluginchecker.PluginUpdateChecker
|
||||
pluginIndex map[string]repo.PluginInfo
|
||||
}
|
||||
|
||||
func (s *updateStep) Title() string {
|
||||
return "Update check"
|
||||
}
|
||||
|
||||
func (s *updateStep) Description() string {
|
||||
return "Checks if an installed plugins has a newer version available."
|
||||
}
|
||||
|
||||
func (s *updateStep) Resolution() string {
|
||||
return "Go to the plugin admin page and upgrade to the latest version."
|
||||
}
|
||||
|
||||
func (s *updateStep) ID() string {
|
||||
return UpdateStepID
|
||||
}
|
||||
|
||||
func (s *updateStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) {
|
||||
pi, ok := it.(*pluginItem)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
p := pi.Plugin
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if !s.updateChecker.IsUpdatable(ctx, *p) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check if plugin has a newer version available
|
||||
info, ok := s.pluginIndex[p.ID]
|
||||
if !ok {
|
||||
// Unable to check updates
|
||||
return nil, nil
|
||||
}
|
||||
if s.updateChecker.CanUpdate(p.ID, p.Info.Version, info.Version, false) {
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityLow,
|
||||
s.ID(),
|
||||
p.Name,
|
||||
p.ID,
|
||||
[]advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "Upgrade",
|
||||
Url: fmt.Sprintf("/plugins/%s?page=version-history", p.ID),
|
||||
},
|
||||
},
|
||||
)}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -341,9 +341,9 @@ type AppDTO struct {
|
|||
}
|
||||
|
||||
const (
|
||||
errorCodeSignatureMissing ErrorCode = "signatureMissing"
|
||||
errorCodeSignatureModified ErrorCode = "signatureModified"
|
||||
errorCodeSignatureInvalid ErrorCode = "signatureInvalid"
|
||||
ErrorCodeSignatureMissing ErrorCode = "signatureMissing"
|
||||
ErrorCodeSignatureModified ErrorCode = "signatureModified"
|
||||
ErrorCodeSignatureInvalid ErrorCode = "signatureInvalid"
|
||||
ErrorCodeFailedBackendStart ErrorCode = "failedBackendStart"
|
||||
ErrorAngular ErrorCode = "angular"
|
||||
)
|
||||
|
|
@ -392,11 +392,11 @@ func (e Error) AsErrorCode() ErrorCode {
|
|||
|
||||
switch e.SignatureStatus {
|
||||
case SignatureStatusInvalid:
|
||||
return errorCodeSignatureInvalid
|
||||
return ErrorCodeSignatureInvalid
|
||||
case SignatureStatusModified:
|
||||
return errorCodeSignatureModified
|
||||
return ErrorCodeSignatureModified
|
||||
case SignatureStatusUnsigned:
|
||||
return errorCodeSignatureMissing
|
||||
return ErrorCodeSignatureMissing
|
||||
case SignatureStatusInternal, SignatureStatusValid:
|
||||
return ""
|
||||
}
|
||||
|
|
@ -411,11 +411,11 @@ func (e *Error) WithMessage(m string) *Error {
|
|||
|
||||
func (e Error) PublicMessage() string {
|
||||
switch e.ErrorCode {
|
||||
case errorCodeSignatureInvalid:
|
||||
case ErrorCodeSignatureInvalid:
|
||||
return "Invalid plugin signature"
|
||||
case errorCodeSignatureModified:
|
||||
case ErrorCodeSignatureModified:
|
||||
return "Plugin signature does not match"
|
||||
case errorCodeSignatureMissing:
|
||||
case ErrorCodeSignatureMissing:
|
||||
return "Plugin signature is missing"
|
||||
case ErrorCodeFailedBackendStart:
|
||||
return "Plugin failed to start"
|
||||
|
|
|
|||
Loading…
Reference in New Issue