mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
FeatureToggles: Add context and and an explicit global check (#78081)
This commit is contained in:
parent
c887ef2c9a
commit
f69fd3726b
@ -108,13 +108,13 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
r.Get("/admin/orgs/edit/:id", authorizeInOrg(ac.UseGlobalOrg, ac.OrgsAccessEvaluator), hs.Index)
|
||||
r.Get("/admin/stats", authorize(ac.EvalPermission(ac.ActionServerStatsRead)), hs.Index)
|
||||
r.Get("/admin/authentication/ldap", authorize(ac.EvalPermission(ac.ActionLDAPStatusRead)), hs.Index)
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagStorage) {
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagStorage) {
|
||||
r.Get("/admin/storage", reqSignedIn, hs.Index)
|
||||
r.Get("/admin/storage/*", reqSignedIn, hs.Index)
|
||||
}
|
||||
|
||||
// feature toggle admin page
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagFeatureToggleAdminPage) {
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagFeatureToggleAdminPage) {
|
||||
r.Get("/admin/featuretoggles", authorize(ac.EvalPermission(ac.ActionFeatureManagementRead)), hs.Index)
|
||||
}
|
||||
|
||||
@ -156,11 +156,11 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
r.Get("/dashboards/*", reqSignedIn, hs.Index)
|
||||
r.Get("/goto/:uid", reqSignedIn, hs.redirectFromShortURL, hs.Index)
|
||||
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagDashboardEmbed) {
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagDashboardEmbed) {
|
||||
r.Get("/d-embed", reqSignedIn, middleware.AddAllowEmbeddingHeader(), hs.Index)
|
||||
}
|
||||
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagPublicDashboards) {
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagPublicDashboards) {
|
||||
// list public dashboards
|
||||
r.Get("/public-dashboards/list", reqSignedIn, hs.Index)
|
||||
|
||||
@ -216,7 +216,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
r.Get("/swagger-ui", swaggerUI)
|
||||
r.Get("/openapi3", openapi3)
|
||||
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagClientTokenRotation) {
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagClientTokenRotation) {
|
||||
r.Post("/api/user/auth-tokens/rotate", routing.Wrap(hs.RotateUserAuthToken))
|
||||
r.Get("/user/auth-tokens/rotate", routing.Wrap(hs.RotateUserAuthTokenRedirect))
|
||||
}
|
||||
@ -277,12 +277,12 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
orgRoute.Get("/quotas", authorize(ac.EvalPermission(ac.ActionOrgsQuotasRead)), routing.Wrap(hs.GetCurrentOrgQuotas))
|
||||
})
|
||||
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagStorage) {
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagStorage) {
|
||||
// Will eventually be replaced with the 'object' route
|
||||
apiRoute.Group("/storage", hs.StorageService.RegisterHTTPRoutes)
|
||||
}
|
||||
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagPanelTitleSearch) {
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagPanelTitleSearch) {
|
||||
apiRoute.Group("/search-v2", hs.SearchV2HTTPService.RegisterHTTPRoutes)
|
||||
}
|
||||
|
||||
@ -392,7 +392,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
apiRoute.Any("/plugin-proxy/:pluginId/*", requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), authorize(ac.EvalPermission(pluginaccesscontrol.ActionAppAccess, pluginIDScope)), hs.ProxyPluginRequest)
|
||||
apiRoute.Any("/plugin-proxy/:pluginId", requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), authorize(ac.EvalPermission(pluginaccesscontrol.ActionAppAccess, pluginIDScope)), hs.ProxyPluginRequest)
|
||||
|
||||
if hs.Cfg.PluginAdminEnabled && (hs.Features.IsEnabled(featuremgmt.FlagManagedPluginsInstall) || !hs.Cfg.PluginAdminExternalManageEnabled) {
|
||||
if hs.Cfg.PluginAdminEnabled && (hs.Features.IsEnabledGlobally(featuremgmt.FlagManagedPluginsInstall) || !hs.Cfg.PluginAdminExternalManageEnabled) {
|
||||
apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) {
|
||||
pluginRoute.Post("/:pluginId/install", authorize(ac.EvalPermission(pluginaccesscontrol.ActionInstall)), routing.Wrap(hs.InstallPlugin))
|
||||
pluginRoute.Post("/:pluginId/uninstall", authorize(ac.EvalPermission(pluginaccesscontrol.ActionInstall)), routing.Wrap(hs.UninstallPlugin))
|
||||
@ -405,7 +405,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
pluginRoute.Get("/:pluginId/metrics", reqOrgAdmin, routing.Wrap(hs.CollectPluginMetrics))
|
||||
})
|
||||
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagFeatureToggleAdminPage) {
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagFeatureToggleAdminPage) {
|
||||
apiRoute.Group("/featuremgmt", func(featuremgmtRoute routing.RouteRegister) {
|
||||
featuremgmtRoute.Get("/state", authorize(ac.EvalPermission(ac.ActionFeatureManagementRead)), hs.GetFeatureMgmtState)
|
||||
featuremgmtRoute.Get("/", authorize(ac.EvalPermission(ac.ActionFeatureManagementRead)), hs.GetFeatureToggles)
|
||||
|
@ -228,7 +228,7 @@ func setupSimpleHTTPServer(features *featuremgmt.FeatureManager) *HTTPServer {
|
||||
features = featuremgmt.WithFeatures()
|
||||
}
|
||||
// nolint:staticcheck
|
||||
cfg := setting.NewCfgWithFeatures(features.IsEnabled)
|
||||
cfg := setting.NewCfgWithFeatures(features.IsEnabledGlobally)
|
||||
|
||||
return &HTTPServer{
|
||||
Cfg: cfg,
|
||||
|
@ -94,7 +94,7 @@ func (hs *HTTPServer) GetDashboard(c *contextmodel.ReqContext) response.Response
|
||||
|
||||
// If public dashboards is enabled and we have a public dashboard, update meta
|
||||
// values
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagPublicDashboards) {
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagPublicDashboards) {
|
||||
publicDashboard, err := hs.PublicDashboardsApi.PublicDashboardService.FindByDashboardUid(c.Req.Context(), c.SignedInUser.GetOrgID(), dash.UID)
|
||||
if err != nil && !errors.Is(err, publicdashboardModels.ErrPublicDashboardNotFound) {
|
||||
return response.Error(http.StatusInternalServerError, "Error while retrieving public dashboards", err)
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/datasource"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
@ -356,7 +357,7 @@ func validateJSONData(ctx context.Context, jsonData *simplejson.Json, cfg *setti
|
||||
}
|
||||
|
||||
// Prevent adding a data source team header with a name that matches the auth proxy header name
|
||||
if features.IsEnabled(featuremgmt.FlagTeamHttpHeaders) {
|
||||
if features.IsEnabled(ctx, featuremgmt.FlagTeamHttpHeaders) {
|
||||
err := validateTeamHTTPHeaderJSON(jsonData)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -43,7 +43,7 @@ const REDACTED = "redacted"
|
||||
func (hs *HTTPServer) GetFolders(c *contextmodel.ReqContext) response.Response {
|
||||
var folders []*folder.Folder
|
||||
var err error
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
if hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagNestedFolders) {
|
||||
folders, err = hs.folderService.GetChildren(c.Req.Context(), &folder.GetChildrenQuery{
|
||||
OrgID: c.SignedInUser.GetOrgID(),
|
||||
Limit: c.QueryInt64("limit"),
|
||||
@ -190,7 +190,7 @@ func (hs *HTTPServer) setDefaultFolderPermissions(ctx context.Context, orgID int
|
||||
}
|
||||
|
||||
isNested := folder.ParentUID != ""
|
||||
if !isNested || !hs.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
if !isNested || !hs.Features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
permissions = append(permissions, []accesscontrol.SetResourcePermissionCommand{
|
||||
{BuiltinRole: string(org.RoleEditor), Permission: dashboards.PERMISSION_EDIT.String()},
|
||||
{BuiltinRole: string(org.RoleViewer), Permission: dashboards.PERMISSION_VIEW.String()},
|
||||
@ -212,7 +212,7 @@ func (hs *HTTPServer) setDefaultFolderPermissions(ctx context.Context, orgID int
|
||||
// 404: notFoundError
|
||||
// 500: internalServerError
|
||||
func (hs *HTTPServer) MoveFolder(c *contextmodel.ReqContext) response.Response {
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
if hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagNestedFolders) {
|
||||
cmd := folder.MoveFolderCommand{}
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
@ -390,7 +390,7 @@ func (hs *HTTPServer) newToFolderDto(c *contextmodel.ReqContext, f *folder.Folde
|
||||
return dtos.Folder{}, err
|
||||
}
|
||||
|
||||
if !hs.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
if !hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagNestedFolders) {
|
||||
return folderDTO, nil
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
|
||||
continue
|
||||
}
|
||||
|
||||
if panel.ID == "datagrid" && !hs.Features.IsEnabled(featuremgmt.FlagEnableDatagridEditing) {
|
||||
if panel.ID == "datagrid" && !hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagEnableDatagridEditing) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features *featuremgmt.
|
||||
t.Helper()
|
||||
db.InitTestDB(t)
|
||||
// nolint:staticcheck
|
||||
cfg.IsFeatureToggleEnabled = features.IsEnabled
|
||||
cfg.IsFeatureToggleEnabled = features.IsEnabledGlobally
|
||||
|
||||
{
|
||||
oldVersion := setting.BuildVersion
|
||||
|
@ -38,7 +38,7 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagIndividualCookiePreferences) {
|
||||
if hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagIndividualCookiePreferences) {
|
||||
if !prefs.Cookies("analytics") {
|
||||
settings.GoogleAnalytics4Id = ""
|
||||
settings.GoogleAnalyticsId = ""
|
||||
@ -166,7 +166,7 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV
|
||||
|
||||
hs.HooksService.RunIndexDataHooks(&data, c)
|
||||
|
||||
data.NavTree.ApplyAdminIA(hs.Features.IsEnabled(featuremgmt.FlagNavAdminSubsections))
|
||||
data.NavTree.ApplyAdminIA(hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagNavAdminSubsections))
|
||||
data.NavTree.Sort()
|
||||
|
||||
return &data, nil
|
||||
@ -244,7 +244,7 @@ func (hs *HTTPServer) getThemeForIndexData(themePrefId string, themeURLParam str
|
||||
|
||||
if pref.IsValidThemeID(themePrefId) {
|
||||
theme := pref.GetThemeByID(themePrefId)
|
||||
if !theme.IsExtra || hs.Features.IsEnabled(featuremgmt.FlagExtraThemes) {
|
||||
if !theme.IsExtra || hs.Features.IsEnabledGlobally(featuremgmt.FlagExtraThemes) {
|
||||
return theme
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ func (hs *HTTPServer) CookieOptionsFromCfg() cookies.CookieOptions {
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) LoginView(c *contextmodel.ReqContext) {
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagClientTokenRotation) {
|
||||
if hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagClientTokenRotation) {
|
||||
if errors.Is(c.LookupTokenErr, authn.ErrTokenNeedsRotation) {
|
||||
c.Redirect(hs.Cfg.AppSubURL + "/")
|
||||
return
|
||||
@ -334,7 +334,7 @@ func (hs *HTTPServer) RedirectResponseWithError(c *contextmodel.ReqContext, err
|
||||
|
||||
func (hs *HTTPServer) redirectURLWithErrorCookie(c *contextmodel.ReqContext, err error) string {
|
||||
setCookie := true
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagIndividualCookiePreferences) {
|
||||
if hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagIndividualCookiePreferences) {
|
||||
var userID int64
|
||||
if c.SignedInUser != nil && !c.SignedInUser.IsNil() {
|
||||
var errID error
|
||||
|
@ -63,7 +63,7 @@ func (hs *HTTPServer) QueryMetricsV2(c *contextmodel.ReqContext) response.Respon
|
||||
|
||||
func (hs *HTTPServer) toJsonStreamingResponse(ctx context.Context, qdr *backend.QueryDataResponse) response.Response {
|
||||
statusWhenError := http.StatusBadRequest
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagDatasourceQueryMultiStatus) {
|
||||
if hs.Features.IsEnabled(ctx, featuremgmt.FlagDatasourceQueryMultiStatus) {
|
||||
statusWhenError = http.StatusMultiStatus
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
func (hs *HTTPServer) registerPlaylistAPI(apiRoute routing.RouteRegister) {
|
||||
// Register the actual handlers
|
||||
apiRoute.Group("/playlists", func(playlistRoute routing.RouteRegister) {
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagKubernetesPlaylists) {
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagKubernetesPlaylists) {
|
||||
// Use k8s client to implement legacy API
|
||||
handler := newPlaylistK8sHandler(hs)
|
||||
playlistRoute.Get("/", handler.searchPlaylists)
|
||||
|
@ -270,7 +270,7 @@ func (proxy *DataSourceProxy) director(req *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
if proxy.features.IsEnabled(featuremgmt.FlagIdForwarding) && auth.IsIDForwardingEnabledForDataSource(proxy.ds) {
|
||||
if proxy.features.IsEnabled(req.Context(), featuremgmt.FlagIdForwarding) && auth.IsIDForwardingEnabledForDataSource(proxy.ds) {
|
||||
proxyutil.ApplyForwardIDHeader(req, proxy.ctx.SignedInUser)
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
@ -17,7 +19,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/util/proxyutil"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
type PluginProxy struct {
|
||||
@ -161,7 +162,7 @@ func (proxy PluginProxy) director(req *http.Request) {
|
||||
|
||||
proxyutil.ApplyUserHeader(proxy.cfg.SendUserHeader, req, proxy.ctx.SignedInUser)
|
||||
|
||||
if proxy.features.IsEnabled(featuremgmt.FlagIdForwarding) {
|
||||
if proxy.features.IsEnabled(req.Context(), featuremgmt.FlagIdForwarding) {
|
||||
proxyutil.ApplyForwardIDHeader(req, proxy.ctx.SignedInUser)
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ type DataPipeline []Node
|
||||
func (dp *DataPipeline) execute(c context.Context, now time.Time, s *Service) (mathexp.Vars, error) {
|
||||
vars := make(mathexp.Vars)
|
||||
|
||||
groupByDSFlag := s.features.IsEnabled(featuremgmt.FlagSseGroupByDatasource)
|
||||
groupByDSFlag := s.features.IsEnabled(c, featuremgmt.FlagSseGroupByDatasource)
|
||||
// Execute datasource nodes first, and grouped by datasource.
|
||||
if groupByDSFlag {
|
||||
dsNodes := []*DSNode{}
|
||||
@ -227,7 +227,7 @@ func (s *Service) buildGraph(req *Request) (*simple.DirectedGraph, error) {
|
||||
case TypeCMDNode:
|
||||
node, err = buildCMDNode(rn, s.features)
|
||||
case TypeMLNode:
|
||||
if s.features.IsEnabled(featuremgmt.FlagMlExpressions) {
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagMlExpressions) {
|
||||
node, err = s.buildMLNode(dp, rn, req)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("fail to parse expression with refID %v: %w", rn.RefID, err)
|
||||
|
@ -388,7 +388,7 @@ func convertDataFramesToResults(ctx context.Context, frames data.Frames, datasou
|
||||
}
|
||||
|
||||
var dt data.FrameType
|
||||
dt, useDataplane, _ := shouldUseDataplane(frames, logger, s.features.IsEnabled(featuremgmt.FlagDisableSSEDataplane))
|
||||
dt, useDataplane, _ := shouldUseDataplane(frames, logger, s.features.IsEnabled(ctx, featuremgmt.FlagDisableSSEDataplane))
|
||||
if useDataplane {
|
||||
logger.Debug("Handling SSE data source query through dataplane", "datatype", dt)
|
||||
result, err := handleDataplaneFrames(ctx, s.tracer, dt, frames)
|
||||
|
@ -81,7 +81,7 @@ func UnmarshalThresholdCommand(rn *rawNode, features featuremgmt.FeatureToggles)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid condition: %w", err)
|
||||
}
|
||||
if firstCondition.UnloadEvaluator != nil && features.IsEnabled(featuremgmt.FlagRecoveryThreshold) {
|
||||
if firstCondition.UnloadEvaluator != nil && features.IsEnabledGlobally(featuremgmt.FlagRecoveryThreshold) {
|
||||
unloading, err := NewThresholdCommand(rn.RefID, referenceVar, firstCondition.UnloadEvaluator.Type, firstCondition.UnloadEvaluator.Params)
|
||||
unloading.Invert = true
|
||||
if err != nil {
|
||||
|
@ -134,7 +134,7 @@ func (s *SocialGoogle) extractFromAPI(ctx context.Context, client *http.Client)
|
||||
}
|
||||
|
||||
func (s *SocialGoogle) AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string {
|
||||
if s.features.IsEnabled(featuremgmt.FlagAccessTokenExpirationCheck) && s.useRefreshToken {
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagAccessTokenExpirationCheck) && s.useRefreshToken {
|
||||
opts = append(opts, oauth2.AccessTypeOffline, oauth2.ApprovalForce)
|
||||
}
|
||||
return s.SocialBase.AuthCodeURL(state, opts...)
|
||||
|
@ -206,7 +206,7 @@ func ProvideService(cfg *setting.Cfg,
|
||||
forceUseGraphAPI: sec.Key("force_use_graph_api").MustBool(false),
|
||||
skipOrgRoleSync: cfg.AzureADSkipOrgRoleSync,
|
||||
}
|
||||
if info.UseRefreshToken && features.IsEnabled(featuremgmt.FlagAccessTokenExpirationCheck) {
|
||||
if info.UseRefreshToken && features.IsEnabledGlobally(featuremgmt.FlagAccessTokenExpirationCheck) {
|
||||
appendUniqueScope(&config, OfflineAccessScope)
|
||||
}
|
||||
}
|
||||
@ -219,7 +219,7 @@ func ProvideService(cfg *setting.Cfg,
|
||||
allowedGroups: util.SplitString(sec.Key("allowed_groups").String()),
|
||||
skipOrgRoleSync: cfg.OktaSkipOrgRoleSync,
|
||||
}
|
||||
if info.UseRefreshToken && features.IsEnabled(featuremgmt.FlagAccessTokenExpirationCheck) {
|
||||
if info.UseRefreshToken && features.IsEnabledGlobally(featuremgmt.FlagAccessTokenExpirationCheck) {
|
||||
appendUniqueScope(&config, OfflineAccessScope)
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ func (l *loggerImpl) Middleware() web.Middleware {
|
||||
// put the start time on context so we can measure it later.
|
||||
r = r.WithContext(log.InitstartTime(r.Context(), time.Now()))
|
||||
|
||||
if l.flags.IsEnabled(featuremgmt.FlagUnifiedRequestLog) {
|
||||
if l.flags.IsEnabled(r.Context(), featuremgmt.FlagUnifiedRequestLog) {
|
||||
r = r.WithContext(errutil.SetUnifiedLogging(r.Context()))
|
||||
}
|
||||
|
||||
@ -128,7 +128,7 @@ func (l *loggerImpl) prepareLogParams(c *contextmodel.ReqContext, duration time.
|
||||
logParams = append(logParams, "handler", handler)
|
||||
}
|
||||
|
||||
if l.flags.IsEnabled(featuremgmt.FlagRequestInstrumentationStatusSource) {
|
||||
if l.flags.IsEnabled(r.Context(), featuremgmt.FlagRequestInstrumentationStatusSource) {
|
||||
rmd := requestmeta.GetRequestMetaData(c.Req.Context())
|
||||
logParams = append(logParams, "status_source", rmd.StatusSource)
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ func RequestMetrics(features featuremgmt.FeatureToggles, cfg *setting.Cfg, promR
|
||||
|
||||
histogramLabels := []string{"handler", "status_code", "method"}
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagRequestInstrumentationStatusSource) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagRequestInstrumentationStatusSource) {
|
||||
histogramLabels = append(histogramLabels, "status_source")
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ func RequestMetrics(features featuremgmt.FeatureToggles, cfg *setting.Cfg, promR
|
||||
histogramLabels = append(histogramLabels, "grafana_team")
|
||||
}
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagHttpSLOLevels) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagHttpSLOLevels) {
|
||||
histogramLabels = append(histogramLabels, "slo_group")
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ func RequestMetrics(features featuremgmt.FeatureToggles, cfg *setting.Cfg, promR
|
||||
Buckets: defBuckets,
|
||||
}
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagEnableNativeHTTPHistogram) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagEnableNativeHTTPHistogram) {
|
||||
// the recommended default value from the prom_client
|
||||
// https://github.com/prometheus/client_golang/blob/main/prometheus/histogram.go#L411
|
||||
// Giving this variable an value means the client will expose the histograms as an
|
||||
@ -95,7 +95,7 @@ func RequestMetrics(features featuremgmt.FeatureToggles, cfg *setting.Cfg, promR
|
||||
handler = "notfound"
|
||||
} else {
|
||||
// log requests where we could not identify handler so we can register them.
|
||||
if features.IsEnabled(featuremgmt.FlagLogRequestsInstrumentedAsUnknown) {
|
||||
if features.IsEnabled(r.Context(), featuremgmt.FlagLogRequestsInstrumentedAsUnknown) {
|
||||
log.Warn("request instrumented as unknown", "path", r.URL.Path, "status_code", status)
|
||||
}
|
||||
}
|
||||
@ -104,7 +104,7 @@ func RequestMetrics(features featuremgmt.FeatureToggles, cfg *setting.Cfg, promR
|
||||
labelValues := []string{handler, code, r.Method}
|
||||
rmd := requestmeta.GetRequestMetaData(r.Context())
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagRequestInstrumentationStatusSource) {
|
||||
if features.IsEnabled(r.Context(), featuremgmt.FlagRequestInstrumentationStatusSource) {
|
||||
labelValues = append(labelValues, string(rmd.StatusSource))
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ func RequestMetrics(features featuremgmt.FeatureToggles, cfg *setting.Cfg, promR
|
||||
labelValues = append(labelValues, rmd.Team)
|
||||
}
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagHttpSLOLevels) {
|
||||
if features.IsEnabled(r.Context(), featuremgmt.FlagHttpSLOLevels) {
|
||||
labelValues = append(labelValues, string(rmd.SLOGroup))
|
||||
}
|
||||
|
||||
|
@ -155,7 +155,7 @@ func (fn ClientMiddlewareFunc) CreateClientMiddleware(next Client) Client {
|
||||
}
|
||||
|
||||
type FeatureToggles interface {
|
||||
IsEnabled(flag string) bool
|
||||
IsEnabledGlobally(flag string) bool
|
||||
GetEnabled(ctx context.Context) map[string]bool
|
||||
}
|
||||
|
||||
|
@ -594,6 +594,6 @@ func (f *FakeFeatureToggles) GetEnabled(_ context.Context) map[string]bool {
|
||||
return f.features
|
||||
}
|
||||
|
||||
func (f *FakeFeatureToggles) IsEnabled(feature string) bool {
|
||||
func (f *FakeFeatureToggles) IsEnabledGlobally(feature string) bool {
|
||||
return f.features[feature]
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ func (s *Service) Base(n PluginInfo) (string, error) {
|
||||
func (s *Service) Module(n PluginInfo) (string, error) {
|
||||
if n.class == plugins.ClassCore {
|
||||
if s.cfg.Features != nil &&
|
||||
s.cfg.Features.IsEnabled(featuremgmt.FlagExternalCorePlugins) &&
|
||||
s.cfg.Features.IsEnabledGlobally(featuremgmt.FlagExternalCorePlugins) &&
|
||||
filepath.Base(n.dir) == "dist" {
|
||||
// The core plugin has been built externally, use the module from the dist folder
|
||||
} else {
|
||||
|
@ -37,7 +37,7 @@ type TestingAPIBuilder struct {
|
||||
}
|
||||
|
||||
func RegisterAPIService(features featuremgmt.FeatureToggles, apiregistration grafanaapiserver.APIRegistrar) *TestingAPIBuilder {
|
||||
if !features.IsEnabled(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) {
|
||||
if !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) {
|
||||
return nil // skip registration unless opting into experimental apis
|
||||
}
|
||||
builder := &TestingAPIBuilder{
|
||||
|
@ -43,7 +43,7 @@ func ProvideService(cfg *setting.Cfg, db db.DB, routeRegister routing.RouteRegis
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagSplitScopes) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagSplitScopes) {
|
||||
// Migrating scopes that haven't been split yet to have kind, attribute and identifier in the DB
|
||||
// This will be removed once we've:
|
||||
// 1) removed the feature toggle and
|
||||
@ -213,9 +213,9 @@ func permissionCacheKey(user identity.Requester) string {
|
||||
|
||||
// DeclarePluginRoles allow the caller to declare, to the service, plugin roles and their assignments
|
||||
// to organization roles ("Viewer", "Editor", "Admin") or "Grafana Admin"
|
||||
func (s *Service) DeclarePluginRoles(_ context.Context, ID, name string, regs []plugins.RoleRegistration) error {
|
||||
func (s *Service) DeclarePluginRoles(ctx context.Context, ID, name string, regs []plugins.RoleRegistration) error {
|
||||
// Protect behind feature toggle
|
||||
if !s.features.IsEnabled(featuremgmt.FlagAccessControlOnCall) {
|
||||
if !s.features.IsEnabled(ctx, featuremgmt.FlagAccessControlOnCall) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -397,7 +397,7 @@ func PermissionMatchesSearchOptions(permission accesscontrol.Permission, searchO
|
||||
}
|
||||
|
||||
func (s *Service) SaveExternalServiceRole(ctx context.Context, cmd accesscontrol.SaveExternalServiceRoleCommand) error {
|
||||
if !(s.features.IsEnabled(featuremgmt.FlagExternalServiceAuth) || s.features.IsEnabled(featuremgmt.FlagExternalServiceAccounts)) {
|
||||
if !(s.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAuth) || s.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAccounts)) {
|
||||
s.log.Debug("Registering an external service role is behind a feature flag, enable it to use this feature.")
|
||||
return nil
|
||||
}
|
||||
@ -410,7 +410,7 @@ func (s *Service) SaveExternalServiceRole(ctx context.Context, cmd accesscontrol
|
||||
}
|
||||
|
||||
func (s *Service) DeleteExternalServiceRole(ctx context.Context, externalServiceID string) error {
|
||||
if !(s.features.IsEnabled(featuremgmt.FlagExternalServiceAuth) || s.features.IsEnabled(featuremgmt.FlagExternalServiceAccounts)) {
|
||||
if !(s.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAuth) || s.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAccounts)) {
|
||||
s.log.Debug("Deleting an external service role is behind a feature flag, enable it to use this feature.")
|
||||
return nil
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ func (api *AccessControlAPI) RegisterAPIEndpoints() {
|
||||
api.RouteRegister.Group("/api/access-control", func(rr routing.RouteRegister) {
|
||||
rr.Get("/user/actions", middleware.ReqSignedIn, routing.Wrap(api.getUserActions))
|
||||
rr.Get("/user/permissions", middleware.ReqSignedIn, routing.Wrap(api.getUserPermissions))
|
||||
if api.features.IsEnabled(featuremgmt.FlagAccessControlOnCall) {
|
||||
if api.features.IsEnabledGlobally(featuremgmt.FlagAccessControlOnCall) {
|
||||
userIDScope := ac.Scope("users", "id", ac.Parameter(":userID"))
|
||||
rr.Get("/users/permissions/search", authorize(ac.EvalPermission(ac.ActionUsersPermissionsRead)), routing.Wrap(api.searchUsersPermissions))
|
||||
rr.Get("/user/:userID/permissions/search", authorize(ac.EvalPermission(ac.ActionUsersPermissionsRead, userIDScope)), routing.Wrap(api.searchUserPermissions))
|
||||
|
@ -655,7 +655,7 @@ func (s *store) createPermissions(sess *db.Session, roleID int64, resource, reso
|
||||
p.RoleID = roleID
|
||||
p.Created = time.Now()
|
||||
p.Updated = time.Now()
|
||||
if s.features.IsEnabled(featuremgmt.FlagSplitScopes) {
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagSplitScopes) {
|
||||
p.Kind, p.Attribute, p.Identifier = p.SplitScope()
|
||||
}
|
||||
permissions = append(permissions, p)
|
||||
|
@ -32,7 +32,7 @@ func ProvideService(
|
||||
) *Service {
|
||||
s := &Service{cfg: cfg, logger: log.New("id-service"), signer: signer, cache: cache, metrics: newMetrics(reg)}
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagIdForwarding) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagIdForwarding) {
|
||||
authnService.RegisterPostAuthHook(s.hook, 140)
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ type LocalSigner struct {
|
||||
}
|
||||
|
||||
func (s *LocalSigner) SignIDToken(ctx context.Context, claims *auth.IDClaims) (string, error) {
|
||||
if !s.features.IsEnabled(featuremgmt.FlagIdForwarding) {
|
||||
if !s.features.IsEnabled(ctx, featuremgmt.FlagIdForwarding) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ func ProvideService(
|
||||
s.RegisterClient(clients.ProvideJWT(jwtService, cfg))
|
||||
}
|
||||
|
||||
if s.cfg.ExtendedJWTAuthEnabled && features.IsEnabled(featuremgmt.FlagExternalServiceAuth) {
|
||||
if s.cfg.ExtendedJWTAuthEnabled && features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAuth) {
|
||||
s.RegisterClient(clients.ProvideExtendedJWT(userService, cfg, signingKeysService, oauthServer))
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ func ProvideService(
|
||||
s.RegisterPostAuthHook(orgUserSyncService.SyncOrgRolesHook, 30)
|
||||
s.RegisterPostAuthHook(userSyncService.SyncLastSeenHook, 120)
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagAccessTokenExpirationCheck) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagAccessTokenExpirationCheck) {
|
||||
s.RegisterPostAuthHook(sync.ProvideOAuthTokenSync(oauthTokenService, sessionService, socialService).SyncOauthTokenHook, 60)
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ func (s *Session) Authenticate(ctx context.Context, r *authn.Request) (*authn.Id
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(featuremgmt.FlagClientTokenRotation) {
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagClientTokenRotation) {
|
||||
if token.NeedsRotation(time.Duration(s.cfg.TokenRotationIntervalMinutes) * time.Minute) {
|
||||
return nil, authn.ErrTokenNeedsRotation.Errorf("token needs to be rotated")
|
||||
}
|
||||
@ -88,7 +88,7 @@ func (s *Session) Priority() uint {
|
||||
}
|
||||
|
||||
func (s *Session) Hook(ctx context.Context, identity *authn.Identity, r *authn.Request) error {
|
||||
if identity.SessionToken == nil || s.features.IsEnabled(featuremgmt.FlagClientTokenRotation) {
|
||||
if identity.SessionToken == nil || s.features.IsEnabled(ctx, featuremgmt.FlagClientTokenRotation) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,9 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
@ -18,8 +21,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
func ProvideService(cfg *setting.Cfg, tracer tracing.Tracer, features *featuremgmt.FeatureManager, authnService authn.Service,
|
||||
@ -140,7 +141,7 @@ func (h *ContextHandler) Middleware(next http.Handler) http.Handler {
|
||||
|
||||
func (h *ContextHandler) deleteInvalidCookieEndOfRequestFunc(reqContext *contextmodel.ReqContext) web.BeforeFunc {
|
||||
return func(w web.ResponseWriter) {
|
||||
if h.features.IsEnabled(featuremgmt.FlagClientTokenRotation) {
|
||||
if h.features.IsEnabled(reqContext.Req.Context(), featuremgmt.FlagClientTokenRotation) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ func ProvideDashboardStore(sqlStore db.DB, cfg *setting.Cfg, features featuremgm
|
||||
}
|
||||
|
||||
func (d *dashboardStore) emitEntityEvent() bool {
|
||||
return d.features != nil && d.features.IsEnabled(featuremgmt.FlagPanelTitleSearch)
|
||||
return d.features != nil && d.features.IsEnabledGlobally(featuremgmt.FlagPanelTitleSearch)
|
||||
}
|
||||
|
||||
func (d *dashboardStore) ValidateDashboardBeforeSave(ctx context.Context, dashboard *dashboards.Dashboard, overwrite bool) (bool, error) {
|
||||
@ -1010,7 +1010,12 @@ func (d *dashboardStore) FindDashboards(ctx context.Context, query *dashboards.F
|
||||
}
|
||||
|
||||
if len(query.FolderUIDs) > 0 {
|
||||
filters = append(filters, searchstore.FolderUIDFilter{Dialect: d.store.GetDialect(), OrgID: orgID, UIDs: query.FolderUIDs, NestedFoldersEnabled: d.features.IsEnabled(featuremgmt.FlagNestedFolders)})
|
||||
filters = append(filters, searchstore.FolderUIDFilter{
|
||||
Dialect: d.store.GetDialect(),
|
||||
OrgID: orgID,
|
||||
UIDs: query.FolderUIDs,
|
||||
NestedFoldersEnabled: d.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders),
|
||||
})
|
||||
}
|
||||
|
||||
var res []dashboards.DashboardSearchProjection
|
||||
|
@ -198,7 +198,7 @@ func (s *Service) AddDataSource(ctx context.Context, cmd *datasources.AddDataSou
|
||||
var err error
|
||||
|
||||
cmd.EncryptedSecureJsonData = make(map[string][]byte)
|
||||
if !s.features.IsEnabled(featuremgmt.FlagDisableSecretsCompatibility) {
|
||||
if !s.features.IsEnabled(ctx, featuremgmt.FlagDisableSecretsCompatibility) {
|
||||
cmd.EncryptedSecureJsonData, err = s.SecretsService.EncryptJsonData(ctx, cmd.SecureJsonData, secrets.WithoutScope())
|
||||
if err != nil {
|
||||
return err
|
||||
@ -689,7 +689,7 @@ func (s *Service) fillWithSecureJSONData(ctx context.Context, cmd *datasources.U
|
||||
}
|
||||
|
||||
cmd.EncryptedSecureJsonData = make(map[string][]byte)
|
||||
if !s.features.IsEnabled(featuremgmt.FlagDisableSecretsCompatibility) {
|
||||
if !s.features.IsEnabled(ctx, featuremgmt.FlagDisableSecretsCompatibility) {
|
||||
cmd.EncryptedSecureJsonData, err = s.SecretsService.EncryptJsonData(ctx, cmd.SecureJsonData, secrets.WithoutScope())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -65,7 +65,7 @@ type OAuth2ServiceImpl struct {
|
||||
func ProvideService(router routing.RouteRegister, bus bus.Bus, db db.DB, cfg *setting.Cfg,
|
||||
extSvcAccSvc serviceaccounts.ExtSvcAccountsService, accessControl ac.AccessControl, acSvc ac.Service, userSvc user.Service,
|
||||
teamSvc team.Service, keySvc signingkeys.Service, fmgmt *featuremgmt.FeatureManager) (*OAuth2ServiceImpl, error) {
|
||||
if !fmgmt.IsEnabled(featuremgmt.FlagExternalServiceAuth) {
|
||||
if !fmgmt.IsEnabledGlobally(featuremgmt.FlagExternalServiceAuth) {
|
||||
return nil, nil
|
||||
}
|
||||
config := &fosite.Config{
|
||||
|
@ -51,14 +51,14 @@ func (r *Registry) RemoveExternalService(ctx context.Context, name string) error
|
||||
|
||||
switch provider {
|
||||
case extsvcauth.ServiceAccounts:
|
||||
if !r.features.IsEnabled(featuremgmt.FlagExternalServiceAccounts) {
|
||||
if !r.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAccounts) {
|
||||
r.logger.Debug("Skipping External Service removal, flag disabled", "service", name, "flag", featuremgmt.FlagExternalServiceAccounts)
|
||||
return nil
|
||||
}
|
||||
r.logger.Debug("Routing External Service removal to the External Service Account service", "service", name)
|
||||
return r.saReg.RemoveExternalService(ctx, name)
|
||||
case extsvcauth.OAuth2Server:
|
||||
if !r.features.IsEnabled(featuremgmt.FlagExternalServiceAuth) {
|
||||
if !r.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAuth) {
|
||||
r.logger.Debug("Skipping External Service removal, flag disabled", "service", name, "flag", featuremgmt.FlagExternalServiceAccounts)
|
||||
return nil
|
||||
}
|
||||
@ -80,14 +80,14 @@ func (r *Registry) SaveExternalService(ctx context.Context, cmd *extsvcauth.Exte
|
||||
|
||||
switch cmd.AuthProvider {
|
||||
case extsvcauth.ServiceAccounts:
|
||||
if !r.features.IsEnabled(featuremgmt.FlagExternalServiceAccounts) {
|
||||
if !r.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAccounts) {
|
||||
r.logger.Warn("Skipping External Service authentication, flag disabled", "service", cmd.Name, "flag", featuremgmt.FlagExternalServiceAccounts)
|
||||
return nil, nil
|
||||
}
|
||||
r.logger.Debug("Routing the External Service registration to the External Service Account service", "service", cmd.Name)
|
||||
return r.saReg.SaveExternalService(ctx, cmd)
|
||||
case extsvcauth.OAuth2Server:
|
||||
if !r.features.IsEnabled(featuremgmt.FlagExternalServiceAuth) {
|
||||
if !r.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAuth) {
|
||||
r.logger.Warn("Skipping External Service authentication, flag disabled", "service", cmd.Name, "flag", featuremgmt.FlagExternalServiceAuth)
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -126,7 +126,12 @@ func (fm *FeatureManager) readFile() error {
|
||||
}
|
||||
|
||||
// IsEnabled checks if a feature is enabled
|
||||
func (fm *FeatureManager) IsEnabled(flag string) bool {
|
||||
func (fm *FeatureManager) IsEnabled(ctx context.Context, flag string) bool {
|
||||
return fm.enabled[flag]
|
||||
}
|
||||
|
||||
// IsEnabledGlobally checks if a feature is for all tenants
|
||||
func (fm *FeatureManager) IsEnabledGlobally(flag string) bool {
|
||||
return fm.enabled[flag]
|
||||
}
|
||||
|
||||
|
@ -10,17 +10,17 @@ import (
|
||||
func TestFeatureManager(t *testing.T) {
|
||||
t.Run("check testing stubs", func(t *testing.T) {
|
||||
ft := WithFeatures("a", "b", "c")
|
||||
require.True(t, ft.IsEnabled("a"))
|
||||
require.True(t, ft.IsEnabled("b"))
|
||||
require.True(t, ft.IsEnabled("c"))
|
||||
require.False(t, ft.IsEnabled("d"))
|
||||
require.True(t, ft.IsEnabledGlobally("a"))
|
||||
require.True(t, ft.IsEnabledGlobally("b"))
|
||||
require.True(t, ft.IsEnabledGlobally("c"))
|
||||
require.False(t, ft.IsEnabledGlobally("d"))
|
||||
|
||||
require.Equal(t, map[string]bool{"a": true, "b": true, "c": true}, ft.GetEnabled(context.Background()))
|
||||
|
||||
// Explicit values
|
||||
ft = WithFeatures("a", true, "b", false)
|
||||
require.True(t, ft.IsEnabled("a"))
|
||||
require.False(t, ft.IsEnabled("b"))
|
||||
require.True(t, ft.IsEnabledGlobally("a"))
|
||||
require.False(t, ft.IsEnabledGlobally("b"))
|
||||
require.Equal(t, map[string]bool{"a": true}, ft.GetEnabled(context.Background()))
|
||||
})
|
||||
|
||||
@ -37,9 +37,9 @@ func TestFeatureManager(t *testing.T) {
|
||||
Name: "b",
|
||||
Expression: "true",
|
||||
})
|
||||
require.False(t, ft.IsEnabled("a"))
|
||||
require.True(t, ft.IsEnabled("b"))
|
||||
require.False(t, ft.IsEnabled("c")) // uknown flag
|
||||
require.False(t, ft.IsEnabledGlobally("a"))
|
||||
require.True(t, ft.IsEnabledGlobally("b"))
|
||||
require.False(t, ft.IsEnabledGlobally("c")) // uknown flag
|
||||
|
||||
// Try changing "requires license"
|
||||
ft.registerFlags(FeatureFlag{
|
||||
@ -49,9 +49,9 @@ func TestFeatureManager(t *testing.T) {
|
||||
Name: "b",
|
||||
RequiresLicense: true, // expression is still "true"
|
||||
})
|
||||
require.False(t, ft.IsEnabled("a"))
|
||||
require.False(t, ft.IsEnabled("b"))
|
||||
require.False(t, ft.IsEnabled("c"))
|
||||
require.False(t, ft.IsEnabledGlobally("a"))
|
||||
require.False(t, ft.IsEnabledGlobally("b"))
|
||||
require.False(t, ft.IsEnabledGlobally("c"))
|
||||
})
|
||||
|
||||
t.Run("check description and docs configs", func(t *testing.T) {
|
||||
|
@ -2,11 +2,21 @@ package featuremgmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type FeatureToggles interface {
|
||||
IsEnabled(flag string) bool
|
||||
// Check if a feature is enabled for a given context.
|
||||
// The settings may be per user, tenant, or globally set in the cloud
|
||||
IsEnabled(ctx context.Context, flag string) bool
|
||||
|
||||
// Check if a flag is configured globally. For now, this is the same
|
||||
// as the function above, however it will move to only checking flags that
|
||||
// are configured by the operator and shared across all tenants.
|
||||
// Use of global feature flags should be limited and careful as they require
|
||||
// a full server restart for a change to take place.
|
||||
IsEnabledGlobally(flag string) bool
|
||||
}
|
||||
|
||||
// FeatureFlagStage indicates the quality level
|
||||
|
@ -74,7 +74,7 @@ func ProvideManagerService(cfg *setting.Cfg, licensing licensing.Licensing) (*Fe
|
||||
|
||||
// Minimum approach to avoid circular dependency
|
||||
// nolint:staticcheck
|
||||
cfg.IsFeatureToggleEnabled = mgmt.IsEnabled
|
||||
cfg.IsFeatureToggleEnabled = mgmt.IsEnabledGlobally
|
||||
return mgmt, nil
|
||||
}
|
||||
|
||||
|
@ -43,8 +43,8 @@ func TestFeatureService(t *testing.T) {
|
||||
require.NotNil(t, mgmt)
|
||||
|
||||
// Enterprise features do not fall though automatically
|
||||
require.False(t, mgmt.IsEnabled("a.yes.default"))
|
||||
require.False(t, mgmt.IsEnabled("a.yes")) // licensed, but not enabled
|
||||
require.False(t, mgmt.IsEnabledGlobally("a.yes.default"))
|
||||
require.False(t, mgmt.IsEnabledGlobally("a.yes")) // licensed, but not enabled
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -156,7 +156,7 @@ func (s *Service) Get(ctx context.Context, cmd *folder.GetFolderQuery) (*folder.
|
||||
return nil, dashboards.ErrFolderAccessDenied
|
||||
}
|
||||
|
||||
if !s.features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
if !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
return dashFolder, nil
|
||||
}
|
||||
|
||||
@ -328,7 +328,7 @@ func (s *Service) deduplicateAvailableFolders(ctx context.Context, folders []*fo
|
||||
}
|
||||
|
||||
func (s *Service) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
|
||||
if !s.features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
if !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
return nil, nil
|
||||
}
|
||||
return s.store.GetParents(ctx, q)
|
||||
@ -360,7 +360,7 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
|
||||
dashFolder := dashboards.NewDashboardFolder(cmd.Title)
|
||||
dashFolder.OrgID = cmd.OrgID
|
||||
|
||||
if s.features.IsEnabled(featuremgmt.FlagNestedFolders) && cmd.ParentUID != "" {
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) && cmd.ParentUID != "" {
|
||||
// Check that the user is allowed to create a subfolder in this folder
|
||||
evaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(cmd.ParentUID))
|
||||
hasAccess, evalErr := s.accessControl.Evaluate(ctx, cmd.SignedInUser, evaluator)
|
||||
@ -801,7 +801,7 @@ func (s *Service) GetDescendantCounts(ctx context.Context, cmd *folder.GetDescen
|
||||
|
||||
result := []string{*cmd.UID}
|
||||
countsMap := make(folder.DescendantCounts, len(s.registry)+1)
|
||||
if s.features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) {
|
||||
subfolders, err := s.getNestedFolders(ctx, cmd.OrgID, *cmd.UID)
|
||||
if err != nil {
|
||||
logger.Error("failed to get subfolders", "error", err)
|
||||
|
@ -46,7 +46,7 @@ func newConfig(cfg *setting.Cfg, features featuremgmt.FeatureToggles) *config {
|
||||
host := fmt.Sprintf("%s:%d", ip, port)
|
||||
|
||||
return &config{
|
||||
enabled: features.IsEnabled(featuremgmt.FlagGrafanaAPIServer),
|
||||
enabled: features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServer),
|
||||
devMode: cfg.Env == setting.Dev,
|
||||
dataPath: filepath.Join(cfg.DataPath, "grafana-apiserver"),
|
||||
ip: ip,
|
||||
|
@ -45,7 +45,7 @@ func ProvideService(cfg *setting.Cfg, features featuremgmt.FeatureToggles, authe
|
||||
s := &gPRCServerService{
|
||||
cfg: cfg,
|
||||
logger: log.New("grpc-server"),
|
||||
enabled: features.IsEnabled(featuremgmt.FlagGrpcServer),
|
||||
enabled: features.IsEnabledGlobally(featuremgmt.FlagGrpcServer),
|
||||
}
|
||||
|
||||
// Register the metric here instead of an init() function so that we do
|
||||
|
@ -21,7 +21,7 @@ func (l *LibraryElementService) registerAPIEndpoints() {
|
||||
l.RouteRegister.Group("/api/library-elements", func(entities routing.RouteRegister) {
|
||||
uidScope := ScopeLibraryPanelsProvider.GetResourceScopeUID(ac.Parameter(":uid"))
|
||||
|
||||
if l.features.IsEnabled(featuremgmt.FlagLibraryPanelRBAC) {
|
||||
if l.features.IsEnabledGlobally(featuremgmt.FlagLibraryPanelRBAC) {
|
||||
entities.Post("/", authorize(ac.EvalPermission(ActionLibraryPanelsCreate)), routing.Wrap(l.createHandler))
|
||||
entities.Delete("/:uid", authorize(ac.EvalPermission(ActionLibraryPanelsDelete, uidScope)), routing.Wrap(l.deleteHandler))
|
||||
entities.Get("/", authorize(ac.EvalPermission(ActionLibraryPanelsRead)), routing.Wrap(l.getAllHandler))
|
||||
@ -176,7 +176,7 @@ func (l *LibraryElementService) getAllHandler(c *contextmodel.ReqContext) respon
|
||||
return toLibraryElementError(err, "Failed to get library elements")
|
||||
}
|
||||
|
||||
if l.features.IsEnabled(featuremgmt.FlagLibraryPanelRBAC) {
|
||||
if l.features.IsEnabled(c.Req.Context(), featuremgmt.FlagLibraryPanelRBAC) {
|
||||
filteredPanels, err := l.filterLibraryPanelsByPermission(c, elementsResult.Elements)
|
||||
if err != nil {
|
||||
return toLibraryElementError(err, "Failed to evaluate permissions")
|
||||
@ -278,7 +278,7 @@ func (l *LibraryElementService) getByNameHandler(c *contextmodel.ReqContext) res
|
||||
return toLibraryElementError(err, "Failed to get library element")
|
||||
}
|
||||
|
||||
if l.features.IsEnabled(featuremgmt.FlagLibraryPanelRBAC) {
|
||||
if l.features.IsEnabled(c.Req.Context(), featuremgmt.FlagLibraryPanelRBAC) {
|
||||
filteredElements, err := l.filterLibraryPanelsByPermission(c, elements)
|
||||
if err != nil {
|
||||
return toLibraryElementError(err, err.Error())
|
||||
|
@ -166,7 +166,7 @@ func (l *LibraryElementService) createLibraryElement(c context.Context, signedIn
|
||||
}
|
||||
|
||||
err = l.SQLStore.WithTransactionalDbSession(c, func(session *db.Session) error {
|
||||
if l.features.IsEnabled(featuremgmt.FlagLibraryPanelRBAC) {
|
||||
if l.features.IsEnabled(c, featuremgmt.FlagLibraryPanelRBAC) {
|
||||
allowed, err := l.AccessControl.Evaluate(c, signedInUser, ac.EvalPermission(ActionLibraryPanelsCreate, dashboards.ScopeFoldersProvider.GetResourceScopeUID(*cmd.FolderUID)))
|
||||
if !allowed {
|
||||
return fmt.Errorf("insufficient permissions for creating library panel in folder with UID %s", *cmd.FolderUID)
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
|
||||
func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink, error) {
|
||||
var configNodes []*navtree.NavLink
|
||||
ctx := c.Req.Context()
|
||||
hasAccess := ac.HasAccess(s.accessControl, c)
|
||||
hasGlobalAccess := ac.HasGlobalAccess(s.accessControl, s.accesscontrolService, c)
|
||||
orgsAccessEvaluator := ac.EvalPermission(ac.ActionOrgsRead)
|
||||
@ -55,7 +56,7 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
|
||||
})
|
||||
}
|
||||
|
||||
disabled, err := s.apiKeyService.IsDisabled(c.Req.Context(), c.SignedInUser.GetOrgID())
|
||||
disabled, err := s.apiKeyService.IsDisabled(ctx, c.SignedInUser.GetOrgID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -101,7 +102,7 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
|
||||
})
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(featuremgmt.FlagFeatureToggleAdminPage) && hasAccess(ac.EvalPermission(ac.ActionFeatureManagementRead)) {
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagFeatureToggleAdminPage) && hasAccess(ac.EvalPermission(ac.ActionFeatureManagementRead)) {
|
||||
configNodes = append(configNodes, &navtree.NavLink{
|
||||
Text: "Feature Toggles",
|
||||
SubTitle: "View and edit feature toggles",
|
||||
@ -111,7 +112,7 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
|
||||
})
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(featuremgmt.FlagCorrelations) && hasAccess(correlations.ConfigurationPageAccess) {
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagCorrelations) && hasAccess(correlations.ConfigurationPageAccess) {
|
||||
configNodes = append(configNodes, &navtree.NavLink{
|
||||
Text: "Correlations",
|
||||
Icon: "gf-glue",
|
||||
@ -121,7 +122,7 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
|
||||
})
|
||||
}
|
||||
|
||||
if hasAccess(ac.EvalPermission(ac.ActionSettingsRead, ac.ScopeSettingsAll)) && s.features.IsEnabled(featuremgmt.FlagStorage) {
|
||||
if hasAccess(ac.EvalPermission(ac.ActionSettingsRead, ac.ScopeSettingsAll)) && s.features.IsEnabled(ctx, featuremgmt.FlagStorage) {
|
||||
storage := &navtree.NavLink{
|
||||
Text: "Storage",
|
||||
Id: "storage",
|
||||
|
@ -238,7 +238,7 @@ func (s *ServiceImpl) addPluginToSection(c *contextmodel.ReqContext, treeRoot *n
|
||||
func (s *ServiceImpl) hasAccessToInclude(c *contextmodel.ReqContext, pluginID string) func(include *plugins.Includes) bool {
|
||||
hasAccess := ac.HasAccess(s.accessControl, c)
|
||||
return func(include *plugins.Includes) bool {
|
||||
useRBAC := s.features.IsEnabled(featuremgmt.FlagAccessControlOnCall) && include.RequiresRBACAction()
|
||||
useRBAC := s.features.IsEnabledGlobally(featuremgmt.FlagAccessControlOnCall) && include.RequiresRBACAction()
|
||||
if useRBAC && !hasAccess(ac.EvalPermission(include.Action)) {
|
||||
s.log.Debug("plugin include is covered by RBAC, user doesn't have access",
|
||||
"plugin", pluginID,
|
||||
@ -267,7 +267,7 @@ func (s *ServiceImpl) readNavigationSettings() {
|
||||
"k6-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightAlertsAndIncidents + 1, Text: "Performance testing", Icon: "k6"},
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(featuremgmt.FlagNavAdminSubsections) && s.features.IsEnabled(featuremgmt.FlagCostManagementUi) {
|
||||
if s.features.IsEnabledGlobally(featuremgmt.FlagNavAdminSubsections) && s.features.IsEnabledGlobally(featuremgmt.FlagCostManagementUi) {
|
||||
// if cost management is enabled we want to nest adaptive metrics and log volume explorer under that plugin
|
||||
// in the admin section
|
||||
s.navigationAppConfig["grafana-adaptive-metrics-app"] = NavigationAppConfig{SectionID: navtree.NavIDCfg}
|
||||
|
@ -358,7 +358,7 @@ func (s *ServiceImpl) buildDashboardNavLinks(c *contextmodel.ReqContext) []*navt
|
||||
Icon: "library-panel",
|
||||
})
|
||||
|
||||
if s.features.IsEnabled(featuremgmt.FlagPublicDashboards) {
|
||||
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagPublicDashboards) {
|
||||
dashboardChildNavs = append(dashboardChildNavs, &navtree.NavLink{
|
||||
Text: "Public dashboards",
|
||||
Id: "dashboards/public",
|
||||
@ -368,7 +368,7 @@ func (s *ServiceImpl) buildDashboardNavLinks(c *contextmodel.ReqContext) []*navt
|
||||
}
|
||||
}
|
||||
|
||||
if s.features.IsEnabled(featuremgmt.FlagScenes) {
|
||||
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagScenes) {
|
||||
dashboardChildNavs = append(dashboardChildNavs, &navtree.NavLink{
|
||||
Text: "Scenes",
|
||||
Id: "scenes",
|
||||
|
@ -184,7 +184,7 @@ func (srv TestingApiSrv) RouteEvalQueries(c *contextmodel.ReqContext, cmd apimod
|
||||
}
|
||||
|
||||
func (srv TestingApiSrv) BacktestAlertRule(c *contextmodel.ReqContext, cmd apimodels.BacktestConfig) response.Response {
|
||||
if !srv.featureManager.IsEnabled(featuremgmt.FlagAlertingBacktesting) {
|
||||
if !srv.featureManager.IsEnabled(c.Req.Context(), featuremgmt.FlagAlertingBacktesting) {
|
||||
return ErrResp(http.StatusNotFound, nil, "Backgtesting API is not enabled")
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ func NewTestMigrationStore(t *testing.T, sqlStore *sqlstore.SQLStore, cfg *setti
|
||||
cfg.UnifiedAlerting.BaseInterval = time.Second * 10
|
||||
}
|
||||
features := featuremgmt.WithFeatures()
|
||||
cfg.IsFeatureToggleEnabled = features.IsEnabledGlobally
|
||||
alertingStore := store.DBstore{
|
||||
SQLStore: sqlStore,
|
||||
Cfg: cfg.UnifiedAlerting,
|
||||
|
@ -246,9 +246,9 @@ func (ng *AlertNG) init() error {
|
||||
Images: ng.ImageService,
|
||||
Clock: clk,
|
||||
Historian: history,
|
||||
DoNotSaveNormalState: ng.FeatureToggles.IsEnabled(featuremgmt.FlagAlertingNoNormalState),
|
||||
DoNotSaveNormalState: ng.FeatureToggles.IsEnabledGlobally(featuremgmt.FlagAlertingNoNormalState),
|
||||
MaxStateSaveConcurrency: ng.Cfg.UnifiedAlerting.MaxStateSaveConcurrency,
|
||||
ApplyNoDataAndErrorToAllStates: ng.FeatureToggles.IsEnabled(featuremgmt.FlagAlertingNoDataErrorExecution),
|
||||
ApplyNoDataAndErrorToAllStates: ng.FeatureToggles.IsEnabledGlobally(featuremgmt.FlagAlertingNoDataErrorExecution),
|
||||
Tracer: ng.tracer,
|
||||
Log: log.New("ngalert.state.manager"),
|
||||
}
|
||||
@ -483,7 +483,7 @@ func applyStateHistoryFeatureToggles(cfg *setting.UnifiedAlertingStateHistorySet
|
||||
// If all toggles are enabled, we listen to the state history config as written.
|
||||
// If any of them are disabled, we ignore the configured backend and treat the toggles as an override.
|
||||
// If multiple toggles are disabled, we go with the most "restrictive" one.
|
||||
if !ft.IsEnabled(featuremgmt.FlagAlertStateHistoryLokiSecondary) {
|
||||
if !ft.IsEnabledGlobally(featuremgmt.FlagAlertStateHistoryLokiSecondary) {
|
||||
// If we cannot even treat Loki as a secondary, we must use annotations only.
|
||||
if backend == historian.BackendTypeMultiple || backend == historian.BackendTypeLoki {
|
||||
logger.Info("Forcing Annotation backend due to state history feature toggles")
|
||||
@ -493,7 +493,7 @@ func applyStateHistoryFeatureToggles(cfg *setting.UnifiedAlertingStateHistorySet
|
||||
}
|
||||
return
|
||||
}
|
||||
if !ft.IsEnabled(featuremgmt.FlagAlertStateHistoryLokiPrimary) {
|
||||
if !ft.IsEnabledGlobally(featuremgmt.FlagAlertStateHistoryLokiPrimary) {
|
||||
// If we're using multiple backends, Loki must be the secondary.
|
||||
if backend == historian.BackendTypeMultiple {
|
||||
logger.Info("Coercing Loki to a secondary backend due to state history feature toggles")
|
||||
@ -509,7 +509,7 @@ func applyStateHistoryFeatureToggles(cfg *setting.UnifiedAlertingStateHistorySet
|
||||
}
|
||||
return
|
||||
}
|
||||
if !ft.IsEnabled(featuremgmt.FlagAlertStateHistoryLokiOnly) {
|
||||
if !ft.IsEnabledGlobally(featuremgmt.FlagAlertStateHistoryLokiOnly) {
|
||||
// If we're not allowed to use Loki only, make it the primary but keep the annotation writes.
|
||||
if backend == historian.BackendTypeLoki {
|
||||
logger.Info("Forcing dual writes to Loki and Annotations due to state history feature toggles")
|
||||
|
@ -30,7 +30,7 @@ func (st DBstore) ListAlertInstances(ctx context.Context, cmd *models.ListAlertI
|
||||
if cmd.RuleUID != "" {
|
||||
addToQuery(` AND rule_uid = ?`, cmd.RuleUID)
|
||||
}
|
||||
if st.FeatureToggles.IsEnabled(featuremgmt.FlagAlertingNoNormalState) {
|
||||
if st.FeatureToggles.IsEnabled(ctx, featuremgmt.FlagAlertingNoNormalState) {
|
||||
s.WriteString(fmt.Sprintf(" AND NOT (current_state = '%s' AND current_reason = '')", models.InstanceStateNormal))
|
||||
}
|
||||
if err := sess.SQL(s.String(), params...).Find(&alertInstances); err != nil {
|
||||
|
@ -44,8 +44,6 @@ func SetupTestEnv(tb testing.TB, baseInterval time.Duration) (*ngalert.AlertNG,
|
||||
tb.Helper()
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
// nolint:staticcheck
|
||||
cfg.IsFeatureToggleEnabled = featuremgmt.WithFeatures().IsEnabled
|
||||
cfg.UnifiedAlerting = setting.UnifiedAlertingSettings{
|
||||
BaseInterval: setting.SchedulerBaseInterval,
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ func (d *Dynamic) setDetectorsFromCache(ctx context.Context) error {
|
||||
|
||||
// IsDisabled returns true if FlagPluginsDynamicAngularDetectionPatterns is not enabled.
|
||||
func (d *Dynamic) IsDisabled() bool {
|
||||
return !d.features.IsEnabled(featuremgmt.FlagPluginsDynamicAngularDetectionPatterns)
|
||||
return !d.features.IsEnabledGlobally(featuremgmt.FlagPluginsDynamicAngularDetectionPatterns)
|
||||
}
|
||||
|
||||
// randomSkew returns a random time.Duration between 0 and maxSkew.
|
||||
|
@ -16,7 +16,7 @@ func ProvideService(cfg *config.Cfg, dynamic *angulardetectorsprovider.Dynamic)
|
||||
var detectorsProvider angulardetector.DetectorsProvider
|
||||
var err error
|
||||
static := angularinspector.NewDefaultStaticDetectorsProvider()
|
||||
if cfg.Features != nil && cfg.Features.IsEnabled(featuremgmt.FlagPluginsDynamicAngularDetectionPatterns) {
|
||||
if cfg.Features != nil && cfg.Features.IsEnabledGlobally(featuremgmt.FlagPluginsDynamicAngularDetectionPatterns) {
|
||||
detectorsProvider = angulardetector.SequenceDetectorsProvider{dynamic, static}
|
||||
} else {
|
||||
detectorsProvider = static
|
||||
|
@ -7,12 +7,13 @@ import (
|
||||
|
||||
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/caching"
|
||||
"github.com/grafana/grafana/pkg/services/contexthandler"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// needed to mock the function for testing
|
||||
@ -93,7 +94,7 @@ func (m *CachingMiddleware) QueryData(ctx context.Context, req *backend.QueryDat
|
||||
// Update the query cache with the result for this metrics request
|
||||
if err == nil && cr.UpdateCacheFn != nil {
|
||||
// If AWS async caching is not enabled, use the old code path
|
||||
if m.features == nil || !m.features.IsEnabled(featuremgmt.FlagAwsAsyncQueryCaching) {
|
||||
if m.features == nil || !m.features.IsEnabled(ctx, featuremgmt.FlagAwsAsyncQueryCaching) {
|
||||
cr.UpdateCacheFn(ctx, resp)
|
||||
} else {
|
||||
// time how long shouldCacheQuery takes
|
||||
|
@ -50,7 +50,7 @@ func (m *LoggerMiddleware) logRequest(ctx context.Context, fn func(ctx context.C
|
||||
if err != nil {
|
||||
logParams = append(logParams, "error", err)
|
||||
}
|
||||
if m.features.IsEnabled(featuremgmt.FlagPluginsInstrumentationStatusSource) {
|
||||
if m.features.IsEnabled(ctx, featuremgmt.FlagPluginsInstrumentationStatusSource) {
|
||||
logParams = append(logParams, "statusSource", pluginrequestmeta.StatusSourceFromContext(ctx))
|
||||
}
|
||||
|
||||
@ -82,7 +82,7 @@ func (m *LoggerMiddleware) QueryData(ctx context.Context, req *backend.QueryData
|
||||
for refID, dr := range resp.Responses {
|
||||
if dr.Error != nil {
|
||||
logParams := []any{"refID", refID, "status", int(dr.Status), "error", dr.Error}
|
||||
if m.features.IsEnabled(featuremgmt.FlagPluginsInstrumentationStatusSource) {
|
||||
if m.features.IsEnabled(ctx, featuremgmt.FlagPluginsInstrumentationStatusSource) {
|
||||
logParams = append(logParams, "statusSource", pluginrequestmeta.StatusSourceFromPluginErrorSource(dr.ErrorSource))
|
||||
}
|
||||
ctxLogger.Error("Partial data response error", logParams...)
|
||||
|
@ -33,7 +33,7 @@ type MetricsMiddleware struct {
|
||||
|
||||
func newMetricsMiddleware(promRegisterer prometheus.Registerer, pluginRegistry registry.Service, features featuremgmt.FeatureToggles) *MetricsMiddleware {
|
||||
var additionalLabels []string
|
||||
if features.IsEnabled(featuremgmt.FlagPluginsInstrumentationStatusSource) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagPluginsInstrumentationStatusSource) {
|
||||
additionalLabels = []string{"status_source"}
|
||||
}
|
||||
pluginRequestCounter := prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
@ -122,7 +122,7 @@ func (m *MetricsMiddleware) instrumentPluginRequest(ctx context.Context, pluginC
|
||||
pluginRequestDurationLabels := []string{pluginCtx.PluginID, endpoint, target}
|
||||
pluginRequestCounterLabels := []string{pluginCtx.PluginID, endpoint, status.String(), target}
|
||||
pluginRequestDurationSecondsLabels := []string{"grafana-backend", pluginCtx.PluginID, endpoint, status.String(), target}
|
||||
if m.features.IsEnabled(featuremgmt.FlagPluginsInstrumentationStatusSource) {
|
||||
if m.features.IsEnabled(ctx, featuremgmt.FlagPluginsInstrumentationStatusSource) {
|
||||
statusSource := pluginrequestmeta.StatusSourceFromContext(ctx)
|
||||
pluginRequestDurationLabels = append(pluginRequestDurationLabels, string(statusSource))
|
||||
pluginRequestCounterLabels = append(pluginRequestCounterLabels, string(statusSource))
|
||||
|
@ -175,7 +175,7 @@ func NewAsExternalStep(cfg *config.Cfg) *AsExternal {
|
||||
|
||||
// Filter will filter out any plugins that are marked to be disabled.
|
||||
func (c *AsExternal) Filter(cl plugins.Class, bundles []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) {
|
||||
if c.cfg.Features == nil || !c.cfg.Features.IsEnabled(featuremgmt.FlagExternalCorePlugins) {
|
||||
if c.cfg.Features == nil || !c.cfg.Features.IsEnabledGlobally(featuremgmt.FlagExternalCorePlugins) {
|
||||
return bundles, nil
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ func TestIntegrationPluginManager(t *testing.T) {
|
||||
Azure: &azsettings.AzureSettings{},
|
||||
|
||||
// nolint:staticcheck
|
||||
IsFeatureToggleEnabled: features.IsEnabled,
|
||||
IsFeatureToggleEnabled: features.IsEnabledGlobally,
|
||||
}
|
||||
|
||||
tracer := tracing.InitializeTracerForTest()
|
||||
|
@ -152,7 +152,7 @@ func NewClientDecorator(
|
||||
func CreateMiddlewares(cfg *setting.Cfg, oAuthTokenService oauthtoken.OAuthTokenService, tracer tracing.Tracer, cachingService caching.CachingService, features *featuremgmt.FeatureManager, promRegisterer prometheus.Registerer, registry registry.Service) []plugins.ClientMiddleware {
|
||||
var middlewares []plugins.ClientMiddleware
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagPluginsInstrumentationStatusSource) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagPluginsInstrumentationStatusSource) {
|
||||
middlewares = []plugins.ClientMiddleware{
|
||||
clientmiddleware.NewPluginRequestMetaMiddleware(),
|
||||
}
|
||||
@ -172,11 +172,11 @@ func CreateMiddlewares(cfg *setting.Cfg, oAuthTokenService oauthtoken.OAuthToken
|
||||
)
|
||||
|
||||
// Placing the new service implementation behind a feature flag until it is known to be stable
|
||||
if features.IsEnabled(featuremgmt.FlagUseCachingService) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagUseCachingService) {
|
||||
middlewares = append(middlewares, clientmiddleware.NewCachingMiddlewareWithFeatureManager(cachingService, features))
|
||||
}
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagIdForwarding) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagIdForwarding) {
|
||||
middlewares = append(middlewares, clientmiddleware.NewForwardIDMiddleware())
|
||||
}
|
||||
|
||||
@ -186,7 +186,7 @@ func CreateMiddlewares(cfg *setting.Cfg, oAuthTokenService oauthtoken.OAuthToken
|
||||
|
||||
middlewares = append(middlewares, clientmiddleware.NewHTTPClientMiddleware())
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagPluginsInstrumentationStatusSource) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagPluginsInstrumentationStatusSource) {
|
||||
// StatusSourceMiddleware should be at the very bottom, or any middlewares below it won't see the
|
||||
// correct status source in their context.Context
|
||||
middlewares = append(middlewares, clientmiddleware.NewStatusSourceMiddleware())
|
||||
|
@ -23,7 +23,7 @@ type Service struct {
|
||||
|
||||
func ProvideService(cfg *config.Cfg, reg extsvcauth.ExternalServiceRegistry, settingsSvc pluginsettings.Service) *Service {
|
||||
s := &Service{
|
||||
featureEnabled: cfg.Features.IsEnabled(featuremgmt.FlagExternalServiceAuth) || cfg.Features.IsEnabled(featuremgmt.FlagExternalServiceAccounts),
|
||||
featureEnabled: cfg.Features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAuth) || cfg.Features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAccounts),
|
||||
log: log.New("plugins.external.registration"),
|
||||
reg: reg,
|
||||
settingsSvc: settingsSvc,
|
||||
|
@ -1,9 +1,11 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
@ -41,7 +43,7 @@ func ProvideApi(
|
||||
}
|
||||
|
||||
// attach api if PublicDashboards feature flag is enabled
|
||||
if features.IsEnabled(featuremgmt.FlagPublicDashboards) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagPublicDashboards) {
|
||||
api.RegisterAPIEndpoints()
|
||||
}
|
||||
|
||||
@ -284,9 +286,9 @@ func (api *Api) DeletePublicDashboard(c *contextmodel.ReqContext) response.Respo
|
||||
}
|
||||
|
||||
// Copied from pkg/api/metrics.go
|
||||
func toJsonStreamingResponse(features *featuremgmt.FeatureManager, qdr *backend.QueryDataResponse) response.Response {
|
||||
func toJsonStreamingResponse(ctx context.Context, features *featuremgmt.FeatureManager, qdr *backend.QueryDataResponse) response.Response {
|
||||
statusWhenError := http.StatusBadRequest
|
||||
if features.IsEnabled(featuremgmt.FlagDatasourceQueryMultiStatus) {
|
||||
if features.IsEnabled(ctx, featuremgmt.FlagDatasourceQueryMultiStatus) {
|
||||
statusWhenError = http.StatusMultiStatus
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
@ -71,7 +72,7 @@ func (api *Api) QueryPublicDashboard(c *contextmodel.ReqContext) response.Respon
|
||||
return response.Err(err)
|
||||
}
|
||||
|
||||
return toJsonStreamingResponse(api.Features, resp)
|
||||
return toJsonStreamingResponse(c.Req.Context(), api.Features, resp)
|
||||
}
|
||||
|
||||
// swagger:route GET /public/dashboards/{accessToken}/annotations dashboard_public getPublicAnnotations
|
||||
|
@ -37,7 +37,7 @@ func (rs *RenderingService) GetRenderUser(ctx context.Context, key string) (*Ren
|
||||
|
||||
var renderUser *RenderUser
|
||||
|
||||
if looksLikeJWT(key) && rs.features.IsEnabled(featuremgmt.FlagRenderAuthJWT) {
|
||||
if looksLikeJWT(key) && rs.features.IsEnabled(ctx, featuremgmt.FlagRenderAuthJWT) {
|
||||
from = "jwt"
|
||||
renderUser = rs.getRenderUserFromJWT(key)
|
||||
} else {
|
||||
|
@ -84,7 +84,7 @@ func ProvideService(cfg *setting.Cfg, features *featuremgmt.FeatureManager, remo
|
||||
}
|
||||
|
||||
var renderKeyProvider renderKeyProvider
|
||||
if features.IsEnabled(featuremgmt.FlagRenderAuthJWT) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagRenderAuthJWT) {
|
||||
renderKeyProvider = &jwtRenderKeyProvider{
|
||||
log: logger,
|
||||
authToken: []byte(cfg.RendererAuthToken),
|
||||
|
@ -116,7 +116,7 @@ func ProvideService(cfg *setting.Cfg, sql db.DB, entityEventStore store.EntityEv
|
||||
}
|
||||
|
||||
func (s *StandardSearchService) IsDisabled() bool {
|
||||
return !s.features.IsEnabled(featuremgmt.FlagPanelTitleSearch)
|
||||
return !s.features.IsEnabledGlobally(featuremgmt.FlagPanelTitleSearch)
|
||||
}
|
||||
|
||||
func (s *StandardSearchService) Run(ctx context.Context) error {
|
||||
|
@ -44,7 +44,7 @@ func (s *DataSourceSecretMigrationService) Migrate(ctx context.Context) error {
|
||||
}
|
||||
logger.Debug(fmt.Sprint("secret migration status is ", migrationStatus))
|
||||
// If this flag is true, delete secrets from the legacy secrets store as they are migrated
|
||||
disableSecretsCompatibility := s.features.IsEnabled(featuremgmt.FlagDisableSecretsCompatibility)
|
||||
disableSecretsCompatibility := s.features.IsEnabled(ctx, featuremgmt.FlagDisableSecretsCompatibility)
|
||||
// If migration hasn't happened, migrate to unified secrets and keep copy in legacy
|
||||
// If a complete migration happened and now backwards compatibility is enabled, copy secrets back to legacy
|
||||
needCompatibility := migrationStatus != compatibleSecretMigrationValue && !disableSecretsCompatibility
|
||||
|
@ -48,7 +48,7 @@ func NewPluginSecretsKVStore(
|
||||
secretsService: secretsService,
|
||||
log: logger,
|
||||
kvstore: kvstore,
|
||||
backwardsCompatibilityDisabled: features.IsEnabled(featuremgmt.FlagDisableSecretsCompatibility),
|
||||
backwardsCompatibilityDisabled: features.IsEnabledGlobally(featuremgmt.FlagDisableSecretsCompatibility),
|
||||
fallbackStore: fallback,
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +147,11 @@ func NewFakeFeatureToggles(t *testing.T, returnValue bool) featuremgmt.FeatureTo
|
||||
}
|
||||
}
|
||||
|
||||
func (f fakeFeatureToggles) IsEnabled(feature string) bool {
|
||||
func (f fakeFeatureToggles) IsEnabledGlobally(feature string) bool {
|
||||
return f.returnValue
|
||||
}
|
||||
|
||||
func (f fakeFeatureToggles) IsEnabled(ctx context.Context, feature string) bool {
|
||||
return f.returnValue
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ func ProvideSecretsService(
|
||||
log: log.New("secrets"),
|
||||
}
|
||||
|
||||
enabled := !features.IsEnabled(featuremgmt.FlagDisableEnvelopeEncryption)
|
||||
enabled := !features.IsEnabledGlobally(featuremgmt.FlagDisableEnvelopeEncryption)
|
||||
|
||||
if enabled {
|
||||
err := s.InitProviders()
|
||||
@ -112,12 +112,12 @@ func (s *SecretsService) InitProviders() (err error) {
|
||||
}
|
||||
|
||||
func (s *SecretsService) registerUsageMetrics() {
|
||||
s.usageStats.RegisterMetricsFunc(func(context.Context) (map[string]any, error) {
|
||||
s.usageStats.RegisterMetricsFunc(func(ctx context.Context) (map[string]any, error) {
|
||||
usageMetrics := make(map[string]any)
|
||||
|
||||
// Enabled / disabled
|
||||
usageMetrics["stats.encryption.envelope_encryption_enabled.count"] = 0
|
||||
if !s.features.IsEnabled(featuremgmt.FlagDisableEnvelopeEncryption) {
|
||||
if !s.features.IsEnabled(ctx, featuremgmt.FlagDisableEnvelopeEncryption) {
|
||||
usageMetrics["stats.encryption.envelope_encryption_enabled.count"] = 1
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ var b64 = base64.RawStdEncoding
|
||||
|
||||
func (s *SecretsService) Encrypt(ctx context.Context, payload []byte, opt secrets.EncryptionOptions) ([]byte, error) {
|
||||
// Use legacy encryption service if featuremgmt.FlagDisableEnvelopeEncryption toggle is on
|
||||
if s.features.IsEnabled(featuremgmt.FlagDisableEnvelopeEncryption) {
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagDisableEnvelopeEncryption) {
|
||||
return s.enc.Encrypt(ctx, payload, setting.SecretKey)
|
||||
}
|
||||
|
||||
@ -333,7 +333,7 @@ func (s *SecretsService) Decrypt(ctx context.Context, payload []byte) ([]byte, e
|
||||
// If encrypted with envelope encryption, the feature is disabled and
|
||||
// no provider is initialized, then we throw an error.
|
||||
if s.encryptedWithEnvelopeEncryption(payload) &&
|
||||
s.features.IsEnabled(featuremgmt.FlagDisableEnvelopeEncryption) &&
|
||||
s.features.IsEnabled(ctx, featuremgmt.FlagDisableEnvelopeEncryption) &&
|
||||
!s.providersInitialized() {
|
||||
err = fmt.Errorf("failed to decrypt a secret encrypted with envelope encryption: envelope encryption is disabled")
|
||||
return nil, err
|
||||
@ -469,7 +469,7 @@ func (s *SecretsService) RotateDataKeys(ctx context.Context) error {
|
||||
func (s *SecretsService) ReEncryptDataKeys(ctx context.Context) error {
|
||||
s.log.Info("Data keys re-encryption triggered")
|
||||
|
||||
if s.features.IsEnabled(featuremgmt.FlagDisableEnvelopeEncryption) {
|
||||
if s.features.IsEnabled(ctx, featuremgmt.FlagDisableEnvelopeEncryption) {
|
||||
s.log.Info("Envelope encryption is not enabled but trying to init providers anyway...")
|
||||
|
||||
if err := s.InitProviders(); err != nil {
|
||||
|
@ -114,7 +114,7 @@ func (m *SecretsMigrator) RollBackSecrets(ctx context.Context) (bool, error) {
|
||||
}
|
||||
|
||||
func (m *SecretsMigrator) initProvidersIfNeeded() error {
|
||||
if m.features.IsEnabled(featuremgmt.FlagDisableEnvelopeEncryption) {
|
||||
if m.features.IsEnabledGlobally(featuremgmt.FlagDisableEnvelopeEncryption) {
|
||||
logger.Info("Envelope encryption is not enabled but trying to init providers anyway...")
|
||||
|
||||
if err := m.secretsSrv.InitProviders(); err != nil {
|
||||
|
@ -48,7 +48,7 @@ func NewServiceAccountsAPI(
|
||||
RouterRegister: routerRegister,
|
||||
log: log.New("serviceaccounts.api"),
|
||||
permissionService: permissionService,
|
||||
isExternalSAEnabled: features.IsEnabled(featuremgmt.FlagExternalServiceAccounts) || features.IsEnabled(featuremgmt.FlagExternalServiceAuth),
|
||||
isExternalSAEnabled: features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAccounts) || features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAuth),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ func ProvideExtSvcAccountsService(acSvc ac.Service, bus bus.Bus, db db.DB, featu
|
||||
skvStore: kvstore.NewSQLSecretsKVStore(db, secretsSvc, logger), // Using SQL store to avoid a cyclic dependency
|
||||
}
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagExternalServiceAccounts) || features.IsEnabled(featuremgmt.FlagExternalServiceAuth) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAccounts) || features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAuth) {
|
||||
// Register the metrics
|
||||
esa.metrics = newMetrics(reg, saSvc, logger)
|
||||
|
||||
@ -95,7 +95,7 @@ func (esa *ExtSvcAccountsService) RetrieveExtSvcAccount(ctx context.Context, org
|
||||
// SaveExternalService creates, updates or delete a service account (and its token) with the requested permissions.
|
||||
func (esa *ExtSvcAccountsService) SaveExternalService(ctx context.Context, cmd *extsvcauth.ExternalServiceRegistration) (*extsvcauth.ExternalService, error) {
|
||||
// This is double proofing, we should never reach here anyway the flags have already been checked.
|
||||
if !esa.features.IsEnabled(featuremgmt.FlagExternalServiceAccounts) && !esa.features.IsEnabled(featuremgmt.FlagExternalServiceAuth) {
|
||||
if !esa.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAccounts) && !esa.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAuth) {
|
||||
esa.logger.Warn("This feature is behind a feature flag, please set it if you want to save external services")
|
||||
return nil, nil
|
||||
}
|
||||
@ -140,7 +140,7 @@ func (esa *ExtSvcAccountsService) SaveExternalService(ctx context.Context, cmd *
|
||||
|
||||
func (esa *ExtSvcAccountsService) RemoveExternalService(ctx context.Context, name string) error {
|
||||
// This is double proofing, we should never reach here anyway the flags have already been checked.
|
||||
if !esa.features.IsEnabled(featuremgmt.FlagExternalServiceAccounts) && !esa.features.IsEnabled(featuremgmt.FlagExternalServiceAuth) {
|
||||
if !esa.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAccounts) && !esa.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAuth) {
|
||||
esa.logger.Warn("This feature is behind a feature flag, please set it if you want to save external services")
|
||||
return nil
|
||||
}
|
||||
@ -172,7 +172,7 @@ func (esa *ExtSvcAccountsService) RemoveExtSvcAccount(ctx context.Context, orgID
|
||||
// ManageExtSvcAccount creates, updates or deletes the service account associated with an external service
|
||||
func (esa *ExtSvcAccountsService) ManageExtSvcAccount(ctx context.Context, cmd *sa.ManageExtSvcAccountCmd) (int64, error) {
|
||||
// This is double proofing, we should never reach here anyway the flags have already been checked.
|
||||
if !esa.features.IsEnabled(featuremgmt.FlagExternalServiceAccounts) && !esa.features.IsEnabled(featuremgmt.FlagExternalServiceAuth) {
|
||||
if !esa.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAccounts) && !esa.features.IsEnabled(ctx, featuremgmt.FlagExternalServiceAuth) {
|
||||
esa.logger.Warn("This feature is behind a feature flag, please set it if you want to save external services")
|
||||
return 0, nil
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ func ProvideServiceAccountsProxy(
|
||||
s := &ServiceAccountsProxy{
|
||||
log: log.New("serviceaccounts.proxy"),
|
||||
proxiedService: proxiedService,
|
||||
isProxyEnabled: features.IsEnabled(featuremgmt.FlagExternalServiceAccounts) || features.IsEnabled(featuremgmt.FlagExternalServiceAuth),
|
||||
isProxyEnabled: features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAccounts) || features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAuth),
|
||||
}
|
||||
|
||||
serviceaccountsAPI := api.NewServiceAccountsAPI(cfg, s, ac, accesscontrolService, routeRegister, permissionService, features)
|
||||
|
@ -184,7 +184,7 @@ func TestMigrations(t *testing.T) {
|
||||
{
|
||||
desc: "without editors can admin",
|
||||
// nolint:staticcheck
|
||||
config: setting.NewCfgWithFeatures(featuremgmt.WithFeatures("accesscontrol").IsEnabled),
|
||||
config: setting.NewCfgWithFeatures(featuremgmt.WithFeatures("accesscontrol").IsEnabledGlobally),
|
||||
expectedRolePerms: map[string][]rawPermission{
|
||||
"managed:users:1:permissions": {{Action: "teams:read", Scope: team1Scope}},
|
||||
"managed:users:2:permissions": {{Action: "teams:read", Scope: team1Scope}},
|
||||
|
@ -84,7 +84,7 @@ func NewAccessControlDashboardPermissionFilter(user identity.Requester, permissi
|
||||
}
|
||||
|
||||
var f PermissionsFilter
|
||||
if features.IsEnabled(featuremgmt.FlagPermissionsFilterRemoveSubquery) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagPermissionsFilterRemoveSubquery) {
|
||||
f = &accessControlDashboardPermissionFilterNoFolderSubquery{
|
||||
accessControlDashboardPermissionFilter: accessControlDashboardPermissionFilter{
|
||||
user: user, folderActions: folderActions, dashboardActions: dashboardActions, features: features,
|
||||
@ -201,7 +201,7 @@ func (f *accessControlDashboardPermissionFilter) buildClauses() {
|
||||
}
|
||||
permSelector.WriteRune(')')
|
||||
|
||||
switch f.features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
switch f.features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
|
||||
case true:
|
||||
if len(permSelectorArgs) > 0 {
|
||||
switch f.recursiveQueriesAreSupported {
|
||||
@ -280,7 +280,7 @@ func (f *accessControlDashboardPermissionFilter) buildClauses() {
|
||||
|
||||
permSelector.WriteRune(')')
|
||||
|
||||
switch f.features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
switch f.features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
|
||||
case true:
|
||||
if len(permSelectorArgs) > 0 {
|
||||
switch f.recursiveQueriesAreSupported {
|
||||
|
@ -116,7 +116,7 @@ func (f *accessControlDashboardPermissionFilterNoFolderSubquery) buildClauses()
|
||||
|
||||
permSelector.WriteRune(')')
|
||||
|
||||
switch f.features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
switch f.features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
|
||||
case true:
|
||||
if len(permSelectorArgs) > 0 {
|
||||
switch f.recursiveQueriesAreSupported {
|
||||
@ -193,7 +193,7 @@ func (f *accessControlDashboardPermissionFilterNoFolderSubquery) buildClauses()
|
||||
}
|
||||
permSelector.WriteRune(')')
|
||||
|
||||
switch f.features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
switch f.features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
|
||||
case true:
|
||||
if len(permSelectorArgs) > 0 {
|
||||
switch f.recursiveQueriesAreSupported {
|
||||
|
@ -37,7 +37,7 @@ func (b *Builder) ToSQL(limit, page int64) (string, []any) {
|
||||
INNER JOIN dashboard ON ids.id = dashboard.id`)
|
||||
b.sql.WriteString("\n")
|
||||
|
||||
if b.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
if b.Features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
|
||||
b.sql.WriteString(
|
||||
`LEFT OUTER JOIN folder ON folder.uid = dashboard.folder_uid AND folder.org_id = dashboard.org_id`)
|
||||
} else {
|
||||
@ -67,7 +67,7 @@ func (b *Builder) buildSelect() {
|
||||
dashboard.folder_id,
|
||||
folder.uid AS folder_uid,
|
||||
`)
|
||||
if b.Features.IsEnabled(featuremgmt.FlagNestedFolders) {
|
||||
if b.Features.IsEnabledGlobally(featuremgmt.FlagNestedFolders) {
|
||||
b.sql.WriteString(`
|
||||
folder.title AS folder_slug,`)
|
||||
} else {
|
||||
|
@ -177,7 +177,7 @@ func makeSQLStoreTestConfig(t *testing.T, tc sqlStoreTest) *setting.Cfg {
|
||||
tc.features = featuremgmt.WithFeatures()
|
||||
}
|
||||
// nolint:staticcheck
|
||||
cfg := setting.NewCfgWithFeatures(tc.features.IsEnabled)
|
||||
cfg := setting.NewCfgWithFeatures(tc.features.IsEnabledGlobally)
|
||||
|
||||
sec, err := cfg.Raw.NewSection("database")
|
||||
require.NoError(t, err)
|
||||
|
@ -45,7 +45,7 @@ func ProvideService(cfg *setting.Cfg, sqlStore db.DB, ac ac.AccessControl,
|
||||
fbStrategies: strategies,
|
||||
}
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagSsoSettingsApi) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsApi) {
|
||||
ssoSettingsApi := api.ProvideApi(svc, routeRegister, ac)
|
||||
ssoSettingsApi.RegisterAPIEndpoints()
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
|
||||
func MigrateEntityStore(xdb db.DB, features featuremgmt.FeatureToggles) error {
|
||||
// Skip if feature flag is not enabled
|
||||
if !features.IsEnabled(featuremgmt.FlagEntityStore) {
|
||||
if !features.IsEnabledGlobally(featuremgmt.FlagEntityStore) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -67,6 +67,6 @@ func MigrateEntityStore(xdb db.DB, features featuremgmt.FeatureToggles) error {
|
||||
}
|
||||
|
||||
return mg.Start(
|
||||
features.IsEnabled(featuremgmt.FlagMigrationLocking),
|
||||
features.IsEnabledGlobally(featuremgmt.FlagMigrationLocking),
|
||||
sql.GetMigrationLockAttemptTimeout())
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ type EntityEventsService interface {
|
||||
}
|
||||
|
||||
func ProvideEntityEventsService(cfg *setting.Cfg, sqlStore db.DB, features featuremgmt.FeatureToggles) EntityEventsService {
|
||||
if !features.IsEnabled(featuremgmt.FlagPanelTitleSearch) {
|
||||
if !features.IsEnabledGlobally(featuremgmt.FlagPanelTitleSearch) {
|
||||
return &dummyEntityEventsService{}
|
||||
}
|
||||
|
||||
|
@ -212,7 +212,7 @@ func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient c
|
||||
QueryString: aws.String(modifiedQueryString),
|
||||
}
|
||||
|
||||
if logsQuery.LogGroups != nil && len(logsQuery.LogGroups) > 0 && e.features.IsEnabled(featuremgmt.FlagCloudWatchCrossAccountQuerying) {
|
||||
if logsQuery.LogGroups != nil && len(logsQuery.LogGroups) > 0 && e.features.IsEnabled(ctx, featuremgmt.FlagCloudWatchCrossAccountQuerying) {
|
||||
var logGroupIdentifiers []string
|
||||
for _, lg := range logsQuery.LogGroups {
|
||||
arn := lg.Arn
|
||||
|
@ -7,8 +7,9 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
|
||||
)
|
||||
|
||||
type LogsAPI struct {
|
||||
@ -43,16 +44,6 @@ func (l *LogsService) GetLogGroupFieldsWithContext(ctx context.Context, request
|
||||
return args.Get(0).([]resources.ResourceResponse[resources.LogGroupField]), args.Error(1)
|
||||
}
|
||||
|
||||
type MockFeatures struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (f *MockFeatures) IsEnabled(feature string) bool {
|
||||
args := f.Called(feature)
|
||||
|
||||
return args.Bool(0)
|
||||
}
|
||||
|
||||
type MockLogEvents struct {
|
||||
cloudwatchlogsiface.CloudWatchLogsAPI
|
||||
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/routes"
|
||||
)
|
||||
|
||||
@ -28,7 +29,7 @@ func (e *cloudWatchExecutor) newResourceMux() *http.ServeMux {
|
||||
mux.HandleFunc("/external-id", routes.ResourceRequestMiddleware(routes.ExternalIdHandler, logger, e.getRequestContext))
|
||||
|
||||
// feature is enabled by default, just putting behind a feature flag in case of unexpected bugs
|
||||
if e.features.IsEnabled("cloudwatchNewRegionsHandler") {
|
||||
if e.features.IsEnabledGlobally(featuremgmt.FlagCloudwatchNewRegionsHandler) {
|
||||
mux.HandleFunc("/regions", routes.ResourceRequestMiddleware(routes.RegionsHandler, logger, e.getRequestContext))
|
||||
} else {
|
||||
mux.HandleFunc("/regions", handleResourceReq(e.handleGetRegions))
|
||||
|
@ -8,17 +8,19 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func TestLogGroupFieldsRoute(t *testing.T) {
|
||||
mockFeatures := mocks.MockFeatures{}
|
||||
mockFeatures := featuremgmt.WithFeatures()
|
||||
reqCtxFunc := func(_ context.Context, pluginCtx backend.PluginContext, region string) (reqCtx models.RequestContext, err error) {
|
||||
return models.RequestContext{Features: &mockFeatures}, err
|
||||
return models.RequestContext{Features: mockFeatures}, err
|
||||
}
|
||||
t.Run("returns 400 if an invalid LogGroupFieldsRequest is used", func(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"net/url"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
|
||||
@ -46,5 +47,5 @@ var newLogGroupsService = func(ctx context.Context, pluginCtx backend.PluginCont
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return services.NewLogGroupsService(reqCtx.LogsAPIProvider, reqCtx.Features.IsEnabled(featuremgmt.FlagCloudWatchCrossAccountQuerying)), nil
|
||||
return services.NewLogGroupsService(reqCtx.LogsAPIProvider, reqCtx.Features.IsEnabled(ctx, featuremgmt.FlagCloudWatchCrossAccountQuerying)), nil
|
||||
}
|
||||
|
@ -8,13 +8,14 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
func TestLogGroupsRoute(t *testing.T) {
|
||||
@ -23,10 +24,9 @@ func TestLogGroupsRoute(t *testing.T) {
|
||||
newLogGroupsService = origLogGroupsService
|
||||
})
|
||||
|
||||
mockFeatures := mocks.MockFeatures{}
|
||||
mockFeatures.On("IsEnabled", featuremgmt.FlagCloudWatchCrossAccountQuerying).Return(false)
|
||||
mockFeatures := featuremgmt.WithFeatures()
|
||||
reqCtxFunc := func(_ context.Context, pluginCtx backend.PluginContext, region string) (reqCtx models.RequestContext, err error) {
|
||||
return models.RequestContext{Features: &mockFeatures}, err
|
||||
return models.RequestContext{Features: mockFeatures}, err
|
||||
}
|
||||
|
||||
t.Run("successfully returns 1 log group with account id", func(t *testing.T) {
|
||||
|
@ -37,7 +37,7 @@ func (e *cloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, logger
|
||||
}
|
||||
|
||||
requestQueries, err := models.ParseMetricDataQueries(req.Queries, startTime, endTime, instance.Settings.Region, logger,
|
||||
e.features.IsEnabled(featuremgmt.FlagCloudWatchCrossAccountQuerying))
|
||||
e.features.IsEnabled(ctx, featuremgmt.FlagCloudWatchCrossAccountQuerying))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -60,7 +60,7 @@ func (e *cloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, logger
|
||||
region := r
|
||||
|
||||
batches := [][]*models.CloudWatchQuery{regionQueries}
|
||||
if e.features.IsEnabled(featuremgmt.FlagCloudWatchBatchQueries) {
|
||||
if e.features.IsEnabled(ctx, featuremgmt.FlagCloudWatchBatchQueries) {
|
||||
batches = getMetricQueryBatches(regionQueries, logger)
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ func (e *cloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, logger
|
||||
return err
|
||||
}
|
||||
|
||||
if e.features.IsEnabled(featuremgmt.FlagCloudWatchWildCardDimensionValues) {
|
||||
if e.features.IsEnabled(ctx, featuremgmt.FlagCloudWatchWildCardDimensionValues) {
|
||||
requestQueries, err = e.getDimensionValuesForWildcards(ctx, req.PluginContext, region, client, requestQueries, instance.tagValueCache, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -173,11 +173,11 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
|
||||
}
|
||||
|
||||
responseOpts := ResponseOpts{
|
||||
metricDataplane: s.features.IsEnabled(featuremgmt.FlagLokiMetricDataplane),
|
||||
logsDataplane: s.features.IsEnabled(featuremgmt.FlagLokiLogsDataplane),
|
||||
metricDataplane: s.features.IsEnabled(ctx, featuremgmt.FlagLokiMetricDataplane),
|
||||
logsDataplane: s.features.IsEnabled(ctx, featuremgmt.FlagLokiLogsDataplane),
|
||||
}
|
||||
|
||||
return queryData(ctx, req, dsInfo, responseOpts, s.tracer, logger, s.features.IsEnabled(featuremgmt.FlagLokiRunQueriesInParallel))
|
||||
return queryData(ctx, req, dsInfo, responseOpts, s.tracer, logger, s.features.IsEnabled(ctx, featuremgmt.FlagLokiRunQueriesInParallel))
|
||||
}
|
||||
|
||||
func queryData(ctx context.Context, req *backend.QueryDataRequest, dsInfo *datasourceInfo, responseOpts ResponseOpts, tracer tracing.Tracer, plog log.Logger, runInParallel bool) (*backend.QueryDataResponse, error) {
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/tracing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||
"github.com/grafana/grafana/pkg/tsdb/prometheus/client"
|
||||
@ -73,7 +74,7 @@ func New(
|
||||
// standard deviation sampler is the default for backwards compatibility
|
||||
exemplarSampler := exemplar.NewStandardDeviationSampler
|
||||
|
||||
if features.IsEnabled(featuremgmt.FlagDisablePrometheusExemplarSampling) {
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagDisablePrometheusExemplarSampling) {
|
||||
exemplarSampler = exemplar.NewNoOpSampler
|
||||
}
|
||||
|
||||
@ -85,7 +86,7 @@ func New(
|
||||
TimeInterval: timeInterval,
|
||||
ID: settings.ID,
|
||||
URL: settings.URL,
|
||||
enableDataplane: features.IsEnabled(featuremgmt.FlagPrometheusDataplane),
|
||||
enableDataplane: features.IsEnabledGlobally(featuremgmt.FlagPrometheusDataplane),
|
||||
exemplarSampler: exemplarSampler,
|
||||
}, nil
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
p "github.com/prometheus/common/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/tsdb/prometheus/kinds/dataquery"
|
||||
|
||||
"github.com/grafana/kindsys"
|
||||
@ -23,6 +24,7 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/prometheus/client"
|
||||
"github.com/grafana/grafana/pkg/tsdb/prometheus/models"
|
||||
@ -440,8 +442,7 @@ func setup() (*testContext, error) {
|
||||
JSONData: json.RawMessage(`{"timeInterval": "15s"}`),
|
||||
}
|
||||
|
||||
features := &fakeFeatureToggles{flags: map[string]bool{"prometheusBufferedClient": false}}
|
||||
|
||||
features := featuremgmt.WithFeatures()
|
||||
opts, err := client.CreateTransportOptions(context.Background(), settings, &setting.Cfg{}, log.New())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -460,14 +461,6 @@ func setup() (*testContext, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
type fakeFeatureToggles struct {
|
||||
flags map[string]bool
|
||||
}
|
||||
|
||||
func (f *fakeFeatureToggles) IsEnabled(feature string) bool {
|
||||
return f.flags[feature]
|
||||
}
|
||||
|
||||
type fakeHttpClientProvider struct {
|
||||
httpclient.Provider
|
||||
opts httpclient.Options
|
||||
|
Loading…
Reference in New Issue
Block a user