mirror of https://github.com/grafana/grafana
setting: configure toggles as true/false instead of array (#43326)
Signed-off-by: bergquist <carl.bergquist@gmail.com>pull/43379/head
parent
dda84dbf1a
commit
f373588810
@ -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 |
||||||
|
} |
||||||
@ -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