1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
package featureflag
import (
"context"
"strconv"
"google.golang.org/grpc/metadata"
)
const (
// 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"
)
// ContextWithExplicitFeatureFlags marks the context such that all feature flags which are checked
// must have been explicitly set in that context. If a feature flag wasn't set to an explicit value,
// then checking this feature flag will panic. This is not for use in production systems, but is
// intended for tests to verify that we test each feature flag properly.
func ContextWithExplicitFeatureFlags(ctx context.Context) context.Context {
return injectIntoIncomingAndOutgoingContext(ctx, explicitFeatureFlagKey, true)
}
// ContextWithFeatureFlag sets the feature flag in both the incoming and outgoing context.
func ContextWithFeatureFlag(ctx context.Context, flag FeatureFlag, enabled bool) context.Context {
return injectIntoIncomingAndOutgoingContext(ctx, flag.MetadataKey(), enabled)
}
// OutgoingCtxWithFeatureFlag sets the feature flag for an outgoing context.
func OutgoingCtxWithFeatureFlag(ctx context.Context, flag FeatureFlag, enabled bool) context.Context {
return outgoingCtxWithFeatureFlag(ctx, flag.MetadataKey(), enabled)
}
func outgoingCtxWithFeatureFlag(ctx context.Context, key string, enabled bool) context.Context {
md, ok := metadata.FromOutgoingContext(ctx)
if !ok {
md = metadata.New(map[string]string{})
}
md = md.Copy()
md.Set(key, strconv.FormatBool(enabled))
return metadata.NewOutgoingContext(ctx, md)
}
// IncomingCtxWithFeatureFlag sets the feature flag for an incoming context. This is NOT meant for
// use in clients that transfer the context across process boundaries.
func IncomingCtxWithFeatureFlag(ctx context.Context, flag FeatureFlag, enabled bool) context.Context {
return incomingCtxWithFeatureFlag(ctx, flag.MetadataKey(), enabled)
}
func incomingCtxWithFeatureFlag(ctx context.Context, key string, enabled bool) context.Context {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
md = metadata.New(map[string]string{})
}
md = md.Copy()
md.Set(key, strconv.FormatBool(enabled))
return metadata.NewIncomingContext(ctx, md)
}
func injectIntoIncomingAndOutgoingContext(ctx context.Context, key string, enabled bool) context.Context {
incomingMD, ok := metadata.FromIncomingContext(ctx)
if !ok {
incomingMD = metadata.New(map[string]string{})
}
incomingMD.Set(key, strconv.FormatBool(enabled))
ctx = metadata.NewIncomingContext(ctx, incomingMD)
return metadata.AppendToOutgoingContext(ctx, key, strconv.FormatBool(enabled))
}
// FromContext returns the set of all feature flags defined in the context. This returns both
// feature flags that are currently defined by Gitaly, but may also return some that aren't defined
// by us in case they match the feature flag prefix but don't have a definition. This function also
// returns the state of the feature flag *as defined in the context*. This value may be overridden.
func FromContext(ctx context.Context) map[FeatureFlag]bool {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return map[FeatureFlag]bool{}
}
flags := map[FeatureFlag]bool{}
for metadataName, values := range md {
if len(values) == 0 {
continue
}
flag, err := FromMetadataKey(metadataName)
if err != nil {
continue
}
flags[flag] = values[0] == "true"
}
return flags
}
|