setting: configure toggles as true/false instead of array (#43326)

Signed-off-by: bergquist <carl.bergquist@gmail.com>
This commit is contained in:
Carl Bergquist 2021-12-20 15:33:11 +01:00 committed by GitHub
parent dda84dbf1a
commit f373588810
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 190 additions and 14 deletions

View File

@ -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/

View File

@ -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/

View File

@ -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()

View 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
}

View 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)
}
}
}
}