Skip to content

Commit a9ee905

Browse files
authored
feat: add feature flag for protocol secrets support (#1626)
Add SM_AGENT_ENABLE_PROTOCOL_SECRETS environment variable to enable protocol secrets support. This allows testing the feature before enabling it by default. - Add boolFromEnv helper function to parse boolean environment variables - Add EnableProtocolSecrets config field and environment variable parsing - Add SupportsProtocolSecrets field to UpdaterOptions and HandlerOpts - Update RegisterProbe calls to use the feature flag instead of hardcoded false - Add tests to verify the feature flag works correctly
1 parent d3d6019 commit a9ee905

File tree

5 files changed

+205
-111
lines changed

5 files changed

+205
-111
lines changed

cmd/synthetic-monitoring-agent/main.go

Lines changed: 70 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"os/exec"
1313
"os/signal"
1414
"path/filepath"
15+
"strconv"
1516
"syscall"
1617
"time"
1718

@@ -59,29 +60,30 @@ func run(args []string, stdout io.Writer) error {
5960
var (
6061
features = feature.NewCollection()
6162
config = struct {
62-
DevMode bool
63-
Debug bool
64-
Verbose bool
65-
ReportVersion bool
66-
GrpcApiServerAddr string
67-
GrpcInsecure bool
68-
ApiToken Secret
69-
EnableChangeLogLevel bool
70-
EnableDisconnect bool
71-
EnablePProf bool
72-
HttpListenAddr string
73-
K6URI string
74-
K6BlacklistedIP string
75-
SelectedPublisher string
76-
TelemetryTimeSpan int
77-
AutoMemLimit bool
78-
MemLimitRatio float64
79-
DisableK6 bool
80-
DisableUsageReports bool
81-
CacheType cache.Kind
82-
CacheLocalCapacity int
83-
CacheLocalTTL time.Duration
84-
MemcachedServers StringList
63+
DevMode bool
64+
Debug bool
65+
Verbose bool
66+
ReportVersion bool
67+
GrpcApiServerAddr string
68+
GrpcInsecure bool
69+
ApiToken Secret
70+
EnableChangeLogLevel bool
71+
EnableDisconnect bool
72+
EnablePProf bool
73+
HttpListenAddr string
74+
K6URI string
75+
K6BlacklistedIP string
76+
SelectedPublisher string
77+
TelemetryTimeSpan int
78+
AutoMemLimit bool
79+
MemLimitRatio float64
80+
DisableK6 bool
81+
DisableUsageReports bool
82+
CacheType cache.Kind
83+
CacheLocalCapacity int
84+
CacheLocalTTL time.Duration
85+
MemcachedServers StringList
86+
EnableProtocolSecrets bool
8587
}{
8688
GrpcApiServerAddr: "localhost:4031",
8789
HttpListenAddr: "localhost:4050",
@@ -172,6 +174,10 @@ func run(args []string, stdout io.Writer) error {
172174
// Using API_TOKEN should be deprecated after March 1st, 2023.
173175
config.ApiToken = Secret(stringFromEnv("API_TOKEN", stringFromEnv("SM_AGENT_API_TOKEN", string(config.ApiToken))))
174176

177+
// Enable protocol secrets support via environment variable.
178+
// This is a feature flag to allow testing before enabling by default.
179+
config.EnableProtocolSecrets = boolFromEnv("SM_AGENT_ENABLE_PROTOCOL_SECRETS", config.EnableProtocolSecrets)
180+
175181
if config.ApiToken == "" {
176182
return fmt.Errorf("invalid API token")
177183
}
@@ -348,21 +354,22 @@ func run(args []string, stdout io.Writer) error {
348354
)
349355

350356
checksUpdater, err := checks.NewUpdater(checks.UpdaterOptions{
351-
Conn: conn,
352-
Logger: zl.With().Str("subsystem", "updater").Logger(),
353-
Backoff: newConnectionBackoff(),
354-
Publisher: publisher,
355-
TenantCh: tenantCh,
356-
IsConnected: readynessHandler.Set,
357-
PromRegisterer: promRegisterer,
358-
Features: features,
359-
K6Runner: k6Runner,
360-
ScraperFactory: scraper.New,
361-
TenantLimits: limits,
362-
SecretProvider: secretProvider,
363-
Telemeter: telemetry,
364-
UsageReporter: usageReporter,
365-
CostAttributionLabels: cals,
357+
Conn: conn,
358+
Logger: zl.With().Str("subsystem", "updater").Logger(),
359+
Backoff: newConnectionBackoff(),
360+
Publisher: publisher,
361+
TenantCh: tenantCh,
362+
IsConnected: readynessHandler.Set,
363+
PromRegisterer: promRegisterer,
364+
Features: features,
365+
K6Runner: k6Runner,
366+
ScraperFactory: scraper.New,
367+
TenantLimits: limits,
368+
SecretProvider: secretProvider,
369+
Telemeter: telemetry,
370+
UsageReporter: usageReporter,
371+
CostAttributionLabels: cals,
372+
SupportsProtocolSecrets: config.EnableProtocolSecrets,
366373
})
367374

368375
if err != nil {
@@ -374,15 +381,16 @@ func run(args []string, stdout io.Writer) error {
374381
})
375382

376383
adhocHandler, err := adhoc.NewHandler(adhoc.HandlerOpts{
377-
Conn: conn,
378-
Logger: zl.With().Str("subsystem", "adhoc").Logger(),
379-
Backoff: newConnectionBackoff(),
380-
Publisher: publisher,
381-
TenantCh: tenantCh,
382-
PromRegisterer: promRegisterer,
383-
Features: features,
384-
K6Runner: k6Runner,
385-
SecretProvider: secretProvider,
384+
Conn: conn,
385+
Logger: zl.With().Str("subsystem", "adhoc").Logger(),
386+
Backoff: newConnectionBackoff(),
387+
Publisher: publisher,
388+
TenantCh: tenantCh,
389+
PromRegisterer: promRegisterer,
390+
Features: features,
391+
K6Runner: k6Runner,
392+
SecretProvider: secretProvider,
393+
SupportsProtocolSecrets: config.EnableProtocolSecrets,
386394
})
387395
if err != nil {
388396
return fmt.Errorf("cannot create ad-hoc checks handler: %w", err)
@@ -445,6 +453,21 @@ func stringFromEnv(name string, override string) string {
445453
return os.Getenv(name)
446454
}
447455

456+
func boolFromEnv(name string, defaultValue bool) bool {
457+
value := os.Getenv(name)
458+
if value == "" {
459+
return defaultValue
460+
}
461+
462+
parsed, err := strconv.ParseBool(value)
463+
if err != nil {
464+
// If parsing fails, return the default value
465+
return defaultValue
466+
}
467+
468+
return parsed
469+
}
470+
448471
func validateK6URI(uri string) (string, error) {
449472
u, err := url.Parse(uri)
450473
if err != nil {

internal/adhoc/adhoc.go

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type Handler struct {
4242
runnerFactory func(context.Context, *sm.AdHocRequest) (*runner, error)
4343
grpcAdhocChecksClientFactory func(conn ClientConn) (sm.AdHocChecksClient, error)
4444
proberFactory prober.ProberFactory
45+
supportsProtocolSecrets bool
4546
}
4647

4748
// Error represents errors returned from this package.
@@ -106,15 +107,16 @@ func (b constantBackoff) Duration() time.Duration { return time.Duration(b) }
106107

107108
// HandlerOpts is used to pass configuration options to the Handler.
108109
type HandlerOpts struct {
109-
Conn ClientConn
110-
Logger zerolog.Logger
111-
Backoff Backoffer
112-
Publisher pusher.Publisher
113-
TenantCh chan<- sm.Tenant
114-
PromRegisterer prometheus.Registerer
115-
Features feature.Collection
116-
K6Runner k6runner.Runner
117-
SecretProvider secrets.SecretProvider
110+
Conn ClientConn
111+
Logger zerolog.Logger
112+
Backoff Backoffer
113+
Publisher pusher.Publisher
114+
TenantCh chan<- sm.Tenant
115+
PromRegisterer prometheus.Registerer
116+
Features feature.Collection
117+
K6Runner k6runner.Runner
118+
SecretProvider secrets.SecretProvider
119+
SupportsProtocolSecrets bool
118120

119121
// these two fields exists so that tests can pass alternate
120122
// implementations, they are unexported so that clients of this
@@ -148,6 +150,7 @@ func NewHandler(opts HandlerOpts) (*Handler, error) {
148150
runnerFactory: opts.runnerFactory,
149151
grpcAdhocChecksClientFactory: opts.grpcAdhocChecksClientFactory,
150152
proberFactory: prober.NewProberFactory(opts.K6Runner, 0, opts.Features, opts.SecretProvider),
153+
supportsProtocolSecrets: opts.SupportsProtocolSecrets,
151154
api: apiInfo{
152155
conn: opts.Conn,
153156
},
@@ -308,11 +311,10 @@ func (h *Handler) loop(ctx context.Context) error {
308311
result, err := client.RegisterProbe(
309312
ctx,
310313
&sm.ProbeInfo{
311-
Version: version.Short(),
312-
Commit: version.Commit(),
313-
Buildstamp: version.Buildstamp(),
314-
// TODO(d0ugal): We will switch this to true when the implementation is complete in the Agent and API.
315-
SupportsProtocolSecrets: false,
314+
Version: version.Short(),
315+
Commit: version.Commit(),
316+
Buildstamp: version.Buildstamp(),
317+
SupportsProtocolSecrets: h.supportsProtocolSecrets,
316318
},
317319
)
318320
if err != nil {

internal/adhoc/adhoc_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,47 @@ func TestNewHandler(t *testing.T) {
5151
require.NotNil(t, h.grpcAdhocChecksClientFactory)
5252
require.Nil(t, h.probe, "probe should not be set at this point")
5353
require.NotNil(t, h.metrics.opsCounter)
54+
require.False(t, h.supportsProtocolSecrets, "default value should be false")
55+
}
56+
57+
func TestHandlerSupportsProtocolSecrets(t *testing.T) {
58+
features := feature.NewCollection()
59+
require.NoError(t, features.Set("adhoc"))
60+
61+
var capturedProbeInfo *sm.ProbeInfo
62+
testClient := &testClient{
63+
logger: zerolog.New(io.Discard),
64+
registerProbeHook: func(info *sm.ProbeInfo) {
65+
capturedProbeInfo = info
66+
},
67+
}
68+
69+
opts := HandlerOpts{
70+
Conn: &grpcTestConn{},
71+
Logger: zerolog.New(io.Discard),
72+
Publisher: channelPublisher(make(chan pusher.Payload)),
73+
TenantCh: make(chan sm.Tenant),
74+
PromRegisterer: prometheus.NewPedanticRegistry(),
75+
Features: features,
76+
SupportsProtocolSecrets: true,
77+
grpcAdhocChecksClientFactory: func(conn ClientConn) (sm.AdHocChecksClient, error) {
78+
return testClient, nil
79+
},
80+
}
81+
82+
h, err := NewHandler(opts)
83+
require.NoError(t, err)
84+
require.NotNil(t, h)
85+
require.True(t, h.supportsProtocolSecrets, "should be set to true")
86+
87+
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
88+
defer cancel()
89+
90+
// Run will call RegisterProbe, which should capture the ProbeInfo
91+
_ = h.Run(ctx)
92+
93+
require.NotNil(t, capturedProbeInfo, "RegisterProbe should have been called")
94+
require.True(t, capturedProbeInfo.SupportsProtocolSecrets, "SupportsProtocolSecrets should be true")
5495
}
5596

5697
func TestHandlerRun(t *testing.T) {
@@ -258,11 +299,16 @@ type testClient struct {
258299
logger zerolog.Logger
259300
registerProbeError error
260301
getAdhocChecksError error
302+
registerProbeHook func(*sm.ProbeInfo)
261303
}
262304

263305
func (c *testClient) RegisterProbe(ctx context.Context, in *sm.ProbeInfo, opts ...grpc.CallOption) (*sm.RegisterProbeResult, error) {
264306
c.logger.Info().Str("func", "RegisterProbe").Interface("in", in).Interface("opts", opts).Send()
265307

308+
if c.registerProbeHook != nil {
309+
c.registerProbeHook(in)
310+
}
311+
266312
if c.registerProbeError != nil {
267313
return nil, c.registerProbeError
268314
}

0 commit comments

Comments
 (0)