mirror of
https://github.com/grafana/grafana.git
synced 2024-11-21 16:38:03 -06:00
setting: configure toggles as true/false instead of array (#43326)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
This commit is contained in:
parent
dda84dbf1a
commit
f373588810
@ -1076,9 +1076,17 @@ grpc_port =
|
||||
license_path =
|
||||
|
||||
[feature_toggles]
|
||||
# enable features, separated by spaces
|
||||
# there are currently two ways to enable feature toggles in the `grafana.ini`.
|
||||
# you can either pass an array of feature you want to enable to the `enable` field or
|
||||
# configure each toggle by setting the name of the toggle to true/false. Toggles set to true/false
|
||||
# will take presidence over toggles in the `enable` list.
|
||||
|
||||
# enable = feature1,feature2
|
||||
enable =
|
||||
|
||||
# feature1 = true
|
||||
# feature2 = false
|
||||
|
||||
[date_formats]
|
||||
# For information on what formatting patterns that are supported https://momentjs.com/docs/#/displaying/
|
||||
|
||||
|
@ -1055,8 +1055,15 @@
|
||||
;license_path =
|
||||
|
||||
[feature_toggles]
|
||||
# enable features, separated by spaces
|
||||
;enable =
|
||||
# there are currently two ways to enable feature toggles in the `grafana.ini`.
|
||||
# you can either pass an array of feature you want to enable to the `enable` field or
|
||||
# configure each toggle by setting the name of the toggle to true/false. Toggles set to true/false
|
||||
# will take presidence over toggles in the `enable` list.
|
||||
|
||||
;enable = feature1,feature2
|
||||
|
||||
;feature1 = true
|
||||
;feature2 = false
|
||||
|
||||
[date_formats]
|
||||
# For information on what formatting patterns that are supported https://momentjs.com/docs/#/displaying/
|
||||
|
@ -1407,17 +1407,6 @@ func (cfg *Cfg) readRenderingSettings(iniFile *ini.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Cfg) readFeatureToggles(iniFile *ini.File) error {
|
||||
// Read and populate feature toggles list
|
||||
featureTogglesSection := iniFile.Section("feature_toggles")
|
||||
cfg.FeatureToggles = make(map[string]bool)
|
||||
featuresTogglesStr := valueAsString(featureTogglesSection, "enable", "")
|
||||
for _, feature := range util.SplitString(featuresTogglesStr) {
|
||||
cfg.FeatureToggles[feature] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readAlertingSettings(iniFile *ini.File) error {
|
||||
alerting := iniFile.Section("alerting")
|
||||
enabled, err := alerting.Key("enabled").Bool()
|
||||
|
74
pkg/setting/setting_feature_toggles.go
Normal file
74
pkg/setting/setting_feature_toggles.go
Normal file
@ -0,0 +1,74 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
featureToggleInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "feature_toggles_info",
|
||||
Help: "info metric that exposes what feature toggles are enabled or not",
|
||||
Namespace: "grafana",
|
||||
}, []string{"name"})
|
||||
|
||||
defaultFeatureToggles = map[string]bool{
|
||||
"recordedQueries": false,
|
||||
"accesscontrol": false,
|
||||
"service-accounts": false,
|
||||
"httpclientprovider_azure_auth": false,
|
||||
}
|
||||
)
|
||||
|
||||
func (cfg *Cfg) readFeatureToggles(iniFile *ini.File) error {
|
||||
toggles, err := overrideDefaultWithConfiguration(iniFile, defaultFeatureToggles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg.FeatureToggles = toggles
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func overrideDefaultWithConfiguration(iniFile *ini.File, featureToggles map[string]bool) (map[string]bool, error) {
|
||||
// Read and populate feature toggles list
|
||||
featureTogglesSection := iniFile.Section("feature_toggles")
|
||||
|
||||
// parse the comma separated list in `enable`.
|
||||
featuresTogglesStr := valueAsString(featureTogglesSection, "enable", "")
|
||||
for _, feature := range util.SplitString(featuresTogglesStr) {
|
||||
featureToggles[feature] = true
|
||||
}
|
||||
|
||||
// read all other settings under [feature_toggles]. If a toggle is
|
||||
// present in both the value in `enable` is overridden.
|
||||
for _, v := range featureTogglesSection.Keys() {
|
||||
if v.Name() == "enable" {
|
||||
continue
|
||||
}
|
||||
|
||||
b, err := strconv.ParseBool(v.Value())
|
||||
if err != nil {
|
||||
return featureToggles, err
|
||||
}
|
||||
|
||||
featureToggles[v.Name()] = b
|
||||
}
|
||||
|
||||
// track if feature toggles are enabled or not using an info metric
|
||||
for k, v := range featureToggles {
|
||||
if v {
|
||||
featureToggleInfo.WithLabelValues(k).Set(1)
|
||||
} else {
|
||||
featureToggleInfo.WithLabelValues(k).Set(0)
|
||||
}
|
||||
}
|
||||
|
||||
return featureToggles, nil
|
||||
}
|
98
pkg/setting/setting_feature_toggles_test.go
Normal file
98
pkg/setting/setting_feature_toggles_test.go
Normal file
@ -0,0 +1,98 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
func TestFeatureToggles(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
conf map[string]string
|
||||
err error
|
||||
expectedToggles map[string]bool
|
||||
defaultToggles map[string]bool
|
||||
}{
|
||||
{
|
||||
name: "can parse feature toggles passed in the `enable` array",
|
||||
conf: map[string]string{
|
||||
"enable": "feature1,feature2",
|
||||
},
|
||||
expectedToggles: map[string]bool{
|
||||
"feature1": true,
|
||||
"feature2": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "can parse feature toggles listed under [feature_toggles]",
|
||||
conf: map[string]string{
|
||||
"enable": "feature1,feature2",
|
||||
"feature3": "true",
|
||||
},
|
||||
expectedToggles: map[string]bool{
|
||||
"feature1": true,
|
||||
"feature2": true,
|
||||
"feature3": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "toggles under [feature_toggles] overrides those in the array",
|
||||
conf: map[string]string{
|
||||
"enable": "feature1,feature2",
|
||||
"feature2": "false",
|
||||
},
|
||||
expectedToggles: map[string]bool{
|
||||
"feature1": true,
|
||||
"feature2": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid boolean value should return syntax error",
|
||||
conf: map[string]string{
|
||||
"enable": "feature1,feature2",
|
||||
"feature2": "invalid",
|
||||
},
|
||||
expectedToggles: map[string]bool{},
|
||||
err: strconv.ErrSyntax,
|
||||
},
|
||||
{
|
||||
name: "should override default feature toggles",
|
||||
defaultToggles: map[string]bool{
|
||||
"feature1": true,
|
||||
},
|
||||
conf: map[string]string{
|
||||
"feature1": "false",
|
||||
},
|
||||
expectedToggles: map[string]bool{
|
||||
"feature1": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
f := ini.Empty()
|
||||
|
||||
toggles, _ := f.NewSection("feature_toggles")
|
||||
for k, v := range tc.conf {
|
||||
_, err := toggles.NewKey(k, v)
|
||||
require.ErrorIs(t, err, nil)
|
||||
}
|
||||
|
||||
dt := map[string]bool{}
|
||||
if len(tc.defaultToggles) > 0 {
|
||||
dt = tc.defaultToggles
|
||||
}
|
||||
|
||||
featureToggles, err := overrideDefaultWithConfiguration(f, dt)
|
||||
require.ErrorIs(t, err, tc.err)
|
||||
|
||||
if err == nil {
|
||||
for k, v := range featureToggles {
|
||||
require.Equal(t, tc.expectedToggles[k], v, tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user