mirror of
https://github.com/grafana/grafana.git
synced 2025-08-03 03:13:49 +08:00
FeatureFlags: Use interface rather than manager (#80000)
This commit is contained in:
@ -600,7 +600,7 @@ func (hs *HTTPServer) GetAnnotationTags(c *contextmodel.ReqContext) response.Res
|
||||
// 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
|
||||
// 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("")
|
||||
return prefix, accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, initialScope string) ([]string, error) {
|
||||
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)}}
|
||||
}
|
||||
|
||||
func setupSimpleHTTPServer(features *featuremgmt.FeatureManager) *HTTPServer {
|
||||
func setupSimpleHTTPServer(features featuremgmt.FeatureToggles) *HTTPServer {
|
||||
if features == nil {
|
||||
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.
|
||||
// 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
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ func (hs *HTTPServer) GetFeatureToggles(ctx *contextmodel.ReqContext) response.R
|
||||
dtos := make([]featuremgmt.FeatureToggleDTO, 0)
|
||||
|
||||
// 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) {
|
||||
continue
|
||||
}
|
||||
@ -67,7 +67,7 @@ func (hs *HTTPServer) UpdateFeatureToggle(ctx *contextmodel.ReqContext) response
|
||||
|
||||
for _, t := range cmd.FeatureToggles {
|
||||
// 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)
|
||||
payload.FeatureToggles[t.Name] = strconv.FormatBool(t.Enabled)
|
||||
} else {
|
||||
@ -82,13 +82,13 @@ func (hs *HTTPServer) UpdateFeatureToggle(ctx *contextmodel.ReqContext) response
|
||||
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")
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) GetFeatureMgmtState(ctx *contextmodel.ReqContext) response.Response {
|
||||
fmState := hs.Features.GetState()
|
||||
fmState := hs.featureManager.GetState()
|
||||
return response.Respond(http.StatusOK, fmState)
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,9 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"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/setting"
|
||||
"github.com/grafana/grafana/pkg/web/webtest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetFeatureToggles(t *testing.T) {
|
||||
@ -405,13 +406,16 @@ func runGetScenario(
|
||||
cfg := setting.NewCfg()
|
||||
cfg.FeatureManagement = settings
|
||||
|
||||
fm := featuremgmt.WithFeatureFlags(append([]*featuremgmt.FeatureFlag{{
|
||||
Name: featuremgmt.FlagFeatureToggleAdminPage,
|
||||
Enabled: true,
|
||||
Stage: featuremgmt.FeatureStageGeneralAvailability,
|
||||
}}, features...))
|
||||
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.Cfg = cfg
|
||||
hs.Features = featuremgmt.WithFeatureFlags(append([]*featuremgmt.FeatureFlag{{
|
||||
Name: featuremgmt.FlagFeatureToggleAdminPage,
|
||||
Enabled: true,
|
||||
Stage: featuremgmt.FeatureStageGeneralAvailability,
|
||||
}}, features...))
|
||||
hs.Features = fm
|
||||
hs.featureManager = fm
|
||||
hs.orgService = orgtest.NewOrgServiceFake()
|
||||
hs.userService = &usertest.FakeUserService{
|
||||
ExpectedUser: &user.User{ID: 1},
|
||||
@ -469,13 +473,16 @@ func runSetScenario(
|
||||
cfg := setting.NewCfg()
|
||||
cfg.FeatureManagement = settings
|
||||
|
||||
features := featuremgmt.WithFeatureFlags(append([]*featuremgmt.FeatureFlag{{
|
||||
Name: featuremgmt.FlagFeatureToggleAdminPage,
|
||||
Enabled: true,
|
||||
Stage: featuremgmt.FeatureStageGeneralAvailability,
|
||||
}}, serverFeatures...))
|
||||
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.Cfg = cfg
|
||||
hs.Features = featuremgmt.WithFeatureFlags(append([]*featuremgmt.FeatureFlag{{
|
||||
Name: featuremgmt.FlagFeatureToggleAdminPage,
|
||||
Enabled: true,
|
||||
Stage: featuremgmt.FeatureStageGeneralAvailability,
|
||||
}}, serverFeatures...))
|
||||
hs.Features = features
|
||||
hs.featureManager = features
|
||||
hs.orgService = orgtest.NewOrgServiceFake()
|
||||
hs.userService = &usertest.FakeUserService{
|
||||
ExpectedUser: &user.User{ID: 1},
|
||||
|
@ -95,7 +95,7 @@ func BenchmarkFolderListAndSearch(b *testing.B) {
|
||||
desc string
|
||||
url string
|
||||
expectedLen int
|
||||
features *featuremgmt.FeatureManager
|
||||
features featuremgmt.FeatureToggles
|
||||
}{
|
||||
{
|
||||
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()
|
||||
|
||||
m := web.New()
|
||||
|
@ -435,7 +435,7 @@ func TestFolderGetAPIEndpoint(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
URL string
|
||||
features *featuremgmt.FeatureManager
|
||||
features featuremgmt.FeatureToggles
|
||||
expectedCode int
|
||||
expectedParentUIDs []string
|
||||
expectedParentOrgIDs []int64
|
||||
|
@ -32,7 +32,7 @@ import (
|
||||
"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()
|
||||
db.InitTestDB(t)
|
||||
// nolint:staticcheck
|
||||
|
@ -126,7 +126,8 @@ type HTTPServer struct {
|
||||
RouteRegister routing.RouteRegister
|
||||
RenderService rendering.Service
|
||||
Cfg *setting.Cfg
|
||||
Features *featuremgmt.FeatureManager
|
||||
Features featuremgmt.FeatureToggles
|
||||
featureManager *featuremgmt.FeatureManager
|
||||
SettingsProvider setting.Provider
|
||||
HooksService *hooks.HooksService
|
||||
navTreeService navtree.Service
|
||||
@ -290,7 +291,8 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
ShortURLService: shortURLService,
|
||||
QueryHistoryService: queryHistoryService,
|
||||
CorrelationsService: correlationsService,
|
||||
Features: features,
|
||||
Features: features, // a read only view of the managers state
|
||||
featureManager: features,
|
||||
StorageService: storageService,
|
||||
RemoteCacheService: remoteCache,
|
||||
ProvisioningService: provisioningService,
|
||||
|
Reference in New Issue
Block a user