Add minor update strategy

pull/104112/head
Hugo Oshiro 3 months ago
parent f580003f6d
commit 332f385290
  1. 3
      conf/defaults.ini
  2. 4
      pkg/api/frontendsettings_test.go
  3. 10
      pkg/api/http_server.go
  4. 6
      pkg/api/plugins_test.go
  5. 6
      pkg/registry/backgroundsvcs/background_services.go
  6. 6
      pkg/server/wire.go
  7. 29
      pkg/services/pluginsintegration/pluginupdatechecker/service.go
  8. 2
      pkg/services/updatemanager/grafana.go
  9. 2
      pkg/services/updatemanager/grafana_test.go
  10. 29
      pkg/services/updatemanager/plugins.go
  11. 2
      pkg/services/updatemanager/plugins_test.go
  12. 2
      pkg/services/updatemanager/updatemanager.go
  13. 2
      pkg/setting/setting.go
  14. 7
      pkg/setting/setting_plugins.go

@ -1846,6 +1846,9 @@ preinstall =
preinstall_async = true
# Disables preinstall feature. It has the same effect as setting preinstall to an empty list.
preinstall_disabled = false
# Update strategy for plugins.
# Available options: "latest", "minor"
update_strategy = latest
#################################### Grafana Live ##########################################
[live]

@ -38,7 +38,7 @@ import (
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/services/ssosettings/ssosettingstests"
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
"github.com/grafana/grafana/pkg/services/updatechecker"
"github.com/grafana/grafana/pkg/services/updatemanager"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web"
@ -93,7 +93,7 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.F
SQLStore: db.InitTestDB(t),
SettingsProvider: setting.ProvideProvider(cfg),
pluginStore: pluginStore,
grafanaUpdateChecker: &updatechecker.GrafanaService{},
grafanaUpdateChecker: &updatemanager.GrafanaService{},
AccessControl: accesscontrolmock.New(),
PluginSettings: pluginsSettings,
pluginsCDNService: pluginsCDN,

@ -107,7 +107,7 @@ import (
"github.com/grafana/grafana/pkg/services/tag"
"github.com/grafana/grafana/pkg/services/team"
tempUser "github.com/grafana/grafana/pkg/services/temp_user"
"github.com/grafana/grafana/pkg/services/updatechecker"
"github.com/grafana/grafana/pkg/services/updatemanager"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/grafana/grafana/pkg/setting"
@ -175,8 +175,8 @@ type HTTPServer struct {
DataSourcesService datasources.DataSourceService
cleanUpService *cleanup.CleanUpService
tracer tracing.Tracer
grafanaUpdateChecker *updatechecker.GrafanaService
pluginsUpdateChecker *updatechecker.PluginsService
grafanaUpdateChecker *updatemanager.GrafanaService
pluginsUpdateChecker *updatemanager.PluginsService
searchUsersService searchusers.Service
queryDataService query.Service
serviceAccountsService serviceaccounts.Service
@ -249,8 +249,8 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
contextHandler *contexthandler.ContextHandler, loggerMiddleware loggermw.Logger, features featuremgmt.FeatureToggles,
alertNG *ngalert.AlertNG, libraryPanelService librarypanels.Service, libraryElementService libraryelements.Service,
quotaService quota.Service, socialService social.Service, tracer tracing.Tracer,
encryptionService encryption.Internal, grafanaUpdateChecker *updatechecker.GrafanaService,
pluginsUpdateChecker *updatechecker.PluginsService, searchUsersService searchusers.Service,
encryptionService encryption.Internal, grafanaUpdateChecker *updatemanager.GrafanaService,
pluginsUpdateChecker *updatemanager.PluginsService, searchUsersService searchusers.Service,
dataSourcesService datasources.DataSourceService, queryDataService query.Service, pluginFileStore plugins.FileStore,
serviceaccountsService serviceaccounts.Service, pluginAssets *pluginassets.Service,
authInfoService login.AuthInfoService, storageService store.StorageService,

@ -51,7 +51,7 @@ import (
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginupdatechecker"
"github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins"
"github.com/grafana/grafana/pkg/services/secrets/kvstore"
"github.com/grafana/grafana/pkg/services/updatechecker"
"github.com/grafana/grafana/pkg/services/updatemanager"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web"
@ -655,7 +655,7 @@ func Test_PluginsList_AccessControl(t *testing.T) {
hs.pluginFileStore = filestore.ProvideService(pluginRegistry)
hs.managedPluginsService = managedplugins.NewNoop()
var err error
hs.pluginsUpdateChecker, err = updatechecker.ProvidePluginsService(
hs.pluginsUpdateChecker, err = updatemanager.ProvidePluginsService(
hs.Cfg,
hs.pluginStore,
nil, // plugins.Installer
@ -854,7 +854,7 @@ func Test_PluginsSettings(t *testing.T) {
hs.pluginAssets = pluginassets.ProvideService(pCfg, pluginCDN, sig, hs.pluginStore)
hs.pluginErrorResolver = pluginerrs.ProvideStore(errTracker)
var err error
hs.pluginsUpdateChecker, err = updatechecker.ProvidePluginsService(
hs.pluginsUpdateChecker, err = updatemanager.ProvidePluginsService(
hs.Cfg,
hs.pluginStore,
&fakes.FakePluginInstaller{},

@ -47,7 +47,7 @@ import (
"github.com/grafana/grafana/pkg/services/store/sanitizer"
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlesimpl"
"github.com/grafana/grafana/pkg/services/team/teamapi"
"github.com/grafana/grafana/pkg/services/updatechecker"
"github.com/grafana/grafana/pkg/services/updatemanager"
)
func ProvideBackgroundServiceRegistry(
@ -55,8 +55,8 @@ func ProvideBackgroundServiceRegistry(
pushGateway *pushhttp.Gateway, notifications *notifications.NotificationService, pluginStore *pluginStore.Service,
rendering *rendering.RenderingService, tokenService auth.UserTokenBackgroundService, tracing *tracing.TracingService,
provisioning *provisioning.ProvisioningServiceImpl, usageStats *uss.UsageStats,
statsCollector *statscollector.Service, grafanaUpdateChecker *updatechecker.GrafanaService,
pluginsUpdateChecker *updatechecker.PluginsService, metrics *metrics.InternalMetricsService,
statsCollector *statscollector.Service, grafanaUpdateChecker *updatemanager.GrafanaService,
pluginsUpdateChecker *updatemanager.PluginsService, metrics *metrics.InternalMetricsService,
secretsService *secretsManager.SecretsService, remoteCache *remotecache.RemoteCache, StorageService store.StorageService, searchService searchV2.SearchService, entityEventsService store.EntityEventsService,
saService *samanager.ServiceAccountsService, grpcServerProvider grpcserver.Provider,
secretMigrationProvider secretsMigrations.SecretMigrationProvider, loginAttemptService *loginattemptimpl.Service,

@ -158,7 +158,7 @@ import (
"github.com/grafana/grafana/pkg/services/team/teamimpl"
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
"github.com/grafana/grafana/pkg/services/temp_user/tempuserimpl"
"github.com/grafana/grafana/pkg/services/updatechecker"
"github.com/grafana/grafana/pkg/services/updatemanager"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/services/user/userimpl"
"github.com/grafana/grafana/pkg/setting"
@ -215,8 +215,8 @@ var wireBasicSet = wire.NewSet(
localcache.ProvideService,
bundleregistry.ProvideService,
wire.Bind(new(supportbundles.Service), new(*bundleregistry.Service)),
updatechecker.ProvideGrafanaService,
updatechecker.ProvidePluginsService,
updatemanager.ProvideGrafanaService,
updatemanager.ProvidePluginsService,
uss.ProvideService,
wire.Bind(new(usagestats.Service), new(*uss.UsageStats)),
validator.ProvideService,

@ -4,6 +4,7 @@ import (
"context"
"slices"
"github.com/Masterminds/semver"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/pluginsintegration/managedplugins"
@ -14,6 +15,7 @@ import (
type PluginUpdateChecker interface {
IsUpdatable(ctx context.Context, plugin pluginstore.Plugin) bool
CanUpdateMinor(currentVersion string, targetVersion string) bool
}
var _ PluginUpdateChecker = (*Service)(nil)
@ -82,3 +84,30 @@ func (s *Service) IsUpdatable(ctx context.Context, plugin pluginstore.Plugin) bo
return true
}
func (s *Service) CanUpdateMinor(currentVersion string, targetVersion string) bool {
// If we are already on the latest version, skip the installation
if currentVersion == targetVersion {
s.log.Debug("Latest plugin already installed", "version", targetVersion)
return false
}
// If the latest version is a new major version, skip the installation
parsedLatestVersion, err := semver.NewVersion(targetVersion)
if err != nil {
s.log.Error("Failed to parse latest version, skipping potential update", "version", targetVersion, "error", err)
return false
}
parsedCurrentVersion, err := semver.NewVersion(currentVersion)
if err != nil {
s.log.Error("Failed to parse current version, skipping potential update", "version", currentVersion, "error", err)
return false
}
if parsedLatestVersion.Major() > parsedCurrentVersion.Major() {
s.log.Debug("New major version available, skipping update due to possible breaking changes", "version", targetVersion)
return false
}
// We should update the plugin
return true
}

@ -1,4 +1,4 @@
package updatechecker
package updatemanager
import (
"context"

@ -1,4 +1,4 @@
package updatechecker
package updatemanager
import (
"context"

@ -1,4 +1,4 @@
package updatechecker
package updatemanager
import (
"context"
@ -44,6 +44,7 @@ type PluginsService struct {
updateCheckURL *url.URL
pluginInstaller plugins.Installer
updateChecker *pluginupdatechecker.Service
updateStrategy string
features featuremgmt.FeatureToggles
}
@ -87,6 +88,7 @@ func ProvidePluginsService(cfg *setting.Cfg,
pluginInstaller: pluginInstaller,
features: features,
updateChecker: updateChecker,
updateStrategy: cfg.PluginUpdateStrategy,
}, nil
}
@ -219,6 +221,23 @@ func (s *PluginsService) canUpdate(ctx context.Context, plugin pluginstore.Plugi
return false
}
if plugin.Info.Version == gcomVersion {
return false
}
if s.updateStrategy == setting.PluginUpdateStrategyLatest {
return s.canUpdateLatest(plugin, gcomVersion)
}
if s.updateStrategy == setting.PluginUpdateStrategyMinor {
return s.canUpdateMinor(plugin, gcomVersion)
}
return s.canUpdateLatest(plugin, gcomVersion)
}
func (s *PluginsService) canUpdateLatest(plugin pluginstore.Plugin, gcomVersion string) bool {
ver1, err1 := version.NewVersion(plugin.Info.Version)
if err1 != nil {
return false
@ -231,6 +250,14 @@ func (s *PluginsService) canUpdate(ctx context.Context, plugin pluginstore.Plugi
return ver1.LessThan(ver2)
}
func (s *PluginsService) canUpdateMinor(plugin pluginstore.Plugin, gcomVersion string) bool {
canUpdate := s.updateChecker.CanUpdateMinor(plugin.Info.Version, gcomVersion)
if !canUpdate {
s.log.Info("Plugin can not be updated to minor version", "pluginID", plugin.ID, "from", plugin.Info.Version, "to", gcomVersion)
}
return canUpdate
}
func (s *PluginsService) pluginIDsCSV(m map[string]pluginstore.Plugin) string {
ids := make([]string, 0, len(m))
for pluginID := range m {

@ -1,4 +1,4 @@
package updatechecker
package updatemanager
import (
"context"

@ -1,4 +1,4 @@
package updatechecker
package updatemanager
import "net/http"

@ -206,6 +206,8 @@ type Cfg struct {
PluginsCDNURLTemplate string
PluginLogBackendRequests bool
PluginUpdateStrategy string
// Panels
DisableSanitizeHtml bool

@ -8,6 +8,11 @@ import (
"github.com/grafana/grafana/pkg/util"
)
const (
PluginUpdateStrategyLatest = "latest"
PluginUpdateStrategyMinor = "minor"
)
// PluginSettings maps plugin id to map of key/value settings.
type PluginSettings map[string]map[string]string
@ -97,5 +102,7 @@ func (cfg *Cfg) readPluginSettings(iniFile *ini.File) error {
cfg.PluginsCDNURLTemplate = strings.TrimRight(pluginsSection.Key("cdn_base_url").MustString(""), "/")
cfg.PluginLogBackendRequests = pluginsSection.Key("log_backend_requests").MustBool(false)
cfg.PluginUpdateStrategy = pluginsSection.Key("update_strategy").In(PluginUpdateStrategyLatest, []string{PluginUpdateStrategyLatest, PluginUpdateStrategyMinor})
return nil
}

Loading…
Cancel
Save