mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
FeatureFlags: Remove enabled from FeatureFlag model (#79960)
This commit is contained in:
@@ -22,6 +22,8 @@ type FeatureManager struct {
|
||||
enabled map[string]bool // only the "on" values
|
||||
config string // path to config file
|
||||
vars map[string]any
|
||||
startup map[string]bool // the explicit values registered at startup
|
||||
warnings map[string]string // potential warnings about the flag
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
@@ -73,16 +75,16 @@ func (fm *FeatureManager) registerFlags(flags ...FeatureFlag) {
|
||||
}
|
||||
|
||||
// meetsRequirements checks if grafana is able to run the given feature due to dev mode or licensing requirements
|
||||
func (fm *FeatureManager) meetsRequirements(ff *FeatureFlag) bool {
|
||||
func (fm *FeatureManager) meetsRequirements(ff *FeatureFlag) (bool, string) {
|
||||
if ff.RequiresDevMode && !fm.isDevMod {
|
||||
return false
|
||||
return false, "requires dev mode"
|
||||
}
|
||||
|
||||
if ff.RequiresLicense && (fm.licensing == nil || !fm.licensing.FeatureEnabled(ff.Name)) {
|
||||
return false
|
||||
return false, "license requirement"
|
||||
}
|
||||
|
||||
return true
|
||||
return true, ""
|
||||
}
|
||||
|
||||
// Update
|
||||
@@ -90,14 +92,16 @@ func (fm *FeatureManager) update() {
|
||||
enabled := make(map[string]bool)
|
||||
for _, flag := range fm.flags {
|
||||
// if grafana cannot run the feature, omit metrics around it
|
||||
if !fm.meetsRequirements(flag) {
|
||||
ok, reason := fm.meetsRequirements(flag)
|
||||
if !ok {
|
||||
fm.warnings[flag.Name] = reason
|
||||
continue
|
||||
}
|
||||
|
||||
// Update the registry
|
||||
track := 0.0
|
||||
// TODO: CEL - expression
|
||||
if flag.Expression == "true" {
|
||||
|
||||
if flag.Expression == "true" || (fm.startup[flag.Name]) {
|
||||
track = 1
|
||||
enabled[flag.Name] = true
|
||||
}
|
||||
@@ -196,30 +200,35 @@ func WithManager(spec ...any) *FeatureManager {
|
||||
idx++
|
||||
}
|
||||
|
||||
features[key] = &FeatureFlag{Name: key, Enabled: val}
|
||||
features[key] = &FeatureFlag{Name: key}
|
||||
if val {
|
||||
enabled[key] = true
|
||||
}
|
||||
}
|
||||
|
||||
return &FeatureManager{enabled: enabled, flags: features}
|
||||
return &FeatureManager{enabled: enabled, flags: features, startup: enabled, warnings: map[string]string{}}
|
||||
}
|
||||
|
||||
// WithFeatureFlags is used to define feature toggles for testing.
|
||||
// WithFeatureManager is used to define feature toggle manager for testing.
|
||||
// It should be used when your test feature toggles require metadata beyond `Name` and `Enabled`.
|
||||
// You should provide a feature toggle Name at a minimum.
|
||||
func WithFeatureFlags(flags []*FeatureFlag) *FeatureManager {
|
||||
func WithFeatureManager(flags []*FeatureFlag, disabled ...string) *FeatureManager {
|
||||
count := len(flags)
|
||||
features := make(map[string]*FeatureFlag, count)
|
||||
enabled := make(map[string]bool, count)
|
||||
|
||||
dis := make(map[string]bool)
|
||||
for _, v := range disabled {
|
||||
dis[v] = true
|
||||
}
|
||||
|
||||
for _, f := range flags {
|
||||
if f.Name == "" {
|
||||
continue
|
||||
}
|
||||
features[f.Name] = f
|
||||
enabled[f.Name] = f.Enabled
|
||||
enabled[f.Name] = !dis[f.Name]
|
||||
}
|
||||
|
||||
return &FeatureManager{enabled: enabled, flags: features}
|
||||
return &FeatureManager{enabled: enabled, flags: features, startup: enabled, warnings: map[string]string{}}
|
||||
}
|
||||
|
@@ -26,7 +26,8 @@ func TestFeatureManager(t *testing.T) {
|
||||
|
||||
t.Run("check license validation", func(t *testing.T) {
|
||||
ft := FeatureManager{
|
||||
flags: map[string]*FeatureFlag{},
|
||||
flags: map[string]*FeatureFlag{},
|
||||
warnings: map[string]string{},
|
||||
}
|
||||
ft.registerFlags(FeatureFlag{
|
||||
Name: "a",
|
||||
|
@@ -110,8 +110,8 @@ func (s *FeatureFlagStage) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// These are properties about the feature, but not the current state or value for it
|
||||
type FeatureFlag struct {
|
||||
// Required properties
|
||||
Name string `json:"name" yaml:"name"` // Unique name
|
||||
Description string `json:"description"`
|
||||
Stage FeatureFlagStage `json:"stage,omitempty"`
|
||||
@@ -131,9 +131,6 @@ type FeatureFlag struct {
|
||||
FrontendOnly bool `json:"frontend,omitempty"` // change is only seen in the frontend
|
||||
HideFromDocs bool `json:"hideFromDocs,omitempty"` // don't add the values to docs
|
||||
|
||||
// This field is only for the feature management API. To enable your feature toggle by default, use `Expression`.
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
|
||||
// These are currently unused
|
||||
DocsURL string `json:"docsURL,omitempty"`
|
||||
RequiresRestart bool `json:"requiresRestart,omitempty"` // The server must be initialized with the value
|
||||
|
@@ -826,11 +826,11 @@ var (
|
||||
{
|
||||
Name: "alertingNoDataErrorExecution",
|
||||
Description: "Changes how Alerting state manager handles execution of NoData/Error",
|
||||
Stage: FeatureStagePrivatePreview,
|
||||
Stage: FeatureStageGeneralAvailability,
|
||||
FrontendOnly: false,
|
||||
Owner: grafanaAlertingSquad,
|
||||
RequiresRestart: true,
|
||||
Enabled: true,
|
||||
Expression: "true", // enabled by default
|
||||
Created: time.Date(2023, time.August, 15, 12, 0, 0, 0, time.UTC),
|
||||
},
|
||||
{
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package featuremgmt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -28,6 +27,8 @@ func ProvideManagerService(cfg *setting.Cfg, licensing licensing.Licensing) (*Fe
|
||||
licensing: licensing,
|
||||
flags: make(map[string]*FeatureFlag, 30),
|
||||
enabled: make(map[string]bool),
|
||||
startup: make(map[string]bool),
|
||||
warnings: make(map[string]string),
|
||||
allowEditing: cfg.FeatureManagement.AllowEditing && cfg.FeatureManagement.UpdateWebhook != "",
|
||||
log: log.New("featuremgmt"),
|
||||
}
|
||||
@@ -41,21 +42,21 @@ func ProvideManagerService(cfg *setting.Cfg, licensing licensing.Licensing) (*Fe
|
||||
return mgmt, err
|
||||
}
|
||||
for key, val := range flags {
|
||||
flag, ok := mgmt.flags[key]
|
||||
_, ok := mgmt.flags[key]
|
||||
if !ok {
|
||||
switch key {
|
||||
// renamed the flag so it supports more panels
|
||||
case "autoMigrateGraphPanels":
|
||||
flag = mgmt.flags[FlagAutoMigrateOldPanels]
|
||||
key = FlagAutoMigrateOldPanels
|
||||
default:
|
||||
flag = &FeatureFlag{
|
||||
mgmt.flags[key] = &FeatureFlag{
|
||||
Name: key,
|
||||
Stage: FeatureStageUnknown,
|
||||
}
|
||||
mgmt.flags[key] = flag
|
||||
mgmt.warnings[key] = "unknown flag in config"
|
||||
}
|
||||
}
|
||||
flag.Expression = fmt.Sprintf("%t", val) // true | false
|
||||
mgmt.startup[key] = val
|
||||
}
|
||||
|
||||
// Load config settings
|
||||
|
@@ -95,7 +95,7 @@ permissionsFilterRemoveSubquery,experimental,@grafana/backend-platform,2023-08-0
|
||||
prometheusConfigOverhaulAuth,GA,@grafana/observability-metrics,2023-07-21,false,false,false,false
|
||||
configurableSchedulerTick,experimental,@grafana/alerting-squad,2023-07-26,false,false,true,false
|
||||
influxdbSqlSupport,GA,@grafana/observability-metrics,2023-08-02,false,false,true,false
|
||||
alertingNoDataErrorExecution,privatePreview,@grafana/alerting-squad,2023-08-15,false,false,true,false
|
||||
alertingNoDataErrorExecution,GA,@grafana/alerting-squad,2023-08-15,false,false,true,false
|
||||
angularDeprecationUI,experimental,@grafana/plugins-platform-backend,2023-08-29,false,false,false,true
|
||||
dashgpt,preview,@grafana/dashboards-squad,2023-11-17,false,false,false,true
|
||||
reportingRetries,preview,@grafana/sharing-squad,2023-08-31,false,false,true,false
|
||||
|
|
Reference in New Issue
Block a user