chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
Copyright (c) 2022 Portainer.io
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Package featureflags implements feature flags for Portainer projects
|
||||
|
||||
Feature flags are used to turn on features that are not production ready.
|
||||
Use the Parse function to enable feature flags and also the pass a list of
|
||||
available flags
|
||||
|
||||
e.g.
|
||||
var SupportedFeatureFlags = []featureflags.Feature{
|
||||
"my-feature",
|
||||
}
|
||||
|
||||
func main() {
|
||||
// parse cli flags
|
||||
|
||||
// pass cli flags and supported feature flags to featureflags.Parse
|
||||
featureflags.Parse([]string{"my-feature"}, SupportedFeatureFlags)
|
||||
}
|
||||
|
||||
...
|
||||
if featureflags.IsEnabled("my-feature") {
|
||||
// do something
|
||||
}
|
||||
*/
|
||||
package featureflags
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// Feature represents a feature that can be enabled or disabled via feature flags
|
||||
type Feature string
|
||||
|
||||
var featureFlags map[Feature]bool
|
||||
|
||||
// String returns the string representation of a feature flag
|
||||
func (f Feature) String() string {
|
||||
return string(f)
|
||||
}
|
||||
|
||||
// IsEnabled returns true if the feature flag is enabled
|
||||
func IsEnabled(feat Feature) bool {
|
||||
return featureFlags[feat]
|
||||
}
|
||||
|
||||
// IsSupported returns true if the feature is supported
|
||||
func IsSupported(feat Feature) bool {
|
||||
_, ok := featureFlags[feat]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// FeatureFlags returns a map of all feature flags.
|
||||
// this is useful in situations where you need to pass all feature flags to a REST handler
|
||||
// function
|
||||
func FeatureFlags() map[Feature]bool {
|
||||
return featureFlags
|
||||
}
|
||||
|
||||
func initSupportedFeatures(supportedFeatures []Feature) {
|
||||
featureFlags = make(map[Feature]bool)
|
||||
for _, feat := range supportedFeatures {
|
||||
featureFlags[feat] = false
|
||||
}
|
||||
}
|
||||
|
||||
// Parse turns on feature flags
|
||||
// It accepts a list of feature flags as strings and a list of supported features.
|
||||
// It will also check for feature flags in the PORTAINER_FEATURE_FLAGS environment variable.
|
||||
// Multiple feature flags can be specified with the PORTAINER_FEATURE_FLAGS environment.
|
||||
// variable using a comma separated list. e.g. "PORTAINER_FEATURE_FLAGS=feature1,feature2".
|
||||
// If a feature flag is not supported, it will be logged and ignored.
|
||||
// If a feature flag is supported, it will be logged and enabled.
|
||||
func Parse(features []string, supportedFeatures []Feature) {
|
||||
initSupportedFeatures(supportedFeatures)
|
||||
|
||||
env := os.Getenv("PORTAINER_FEATURE_FLAGS")
|
||||
envFeatures := []string{}
|
||||
if env != "" {
|
||||
envFeatures = strings.Split(env, ",")
|
||||
}
|
||||
|
||||
features = append(features, envFeatures...)
|
||||
|
||||
// loop through feature flags to check if they are supported
|
||||
for _, feat := range features {
|
||||
f := Feature(strings.ToLower(feat))
|
||||
if _, ok := featureFlags[f]; !ok {
|
||||
log.Warn().Str("feature", f.String()).Msgf("unknown feature flag")
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
featureFlags[f] = true
|
||||
log.Info().Str("feature", f.String()).Msg("enabling feature")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package featureflags
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_enableFeaturesFromFlags(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
supportedFeatures := []Feature{"supported", "supported2", "supported3", "supported4", "supported5"}
|
||||
|
||||
t.Run("supported features should be supported", func(t *testing.T) {
|
||||
initSupportedFeatures(supportedFeatures)
|
||||
|
||||
for _, featureFlag := range supportedFeatures {
|
||||
is.True(IsSupported(featureFlag))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("unsupported features should not be supported", func(t *testing.T) {
|
||||
initSupportedFeatures(supportedFeatures)
|
||||
|
||||
is.False(IsSupported("unsupported"))
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
cliFeatureFlags []string
|
||||
envFeatureFlags []string
|
||||
}{
|
||||
{[]string{"supported", "supported2"}, []string{"supported3", "supported4"}},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := os.Unsetenv("PORTAINER_FEATURE_FLAGS")
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Setenv("PORTAINER_FEATURE_FLAGS", strings.Join(test.envFeatureFlags, ","))
|
||||
|
||||
t.Run("testing", func(t *testing.T) {
|
||||
Parse(test.cliFeatureFlags, supportedFeatures)
|
||||
supported := toFeatureMap(test.cliFeatureFlags, test.envFeatureFlags)
|
||||
|
||||
// add env flags to supported flags
|
||||
for _, featureFlag := range test.envFeatureFlags {
|
||||
supported[Feature(featureFlag)] = true
|
||||
}
|
||||
|
||||
for _, featureFlag := range supportedFeatures {
|
||||
if _, ok := supported[featureFlag]; ok {
|
||||
is.True(IsEnabled(featureFlag))
|
||||
} else {
|
||||
is.False(IsEnabled(featureFlag))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// helper
|
||||
func toFeatureMap(cliFeatures []string, envFeatures []string) map[Feature]bool {
|
||||
m := map[Feature]bool{}
|
||||
for _, s := range cliFeatures {
|
||||
m[Feature(s)] = true
|
||||
}
|
||||
|
||||
for _, s := range envFeatures {
|
||||
m[Feature(s)] = true
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
Reference in New Issue
Block a user