Skip to content

Commit 8bb0257

Browse files
authored
Integrate go-playground/validator for improved configuration validation (#768)
Signed-off-by: Giuseppe Ognibene <[email protected]>
1 parent 7405cbe commit 8bb0257

File tree

5 files changed

+115
-133
lines changed

5 files changed

+115
-133
lines changed

pkg/config/ebpf_tracer.go

Lines changed: 32 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import (
1414
type ContextPropagationMode uint8
1515

1616
type RedisDBCacheConfig struct {
17-
Enabled bool `yaml:"enabled" env:"OTEL_EBPF_BPF_REDIS_DB_CACHE_ENABLED"`
18-
MaxSize int `yaml:"max_size" env:"OTEL_EBPF_BPF_REDIS_DB_CACHE_MAX_SIZE"`
17+
Enabled bool `yaml:"enabled" env:"OTEL_EBPF_BPF_REDIS_DB_CACHE_ENABLED" validate:"boolean"`
18+
MaxSize int `yaml:"max_size" env:"OTEL_EBPF_BPF_REDIS_DB_CACHE_MAX_SIZE" validate:"gt=0"`
1919
}
2020

2121
const (
@@ -25,86 +25,84 @@ const (
2525
ContextPropagationDisabled
2626
)
2727

28-
const bufferSizeMax = 8192
29-
3028
// EBPFTracer configuration for eBPF programs
3129
type EBPFTracer struct {
3230
// Enables logging of eBPF program events
33-
BpfDebug bool `yaml:"bpf_debug" env:"OTEL_EBPF_BPF_DEBUG"`
31+
BpfDebug bool `yaml:"bpf_debug" env:"OTEL_EBPF_BPF_DEBUG" validate:"boolean"`
3432

3533
// WakeupLen specifies how many messages need to be accumulated in the eBPF ringbuffer
3634
// before sending a wakeup request.
3735
// High values of WakeupLen could add a noticeable metric delay in services with low
3836
// requests/second.
3937
// Must be at least 0
4038
// TODO: see if there is a way to force eBPF to wakeup userspace on timeout
41-
WakeupLen int `yaml:"wakeup_len" env:"OTEL_EBPF_BPF_WAKEUP_LEN"`
39+
WakeupLen int `yaml:"wakeup_len" env:"OTEL_EBPF_BPF_WAKEUP_LEN" validate:"gte=0"`
4240

4341
// BatchLength allows specifying how many traces will be batched at the initial
4442
// stage before being forwarded to the next stage
4543
// Must be at least 1
46-
BatchLength int `yaml:"batch_length" env:"OTEL_EBPF_BPF_BATCH_LENGTH"`
44+
BatchLength int `yaml:"batch_length" env:"OTEL_EBPF_BPF_BATCH_LENGTH" validate:"gt=0"`
4745

4846
// BatchTimeout specifies the timeout to forward the data batch if it didn't
4947
// reach the BatchLength size
50-
BatchTimeout time.Duration `yaml:"batch_timeout" env:"OTEL_EBPF_BPF_BATCH_TIMEOUT"`
48+
BatchTimeout time.Duration `yaml:"batch_timeout" env:"OTEL_EBPF_BPF_BATCH_TIMEOUT" validate:"gte=0"`
5149

5250
// If enabled, the kprobes based HTTP request tracking will start tracking the request
5351
// headers to process any 'Traceparent' fields.
54-
TrackRequestHeaders bool `yaml:"track_request_headers" env:"OTEL_EBPF_BPF_TRACK_REQUEST_HEADERS"`
52+
TrackRequestHeaders bool `yaml:"track_request_headers" env:"OTEL_EBPF_BPF_TRACK_REQUEST_HEADERS" validate:"boolean"`
5553

5654
// Must be at least 0
57-
HTTPRequestTimeout time.Duration `yaml:"http_request_timeout" env:"OTEL_EBPF_BPF_HTTP_REQUEST_TIMEOUT"`
55+
HTTPRequestTimeout time.Duration `yaml:"http_request_timeout" env:"OTEL_EBPF_BPF_HTTP_REQUEST_TIMEOUT" validate:"gte=0"`
5856

5957
// Deprecated: equivalent to ContextPropagationAll
60-
ContextPropagationEnabled bool `yaml:"enable_context_propagation" env:"OTEL_EBPF_BPF_ENABLE_CONTEXT_PROPAGATION"`
58+
ContextPropagationEnabled bool `yaml:"enable_context_propagation" env:"OTEL_EBPF_BPF_ENABLE_CONTEXT_PROPAGATION" validate:"boolean"`
6159

6260
// Enables distributed context propagation.
63-
ContextPropagation ContextPropagationMode `yaml:"context_propagation" env:"OTEL_EBPF_BPF_CONTEXT_PROPAGATION"`
61+
ContextPropagation ContextPropagationMode `yaml:"context_propagation" env:"OTEL_EBPF_BPF_CONTEXT_PROPAGATION" validate:"oneof=0 1 2 3"`
6462

6563
// Skips checking the kernel version for bpf_loop functionality. Some modified kernels have this
6664
// backported prior to version 5.17.
67-
OverrideBPFLoopEnabled bool `yaml:"override_bpfloop_enabled" env:"OTEL_EBPF_OVERRIDE_BPF_LOOP_ENABLED"`
65+
OverrideBPFLoopEnabled bool `yaml:"override_bpfloop_enabled" env:"OTEL_EBPF_OVERRIDE_BPF_LOOP_ENABLED" validate:"boolean"`
6866

6967
// Select the TC attachment backend: accepted values are 'tc' (netlink),
7068
// and 'tcx'
71-
TCBackend TCBackend `yaml:"traffic_control_backend" env:"OTEL_EBPF_BPF_TC_BACKEND"`
69+
TCBackend TCBackend `yaml:"traffic_control_backend" env:"OTEL_EBPF_BPF_TC_BACKEND" validate:"oneof=1 2 3"`
7270

73-
// Disables Beyla black-box context propagation. Used for testing purposes only.
74-
DisableBlackBoxCP bool `yaml:"disable_black_box_cp" env:"OTEL_EBPF_BPF_DISABLE_BLACK_BOX_CP"`
71+
// Disables OBI black-box context propagation. Used for testing purposes only.
72+
DisableBlackBoxCP bool `yaml:"disable_black_box_cp" env:"OTEL_EBPF_BPF_DISABLE_BLACK_BOX_CP" validate:"boolean"`
7573

7674
// Optimizes for getting requests information immediately when request response is seen
77-
HighRequestVolume bool `yaml:"high_request_volume" env:"OTEL_EBPF_BPF_HIGH_REQUEST_VOLUME"`
75+
HighRequestVolume bool `yaml:"high_request_volume" env:"OTEL_EBPF_BPF_HIGH_REQUEST_VOLUME" validate:"boolean"`
7876

7977
// Enables the heuristic based detection of SQL requests. This can be used to detect
80-
// talking to databases other than the ones we recognize in Beyla, like Postgres and MySQL
81-
HeuristicSQLDetect bool `yaml:"heuristic_sql_detect" env:"OTEL_EBPF_HEURISTIC_SQL_DETECT"`
78+
// talking to databases other than the ones we recognize in OBI, like Postgres and MySQL
79+
HeuristicSQLDetect bool `yaml:"heuristic_sql_detect" env:"OTEL_EBPF_HEURISTIC_SQL_DETECT" validate:"boolean"`
8280

8381
// Enables GPU instrumentation for CUDA kernel launches and allocations
84-
InstrumentGPU bool `yaml:"instrument_gpu" env:"OTEL_EBPF_INSTRUMENT_GPU"`
82+
InstrumentGPU bool `yaml:"instrument_gpu" env:"OTEL_EBPF_INSTRUMENT_GPU" validate:"boolean"`
8583

8684
// Enables debug printing of the protocol data
87-
ProtocolDebug bool `yaml:"protocol_debug_print" env:"OTEL_EBPF_PROTOCOL_DEBUG_PRINT"`
85+
ProtocolDebug bool `yaml:"protocol_debug_print" env:"OTEL_EBPF_PROTOCOL_DEBUG_PRINT" validate:"boolean"`
8886

8987
// Enables Java instrumentation with the OpenTelemetry JDK Agent
90-
UseOTelSDKForJava bool `yaml:"use_otel_sdk_for_java" env:"OTEL_EBPF_USE_OTEL_SDK_FOR_JAVA"`
88+
UseOTelSDKForJava bool `yaml:"use_otel_sdk_for_java" env:"OTEL_EBPF_USE_OTEL_SDK_FOR_JAVA" validate:"boolean"`
9189

9290
RedisDBCache RedisDBCacheConfig `yaml:"redis_db_cache"`
9391

9492
// Limit max data buffer size per protocol.
9593
BufferSizes EBPFBufferSizes `yaml:"buffer_sizes"`
9694

9795
// MySQL prepared statements cache size.
98-
MySQLPreparedStatementsCacheSize int `yaml:"mysql_prepared_statements_cache_size" env:"OTEL_EBPF_BPF_MYSQL_PREPARED_STATEMENTS_CACHE_SIZE"`
96+
MySQLPreparedStatementsCacheSize int `yaml:"mysql_prepared_statements_cache_size" env:"OTEL_EBPF_BPF_MYSQL_PREPARED_STATEMENTS_CACHE_SIZE" validate:"gt=0"`
9997

10098
// Postgres prepared statements cache size.
101-
PostgresPreparedStatementsCacheSize int `yaml:"postgres_prepared_statements_cache_size" env:"OTEL_EBPF_BPF_POSTGRES_PREPARED_STATEMENTS_CACHE_SIZE"`
99+
PostgresPreparedStatementsCacheSize int `yaml:"postgres_prepared_statements_cache_size" env:"OTEL_EBPF_BPF_POSTGRES_PREPARED_STATEMENTS_CACHE_SIZE" validate:"gt=0"`
102100

103101
// Kafka Topic UUID to Name cache size.
104-
KafkaTopicUUIDCacheSize int `yaml:"kafka_topic_uuid_cache_size" env:"OTEL_KAFKA_TOPIC_UUID_CACHE_SIZE"`
102+
KafkaTopicUUIDCacheSize int `yaml:"kafka_topic_uuid_cache_size" env:"OTEL_KAFKA_TOPIC_UUID_CACHE_SIZE" validate:"gt=0"`
105103

106104
// MongoDB requests cache size.
107-
MongoRequestsCacheSize int `yaml:"mongo_requests_cache_size" env:"OTEL_EBPF_BPF_MONGO_REQUESTS_CACHE_SIZE"`
105+
MongoRequestsCacheSize int `yaml:"mongo_requests_cache_size" env:"OTEL_EBPF_BPF_MONGO_REQUESTS_CACHE_SIZE" validate:"gt=0"`
108106

109107
// Configure data extraction/parsing based on protocol
110108
PayloadExtraction PayloadExtraction `yaml:"payload_extraction"`
@@ -114,70 +112,28 @@ type EBPFTracer struct {
114112
// Max: 8192 bytes.
115113
// Default: 0 (disabled).
116114
type EBPFBufferSizes struct {
117-
HTTP uint32 `yaml:"http" env:"OTEL_EBPF_BPF_BUFFER_SIZE_HTTP"`
118-
MySQL uint32 `yaml:"mysql" env:"OTEL_EBPF_BPF_BUFFER_SIZE_MYSQL"`
119-
Postgres uint32 `yaml:"postgres" env:"OTEL_EBPF_BPF_BUFFER_SIZE_POSTGRES"`
115+
HTTP uint32 `yaml:"http" env:"OTEL_EBPF_BPF_BUFFER_SIZE_HTTP" validate:"lte=8192"`
116+
MySQL uint32 `yaml:"mysql" env:"OTEL_EBPF_BPF_BUFFER_SIZE_MYSQL" validate:"lte=8192"`
117+
Postgres uint32 `yaml:"postgres" env:"OTEL_EBPF_BPF_BUFFER_SIZE_POSTGRES" validate:"lte=8192"`
120118
}
121119

122120
func (c *EBPFTracer) Validate() error {
123-
// WakeupLen is used to calculate the wakeup_data_bytes for the ringbuf
124-
if c.WakeupLen < 0 {
125-
return errors.New("ebpf.wakeup_len in the YAML configuration file or OTEL_EBPF_BPF_WAKEUP_LEN must be at least 1")
126-
}
127-
if c.BatchLength < 1 {
128-
return errors.New("ebpf.batch_length in the YAML configuration file or OTEL_EBPF_BPF_BATCH_LENGTH must be at least 1")
129-
}
130-
131-
if c.BatchTimeout <= 0 {
132-
return errors.New("ebpf.batch_timeout in the YAML configuration file or OTEL_EBPF_BPF_BATCH_TIMEOUT must be greater than 0")
133-
}
134-
135-
if c.HTTPRequestTimeout < 0 {
136-
return errors.New("ebpf.http_request_timeout in the YAML configuration file or OTEL_EBPF_BPF_HTTP_REQUEST_TIMEOUT must be greater than or equal to 0")
137-
}
138-
139-
if !c.TCBackend.Valid() {
140-
return errors.New("invalid ebpf.traffic_control_backend in the YAML configuration file or OTEL_EBPF_BPF_TC_BACKEND value, must be 'tc' or 'tcx' or 'auto'")
141-
}
142-
143-
// remove after deleting ContextPropagationEnabled
121+
// TODO remove after deleting ContextPropagationEnabled
144122
if c.ContextPropagationEnabled && c.ContextPropagation != ContextPropagationDisabled {
145123
return errors.New("ebpf.enable_context_propagation and ebpf.context_propagation in the YAML configuration file or OTEL_EBPF_BPF_ENABLE_CONTEXT_PROPAGATION and OTEL_EBPF_BPF_CONTEXT_PROPAGATION are mutually exclusive")
146124
}
147125

126+
return nil
127+
}
128+
129+
func (c *EBPFTracer) IsContextPropagationEnabled() {
148130
// TODO deprecated (REMOVE)
149131
// remove after deleting ContextPropagationEnabled
150132
if c.ContextPropagationEnabled {
151133
slog.Warn("DEPRECATION NOTICE: 'ebpf.enable_context_propagation' configuration option has been " +
152134
"deprecated and will be removed in the future - use 'ebpf.context_propagation' instead")
153135
c.ContextPropagation = ContextPropagationAll
154136
}
155-
156-
if err := c.RedisDBCache.Validate(); err != nil {
157-
return err
158-
}
159-
160-
if err := c.BufferSizes.Validate(); err != nil {
161-
return err
162-
}
163-
164-
if c.MySQLPreparedStatementsCacheSize <= 0 {
165-
return errors.New("ebpf.mysql_prepared_statements_cache_size in the YAML configuration file or OTEL_EBPF_BPF_MYSQL_PREPARED_STATEMENTS_CACHE_SIZE must be greater than 0")
166-
}
167-
168-
if c.PostgresPreparedStatementsCacheSize <= 0 {
169-
return errors.New("ebpf.postgres_prepared_statements_cache_size in the YAML configuration file or OTEL_EBPF_BPF_POSTGRES_PREPARED_STATEMENTS_CACHE_SIZE must be greater than 0")
170-
}
171-
172-
if c.KafkaTopicUUIDCacheSize <= 0 {
173-
return errors.New("ebpf.kafka_topic_uuid_cache_size in the YAML configuration file or OTEL_KAFKA_TOPIC_UUID_CACHE_SIZE must be greater than 0")
174-
}
175-
176-
if c.MongoRequestsCacheSize <= 0 {
177-
return errors.New("ebpf.mongo_requests_cache_size in the YAML configuration file or OTEL_EBPF_BPF_MONGO_REQUESTS_CACHE_SIZE must be greater than 0")
178-
}
179-
180-
return nil
181137
}
182138

183139
func (m *ContextPropagationMode) UnmarshalText(text []byte) error {
@@ -213,23 +169,3 @@ func (m ContextPropagationMode) MarshalText() ([]byte, error) {
213169

214170
return nil, fmt.Errorf("invalid context propagation mode: %d", m)
215171
}
216-
217-
func (r RedisDBCacheConfig) Validate() error {
218-
if r.MaxSize <= 0 {
219-
return errors.New("ebpf.redis_db_cache.max_size in the YAML configuration file or OTEL_EBPF_BPF_REDIS_DB_CACHE_MAX_SIZE must be greater than 0")
220-
}
221-
return nil
222-
}
223-
224-
func (b EBPFBufferSizes) Validate() error {
225-
if b.HTTP > bufferSizeMax {
226-
return fmt.Errorf("ebpf.buffer_sizes.http in YAML configuration file or OTEL_EBPF_BPF_BUFFER_SIZE_HTTP too large: %d, max is %d", b.HTTP, bufferSizeMax)
227-
}
228-
if b.MySQL > bufferSizeMax {
229-
return fmt.Errorf("ebpf.buffer_sizes.mysql in YAML configuration file or OTEL_EBPF_BPF_BUFFER_SIZE_MYSQL too large: %d, max is %d", b.MySQL, bufferSizeMax)
230-
}
231-
if b.Postgres > bufferSizeMax {
232-
return fmt.Errorf("ebpf.buffer_sizes.postgres in YAML configuration file or OTEL_EBPF_BPF_BUFFER_SIZE_POSTGRES too large: %d, max is %d", b.Postgres, bufferSizeMax)
233-
}
234-
return nil
235-
}

pkg/config/payload_extraction.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ type HTTPConfig struct {
1414

1515
type GraphQLConfig struct {
1616
// Enable GraphQL payload extraction and parsing
17-
Enabled bool `yaml:"enabled" env:"OTEL_EBPF_HTTP_GRAPHQL_ENABLED"`
17+
Enabled bool `yaml:"enabled" env:"OTEL_EBPF_HTTP_GRAPHQL_ENABLED" validate:"boolean"`
1818
}

pkg/internal/netolly/flow/reverse_dns.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,18 @@ var netLookupAddr = net.LookupAddr
3636
// from the documentation. This means that it does not impact in the overall Beyla performance.
3737
type ReverseDNS struct {
3838
// Type of ReverseDNS. Values are "none" (default), "local" and "ebpf"
39-
Type string `yaml:"type" env:"OTEL_EBPF_NETWORK_REVERSE_DNS_TYPE"`
39+
Type string `yaml:"type" env:"OTEL_EBPF_NETWORK_REVERSE_DNS_TYPE" validate:"oneof=none local ebpf"`
4040

4141
// CacheLen only applies to the "local" and "ebpf" ReverseDNS type. It
4242
// specifies the max size of the LRU cache that is checked before
4343
// performing the name lookup. Default: 256
44-
CacheLen int `yaml:"cache_len" env:"OTEL_EBPF_NETWORK_REVERSE_DNS_CACHE_LEN"`
44+
CacheLen int `yaml:"cache_len" env:"OTEL_EBPF_NETWORK_REVERSE_DNS_CACHE_LEN" validate:"gte=0"`
4545

4646
// CacheTTL only applies to the "local" and "ebpf" ReverseDNS type. It
4747
// specifies the time-to-live of a cached IP->hostname entry. After the
4848
// cached entry becomes older than this time, the IP->hostname entry will be looked
4949
// up again.
50-
CacheTTL time.Duration `yaml:"cache_expiry" env:"OTEL_EBPF_NETWORK_REVERSE_DNS_CACHE_TTL"`
50+
CacheTTL time.Duration `yaml:"cache_expiry" env:"OTEL_EBPF_NETWORK_REVERSE_DNS_CACHE_TTL" validate:"gte=0"`
5151
}
5252

5353
func (r ReverseDNS) Enabled() bool {

pkg/obi/config.go

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
"github.com/caarlos0/env/v9"
13+
"github.com/go-playground/validator/v10"
1314
"gopkg.in/yaml.v3"
1415

1516
"go.opentelemetry.io/obi/pkg/components/imetrics"
@@ -29,9 +30,16 @@ import (
2930
"go.opentelemetry.io/obi/pkg/transform"
3031
)
3132

33+
// CustomValidations is a map of tag:function for custom validations
34+
type CustomValidations map[string]validator.Func
35+
36+
const (
37+
validationTagAgentIPIface = "agentIPIface"
38+
)
39+
3240
const ReporterLRUSize = 256
3341

34-
// Features that can be enabled in Beyla (can be at the same time): App O11y and/or Net O11y
42+
// Features that can be enabled in OBI (can be at the same time): App O11y and/or Net O11y
3543
type Feature uint
3644

3745
const (
@@ -240,7 +248,7 @@ type Config struct {
240248
ShutdownTimeout time.Duration `yaml:"shutdown_timeout" env:"OTEL_EBPF_SHUTDOWN_TIMEOUT"`
241249

242250
// Check for required system capabilities and bail if they are not
243-
// present. If set to 'false', Beyla will still print a list of missing
251+
// present. If set to 'false', OBI will still print a list of missing
244252
// capabilities, but the execution will continue
245253
EnforceSysCaps bool `yaml:"enforce_sys_caps" env:"OTEL_EBPF_ENFORCE_SYS_CAPS"`
246254

@@ -287,7 +295,7 @@ type Attributes struct {
287295
}
288296

289297
type HostIDConfig struct {
290-
// Override allows overriding the reported host.id in Beyla
298+
// Override allows overriding the reported host.id in OBI
291299
Override string `yaml:"override" env:"OTEL_EBPF_HOST_ID"`
292300
// FetchTimeout specifies the timeout for trying to fetch the HostID from diverse Cloud Providers
293301
FetchTimeout time.Duration `yaml:"fetch_timeout" env:"OTEL_EBPF_HOST_ID_FETCH_TIMEOUT"`
@@ -307,6 +315,21 @@ func (e ConfigError) Error() string {
307315
//
308316
//nolint:cyclop
309317
func (c *Config) Validate() error {
318+
validate := validator.New(validator.WithRequiredStructEnabled())
319+
320+
// for future custom validations
321+
customValidations := CustomValidations{
322+
validationTagAgentIPIface: ValidateAgentIPIface,
323+
}
324+
325+
if err := registerCustomValidations(validate, customValidations); err != nil {
326+
return ConfigError("error registering custom validations: " + err.Error())
327+
}
328+
329+
if err := validate.Struct(c); err != nil {
330+
return ConfigError(err.Error())
331+
}
332+
310333
if err := c.Discovery.Validate(); err != nil {
311334
return ConfigError(err.Error())
312335
}
@@ -366,9 +389,8 @@ func (c *Config) Validate() error {
366389
return ConfigError("you can't enable OTEL internal metrics without enabling OTEL metrics")
367390
}
368391

369-
if err := c.EBPF.Validate(); err != nil {
370-
return ConfigError(err.Error())
371-
}
392+
// TODO deprecated (REMOVE)
393+
c.EBPF.IsContextPropagationEnabled()
372394

373395
return nil
374396
}
@@ -389,7 +411,7 @@ func (c *Config) willUseTC() bool {
389411
(c.Enabled(FeatureNetO11y) && c.NetworkFlows.Source == EbpfSourceTC)
390412
}
391413

392-
// Enabled checks if a given Beyla feature is enabled according to the global configuration
414+
// Enabled checks if a given OBI feature is enabled according to the global configuration
393415
func (c *Config) Enabled(feature Feature) bool {
394416
switch feature {
395417
case FeatureNetO11y:
@@ -409,7 +431,7 @@ func (c *Config) SpanMetricsEnabledForTraces() bool {
409431
}
410432

411433
// ExternalLogger sets the logging capabilities of OBI.
412-
// Used for integrating Beyla with an external logging system (for example Alloy)
434+
// Used for integrating OBI with an external logging system (for example Alloy)
413435
// TODO: maybe this method has too many responsibilities, as it affects the global logger.
414436
func (c *Config) ExternalLogger(handler slog.Handler, debugMode bool) {
415437
slog.SetDefault(slog.New(handler))
@@ -446,3 +468,12 @@ func LoadConfig(file io.Reader) (*Config, error) {
446468

447469
return &cfg, nil
448470
}
471+
472+
func registerCustomValidations(validate *validator.Validate, customValidations CustomValidations) error {
473+
for k, v := range customValidations {
474+
if err := validate.RegisterValidation(k, v); err != nil {
475+
return fmt.Errorf("cannot add validation with the given tag %q: %w", k, err)
476+
}
477+
}
478+
return nil
479+
}

0 commit comments

Comments
 (0)