Files
grafana/pkg/services/secrets/kvstore/plugin_mig.go
T
Michael Mandrus 72d9de3a0f Secrets: Implement Secret Plugin required flag and fatal crash on startup (#52552)
* add special handling on the plugin gathering side to check whether secrets manager plugins are enabled or not

* show disabled badge in front end if the plugin is not enabled

* Only show error in disabled badge hover if one is present (otherwise it shows "undefined")

* refactor to make use of fields already available in the DTO

* fix typo

* if there is no error returned for the plugin, just show 'disabled'

* fix typo

* Update public/app/features/plugins/admin/components/Badges/PluginDisabledBadge.tsx

Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>

* Update frontendsettings.go

add clarifying comment

* fix unit test

* rework task to use new frontend property combined with plugin type to determine if the plugin should be disabled

* Update helpers.test.ts

revert test change

* fix unit test

* show custom uninstall message if the plugin is a secrets manager

* bogus commit to trigger precommit

* undo commit

* run precommit manually

* add some consts

* refactor a bit to pull plugin error management up a level

* re-add code squashed in merge

* fix compile issues

* add code to set plugin error fatal flag after secret migration

* refactor to move plugin startup out of Should Check func

* re-add important check

* make plugin startup errors fatal the first time we set a secret on the plugin

* rename func to make intent clearler

* remove unnecessary duplicate code from plugin mig

* fix compile error

* fix more compile errors

* add some extra logging to secrets migration

* have remote_plugin secret service managed plugin error fatal flag directly

* add blank file for eventual unit tests

* fix linting issues

* changes from PR review

* quick bit of cleanup

* add comment explaining design decision

* move more common test helpers to file

* slightly update to first time Get secret call

* add unit tests

* remove override func from provider

* fix linting issues

* add test cleanup step

* add some comments about refactoring to hacky test function

Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
2022-07-25 12:37:47 -04:00

108 lines
4.0 KiB
Go

package kvstore
import (
"context"
"github.com/grafana/grafana/pkg/infra/kvstore"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
)
// PluginSecretMigrationService This migrator will handle migration of datasource secrets (aka Unified secrets)
// into the plugin secrets configured
type PluginSecretMigrationService struct {
secretsStore SecretsKVStore
cfg *setting.Cfg
logger log.Logger
sqlStore sqlstore.Store
secretsService secrets.Service
remoteCheck UseRemoteSecretsPluginCheck
kvstore kvstore.KVStore
getAllFunc func(ctx context.Context) ([]Item, error)
}
func ProvidePluginSecretMigrationService(
secretsStore SecretsKVStore,
cfg *setting.Cfg,
sqlStore sqlstore.Store,
secretsService secrets.Service,
remoteCheck UseRemoteSecretsPluginCheck,
kvstore kvstore.KVStore,
) *PluginSecretMigrationService {
return &PluginSecretMigrationService{
secretsStore: secretsStore,
cfg: cfg,
logger: log.New("sec-plugin-mig"),
sqlStore: sqlStore,
secretsService: secretsService,
remoteCheck: remoteCheck,
kvstore: kvstore,
}
}
func (s *PluginSecretMigrationService) Migrate(ctx context.Context) error {
// Check if we should migrate to plugin - default false
if s.cfg.SectionWithEnvOverrides("secrets").Key("migrate_to_plugin").MustBool(false) && s.remoteCheck.ShouldUseRemoteSecretsPlugin() {
s.logger.Debug("starting migration of unified secrets to the plugin")
// we need to instantiate the secretsKVStore as this is not on wire, and in this scenario,
// the secrets store would be the plugin.
secretsSql := &secretsKVStoreSQL{
sqlStore: s.sqlStore,
secretsService: s.secretsService,
log: s.logger,
decryptionCache: decryptionCache{
cache: make(map[int64]cachedDecrypted),
},
GetAllFuncOverride: s.getAllFunc,
}
// before we start migrating, check see if plugin startup failures were already fatal
namespacedKVStore := GetNamespacedKVStore(s.kvstore)
wasFatal, err := isPluginStartupErrorFatal(ctx, namespacedKVStore)
if err != nil {
s.logger.Warn("unabled to determine whether plugin startup failures are fatal - continuing migration anyway.")
}
allSec, err := secretsSql.GetAll(ctx)
if err != nil {
return nil
}
// We just set it again as the current secret store should be the plugin secret
for _, sec := range allSec {
err = s.secretsStore.Set(ctx, *sec.OrgId, *sec.Namespace, *sec.Type, sec.Value)
if err != nil {
return err
}
}
s.logger.Debug("migrated unified secrets to plugin", "number of secrets", len(allSec))
// as no err was returned, when we delete all the secrets from the sql store
for index, sec := range allSec {
err = secretsSql.Del(ctx, *sec.OrgId, *sec.Namespace, *sec.Type)
if err != nil {
s.logger.Error("plugin migrator encountered error while deleting unified secrets")
if index == 0 && !wasFatal {
// old unified secrets still exists, so plugin startup errors are still not fatal, unless they were before we started
err := setPluginStartupErrorFatal(ctx, namespacedKVStore, false)
if err != nil {
s.logger.Error("error reverting plugin failure fatal status", "error", err.Error())
} else {
s.logger.Debug("application will continue to function without the secrets plugin")
}
}
return err
}
}
s.logger.Debug("deleted unified secrets after migration", "number of secrets", len(allSec))
}
return nil
}
// This is here to support testing and should normally not be called
// An edge case we are unit testing requires the GetAll function to return a value, but the Del function to return an error.
// This is not possible with the code as written, so this override function is a workaround. Should be refactored.
func (s *PluginSecretMigrationService) overrideGetAllFunc(getAllFunc func(ctx context.Context) ([]Item, error)) {
s.getAllFunc = getAllFunc
}