Chore: Migrate pluginsAutoUpdate flag to OpenFeature (#114404)
* Chore: Migrate pluginsAutoUpdate flag to OpenFeature * Update workspace * fixup! Chore: Migrate pluginsAutoUpdate flag to OpenFeature * Add a test * Refactor * Apply suggestion from @hairyhenderson Co-authored-by: Dave Henderson <dave.henderson@grafana.com> * Apply suggestions * Update pkg/services/updatemanager/plugins_test.go Co-authored-by: Will Browne <wbrowne@users.noreply.github.com> * Reorder code blocks --------- Co-authored-by: Dave Henderson <dave.henderson@grafana.com> Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
This commit is contained in:
co-authored by
Dave Henderson
Will Browne
parent
094b6a36dc
commit
3ec1c27ad4
@@ -13,6 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
"github.com/open-feature/go-sdk/openfeature"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/httpclient/httpclientprovider"
|
||||
@@ -96,11 +97,7 @@ func (s *PluginsService) IsDisabled() bool {
|
||||
}
|
||||
|
||||
func (s *PluginsService) Run(ctx context.Context) error {
|
||||
s.instrumentedCheckForUpdates(ctx)
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagPluginsAutoUpdate) {
|
||||
s.updateAll(ctx)
|
||||
}
|
||||
s.checkAndUpdate(ctx)
|
||||
|
||||
ticker := time.NewTicker(time.Minute * 10)
|
||||
run := true
|
||||
@@ -108,11 +105,7 @@ func (s *PluginsService) Run(ctx context.Context) error {
|
||||
for run {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
s.instrumentedCheckForUpdates(ctx)
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagPluginsAutoUpdate) {
|
||||
s.updateAll(ctx)
|
||||
}
|
||||
s.checkAndUpdate(ctx)
|
||||
case <-ctx.Done():
|
||||
run = false
|
||||
}
|
||||
@@ -140,6 +133,14 @@ func (s *PluginsService) HasUpdate(ctx context.Context, pluginID string) (string
|
||||
return "", false
|
||||
}
|
||||
|
||||
// checkAndUpdate checks for updates and applies them if auto-update is enabled.
|
||||
func (s *PluginsService) checkAndUpdate(ctx context.Context) {
|
||||
s.instrumentedCheckForUpdates(ctx)
|
||||
if openfeature.NewDefaultClient().Boolean(ctx, featuremgmt.FlagPluginsAutoUpdate, false, openfeature.TransactionContext(ctx)) {
|
||||
s.updateAll(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PluginsService) instrumentedCheckForUpdates(ctx context.Context) {
|
||||
start := time.Now()
|
||||
ctx, span := s.tracer.Start(ctx, "updatechecker.PluginsService.checkForUpdates")
|
||||
@@ -226,8 +227,7 @@ func (s *PluginsService) canUpdate(ctx context.Context, plugin pluginstore.Plugi
|
||||
return false
|
||||
}
|
||||
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagPluginsAutoUpdate) {
|
||||
if openfeature.NewDefaultClient().Boolean(ctx, featuremgmt.FlagPluginsAutoUpdate, false, openfeature.TransactionContext(ctx)) {
|
||||
return s.updateChecker.CanUpdate(plugin.ID, plugin.Info.Version, gcomVersion, s.updateStrategy == setting.PluginUpdateStrategyMinor)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/open-feature/go-sdk/openfeature"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
@@ -19,6 +21,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginchecker"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/provisionedplugins"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type mockPluginPreinstall struct {
|
||||
@@ -240,6 +243,7 @@ func TestPluginUpdateChecker_checkForUpdates(t *testing.T) {
|
||||
require.Empty(t, svc.availableUpdates["test-core-panel"])
|
||||
})
|
||||
}
|
||||
|
||||
func TestPluginUpdateChecker_updateAll(t *testing.T) {
|
||||
t.Run("update is available", func(t *testing.T) {
|
||||
pluginsFakeStore := map[string]string{}
|
||||
@@ -300,3 +304,88 @@ func (c *fakeHTTPClient) Do(req *http.Request) (*http.Response, error) {
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func TestPluginsService_PluginsAutoUpdateFlag(t *testing.T) {
|
||||
updateCheckURL, _ := url.Parse("https://grafana.com/api/plugins/versioncheck")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
flagEnabled bool
|
||||
expectUpdate bool
|
||||
}{
|
||||
{
|
||||
name: "pluginsAutoUpdate enabled calls updateAll",
|
||||
flagEnabled: true,
|
||||
expectUpdate: true,
|
||||
},
|
||||
{
|
||||
name: "pluginsAutoUpdate disabled does not call updateAll",
|
||||
flagEnabled: false,
|
||||
expectUpdate: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
setupOpenFeatureProvider(t, tt.flagEnabled)
|
||||
|
||||
updateCallCount := 0
|
||||
|
||||
availableUpdates := map[string]availableUpdate{
|
||||
"test-plugin": {
|
||||
localVersion: "0.9.0",
|
||||
availableVersion: "1.0.0",
|
||||
},
|
||||
}
|
||||
|
||||
svc := &PluginsService{
|
||||
availableUpdates: availableUpdates,
|
||||
httpClient: &fakeHTTPClient{
|
||||
fakeResp: `[]`,
|
||||
},
|
||||
log: log.NewNopLogger(),
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
updateCheckURL: updateCheckURL,
|
||||
updateChecker: pluginchecker.ProvideService(managedplugins.NewNoop(), provisionedplugins.NewNoop(), &mockPluginPreinstall{}),
|
||||
pluginStore: &pluginstore.FakePluginStore{PluginList: []pluginstore.Plugin{}},
|
||||
pluginInstaller: &pluginfakes.FakePluginInstaller{
|
||||
AddFunc: func(ctx context.Context, pluginID, version string, opts plugins.AddOpts) error {
|
||||
updateCallCount++
|
||||
return nil
|
||||
},
|
||||
},
|
||||
grafanaVersion: "10.0.0",
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
// Test the synchronous initialization work directly, without the long-running ticker loop
|
||||
svc.checkAndUpdate(ctx)
|
||||
|
||||
if tt.expectUpdate {
|
||||
require.Equal(t, updateCallCount, 1, "updateAll should be called when flag is enabled")
|
||||
} else {
|
||||
require.Equal(t, 0, updateCallCount, "updateAll should not be called when flag is disabled")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var openfeatureTestMutex sync.Mutex
|
||||
|
||||
func setupOpenFeatureProvider(t *testing.T, flagValue bool) {
|
||||
t.Helper()
|
||||
openfeatureTestMutex.Lock()
|
||||
|
||||
err := featuremgmt.InitOpenFeature(featuremgmt.OpenFeatureConfig{
|
||||
ProviderType: setting.StaticProviderType,
|
||||
StaticFlags: map[string]bool{
|
||||
featuremgmt.FlagPluginsAutoUpdate: flagValue,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Cleanup(func() {
|
||||
_ = openfeature.SetProviderAndWait(openfeature.NoopProvider{})
|
||||
openfeatureTestMutex.Unlock()
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user