Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <psteinhardt@gitlab.com>2022-06-30 15:16:12 +0300
committerPatrick Steinhardt <psteinhardt@gitlab.com>2022-07-06 09:12:53 +0300
commitd460daf3ab1a1b11587a362e5b796d13b57947b1 (patch)
treed61d68741f01ae3bc4e679dd8c7e3b398f3f14ca
parentdc6452d0da11be7750572bea9515b247d1659ffd (diff)
featureflag: Add constructor from metadata key
Extract the logic to construct a feature flag from its gRPC metadata key from `FromContext()` into a new `FromMetadataKey()` function. This will be used at a later point.
-rw-r--r--internal/metadata/featureflag/context.go19
-rw-r--r--internal/metadata/featureflag/featureflag.go29
-rw-r--r--internal/metadata/featureflag/featureflag_test.go62
3 files changed, 94 insertions, 16 deletions
diff --git a/internal/metadata/featureflag/context.go b/internal/metadata/featureflag/context.go
index ca9b40cb2..25f269d60 100644
--- a/internal/metadata/featureflag/context.go
+++ b/internal/metadata/featureflag/context.go
@@ -10,9 +10,6 @@ import (
)
const (
- // ffPrefix is the prefix used for Gitaly-scoped feature flags.
- ffPrefix = "gitaly-feature-"
-
// explicitFeatureFlagKey is used by ContextWithExplicitFeatureFlags to mark a context as
// requiring all feature flags to have been explicitly defined.
explicitFeatureFlagKey = "require_explicit_feature_flag_checks"
@@ -99,19 +96,9 @@ func FromContext(ctx context.Context) map[FeatureFlag]bool {
flags := map[FeatureFlag]bool{}
for rawName, value := range rawFlags {
- flagName := strings.TrimPrefix(rawName, ffPrefix)
- flagName = strings.ReplaceAll(flagName, "-", "_")
-
- // Try to look up the feature flag definition. If we don't know this flag, we
- // instead return a manually constructed feature flag that we pretend to be off by
- // default. We cannot ignore any unknown feature flags though given that we may
- // want to pass them down to other systems that may know the definition.
- flag, ok := flagsByName[flagName]
- if !ok {
- flag = FeatureFlag{
- Name: flagName,
- OnByDefault: false,
- }
+ flag, err := FromMetadataKey(rawName)
+ if err != nil {
+ continue
}
flags[flag] = value == "true"
diff --git a/internal/metadata/featureflag/featureflag.go b/internal/metadata/featureflag/featureflag.go
index 60d0d0530..e2c91568f 100644
--- a/internal/metadata/featureflag/featureflag.go
+++ b/internal/metadata/featureflag/featureflag.go
@@ -36,6 +36,11 @@ var (
flagsByName = map[string]FeatureFlag{}
)
+const (
+ // ffPrefix is the prefix used for Gitaly-scoped feature flags.
+ ffPrefix = "gitaly-feature-"
+)
+
// DefinedFlags returns the set of feature flags that have been explicitly defined.
func DefinedFlags() []FeatureFlag {
flags := make([]FeatureFlag, 0, len(flagsByName))
@@ -79,6 +84,30 @@ func NewFeatureFlag(name, version, rolloutIssueURL string, onByDefault bool) Fea
return featureFlag
}
+// FromMetadataKey parses the given gRPC metadata key into a Gitaly feature flag and performs the
+// necessary conversions. Returns an error in case the metadata does not refer to a feature flag.
+//
+// This function tries to look up the default value via our set of flag definitions. In case the
+// flag definition is unknown to Gitaly it assumes a default value of `false`.
+func FromMetadataKey(metadataKey string) (FeatureFlag, error) {
+ if !strings.HasPrefix(metadataKey, ffPrefix) {
+ return FeatureFlag{}, fmt.Errorf("not a feature flag: %q", metadataKey)
+ }
+
+ flagName := strings.TrimPrefix(metadataKey, ffPrefix)
+ flagName = strings.ReplaceAll(flagName, "-", "_")
+
+ flag, ok := flagsByName[flagName]
+ if !ok {
+ flag = FeatureFlag{
+ Name: flagName,
+ OnByDefault: false,
+ }
+ }
+
+ return flag, nil
+}
+
// FormatWithValue converts the feature flag into a string with the given state. Note that this
// function uses the feature flag name and not the raw metadata key as used in gRPC metadata.
func (ff FeatureFlag) FormatWithValue(enabled bool) string {
diff --git a/internal/metadata/featureflag/featureflag_test.go b/internal/metadata/featureflag/featureflag_test.go
index 8944e21fd..7a5d5915b 100644
--- a/internal/metadata/featureflag/featureflag_test.go
+++ b/internal/metadata/featureflag/featureflag_test.go
@@ -1,12 +1,74 @@
package featureflag
import (
+ "fmt"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/metadata"
)
+func TestFeatureFlag_FromMetadataKey(t *testing.T) {
+ defer func(old map[string]FeatureFlag) {
+ flagsByName = old
+ }(flagsByName)
+
+ defaultEnabled := FeatureFlag{
+ Name: "default_enabled",
+ OnByDefault: true,
+ }
+ defaultDisabled := FeatureFlag{
+ Name: "default_disabled",
+ OnByDefault: false,
+ }
+
+ flagsByName = map[string]FeatureFlag{
+ defaultEnabled.Name: defaultEnabled,
+ defaultDisabled.Name: defaultDisabled,
+ }
+
+ for _, tc := range []struct {
+ desc string
+ metadataKey string
+ expectedErr error
+ expectedFlag FeatureFlag
+ }{
+ {
+ desc: "empty key",
+ metadataKey: "",
+ expectedErr: fmt.Errorf("not a feature flag: \"\""),
+ },
+ {
+ desc: "invalid prefix",
+ metadataKey: "invalid-prefix",
+ expectedErr: fmt.Errorf("not a feature flag: \"invalid-prefix\""),
+ },
+ {
+ desc: "default enabled flag",
+ metadataKey: defaultEnabled.MetadataKey(),
+ expectedFlag: defaultEnabled,
+ },
+ {
+ desc: "default disabled flag",
+ metadataKey: defaultDisabled.MetadataKey(),
+ expectedFlag: defaultDisabled,
+ },
+ {
+ desc: "undefined flag",
+ metadataKey: "gitaly-feature-not-defined",
+ expectedFlag: FeatureFlag{
+ Name: "not_defined",
+ },
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ flag, err := FromMetadataKey(tc.metadataKey)
+ require.Equal(t, tc.expectedErr, err)
+ require.Equal(t, tc.expectedFlag, flag)
+ })
+ }
+}
+
func TestFeatureFlag_enabled(t *testing.T) {
for _, tc := range []struct {
desc string