Skip to content

Commit e8fd680

Browse files
feat: Add toggle for disabling getMetadata request (#1693)
<!-- Please use this template for your pull request. --> <!-- Please use the sections that you need and delete other sections --> ## This PR <!-- add the description of the PR here --> - adds a toggle `disable-sync-metadata` for the Sync Service to disable or enable the deprecated `getMetadata` request ### Related Issues [#1688 [FEATURE] Temporary Context Enrichment toggle](#1688) --------- Signed-off-by: Konvalinka <[email protected]>
1 parent 750aa17 commit e8fd680

File tree

7 files changed

+237
-203
lines changed

7 files changed

+237
-203
lines changed

docs/reference/flagd-cli/flagd_start.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ flagd start [flags]
1414
-H, --context-from-header stringToString add key-value pairs to map header values to context values, where key is Header name, value is context key (default [])
1515
-X, --context-value stringToString add arbitrary key value pairs to the flag evaluation context (default [])
1616
-C, --cors-origin strings CORS allowed origins, * will allow all origins
17+
--disable-sync-metadata Disables the getMetadata endpoint of the sync service. Defaults to false, but will default to true in later versions.
1718
-h, --help help for start
1819
-z, --log-format string Set the logging format, e.g. console or json (default "console")
1920
-m, --management-port int32 Port for management operations (default 8014)

flagd/cmd/start.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const (
3636
syncPortFlagName = "sync-port"
3737
syncSocketPathFlagName = "sync-socket-path"
3838
uriFlagName = "uri"
39+
disableSyncMetadata = "disable-sync-metadata"
3940
contextValueFlagName = "context-value"
4041
headerToContextKeyFlagName = "context-from-header"
4142
streamDeadlineFlagName = "stream-deadline"
@@ -89,6 +90,7 @@ func init() {
8990
flags.StringToStringP(headerToContextKeyFlagName, "H", map[string]string{}, "add key-value pairs to map "+
9091
"header values to context values, where key is Header name, value is context key")
9192
flags.Duration(streamDeadlineFlagName, 0, "Set a server-side deadline for flagd sync and event streams (default 0, means no deadline).")
93+
flags.Bool(disableSyncMetadata, false, "Disables the getMetadata endpoint of the sync service. Defaults to false, but will default to true in later versions.")
9294

9395
bindFlags(flags)
9496
}
@@ -114,6 +116,7 @@ func bindFlags(flags *pflag.FlagSet) {
114116
_ = viper.BindPFlag(contextValueFlagName, flags.Lookup(contextValueFlagName))
115117
_ = viper.BindPFlag(headerToContextKeyFlagName, flags.Lookup(headerToContextKeyFlagName))
116118
_ = viper.BindPFlag(streamDeadlineFlagName, flags.Lookup(streamDeadlineFlagName))
119+
_ = viper.BindPFlag(disableSyncMetadata, flags.Lookup(disableSyncMetadata))
117120
}
118121

119122
// startCmd represents the start command
@@ -186,6 +189,7 @@ var startCmd = &cobra.Command{
186189
SyncServicePort: viper.GetUint16(syncPortFlagName),
187190
SyncServiceSocketPath: viper.GetString(syncSocketPathFlagName),
188191
StreamDeadline: viper.GetDuration(streamDeadlineFlagName),
192+
DisableSyncMetadata: viper.GetBool(disableSyncMetadata),
189193
SyncProviders: syncProviders,
190194
ContextValues: contextValuesToMap,
191195
HeaderToContextKeyMappings: headerToContextKeyMappings,

flagd/pkg/runtime/from_config.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type Config struct {
3939
SyncServicePort uint16
4040
SyncServiceSocketPath string
4141
StreamDeadline time.Duration
42+
DisableSyncMetadata bool
4243

4344
SyncProviders []sync.SourceConfig
4445
CORS []string
@@ -116,15 +117,16 @@ func FromConfig(logger *logger.Logger, version string, config Config) (*Runtime,
116117

117118
// flag sync service
118119
flagSyncService, err := flagsync.NewSyncService(flagsync.SvcConfigurations{
119-
Logger: logger.WithFields(zap.String("component", "FlagSyncService")),
120-
Port: config.SyncServicePort,
121-
Sources: sources,
122-
Store: s,
123-
ContextValues: config.ContextValues,
124-
KeyPath: config.ServiceKeyPath,
125-
CertPath: config.ServiceCertPath,
126-
SocketPath: config.SyncServiceSocketPath,
127-
StreamDeadline: config.StreamDeadline,
120+
Logger: logger.WithFields(zap.String("component", "FlagSyncService")),
121+
Port: config.SyncServicePort,
122+
Sources: sources,
123+
Store: s,
124+
ContextValues: config.ContextValues,
125+
KeyPath: config.ServiceKeyPath,
126+
CertPath: config.ServiceCertPath,
127+
SocketPath: config.SyncServiceSocketPath,
128+
StreamDeadline: config.StreamDeadline,
129+
DisableSyncMetadata: config.DisableSyncMetadata,
128130
})
129131
if err != nil {
130132
return nil, fmt.Errorf("error creating sync service: %w", err)

flagd/pkg/service/flag-sync/handler.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import (
44
"context"
55
"errors"
66
"fmt"
7-
"maps"
87
"google.golang.org/grpc/codes"
98
"google.golang.org/grpc/status"
9+
"maps"
1010
"time"
1111

1212
"buf.build/gen/go/open-feature/flagd/grpc/go/flagd/sync/v1/syncv1grpc"
@@ -17,10 +17,11 @@ import (
1717

1818
// syncHandler implements the sync contract
1919
type syncHandler struct {
20-
mux *Multiplexer
21-
log *logger.Logger
22-
contextValues map[string]any
23-
deadline time.Duration
20+
mux *Multiplexer
21+
log *logger.Logger
22+
contextValues map[string]any
23+
deadline time.Duration
24+
disableSyncMetadata bool
2425
}
2526

2627
func (s syncHandler) SyncFlags(req *syncv1.SyncFlagsRequest, server syncv1grpc.FlagSyncService_SyncFlagsServer) error {
@@ -44,7 +45,6 @@ func (s syncHandler) SyncFlags(req *syncv1.SyncFlagsRequest, server syncv1grpc.F
4445
for {
4546
select {
4647
case payload := <-muxPayload:
47-
4848
metadataSrc := make(map[string]any)
4949
maps.Copy(metadataSrc, s.contextValues)
5050

@@ -58,10 +58,7 @@ func (s syncHandler) SyncFlags(req *syncv1.SyncFlagsRequest, server syncv1grpc.F
5858
return fmt.Errorf("error constructing metadata response")
5959
}
6060

61-
err = server.Send(&syncv1.SyncFlagsResponse{
62-
FlagConfiguration: payload.flags,
63-
SyncContext: metadata,
64-
})
61+
err = server.Send(&syncv1.SyncFlagsResponse{FlagConfiguration: payload.flags, SyncContext: metadata})
6562
if err != nil {
6663
s.log.Debug(fmt.Sprintf("error sending stream response: %v", err))
6764
return fmt.Errorf("error sending stream response: %w", err)
@@ -97,6 +94,9 @@ func (s syncHandler) FetchAllFlags(_ context.Context, req *syncv1.FetchAllFlagsR
9794
func (s syncHandler) GetMetadata(_ context.Context, _ *syncv1.GetMetadataRequest) (
9895
*syncv1.GetMetadataResponse, error,
9996
) {
97+
if s.disableSyncMetadata {
98+
return nil, status.Error(codes.Unimplemented, "metadata endpoint disabled")
99+
}
100100
metadataSrc := make(map[string]any)
101101
for k, v := range s.contextValues {
102102
metadataSrc[k] = v

flagd/pkg/service/flag-sync/handler_test.go

Lines changed: 48 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -54,54 +54,55 @@ func TestSyncHandler_SyncFlags(t *testing.T) {
5454
},
5555
}
5656

57-
for _, tt := range tests {
58-
t.Run(tt.name, func(t *testing.T) {
59-
// Shared handler for testing both GetMetadata & SyncFlags methods
60-
flagStore := store.NewFlags()
61-
mp, err := NewMux(flagStore, tt.sources)
62-
require.NoError(t, err)
63-
64-
handler := syncHandler{
65-
mux: mp,
66-
contextValues: tt.contextValues,
67-
log: logger.NewLogger(nil, false),
68-
}
69-
70-
// Test getting metadata from `GetMetadata` (deprecated)
71-
// remove when `GetMetadata` is full removed and deprecated
72-
metaResp, err := handler.GetMetadata(context.Background(), &syncv1.GetMetadataRequest{})
73-
require.NoError(t, err)
74-
respMetadata := metaResp.GetMetadata().AsMap()
75-
assert.Equal(t, tt.wantMetadata, respMetadata)
76-
77-
// Test metadata from sync_context
78-
stream := &mockSyncFlagsServer{
79-
ctx: context.Background(),
80-
mu: sync.Mutex{},
81-
respReady: make(chan struct{}, 1),
82-
}
83-
84-
go func() {
85-
err := handler.SyncFlags(&syncv1.SyncFlagsRequest{}, stream)
86-
assert.NoError(t, err)
87-
}()
88-
89-
select {
90-
case <-stream.respReady:
91-
syncResp := stream.GetLastResponse()
92-
assert.NotNil(t, syncResp)
93-
94-
syncMetadata := syncResp.GetSyncContext().AsMap()
95-
assert.Equal(t, tt.wantMetadata, syncMetadata)
96-
97-
// Check the two metadatas are equal
57+
for _, disableSyncMetadata := range []bool{true, false} {
58+
for _, tt := range tests {
59+
t.Run(tt.name, func(t *testing.T) {
60+
// Shared handler for testing both GetMetadata & SyncFlags methods
61+
flagStore := store.NewFlags()
62+
mp, err := NewMux(flagStore, tt.sources)
63+
require.NoError(t, err)
64+
65+
handler := syncHandler{
66+
mux: mp,
67+
contextValues: tt.contextValues,
68+
log: logger.NewLogger(nil, false),
69+
disableSyncMetadata: disableSyncMetadata,
70+
}
71+
72+
// Test getting metadata from `GetMetadata` (deprecated)
9873
// remove when `GetMetadata` is full removed and deprecated
99-
assert.Equal(t, respMetadata, syncMetadata)
100-
case <-time.After(time.Second):
101-
t.Fatal("timeout waiting for response")
102-
}
103-
104-
})
74+
metaResp, err := handler.GetMetadata(context.Background(), &syncv1.GetMetadataRequest{})
75+
if !disableSyncMetadata {
76+
require.NoError(t, err)
77+
respMetadata := metaResp.GetMetadata().AsMap()
78+
assert.Equal(t, tt.wantMetadata, respMetadata)
79+
} else {
80+
assert.NotNil(t, err)
81+
}
82+
83+
// Test metadata from sync_context
84+
stream := &mockSyncFlagsServer{
85+
ctx: context.Background(),
86+
mu: sync.Mutex{},
87+
respReady: make(chan struct{}, 1),
88+
}
89+
90+
go func() {
91+
err := handler.SyncFlags(&syncv1.SyncFlagsRequest{}, stream)
92+
assert.NoError(t, err)
93+
}()
94+
95+
select {
96+
case <-stream.respReady:
97+
syncResp := stream.GetLastResponse()
98+
assert.NotNil(t, syncResp)
99+
syncMetadata := syncResp.GetSyncContext().AsMap()
100+
assert.Equal(t, tt.wantMetadata, syncMetadata)
101+
case <-time.After(time.Second):
102+
t.Fatal("timeout waiting for response")
103+
}
104+
})
105+
}
105106
}
106107
}
107108

flagd/pkg/service/flag-sync/sync_service.go

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,16 @@ type ISyncService interface {
2525
}
2626

2727
type SvcConfigurations struct {
28-
Logger *logger.Logger
29-
Port uint16
30-
Sources []string
31-
Store *store.State
32-
ContextValues map[string]any
33-
CertPath string
34-
KeyPath string
35-
SocketPath string
36-
StreamDeadline time.Duration
28+
Logger *logger.Logger
29+
Port uint16
30+
Sources []string
31+
Store *store.State
32+
ContextValues map[string]any
33+
CertPath string
34+
KeyPath string
35+
SocketPath string
36+
StreamDeadline time.Duration
37+
DisableSyncMetadata bool
3738
}
3839

3940
type Service struct {
@@ -82,10 +83,11 @@ func NewSyncService(cfg SvcConfigurations) (*Service, error) {
8283
}
8384

8485
syncv1grpc.RegisterFlagSyncServiceServer(server, &syncHandler{
85-
mux: mux,
86-
log: l,
87-
contextValues: cfg.ContextValues,
88-
deadline: cfg.StreamDeadline,
86+
mux: mux,
87+
log: l,
88+
contextValues: cfg.ContextValues,
89+
deadline: cfg.StreamDeadline,
90+
disableSyncMetadata: cfg.DisableSyncMetadata,
8991
})
9092

9193
var lis net.Listener

0 commit comments

Comments
 (0)