FeatureFlags: Use interface rather than manager (#80000)

pull/79960/head^2
Ryan McKinley 1 year ago committed by GitHub
parent e550829dae
commit 1caaa56de0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      pkg/api/annotations.go
  2. 2
      pkg/api/common_test.go
  3. 2
      pkg/api/datasources.go
  4. 8
      pkg/api/featuremgmt.go
  5. 23
      pkg/api/featuremgmt_test.go
  6. 4
      pkg/api/folder_bench_test.go
  7. 2
      pkg/api/folder_test.go
  8. 2
      pkg/api/frontendsettings_test.go
  9. 6
      pkg/api/http_server.go
  10. 2
      pkg/infra/usagestats/statscollector/service_test.go
  11. 4
      pkg/login/social/connectors/azuread_oauth.go
  12. 4
      pkg/login/social/connectors/generic_oauth.go
  13. 4
      pkg/login/social/connectors/github_oauth.go
  14. 4
      pkg/login/social/connectors/gitlab_oauth.go
  15. 4
      pkg/login/social/connectors/google_oauth.go
  16. 4
      pkg/login/social/connectors/grafana_com_oauth.go
  17. 4
      pkg/login/social/connectors/okta_oauth.go
  18. 4
      pkg/login/social/connectors/social_base.go
  19. 4
      pkg/login/social/socialimpl/service.go
  20. 2
      pkg/login/social/socialimpl/service_test.go
  21. 6
      pkg/plugins/config/config.go
  22. 15
      pkg/plugins/envvars/envvars_test.go
  23. 5
      pkg/plugins/ifaces.go
  24. 23
      pkg/plugins/manager/fakes/fakes.go
  25. 4
      pkg/plugins/manager/loader/finder/local.go
  26. 6
      pkg/services/accesscontrol/acimpl/service.go
  27. 4
      pkg/services/accesscontrol/api/api.go
  28. 4
      pkg/services/authn/clients/session.go
  29. 2
      pkg/services/authn/clients/session_test.go
  30. 4
      pkg/services/contexthandler/contexthandler.go
  31. 6
      pkg/services/featuremgmt/manager.go
  32. 4
      pkg/services/featuremgmt/manager_test.go
  33. 4
      pkg/services/featuremgmt/models.go
  34. 2
      pkg/services/featuremgmt/usage_stats_test.go
  35. 2
      pkg/services/folder/folderimpl/folder_test.go
  36. 4
      pkg/services/navtree/navtreeimpl/navtree.go
  37. 4
      pkg/services/pluginsintegration/clientmiddleware/caching_middleware.go
  38. 2
      pkg/services/pluginsintegration/config/config.go
  39. 4
      pkg/services/pluginsintegration/loader/loader_test.go
  40. 3
      pkg/services/pluginsintegration/pipeline/steps_test.go
  41. 6
      pkg/services/publicdashboards/api/api.go
  42. 4
      pkg/services/secrets/kvstore/test_helpers.go
  43. 2
      pkg/services/secrets/manager/helpers.go
  44. 2
      pkg/services/serviceaccounts/extsvcaccounts/service.go
  45. 5
      pkg/services/serviceaccounts/extsvcaccounts/service_test.go
  46. 8
      pkg/services/sqlstore/permissions/dashboard_test.go
  47. 2
      pkg/services/ssosettings/ssosettingsimpl/service.go
  48. 20
      pkg/tsdb/influxdb/mocks_test.go

@ -600,7 +600,7 @@ func (hs *HTTPServer) GetAnnotationTags(c *contextmodel.ReqContext) response.Res
// where <type> is the type of annotation with id <id>. // where <type> is the type of annotation with id <id>.
// If annotationPermissionUpdate feature toggle is enabled, dashboard annotation scope will be resolved to the corresponding // If annotationPermissionUpdate feature toggle is enabled, dashboard annotation scope will be resolved to the corresponding
// dashboard and folder scopes (eg, "dashboards:uid:<annotation_dashboard_uid>", "folders:uid:<parent_folder_uid>" etc). // dashboard and folder scopes (eg, "dashboards:uid:<annotation_dashboard_uid>", "folders:uid:<parent_folder_uid>" etc).
func AnnotationTypeScopeResolver(annotationsRepo annotations.Repository, features *featuremgmt.FeatureManager, dashSvc dashboards.DashboardService, folderSvc folder.Service) (string, accesscontrol.ScopeAttributeResolver) { func AnnotationTypeScopeResolver(annotationsRepo annotations.Repository, features featuremgmt.FeatureToggles, dashSvc dashboards.DashboardService, folderSvc folder.Service) (string, accesscontrol.ScopeAttributeResolver) {
prefix := accesscontrol.ScopeAnnotationsProvider.GetResourceScope("") prefix := accesscontrol.ScopeAnnotationsProvider.GetResourceScope("")
return prefix, accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, initialScope string) ([]string, error) { return prefix, accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, initialScope string) ([]string, error) {
scopeParts := strings.Split(initialScope, ":") scopeParts := strings.Split(initialScope, ":")

@ -258,7 +258,7 @@ func userWithPermissions(orgID int64, permissions []accesscontrol.Permission) *u
return &user.SignedInUser{IsAnonymous: true, OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByAction(permissions)}} return &user.SignedInUser{IsAnonymous: true, OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByAction(permissions)}}
} }
func setupSimpleHTTPServer(features *featuremgmt.FeatureManager) *HTTPServer { func setupSimpleHTTPServer(features featuremgmt.FeatureToggles) *HTTPServer {
if features == nil { if features == nil {
features = featuremgmt.WithFeatures() features = featuremgmt.WithFeatures()
} }

@ -339,7 +339,7 @@ func validateURL(cmdType string, url string) response.Response {
// validateJSONData prevents the user from adding a custom header with name that matches the auth proxy header name. // validateJSONData prevents the user from adding a custom header with name that matches the auth proxy header name.
// This is done to prevent data source proxy from being used to circumvent auth proxy. // This is done to prevent data source proxy from being used to circumvent auth proxy.
// For more context take a look at CVE-2022-35957 // For more context take a look at CVE-2022-35957
func validateJSONData(ctx context.Context, jsonData *simplejson.Json, cfg *setting.Cfg, features *featuremgmt.FeatureManager) error { func validateJSONData(ctx context.Context, jsonData *simplejson.Json, cfg *setting.Cfg, features featuremgmt.FeatureToggles) error {
if jsonData == nil { if jsonData == nil {
return nil return nil
} }

@ -25,7 +25,7 @@ func (hs *HTTPServer) GetFeatureToggles(ctx *contextmodel.ReqContext) response.R
dtos := make([]featuremgmt.FeatureToggleDTO, 0) dtos := make([]featuremgmt.FeatureToggleDTO, 0)
// loop through features an add features that should be visible to dtos // loop through features an add features that should be visible to dtos
for _, ft := range hs.Features.GetFlags() { for _, ft := range hs.featureManager.GetFlags() {
if isFeatureHidden(ft, cfg.HiddenToggles) { if isFeatureHidden(ft, cfg.HiddenToggles) {
continue continue
} }
@ -67,7 +67,7 @@ func (hs *HTTPServer) UpdateFeatureToggle(ctx *contextmodel.ReqContext) response
for _, t := range cmd.FeatureToggles { for _, t := range cmd.FeatureToggles {
// make sure flag exists, and only continue if flag is writeable // make sure flag exists, and only continue if flag is writeable
if f, ok := hs.Features.LookupFlag(t.Name); ok && isFeatureWriteable(f, hs.Cfg.FeatureManagement.ReadOnlyToggles) { if f, ok := hs.featureManager.LookupFlag(t.Name); ok && isFeatureWriteable(f, hs.Cfg.FeatureManagement.ReadOnlyToggles) {
hs.log.Info("UpdateFeatureToggle: updating toggle", "toggle_name", t.Name, "enabled", t.Enabled, "username", ctx.SignedInUser.Login) hs.log.Info("UpdateFeatureToggle: updating toggle", "toggle_name", t.Name, "enabled", t.Enabled, "username", ctx.SignedInUser.Login)
payload.FeatureToggles[t.Name] = strconv.FormatBool(t.Enabled) payload.FeatureToggles[t.Name] = strconv.FormatBool(t.Enabled)
} else { } else {
@ -82,13 +82,13 @@ func (hs *HTTPServer) UpdateFeatureToggle(ctx *contextmodel.ReqContext) response
return response.Respond(http.StatusBadRequest, "Failed to perform webhook request") return response.Respond(http.StatusBadRequest, "Failed to perform webhook request")
} }
hs.Features.SetRestartRequired() hs.featureManager.SetRestartRequired()
return response.Respond(http.StatusOK, "feature toggles updated successfully") return response.Respond(http.StatusOK, "feature toggles updated successfully")
} }
func (hs *HTTPServer) GetFeatureMgmtState(ctx *contextmodel.ReqContext) response.Response { func (hs *HTTPServer) GetFeatureMgmtState(ctx *contextmodel.ReqContext) response.Response {
fmState := hs.Features.GetState() fmState := hs.featureManager.GetState()
return response.Respond(http.StatusOK, fmState) return response.Respond(http.StatusOK, fmState)
} }

@ -8,6 +8,9 @@ import (
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
@ -16,8 +19,6 @@ import (
"github.com/grafana/grafana/pkg/services/user/usertest" "github.com/grafana/grafana/pkg/services/user/usertest"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web/webtest" "github.com/grafana/grafana/pkg/web/webtest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestGetFeatureToggles(t *testing.T) { func TestGetFeatureToggles(t *testing.T) {
@ -405,13 +406,16 @@ func runGetScenario(
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.FeatureManagement = settings cfg.FeatureManagement = settings
server := SetupAPITestServer(t, func(hs *HTTPServer) { fm := featuremgmt.WithFeatureFlags(append([]*featuremgmt.FeatureFlag{{
hs.Cfg = cfg
hs.Features = featuremgmt.WithFeatureFlags(append([]*featuremgmt.FeatureFlag{{
Name: featuremgmt.FlagFeatureToggleAdminPage, Name: featuremgmt.FlagFeatureToggleAdminPage,
Enabled: true, Enabled: true,
Stage: featuremgmt.FeatureStageGeneralAvailability, Stage: featuremgmt.FeatureStageGeneralAvailability,
}}, features...)) }}, features...))
server := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.Cfg = cfg
hs.Features = fm
hs.featureManager = fm
hs.orgService = orgtest.NewOrgServiceFake() hs.orgService = orgtest.NewOrgServiceFake()
hs.userService = &usertest.FakeUserService{ hs.userService = &usertest.FakeUserService{
ExpectedUser: &user.User{ID: 1}, ExpectedUser: &user.User{ID: 1},
@ -469,13 +473,16 @@ func runSetScenario(
cfg := setting.NewCfg() cfg := setting.NewCfg()
cfg.FeatureManagement = settings cfg.FeatureManagement = settings
server := SetupAPITestServer(t, func(hs *HTTPServer) { features := featuremgmt.WithFeatureFlags(append([]*featuremgmt.FeatureFlag{{
hs.Cfg = cfg
hs.Features = featuremgmt.WithFeatureFlags(append([]*featuremgmt.FeatureFlag{{
Name: featuremgmt.FlagFeatureToggleAdminPage, Name: featuremgmt.FlagFeatureToggleAdminPage,
Enabled: true, Enabled: true,
Stage: featuremgmt.FeatureStageGeneralAvailability, Stage: featuremgmt.FeatureStageGeneralAvailability,
}}, serverFeatures...)) }}, serverFeatures...))
server := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.Cfg = cfg
hs.Features = features
hs.featureManager = features
hs.orgService = orgtest.NewOrgServiceFake() hs.orgService = orgtest.NewOrgServiceFake()
hs.userService = &usertest.FakeUserService{ hs.userService = &usertest.FakeUserService{
ExpectedUser: &user.User{ID: 1}, ExpectedUser: &user.User{ID: 1},

@ -95,7 +95,7 @@ func BenchmarkFolderListAndSearch(b *testing.B) {
desc string desc string
url string url string
expectedLen int expectedLen int
features *featuremgmt.FeatureManager features featuremgmt.FeatureToggles
}{ }{
{ {
desc: "impl=default nested_folders=on get root folders", desc: "impl=default nested_folders=on get root folders",
@ -423,7 +423,7 @@ func setupDB(b testing.TB) benchScenario {
} }
} }
func setupServer(b testing.TB, sc benchScenario, features *featuremgmt.FeatureManager) *web.Macaron { func setupServer(b testing.TB, sc benchScenario, features featuremgmt.FeatureToggles) *web.Macaron {
b.Helper() b.Helper()
m := web.New() m := web.New()

@ -435,7 +435,7 @@ func TestFolderGetAPIEndpoint(t *testing.T) {
type testCase struct { type testCase struct {
description string description string
URL string URL string
features *featuremgmt.FeatureManager features featuremgmt.FeatureToggles
expectedCode int expectedCode int
expectedParentUIDs []string expectedParentUIDs []string
expectedParentOrgIDs []int64 expectedParentOrgIDs []int64

@ -32,7 +32,7 @@ import (
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
) )
func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features *featuremgmt.FeatureManager, pstore pluginstore.Store, psettings pluginsettings.Service) (*web.Mux, *HTTPServer) { func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.FeatureToggles, pstore pluginstore.Store, psettings pluginsettings.Service) (*web.Mux, *HTTPServer) {
t.Helper() t.Helper()
db.InitTestDB(t) db.InitTestDB(t)
// nolint:staticcheck // nolint:staticcheck

@ -126,7 +126,8 @@ type HTTPServer struct {
RouteRegister routing.RouteRegister RouteRegister routing.RouteRegister
RenderService rendering.Service RenderService rendering.Service
Cfg *setting.Cfg Cfg *setting.Cfg
Features *featuremgmt.FeatureManager Features featuremgmt.FeatureToggles
featureManager *featuremgmt.FeatureManager
SettingsProvider setting.Provider SettingsProvider setting.Provider
HooksService *hooks.HooksService HooksService *hooks.HooksService
navTreeService navtree.Service navTreeService navtree.Service
@ -290,7 +291,8 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
ShortURLService: shortURLService, ShortURLService: shortURLService,
QueryHistoryService: queryHistoryService, QueryHistoryService: queryHistoryService,
CorrelationsService: correlationsService, CorrelationsService: correlationsService,
Features: features, Features: features, // a read only view of the managers state
featureManager: features,
StorageService: storageService, StorageService: storageService,
RemoteCacheService: remoteCache, RemoteCacheService: remoteCache,
ProvisioningService: provisioningService, ProvisioningService: provisioningService,

@ -382,7 +382,7 @@ func createService(t testing.TB, cfg *setting.Cfg, store db.DB, statsService sta
store, store,
&mockSocial{}, &mockSocial{},
&pluginstore.FakePluginStore{}, &pluginstore.FakePluginStore{},
featuremgmt.WithFeatures("feature1", "feature2"), featuremgmt.WithManager("feature1", "feature2"),
o.datasources, o.datasources,
httpclient.NewProvider(sdkhttpclient.ProviderOptions{Middlewares: []sdkhttpclient.Middleware{}}), httpclient.NewProvider(sdkhttpclient.ProviderOptions{Middlewares: []sdkhttpclient.Middleware{}}),
) )

@ -72,10 +72,10 @@ type keySetJWKS struct {
jose.JSONWebKeySet jose.JSONWebKeySet
} }
func NewAzureADProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager, cache remotecache.CacheStorage) *SocialAzureAD { func NewAzureADProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features featuremgmt.FeatureToggles, cache remotecache.CacheStorage) *SocialAzureAD {
config := createOAuthConfig(info, cfg, social.AzureADProviderName) config := createOAuthConfig(info, cfg, social.AzureADProviderName)
provider := &SocialAzureAD{ provider := &SocialAzureAD{
SocialBase: newSocialBase(social.AzureADProviderName, config, info, cfg.AutoAssignOrgRole, *features), SocialBase: newSocialBase(social.AzureADProviderName, config, info, cfg.AutoAssignOrgRole, features),
cache: cache, cache: cache,
allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]), allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]),
forceUseGraphAPI: MustBool(info.Extra[forceUseGraphAPIKey], false), forceUseGraphAPI: MustBool(info.Extra[forceUseGraphAPIKey], false),

@ -45,10 +45,10 @@ type SocialGenericOAuth struct {
teamIds []string teamIds []string
} }
func NewGenericOAuthProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager) *SocialGenericOAuth { func NewGenericOAuthProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features featuremgmt.FeatureToggles) *SocialGenericOAuth {
config := createOAuthConfig(info, cfg, social.GenericOAuthProviderName) config := createOAuthConfig(info, cfg, social.GenericOAuthProviderName)
provider := &SocialGenericOAuth{ provider := &SocialGenericOAuth{
SocialBase: newSocialBase(social.GenericOAuthProviderName, config, info, cfg.AutoAssignOrgRole, *features), SocialBase: newSocialBase(social.GenericOAuthProviderName, config, info, cfg.AutoAssignOrgRole, features),
teamsUrl: info.TeamsUrl, teamsUrl: info.TeamsUrl,
emailAttributeName: info.EmailAttributeName, emailAttributeName: info.EmailAttributeName,
emailAttributePath: info.EmailAttributePath, emailAttributePath: info.EmailAttributePath,

@ -53,13 +53,13 @@ var (
"User is not a member of one of the required organizations. Please contact identity provider administrator.")) "User is not a member of one of the required organizations. Please contact identity provider administrator."))
) )
func NewGitHubProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager) *SocialGithub { func NewGitHubProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features featuremgmt.FeatureToggles) *SocialGithub {
teamIdsSplitted := util.SplitString(info.Extra[teamIdsKey]) teamIdsSplitted := util.SplitString(info.Extra[teamIdsKey])
teamIds := mustInts(teamIdsSplitted) teamIds := mustInts(teamIdsSplitted)
config := createOAuthConfig(info, cfg, social.GitHubProviderName) config := createOAuthConfig(info, cfg, social.GitHubProviderName)
provider := &SocialGithub{ provider := &SocialGithub{
SocialBase: newSocialBase(social.GitHubProviderName, config, info, cfg.AutoAssignOrgRole, *features), SocialBase: newSocialBase(social.GitHubProviderName, config, info, cfg.AutoAssignOrgRole, features),
teamIds: teamIds, teamIds: teamIds,
allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]), allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]),
} }

@ -52,10 +52,10 @@ type userData struct {
IsGrafanaAdmin *bool `json:"-"` IsGrafanaAdmin *bool `json:"-"`
} }
func NewGitLabProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager) *SocialGitlab { func NewGitLabProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features featuremgmt.FeatureToggles) *SocialGitlab {
config := createOAuthConfig(info, cfg, social.GitlabProviderName) config := createOAuthConfig(info, cfg, social.GitlabProviderName)
provider := &SocialGitlab{ provider := &SocialGitlab{
SocialBase: newSocialBase(social.GitlabProviderName, config, info, cfg.AutoAssignOrgRole, *features), SocialBase: newSocialBase(social.GitlabProviderName, config, info, cfg.AutoAssignOrgRole, features),
} }
if features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) { if features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) {

@ -38,10 +38,10 @@ type googleUserData struct {
rawJSON []byte `json:"-"` rawJSON []byte `json:"-"`
} }
func NewGoogleProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager) *SocialGoogle { func NewGoogleProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features featuremgmt.FeatureToggles) *SocialGoogle {
config := createOAuthConfig(info, cfg, social.GoogleProviderName) config := createOAuthConfig(info, cfg, social.GoogleProviderName)
provider := &SocialGoogle{ provider := &SocialGoogle{
SocialBase: newSocialBase(social.GoogleProviderName, config, info, cfg.AutoAssignOrgRole, *features), SocialBase: newSocialBase(social.GoogleProviderName, config, info, cfg.AutoAssignOrgRole, features),
} }
if strings.HasPrefix(info.ApiUrl, legacyAPIURL) { if strings.HasPrefix(info.ApiUrl, legacyAPIURL) {

@ -33,7 +33,7 @@ type OrgRecord struct {
Login string `json:"login"` Login string `json:"login"`
} }
func NewGrafanaComProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager) *SocialGrafanaCom { func NewGrafanaComProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features featuremgmt.FeatureToggles) *SocialGrafanaCom {
// Override necessary settings // Override necessary settings
info.AuthUrl = cfg.GrafanaComURL + "/oauth2/authorize" info.AuthUrl = cfg.GrafanaComURL + "/oauth2/authorize"
info.TokenUrl = cfg.GrafanaComURL + "/api/oauth2/token" info.TokenUrl = cfg.GrafanaComURL + "/api/oauth2/token"
@ -41,7 +41,7 @@ func NewGrafanaComProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings
config := createOAuthConfig(info, cfg, social.GrafanaComProviderName) config := createOAuthConfig(info, cfg, social.GrafanaComProviderName)
provider := &SocialGrafanaCom{ provider := &SocialGrafanaCom{
SocialBase: newSocialBase(social.GrafanaComProviderName, config, info, cfg.AutoAssignOrgRole, *features), SocialBase: newSocialBase(social.GrafanaComProviderName, config, info, cfg.AutoAssignOrgRole, features),
url: cfg.GrafanaComURL, url: cfg.GrafanaComURL,
allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]), allowedOrganizations: util.SplitString(info.Extra[allowedOrganizationsKey]),
} }

@ -44,10 +44,10 @@ type OktaClaims struct {
Name string `json:"name"` Name string `json:"name"`
} }
func NewOktaProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager) *SocialOkta { func NewOktaProvider(info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features featuremgmt.FeatureToggles) *SocialOkta {
config := createOAuthConfig(info, cfg, social.OktaProviderName) config := createOAuthConfig(info, cfg, social.OktaProviderName)
provider := &SocialOkta{ provider := &SocialOkta{
SocialBase: newSocialBase(social.OktaProviderName, config, info, cfg.AutoAssignOrgRole, *features), SocialBase: newSocialBase(social.OktaProviderName, config, info, cfg.AutoAssignOrgRole, features),
} }
if info.UseRefreshToken { if info.UseRefreshToken {

@ -26,14 +26,14 @@ type SocialBase struct {
info *social.OAuthInfo info *social.OAuthInfo
log log.Logger log log.Logger
autoAssignOrgRole string autoAssignOrgRole string
features featuremgmt.FeatureManager features featuremgmt.FeatureToggles
} }
func newSocialBase(name string, func newSocialBase(name string,
config *oauth2.Config, config *oauth2.Config,
info *social.OAuthInfo, info *social.OAuthInfo,
autoAssignOrgRole string, autoAssignOrgRole string,
features featuremgmt.FeatureManager, features featuremgmt.FeatureToggles,
) *SocialBase { ) *SocialBase {
logger := log.New("oauth." + name) logger := log.New("oauth." + name)

@ -37,7 +37,7 @@ type SocialService struct {
} }
func ProvideService(cfg *setting.Cfg, func ProvideService(cfg *setting.Cfg,
features *featuremgmt.FeatureManager, features featuremgmt.FeatureToggles,
usageStats usagestats.Service, usageStats usagestats.Service,
bundleRegistry supportbundles.Service, bundleRegistry supportbundles.Service,
cache remotecache.CacheStorage, cache remotecache.CacheStorage,
@ -228,7 +228,7 @@ func (ss *SocialService) getUsageStats(ctx context.Context) (map[string]any, err
return m, nil return m, nil
} }
func createOAuthConnector(name string, info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features *featuremgmt.FeatureManager, cache remotecache.CacheStorage) (social.SocialConnector, error) { func createOAuthConnector(name string, info *social.OAuthInfo, cfg *setting.Cfg, ssoSettings ssosettings.Service, features featuremgmt.FeatureToggles, cache remotecache.CacheStorage) (social.SocialConnector, error) {
switch name { switch name {
case social.AzureADProviderName: case social.AzureADProviderName:
return connectors.NewAzureADProvider(info, cfg, ssoSettings, features, cache), nil return connectors.NewAzureADProvider(info, cfg, ssoSettings, features, cache), nil

@ -22,7 +22,7 @@ import (
func TestSocialService_ProvideService(t *testing.T) { func TestSocialService_ProvideService(t *testing.T) {
type testEnv struct { type testEnv struct {
features *featuremgmt.FeatureManager features featuremgmt.FeatureToggles
} }
testCases := []struct { testCases := []struct {
name string name string

@ -3,8 +3,8 @@ package config
import ( import (
"github.com/grafana/grafana-azure-sdk-go/azsettings" "github.com/grafana/grafana-azure-sdk-go/azsettings"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/log" "github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -44,7 +44,7 @@ type Cfg struct {
GrafanaAppURL string GrafanaAppURL string
GrafanaAppSubURL string GrafanaAppSubURL string
Features plugins.FeatureToggles Features featuremgmt.FeatureToggles
AngularSupportEnabled bool AngularSupportEnabled bool
HideAngularDeprecation []string HideAngularDeprecation []string
@ -52,7 +52,7 @@ type Cfg struct {
func NewCfg(devMode bool, pluginsPath string, pluginSettings setting.PluginSettings, pluginsAllowUnsigned []string, func NewCfg(devMode bool, pluginsPath string, pluginSettings setting.PluginSettings, pluginsAllowUnsigned []string,
awsAllowedAuthProviders []string, awsAssumeRoleEnabled bool, awsExternalId string, azure *azsettings.AzureSettings, secureSocksDSProxy setting.SecureSocksDSProxySettings, awsAllowedAuthProviders []string, awsAssumeRoleEnabled bool, awsExternalId string, azure *azsettings.AzureSettings, secureSocksDSProxy setting.SecureSocksDSProxySettings,
grafanaVersion string, logDatasourceRequests bool, pluginsCDNURLTemplate string, appURL string, appSubURL string, tracing Tracing, features plugins.FeatureToggles, angularSupportEnabled bool, grafanaVersion string, logDatasourceRequests bool, pluginsCDNURLTemplate string, appURL string, appSubURL string, tracing Tracing, features featuremgmt.FeatureToggles, angularSupportEnabled bool,
grafanaComURL string, disablePlugins []string, hideAngularDeprecation []string, forwardHostEnvVars []string) *Cfg { grafanaComURL string, disablePlugins []string, hideAngularDeprecation []string, forwardHostEnvVars []string) *Cfg {
return &Cfg{ return &Cfg{
log: log.New("plugin.cfg"), log: log.New("plugin.cfg"),

@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana-azure-sdk-go/azsettings" "github.com/grafana/grafana-azure-sdk-go/azsettings"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/auth" "github.com/grafana/grafana/pkg/plugins/auth"
"github.com/grafana/grafana/pkg/plugins/config" "github.com/grafana/grafana/pkg/plugins/config"
@ -726,27 +727,27 @@ func TestService_GetConfigMap(t *testing.T) {
func TestService_GetConfigMap_featureToggles(t *testing.T) { func TestService_GetConfigMap_featureToggles(t *testing.T) {
t.Run("Feature toggles list is deterministic", func(t *testing.T) { t.Run("Feature toggles list is deterministic", func(t *testing.T) {
tcs := []struct { tcs := []struct {
enabledFeatures []string features featuremgmt.FeatureToggles
expectedConfig map[string]string expectedConfig map[string]string
}{ }{
{ {
enabledFeatures: nil, features: nil,
expectedConfig: map[string]string{}, expectedConfig: map[string]string{},
}, },
{ {
enabledFeatures: []string{}, features: featuremgmt.WithFeatures(),
expectedConfig: map[string]string{}, expectedConfig: map[string]string{},
}, },
{ {
enabledFeatures: []string{"A", "B", "C"}, features: featuremgmt.WithFeatures("A", "B", "C"),
expectedConfig: map[string]string{"GF_INSTANCE_FEATURE_TOGGLES_ENABLE": "A,B,C"}, expectedConfig: map[string]string{"GF_INSTANCE_FEATURE_TOGGLES_ENABLE": "A,B,C"},
}, },
{ {
enabledFeatures: []string{"C", "B", "A"}, features: featuremgmt.WithFeatures("C", "B", "A"),
expectedConfig: map[string]string{"GF_INSTANCE_FEATURE_TOGGLES_ENABLE": "A,B,C"}, expectedConfig: map[string]string{"GF_INSTANCE_FEATURE_TOGGLES_ENABLE": "A,B,C"},
}, },
{ {
enabledFeatures: []string{"b", "a", "c", "d"}, features: featuremgmt.WithFeatures("b", "a", "c", "d"),
expectedConfig: map[string]string{"GF_INSTANCE_FEATURE_TOGGLES_ENABLE": "a,b,c,d"}, expectedConfig: map[string]string{"GF_INSTANCE_FEATURE_TOGGLES_ENABLE": "a,b,c,d"},
}, },
} }
@ -754,7 +755,7 @@ func TestService_GetConfigMap_featureToggles(t *testing.T) {
for _, tc := range tcs { for _, tc := range tcs {
s := &Service{ s := &Service{
cfg: &config.Cfg{ cfg: &config.Cfg{
Features: fakes.NewFakeFeatureToggles(tc.enabledFeatures...), Features: tc.features,
}, },
} }
require.Equal(t, tc.expectedConfig, s.GetConfigMap(context.Background(), "", nil)) require.Equal(t, tc.expectedConfig, s.GetConfigMap(context.Background(), "", nil))

@ -149,11 +149,6 @@ func (fn ClientMiddlewareFunc) CreateClientMiddleware(next Client) Client {
return fn(next) return fn(next)
} }
type FeatureToggles interface {
IsEnabledGlobally(flag string) bool
GetEnabled(ctx context.Context) map[string]bool
}
type SignatureCalculator interface { type SignatureCalculator interface {
Calculate(ctx context.Context, src PluginSource, plugin FoundPlugin) (Signature, error) Calculate(ctx context.Context, src PluginSource, plugin FoundPlugin) (Signature, error)
} }

@ -574,26 +574,3 @@ func (p *FakeBackendPlugin) Kill() {
defer p.mutex.Unlock() defer p.mutex.Unlock()
p.Running = false p.Running = false
} }
type FakeFeatureToggles struct {
features map[string]bool
}
func NewFakeFeatureToggles(features ...string) *FakeFeatureToggles {
m := make(map[string]bool)
for _, f := range features {
m[f] = true
}
return &FakeFeatureToggles{
features: m,
}
}
func (f *FakeFeatureToggles) GetEnabled(_ context.Context) map[string]bool {
return f.features
}
func (f *FakeFeatureToggles) IsEnabledGlobally(feature string) bool {
return f.features[feature]
}

@ -26,10 +26,10 @@ var (
type Local struct { type Local struct {
log log.Logger log log.Logger
production bool production bool
features plugins.FeatureToggles features featuremgmt.FeatureToggles
} }
func NewLocalFinder(devMode bool, features plugins.FeatureToggles) *Local { func NewLocalFinder(devMode bool, features featuremgmt.FeatureToggles) *Local {
return &Local{ return &Local{
production: !devMode, production: !devMode,
log: log.New("local.finder"), log: log.New("local.finder"),

@ -41,7 +41,7 @@ var SharedWithMeFolderPermission = accesscontrol.Permission{
} }
func ProvideService(cfg *setting.Cfg, db db.DB, routeRegister routing.RouteRegister, cache *localcache.CacheService, func ProvideService(cfg *setting.Cfg, db db.DB, routeRegister routing.RouteRegister, cache *localcache.CacheService,
accessControl accesscontrol.AccessControl, features *featuremgmt.FeatureManager) (*Service, error) { accessControl accesscontrol.AccessControl, features featuremgmt.FeatureToggles) (*Service, error) {
service := ProvideOSSService(cfg, database.ProvideService(db), cache, features) service := ProvideOSSService(cfg, database.ProvideService(db), cache, features)
api.NewAccessControlAPI(routeRegister, accessControl, service, features).RegisterAPIEndpoints() api.NewAccessControlAPI(routeRegister, accessControl, service, features).RegisterAPIEndpoints()
@ -62,7 +62,7 @@ func ProvideService(cfg *setting.Cfg, db db.DB, routeRegister routing.RouteRegis
return service, nil return service, nil
} }
func ProvideOSSService(cfg *setting.Cfg, store store, cache *localcache.CacheService, features *featuremgmt.FeatureManager) *Service { func ProvideOSSService(cfg *setting.Cfg, store store, cache *localcache.CacheService, features featuremgmt.FeatureToggles) *Service {
s := &Service{ s := &Service{
cfg: cfg, cfg: cfg,
store: store, store: store,
@ -93,7 +93,7 @@ type Service struct {
cache *localcache.CacheService cache *localcache.CacheService
registrations accesscontrol.RegistrationList registrations accesscontrol.RegistrationList
roles map[string]*accesscontrol.RoleDTO roles map[string]*accesscontrol.RoleDTO
features *featuremgmt.FeatureManager features featuremgmt.FeatureToggles
} }
func (s *Service) GetUsageStats(_ context.Context) map[string]any { func (s *Service) GetUsageStats(_ context.Context) map[string]any {

@ -15,7 +15,7 @@ import (
) )
func NewAccessControlAPI(router routing.RouteRegister, accesscontrol ac.AccessControl, service ac.Service, func NewAccessControlAPI(router routing.RouteRegister, accesscontrol ac.AccessControl, service ac.Service,
features *featuremgmt.FeatureManager) *AccessControlAPI { features featuremgmt.FeatureToggles) *AccessControlAPI {
return &AccessControlAPI{ return &AccessControlAPI{
RouteRegister: router, RouteRegister: router,
Service: service, Service: service,
@ -28,7 +28,7 @@ type AccessControlAPI struct {
Service ac.Service Service ac.Service
AccessControl ac.AccessControl AccessControl ac.AccessControl
RouteRegister routing.RouteRegister RouteRegister routing.RouteRegister
features *featuremgmt.FeatureManager features featuremgmt.FeatureToggles
} }
func (api *AccessControlAPI) RegisterAPIEndpoints() { func (api *AccessControlAPI) RegisterAPIEndpoints() {

@ -19,7 +19,7 @@ var _ authn.HookClient = new(Session)
var _ authn.ContextAwareClient = new(Session) var _ authn.ContextAwareClient = new(Session)
func ProvideSession(cfg *setting.Cfg, sessionService auth.UserTokenService, func ProvideSession(cfg *setting.Cfg, sessionService auth.UserTokenService,
features *featuremgmt.FeatureManager) *Session { features featuremgmt.FeatureToggles) *Session {
return &Session{ return &Session{
cfg: cfg, cfg: cfg,
features: features, features: features,
@ -30,7 +30,7 @@ func ProvideSession(cfg *setting.Cfg, sessionService auth.UserTokenService,
type Session struct { type Session struct {
cfg *setting.Cfg cfg *setting.Cfg
features *featuremgmt.FeatureManager features featuremgmt.FeatureToggles
sessionService auth.UserTokenService sessionService auth.UserTokenService
log log.Logger log log.Logger
} }

@ -64,7 +64,7 @@ func TestSession_Authenticate(t *testing.T) {
type fields struct { type fields struct {
sessionService auth.UserTokenService sessionService auth.UserTokenService
features *featuremgmt.FeatureManager features featuremgmt.FeatureToggles
} }
type args struct { type args struct {
r *authn.Request r *authn.Request

@ -25,7 +25,7 @@ import (
"github.com/grafana/grafana/pkg/web" "github.com/grafana/grafana/pkg/web"
) )
func ProvideService(cfg *setting.Cfg, tracer tracing.Tracer, features *featuremgmt.FeatureManager, authnService authn.Service, func ProvideService(cfg *setting.Cfg, tracer tracing.Tracer, features featuremgmt.FeatureToggles, authnService authn.Service,
) *ContextHandler { ) *ContextHandler {
return &ContextHandler{ return &ContextHandler{
Cfg: cfg, Cfg: cfg,
@ -39,7 +39,7 @@ func ProvideService(cfg *setting.Cfg, tracer tracing.Tracer, features *featuremg
type ContextHandler struct { type ContextHandler struct {
Cfg *setting.Cfg Cfg *setting.Cfg
tracer tracing.Tracer tracer tracing.Tracer
features *featuremgmt.FeatureManager features featuremgmt.FeatureToggles
authnService authn.Service authnService authn.Service
} }

@ -174,10 +174,14 @@ func (fm *FeatureManager) LookupFlag(name string) (FeatureFlag, bool) {
// ############# Test Functions ############# // ############# Test Functions #############
func WithFeatures(spec ...any) FeatureToggles {
return WithManager(spec...)
}
// WithFeatures is used to define feature toggles for testing. // WithFeatures is used to define feature toggles for testing.
// The arguments are a list of strings that are optionally followed by a boolean value for example: // The arguments are a list of strings that are optionally followed by a boolean value for example:
// WithFeatures([]any{"my_feature", "other_feature"}) or WithFeatures([]any{"my_feature", true}) // WithFeatures([]any{"my_feature", "other_feature"}) or WithFeatures([]any{"my_feature", true})
func WithFeatures(spec ...any) *FeatureManager { func WithManager(spec ...any) *FeatureManager {
count := len(spec) count := len(spec)
features := make(map[string]*FeatureFlag, count) features := make(map[string]*FeatureFlag, count)
enabled := make(map[string]bool, count) enabled := make(map[string]bool, count)

@ -9,7 +9,7 @@ import (
func TestFeatureManager(t *testing.T) { func TestFeatureManager(t *testing.T) {
t.Run("check testing stubs", func(t *testing.T) { t.Run("check testing stubs", func(t *testing.T) {
ft := WithFeatures("a", "b", "c") ft := WithManager("a", "b", "c")
require.True(t, ft.IsEnabledGlobally("a")) require.True(t, ft.IsEnabledGlobally("a"))
require.True(t, ft.IsEnabledGlobally("b")) require.True(t, ft.IsEnabledGlobally("b"))
require.True(t, ft.IsEnabledGlobally("c")) require.True(t, ft.IsEnabledGlobally("c"))
@ -18,7 +18,7 @@ func TestFeatureManager(t *testing.T) {
require.Equal(t, map[string]bool{"a": true, "b": true, "c": true}, ft.GetEnabled(context.Background())) require.Equal(t, map[string]bool{"a": true, "b": true, "c": true}, ft.GetEnabled(context.Background()))
// Explicit values // Explicit values
ft = WithFeatures("a", true, "b", false) ft = WithManager("a", true, "b", false)
require.True(t, ft.IsEnabledGlobally("a")) require.True(t, ft.IsEnabledGlobally("a"))
require.False(t, ft.IsEnabledGlobally("b")) require.False(t, ft.IsEnabledGlobally("b"))
require.Equal(t, map[string]bool{"a": true}, ft.GetEnabled(context.Background())) require.Equal(t, map[string]bool{"a": true}, ft.GetEnabled(context.Background()))

@ -18,6 +18,10 @@ type FeatureToggles interface {
// Use of global feature flags should be limited and careful as they require // Use of global feature flags should be limited and careful as they require
// a full server restart for a change to take place. // a full server restart for a change to take place.
IsEnabledGlobally(flag string) bool IsEnabledGlobally(flag string) bool
// Get the enabled flags -- this *may* also include disabled flags (with value false)
// but it is guaranteed to have the enabled ones listed
GetEnabled(ctx context.Context) map[string]bool
} }
// FeatureFlagStage indicates the quality level // FeatureFlagStage indicates the quality level

@ -8,7 +8,7 @@ import (
) )
func TestFeatureUsageStats(t *testing.T) { func TestFeatureUsageStats(t *testing.T) {
featureManagerWithAllFeatures := WithFeatures( featureManagerWithAllFeatures := WithManager(
"database_metrics", "database_metrics",
"live-config", "live-config",
"UPPER_SNAKE_CASE", "UPPER_SNAKE_CASE",

@ -587,7 +587,7 @@ func TestIntegrationNestedFolderService(t *testing.T) {
testCases := []struct { testCases := []struct {
service *Service service *Service
featuresFlag *featuremgmt.FeatureManager featuresFlag featuremgmt.FeatureToggles
prefix string prefix string
depth int depth int
forceDelete bool forceDelete bool

@ -33,7 +33,7 @@ type ServiceImpl struct {
pluginStore pluginstore.Store pluginStore pluginstore.Store
pluginSettings pluginsettings.Service pluginSettings pluginsettings.Service
starService star.Service starService star.Service
features *featuremgmt.FeatureManager features featuremgmt.FeatureToggles
dashboardService dashboards.DashboardService dashboardService dashboards.DashboardService
accesscontrolService ac.Service accesscontrolService ac.Service
kvStore kvstore.KVStore kvStore kvstore.KVStore
@ -52,7 +52,7 @@ type NavigationAppConfig struct {
Icon string Icon string
} }
func ProvideService(cfg *setting.Cfg, accessControl ac.AccessControl, pluginStore pluginstore.Store, pluginSettings pluginsettings.Service, starService star.Service, features *featuremgmt.FeatureManager, dashboardService dashboards.DashboardService, accesscontrolService ac.Service, kvStore kvstore.KVStore, apiKeyService apikey.Service, license licensing.Licensing) navtree.Service { func ProvideService(cfg *setting.Cfg, accessControl ac.AccessControl, pluginStore pluginstore.Store, pluginSettings pluginsettings.Service, starService star.Service, features featuremgmt.FeatureToggles, dashboardService dashboards.DashboardService, accesscontrolService ac.Service, kvStore kvstore.KVStore, apiKeyService apikey.Service, license licensing.Licensing) navtree.Service {
service := &ServiceImpl{ service := &ServiceImpl{
cfg: cfg, cfg: cfg,
log: log.New("navtree service"), log: log.New("navtree service"),

@ -27,7 +27,7 @@ func NewCachingMiddleware(cachingService caching.CachingService) plugins.ClientM
// NewCachingMiddlewareWithFeatureManager creates a new plugins.ClientMiddleware that will // NewCachingMiddlewareWithFeatureManager creates a new plugins.ClientMiddleware that will
// attempt to read and write query results to the cache with a feature manager // attempt to read and write query results to the cache with a feature manager
func NewCachingMiddlewareWithFeatureManager(cachingService caching.CachingService, features *featuremgmt.FeatureManager) plugins.ClientMiddleware { func NewCachingMiddlewareWithFeatureManager(cachingService caching.CachingService, features featuremgmt.FeatureToggles) plugins.ClientMiddleware {
log := log.New("caching_middleware") log := log.New("caching_middleware")
if err := prometheus.Register(QueryCachingRequestHistogram); err != nil { if err := prometheus.Register(QueryCachingRequestHistogram); err != nil {
log.Error("Error registering prometheus collector 'QueryRequestHistogram'", "error", err) log.Error("Error registering prometheus collector 'QueryRequestHistogram'", "error", err)
@ -49,7 +49,7 @@ type CachingMiddleware struct {
next plugins.Client next plugins.Client
caching caching.CachingService caching caching.CachingService
log log.Logger log log.Logger
features *featuremgmt.FeatureManager features featuremgmt.FeatureToggles
} }
// QueryData receives a data request and attempts to access results already stored in the cache for that request. // QueryData receives a data request and attempts to access results already stored in the cache for that request.

@ -9,7 +9,7 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
func ProvideConfig(settingProvider setting.Provider, grafanaCfg *setting.Cfg, features *featuremgmt.FeatureManager) (*pCfg.Cfg, error) { func ProvideConfig(settingProvider setting.Provider, grafanaCfg *setting.Cfg, features featuremgmt.FeatureToggles) (*pCfg.Cfg, error) {
plugins := settingProvider.Section("plugins") plugins := settingProvider.Section("plugins")
allowedUnsigned := grafanaCfg.PluginsAllowUnsigned allowedUnsigned := grafanaCfg.PluginsAllowUnsigned
if len(plugins.KeyValue("allow_loading_unsigned_plugins").Value()) > 0 { if len(plugins.KeyValue("allow_loading_unsigned_plugins").Value()) > 0 {

@ -507,7 +507,7 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
t.Run("Load a plugin with oauth client registration", func(t *testing.T) { t.Run("Load a plugin with oauth client registration", func(t *testing.T) {
cfg := &config.Cfg{ cfg := &config.Cfg{
Features: fakes.NewFakeFeatureToggles(featuremgmt.FlagExternalServiceAuth), Features: featuremgmt.WithFeatures(featuremgmt.FlagExternalServiceAuth),
PluginsAllowUnsigned: []string{"grafana-test-datasource"}, PluginsAllowUnsigned: []string{"grafana-test-datasource"},
} }
pluginPaths := []string{filepath.Join(testDataDir(t), "oauth-external-registration")} pluginPaths := []string{filepath.Join(testDataDir(t), "oauth-external-registration")}
@ -608,7 +608,7 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
t.Run("Load a plugin with service account registration", func(t *testing.T) { t.Run("Load a plugin with service account registration", func(t *testing.T) {
cfg := &config.Cfg{ cfg := &config.Cfg{
Features: fakes.NewFakeFeatureToggles(featuremgmt.FlagExternalServiceAuth), Features: featuremgmt.WithFeatures(featuremgmt.FlagExternalServiceAuth),
PluginsAllowUnsigned: []string{"grafana-test-datasource"}, PluginsAllowUnsigned: []string{"grafana-test-datasource"},
} }
pluginPaths := []string{filepath.Join(testDataDir(t), "external-registration")} pluginPaths := []string{filepath.Join(testDataDir(t), "external-registration")}

@ -3,12 +3,13 @@ package pipeline
import ( import (
"testing" "testing"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/config" "github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/log" "github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
) )
func TestSkipPlugins(t *testing.T) { func TestSkipPlugins(t *testing.T) {

@ -27,7 +27,7 @@ type Api struct {
accessControl accesscontrol.AccessControl accessControl accesscontrol.AccessControl
cfg *setting.Cfg cfg *setting.Cfg
features *featuremgmt.FeatureManager features featuremgmt.FeatureToggles
log log.Logger log log.Logger
routeRegister routing.RouteRegister routeRegister routing.RouteRegister
} }
@ -36,7 +36,7 @@ func ProvideApi(
pd publicdashboards.Service, pd publicdashboards.Service,
rr routing.RouteRegister, rr routing.RouteRegister,
ac accesscontrol.AccessControl, ac accesscontrol.AccessControl,
features *featuremgmt.FeatureManager, features featuremgmt.FeatureToggles,
md publicdashboards.Middleware, md publicdashboards.Middleware,
cfg *setting.Cfg, cfg *setting.Cfg,
) *Api { ) *Api {
@ -297,7 +297,7 @@ func (api *Api) DeletePublicDashboard(c *contextmodel.ReqContext) response.Respo
} }
// Copied from pkg/api/metrics.go // Copied from pkg/api/metrics.go
func toJsonStreamingResponse(ctx context.Context, features *featuremgmt.FeatureManager, qdr *backend.QueryDataResponse) response.Response { func toJsonStreamingResponse(ctx context.Context, features featuremgmt.FeatureToggles, qdr *backend.QueryDataResponse) response.Response {
statusWhenError := http.StatusBadRequest statusWhenError := http.StatusBadRequest
if features.IsEnabled(ctx, featuremgmt.FlagDatasourceQueryMultiStatus) { if features.IsEnabled(ctx, featuremgmt.FlagDatasourceQueryMultiStatus) {
statusWhenError = http.StatusMultiStatus statusWhenError = http.StatusMultiStatus

@ -155,6 +155,10 @@ func (f fakeFeatureToggles) IsEnabled(ctx context.Context, feature string) bool
return f.returnValue return f.returnValue
} }
func (f fakeFeatureToggles) GetEnabled(ctx context.Context) map[string]bool {
return map[string]bool{}
}
// Fake grpc secrets plugin impl // Fake grpc secrets plugin impl
type fakeGRPCSecretsPlugin struct { type fakeGRPCSecretsPlugin struct {
kv map[Key]string kv map[Key]string

@ -23,7 +23,7 @@ func SetupDisabledTestService(tb testing.TB, store secrets.Store) *SecretsServic
return setupTestService(tb, store, featuremgmt.WithFeatures(featuremgmt.FlagDisableEnvelopeEncryption)) return setupTestService(tb, store, featuremgmt.WithFeatures(featuremgmt.FlagDisableEnvelopeEncryption))
} }
func setupTestService(tb testing.TB, store secrets.Store, features *featuremgmt.FeatureManager) *SecretsService { func setupTestService(tb testing.TB, store secrets.Store, features featuremgmt.FeatureToggles) *SecretsService {
tb.Helper() tb.Helper()
defaultKey := "SdlklWklckeLS" defaultKey := "SdlklWklckeLS"
if len(setting.SecretKey) > 0 { if len(setting.SecretKey) > 0 {

@ -26,7 +26,7 @@ import (
type ExtSvcAccountsService struct { type ExtSvcAccountsService struct {
acSvc ac.Service acSvc ac.Service
features *featuremgmt.FeatureManager features featuremgmt.FeatureToggles
logger log.Logger logger log.Logger
metrics *metrics metrics *metrics
saSvc sa.Service saSvc sa.Service

@ -4,6 +4,9 @@ import (
"context" "context"
"testing" "testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/localcache" "github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/tracing"
@ -18,8 +21,6 @@ import (
sa "github.com/grafana/grafana/pkg/services/serviceaccounts" sa "github.com/grafana/grafana/pkg/services/serviceaccounts"
"github.com/grafana/grafana/pkg/services/serviceaccounts/tests" "github.com/grafana/grafana/pkg/services/serviceaccounts/tests"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
) )
type TestEnv struct { type TestEnv struct {

@ -195,7 +195,7 @@ func TestIntegration_DashboardPermissionFilter(t *testing.T) {
usr := &user.SignedInUser{OrgID: 1, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}} usr := &user.SignedInUser{OrgID: 1, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}}
for _, features := range []*featuremgmt.FeatureManager{featuremgmt.WithFeatures(), featuremgmt.WithFeatures(featuremgmt.FlagPermissionsFilterRemoveSubquery)} { for _, features := range []featuremgmt.FeatureToggles{featuremgmt.WithFeatures(), featuremgmt.WithFeatures(featuremgmt.FlagPermissionsFilterRemoveSubquery)} {
m := features.GetEnabled(context.Background()) m := features.GetEnabled(context.Background())
keys := make([]string, 0, len(m)) keys := make([]string, 0, len(m))
for k := range m { for k := range m {
@ -394,7 +394,7 @@ func TestIntegration_DashboardPermissionFilter_WithSelfContainedPermissions(t *t
usr := &user.SignedInUser{OrgID: 1, OrgRole: org.RoleViewer, AuthenticatedBy: login.ExtendedJWTModule, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.signedInUserPermissions)}} usr := &user.SignedInUser{OrgID: 1, OrgRole: org.RoleViewer, AuthenticatedBy: login.ExtendedJWTModule, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.signedInUserPermissions)}}
for _, features := range []*featuremgmt.FeatureManager{featuremgmt.WithFeatures(), featuremgmt.WithFeatures(featuremgmt.FlagPermissionsFilterRemoveSubquery)} { for _, features := range []featuremgmt.FeatureToggles{featuremgmt.WithFeatures(), featuremgmt.WithFeatures(featuremgmt.FlagPermissionsFilterRemoveSubquery)} {
m := features.GetEnabled(context.Background()) m := features.GetEnabled(context.Background())
keys := make([]string, 0, len(m)) keys := make([]string, 0, len(m))
for k := range m { for k := range m {
@ -545,7 +545,7 @@ func TestIntegration_DashboardNestedPermissionFilter(t *testing.T) {
}) })
usr := &user.SignedInUser{OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByAction(tc.permissions)}} usr := &user.SignedInUser{OrgID: orgID, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{orgID: accesscontrol.GroupScopesByAction(tc.permissions)}}
for _, features := range []*featuremgmt.FeatureManager{featuremgmt.WithFeatures(tc.features...), featuremgmt.WithFeatures(append(tc.features, featuremgmt.FlagPermissionsFilterRemoveSubquery)...)} { for _, features := range []featuremgmt.FeatureToggles{featuremgmt.WithFeatures(tc.features...), featuremgmt.WithFeatures(append(tc.features, featuremgmt.FlagPermissionsFilterRemoveSubquery)...)} {
m := features.GetEnabled(context.Background()) m := features.GetEnabled(context.Background())
keys := make([]string, 0, len(m)) keys := make([]string, 0, len(m))
for k := range m { for k := range m {
@ -716,7 +716,7 @@ func TestIntegration_DashboardNestedPermissionFilter_WithSelfContainedPermission
}), }),
}, },
} }
for _, features := range []*featuremgmt.FeatureManager{featuremgmt.WithFeatures(tc.features...), featuremgmt.WithFeatures(append(tc.features, featuremgmt.FlagPermissionsFilterRemoveSubquery)...)} { for _, features := range []featuremgmt.FeatureToggles{featuremgmt.WithFeatures(tc.features...), featuremgmt.WithFeatures(append(tc.features, featuremgmt.FlagPermissionsFilterRemoveSubquery)...)} {
m := features.GetEnabled(context.Background()) m := features.GetEnabled(context.Background())
keys := make([]string, 0, len(m)) keys := make([]string, 0, len(m))
for k := range m { for k := range m {

@ -34,7 +34,7 @@ type SSOSettingsService struct {
} }
func ProvideService(cfg *setting.Cfg, sqlStore db.DB, ac ac.AccessControl, func ProvideService(cfg *setting.Cfg, sqlStore db.DB, ac ac.AccessControl,
routeRegister routing.RouteRegister, features *featuremgmt.FeatureManager, routeRegister routing.RouteRegister, features featuremgmt.FeatureToggles,
secrets secrets.Service) *SSOSettingsService { secrets secrets.Service) *SSOSettingsService {
strategies := []ssosettings.FallbackStrategy{ strategies := []ssosettings.FallbackStrategy{
strategies.NewOAuthStrategy(cfg), strategies.NewOAuthStrategy(cfg),

@ -118,22 +118,8 @@ func GetMockService(version string, rt RoundTripper) *Service {
version: version, version: version,
fakeRoundTripper: rt, fakeRoundTripper: rt,
}, },
features: &fakeFeatureToggles{
flags: map[string]bool{
featuremgmt.FlagInfluxqlStreamingParser: false,
},
},
}
}
type fakeFeatureToggles struct { // featuremgmt.FlagInfluxqlStreamingParser: false
flags map[string]bool features: featuremgmt.WithFeatures(),
} }
func (f *fakeFeatureToggles) IsEnabledGlobally(flag string) bool {
return f.flags[flag]
}
func (f *fakeFeatureToggles) IsEnabled(ctx context.Context, flag string) bool {
return f.flags[flag]
} }

Loading…
Cancel
Save