From d4c32f388b16200848cf472c2862925419bf93ff Mon Sep 17 00:00:00 2001 From: Hugo Kiyodi Oshiro Date: Mon, 28 Aug 2023 18:11:34 +0200 Subject: [PATCH] Add feature flags from environment variable (#722) * Add featuretoggles package * Change featuretoggles to experimental package * Update folder.golden.txt --------- Co-authored-by: Giuseppe Guerra --- experimental/featuretoggles/featuretoggles.go | 47 +++++++++++++++++++ .../featuretoggles/featuretoggles_test.go | 30 ++++++++++++ experimental/testdata/folder.golden.txt | 36 +++++++------- 3 files changed, 95 insertions(+), 18 deletions(-) create mode 100644 experimental/featuretoggles/featuretoggles.go create mode 100644 experimental/featuretoggles/featuretoggles_test.go diff --git a/experimental/featuretoggles/featuretoggles.go b/experimental/featuretoggles/featuretoggles.go new file mode 100644 index 000000000..17429aa76 --- /dev/null +++ b/experimental/featuretoggles/featuretoggles.go @@ -0,0 +1,47 @@ +package featuretoggles + +import ( + "os" + "strings" +) + +// envFeatureTogglesEnable is the environment variable set by Grafana containing the list of enabled feature toggles. +const envFeatureTogglesEnable = "GF_INSTANCE_FEATURE_TOGGLES_ENABLE" + +// FeatureToggles can check if feature toggles are enabled on the Grafana instance. +type FeatureToggles interface { + // IsEnabled returns true if the provided feature flag is set. + IsEnabled(flag string) bool +} + +// featureToggles implements a FeatureToggles that returns true if a flag is present in the flags map. +type featureToggles struct { + // flags is a set-like map of feature flags that are enabled. + flags map[string]struct{} +} + +// IsEnabled returns true if flag is contained in f.flags. +func (f featureToggles) IsEnabled(flag string) bool { + _, ok := f.flags[flag] + return ok +} + +// newFeatureTogglesFromEnv returns a new featureToggles instance with its flags set from environment variables. +func newFeatureTogglesFromEnv() featureToggles { + return featureToggles{flags: flagsMapFromEnv()} +} + +// flagsMapFromEnv returns a new set-like map[string]struct{}, where the keys are the space-separated names in +// the `envFeatureTogglesEnable` env var. +func flagsMapFromEnv() map[string]struct{} { + flags := strings.Split(os.Getenv(envFeatureTogglesEnable), ",") + r := make(map[string]struct{}, len(flags)) + for _, flag := range flags { + r[flag] = struct{}{} + } + return r +} + +// DefaultFeatureToggles is the default feature toggles implementation. +// It contains the same feature toggles as the Grafana instance where the plugin is running. +var DefaultFeatureToggles FeatureToggles = newFeatureTogglesFromEnv() diff --git a/experimental/featuretoggles/featuretoggles_test.go b/experimental/featuretoggles/featuretoggles_test.go new file mode 100644 index 000000000..2d0bb734f --- /dev/null +++ b/experimental/featuretoggles/featuretoggles_test.go @@ -0,0 +1,30 @@ +package featuretoggles + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEnvFeatureToggles(t *testing.T) { + t.Run("should work when flag is provided", func(t *testing.T) { + t.Setenv(envFeatureTogglesEnable, "") + flags := newFeatureTogglesFromEnv() + require.False(t, flags.IsEnabled("abc")) + }) + + t.Run("should work when single flag is provided", func(t *testing.T) { + t.Setenv(envFeatureTogglesEnable, "abc") + flags := newFeatureTogglesFromEnv() + require.True(t, flags.IsEnabled("abc")) + require.False(t, flags.IsEnabled("def")) + }) + + t.Run("should work when multiple flags are provided", func(t *testing.T) { + t.Setenv(envFeatureTogglesEnable, "abc,def") + flags := newFeatureTogglesFromEnv() + require.True(t, flags.IsEnabled("abc")) + require.True(t, flags.IsEnabled("def")) + require.False(t, flags.IsEnabled("ghi")) + }) +} diff --git a/experimental/testdata/folder.golden.txt b/experimental/testdata/folder.golden.txt index 4ef147af7..e818def7a 100644 --- a/experimental/testdata/folder.golden.txt +++ b/experimental/testdata/folder.golden.txt @@ -9,24 +9,24 @@ Frame[0] { "pathSeparator": "/" } Name: -Dimensions: 2 Fields by 17 Rows -+----------------------+------------------+ -| Name: name | Name: media-type | -| Labels: | Labels: | -| Type: []string | Type: []string | -+----------------------+------------------+ -| README.md | | -| actions | directory | -| authclient | directory | -| datasourcetest | directory | -| e2e | directory | -| fileinfo.go | | -| fileinfo_test.go | | -| frame_sorter.go | | -| frame_sorter_test.go | | -| ... | ... | -+----------------------+------------------+ +Dimensions: 2 Fields by 18 Rows ++------------------+------------------+ +| Name: name | Name: media-type | +| Labels: | Labels: | +| Type: []string | Type: []string | ++------------------+------------------+ +| README.md | | +| actions | directory | +| authclient | directory | +| datasourcetest | directory | +| e2e | directory | +| featuretoggles | directory | +| fileinfo.go | | +| fileinfo_test.go | | +| frame_sorter.go | | +| ... | ... | ++------------------+------------------+ ====== TEST DATA RESPONSE (arrow base64) ====== -FRAME=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 +FRAME=QVJST1cxAAD/////yAEAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEEAAoADAAAAAgABAAKAAAACAAAALgAAAADAAAATAAAACgAAAAEAAAAwP7//wgAAAAMAAAAAAAAAAAAAAAFAAAAcmVmSWQAAADg/v//CAAAAAwAAAAAAAAAAAAAAAQAAABuYW1lAAAAAAD///8IAAAAUAAAAEQAAAB7InR5cGUiOiJkaXJlY3RvcnktbGlzdGluZyIsInR5cGVWZXJzaW9uIjpbMCwwXSwicGF0aFNlcGFyYXRvciI6Ii8ifQAAAAAEAAAAbWV0YQAAAAACAAAAeAAAAAQAAACi////FAAAADwAAAA8AAAAAAAABTgAAAABAAAABAAAAJD///8IAAAAEAAAAAYAAABzdHJpbmcAAAYAAAB0c3R5cGUAAAAAAACI////CgAAAG1lZGlhLXR5cGUAAAAAEgAYABQAAAATAAwAAAAIAAQAEgAAABQAAABEAAAASAAAAAAAAAVEAAAAAQAAAAwAAAAIAAwACAAEAAgAAAAIAAAAEAAAAAYAAABzdHJpbmcAAAYAAAB0c3R5cGUAAAAAAAAEAAQABAAAAAQAAABuYW1lAAAAAP/////YAAAAFAAAAAAAAAAMABYAFAATAAwABAAMAAAA8AEAAAAAAAAUAAAAAAAAAwQACgAYAAwACAAEAAoAAAAUAAAAeAAAABIAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMAAAAAAAAAFAAAAAAAAAA7gAAAAAAAABAAQAAAAAAAAAAAAAAAAAAQAEAAAAAAABMAAAAAAAAAJABAAAAAAAAWgAAAAAAAAAAAAAAAgAAABIAAAAAAAAAAAAAAAAAAAASAAAAAAAAAAAAAAAAAAAAAAAAAAkAAAAQAAAAGgAAACgAAAArAAAAOQAAAEQAAABUAAAAYwAAAHcAAACRAAAAsAAAALsAAADBAAAAxQAAANgAAADmAAAA7gAAAAAAAABSRUFETUUubWRhY3Rpb25zYXV0aGNsaWVudGRhdGFzb3VyY2V0ZXN0ZTJlZmVhdHVyZXRvZ2dsZXNmaWxlaW5mby5nb2ZpbGVpbmZvX3Rlc3QuZ29mcmFtZV9zb3J0ZXIuZ29mcmFtZV9zb3J0ZXJfdGVzdC5nb2dvbGRlbl9yZXNwb25zZV9jaGVja2VyLmdvZ29sZGVuX3Jlc3BvbnNlX2NoZWNrZXJfdGVzdC5nb2h0dHBfbG9nZ2VybWFjcm9zbW9ja29hdXRodG9rZW5yZXRyaWV2ZXJyZXN0X2NsaWVudC5nb3Rlc3RkYXRhAAAAAAAAAAAAAAkAAAASAAAAGwAAACQAAAAtAAAALQAAAC0AAAAtAAAALQAAAC0AAAAtAAAANgAAAD8AAABIAAAAUQAAAFEAAABaAAAAAAAAAGRpcmVjdG9yeWRpcmVjdG9yeWRpcmVjdG9yeWRpcmVjdG9yeWRpcmVjdG9yeWRpcmVjdG9yeWRpcmVjdG9yeWRpcmVjdG9yeWRpcmVjdG9yeWRpcmVjdG9yeQAAAAAAABAAAAAMABQAEgAMAAgABAAMAAAAEAAAACwAAAA8AAAAAAAEAAEAAADYAQAAAAAAAOAAAAAAAAAA8AEAAAAAAAAAAAAAAAAAAAAAAAAAAAoADAAAAAgABAAKAAAACAAAALgAAAADAAAATAAAACgAAAAEAAAAwP7//wgAAAAMAAAAAAAAAAAAAAAFAAAAcmVmSWQAAADg/v//CAAAAAwAAAAAAAAAAAAAAAQAAABuYW1lAAAAAAD///8IAAAAUAAAAEQAAAB7InR5cGUiOiJkaXJlY3RvcnktbGlzdGluZyIsInR5cGVWZXJzaW9uIjpbMCwwXSwicGF0aFNlcGFyYXRvciI6Ii8ifQAAAAAEAAAAbWV0YQAAAAACAAAAeAAAAAQAAACi////FAAAADwAAAA8AAAAAAAABTgAAAABAAAABAAAAJD///8IAAAAEAAAAAYAAABzdHJpbmcAAAYAAAB0c3R5cGUAAAAAAACI////CgAAAG1lZGlhLXR5cGUAAAAAEgAYABQAAAATAAwAAAAIAAQAEgAAABQAAABEAAAASAAAAAAAAAVEAAAAAQAAAAwAAAAIAAwACAAEAAgAAAAIAAAAEAAAAAYAAABzdHJpbmcAAAYAAAB0c3R5cGUAAAAAAAAEAAQABAAAAAQAAABuYW1lAAAAAPgBAABBUlJPVzE=