Skip to content

Commit df663ee

Browse files
committed
otelconf: add unmarshaling / validation for batch processors
Updates otelconf v1.0.0 to include unmarshaling that validates the fields for batch log processor and batch span processor configuration. Part of splitting open-telemetry#8026 Signed-off-by: alex boten <[email protected]>
1 parent 1f0bb77 commit df663ee

File tree

4 files changed

+312
-0
lines changed

4 files changed

+312
-0
lines changed

otelconf/config_common.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,40 @@ func newErrGreaterThanZero(field string) error {
4343
return &errBound{Field: field, Bound: 0, Op: ">"}
4444
}
4545

46+
// validateBatchLogRecordProcessor handles validation for BatchLogRecordProcessor.
47+
func validateBatchLogRecordProcessor(plain *BatchLogRecordProcessor) error {
48+
if plain.ExportTimeout != nil && 0 > *plain.ExportTimeout {
49+
return fmt.Errorf("field %s: must be >= %v", "export_timeout", 0)
50+
}
51+
if plain.MaxExportBatchSize != nil && 0 >= *plain.MaxExportBatchSize {
52+
return fmt.Errorf("field %s: must be > %v", "max_export_batch_size", 0)
53+
}
54+
if plain.MaxQueueSize != nil && 0 >= *plain.MaxQueueSize {
55+
return fmt.Errorf("field %s: must be > %v", "max_queue_size", 0)
56+
}
57+
if plain.ScheduleDelay != nil && 0 > *plain.ScheduleDelay {
58+
return fmt.Errorf("field %s: must be >= %v", "schedule_delay", 0)
59+
}
60+
return nil
61+
}
62+
63+
// validateBatchSpanProcessor handles validation for BatchSpanProcessor.
64+
func validateBatchSpanProcessor(plain *BatchSpanProcessor) error {
65+
if plain.ExportTimeout != nil && 0 > *plain.ExportTimeout {
66+
return fmt.Errorf("field %s: must be >= %v", "export_timeout", 0)
67+
}
68+
if plain.MaxExportBatchSize != nil && 0 >= *plain.MaxExportBatchSize {
69+
return fmt.Errorf("field %s: must be > %v", "max_export_batch_size", 0)
70+
}
71+
if plain.MaxQueueSize != nil && 0 >= *plain.MaxQueueSize {
72+
return fmt.Errorf("field %s: must be > %v", "max_queue_size", 0)
73+
}
74+
if plain.ScheduleDelay != nil && 0 > *plain.ScheduleDelay {
75+
return fmt.Errorf("field %s: must be >= %v", "schedule_delay", 0)
76+
}
77+
return nil
78+
}
79+
4680
// validateCardinalityLimits handles validation for CardinalityLimits.
4781
func validateCardinalityLimits(plain *CardinalityLimits) error {
4882
if plain.Counter != nil && 0 >= *plain.Counter {

otelconf/config_json.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,48 @@ import (
88
"errors"
99
)
1010

11+
// UnmarshalJSON implements json.Unmarshaler.
12+
func (j *BatchLogRecordProcessor) UnmarshalJSON(b []byte) error {
13+
var raw map[string]any
14+
if err := json.Unmarshal(b, &raw); err != nil {
15+
return errors.Join(errors.New("unmarshaling error BatchLogRecordProcessor"))
16+
}
17+
if _, ok := raw["exporter"]; raw != nil && !ok {
18+
return errors.New("field exporter in BatchLogRecordProcessor: required")
19+
}
20+
type Plain BatchLogRecordProcessor
21+
var plain Plain
22+
if err := json.Unmarshal(b, &plain); err != nil {
23+
return err
24+
}
25+
if err := validateBatchLogRecordProcessor((*BatchLogRecordProcessor)(&plain)); err != nil {
26+
return err
27+
}
28+
*j = BatchLogRecordProcessor(plain)
29+
return nil
30+
}
31+
32+
// UnmarshalJSON implements json.Unmarshaler.
33+
func (j *BatchSpanProcessor) UnmarshalJSON(b []byte) error {
34+
var raw map[string]any
35+
if err := json.Unmarshal(b, &raw); err != nil {
36+
return errors.Join(errors.New("unmarshaling error BatchSpanProcessor"))
37+
}
38+
if _, ok := raw["exporter"]; raw != nil && !ok {
39+
return errors.New("field exporter in BatchSpanProcessor: required")
40+
}
41+
type Plain BatchSpanProcessor
42+
var plain Plain
43+
if err := json.Unmarshal(b, &plain); err != nil {
44+
return err
45+
}
46+
if err := validateBatchSpanProcessor((*BatchSpanProcessor)(&plain)); err != nil {
47+
return err
48+
}
49+
*j = BatchSpanProcessor(plain)
50+
return nil
51+
}
52+
1153
// UnmarshalJSON implements json.Unmarshaler.
1254
func (j *CardinalityLimits) UnmarshalJSON(value []byte) error {
1355
type Plain CardinalityLimits

otelconf/config_test.go

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,200 @@ import (
1010
"go.yaml.in/yaml/v3"
1111
)
1212

13+
func TestUnmarshalBatchLogRecordProcessor(t *testing.T) {
14+
for _, tt := range []struct {
15+
name string
16+
yamlConfig []byte
17+
jsonConfig []byte
18+
wantErr string
19+
}{
20+
{
21+
name: "valid with console exporter",
22+
jsonConfig: []byte(`{"exporter":{"console":{}}}`),
23+
yamlConfig: []byte("exporter:\n console: {}"),
24+
},
25+
{
26+
name: "valid with all fields positive",
27+
jsonConfig: []byte(`{"exporter":{"console":{}},"export_timeout":5000,"max_export_batch_size":512,"max_queue_size":2048,"schedule_delay":1000}`),
28+
yamlConfig: []byte("exporter:\n console: {}\nexport_timeout: 5000\nmax_export_batch_size: 512\nmax_queue_size: 2048\nschedule_delay: 1000"),
29+
},
30+
{
31+
name: "valid with zero export_timeout",
32+
jsonConfig: []byte(`{"exporter":{"console":{}},"export_timeout":0}`),
33+
yamlConfig: []byte("exporter:\n console: {}\nexport_timeout: 0"),
34+
},
35+
{
36+
name: "valid with zero schedule_delay",
37+
jsonConfig: []byte(`{"exporter":{"console":{}},"schedule_delay":0}`),
38+
yamlConfig: []byte("exporter:\n console: {}\nschedule_delay: 0"),
39+
},
40+
{
41+
name: "missing required exporter field",
42+
jsonConfig: []byte(`{}`),
43+
yamlConfig: []byte("{}"),
44+
wantErr: "field exporter in BatchLogRecordProcessor: required",
45+
},
46+
{
47+
name: "invalid data",
48+
jsonConfig: []byte(`{:2000}`),
49+
yamlConfig: []byte("exporter:\n console: {}\nexport_timeout: !!str str"),
50+
wantErr: "unmarshaling error BatchLogRecordProcessor",
51+
},
52+
{
53+
name: "invalid export_timeout negative",
54+
jsonConfig: []byte(`{"exporter":{"console":{}},"export_timeout":-1}`),
55+
yamlConfig: []byte("exporter:\n console: {}\nexport_timeout: -1"),
56+
wantErr: "field export_timeout: must be >= 0",
57+
},
58+
{
59+
name: "invalid max_export_batch_size zero",
60+
jsonConfig: []byte(`{"exporter":{"console":{}},"max_export_batch_size":0}`),
61+
yamlConfig: []byte("exporter:\n console: {}\nmax_export_batch_size: 0"),
62+
wantErr: "field max_export_batch_size: must be > 0",
63+
},
64+
{
65+
name: "invalid max_export_batch_size negative",
66+
jsonConfig: []byte(`{"exporter":{"console":{}},"max_export_batch_size":-1}`),
67+
yamlConfig: []byte("exporter:\n console: {}\nmax_export_batch_size: -1"),
68+
wantErr: "field max_export_batch_size: must be > 0",
69+
},
70+
{
71+
name: "invalid max_queue_size zero",
72+
jsonConfig: []byte(`{"exporter":{"console":{}},"max_queue_size":0}`),
73+
yamlConfig: []byte("exporter:\n console: {}\nmax_queue_size: 0"),
74+
wantErr: "field max_queue_size: must be > 0",
75+
},
76+
{
77+
name: "invalid max_queue_size negative",
78+
jsonConfig: []byte(`{"exporter":{"console":{}},"max_queue_size":-1}`),
79+
yamlConfig: []byte("exporter:\n console: {}\nmax_queue_size: -1"),
80+
wantErr: "field max_queue_size: must be > 0",
81+
},
82+
{
83+
name: "invalid schedule_delay negative",
84+
jsonConfig: []byte(`{"exporter":{"console":{}},"schedule_delay":-1}`),
85+
yamlConfig: []byte("exporter:\n console: {}\nschedule_delay: -1"),
86+
wantErr: "field schedule_delay: must be >= 0",
87+
},
88+
} {
89+
t.Run(tt.name, func(t *testing.T) {
90+
bsp := BatchLogRecordProcessor{}
91+
err := bsp.UnmarshalJSON(tt.jsonConfig)
92+
if tt.wantErr != "" {
93+
require.Error(t, err)
94+
require.Contains(t, err.Error(), tt.wantErr)
95+
} else {
96+
require.NoError(t, err)
97+
}
98+
bsp = BatchLogRecordProcessor{}
99+
err = yaml.Unmarshal(tt.yamlConfig, &bsp)
100+
if tt.wantErr != "" {
101+
require.Error(t, err)
102+
require.Contains(t, err.Error(), tt.wantErr)
103+
} else {
104+
require.NoError(t, err)
105+
}
106+
})
107+
}
108+
}
109+
110+
func TestUnmarshalBatchSpanProcessor(t *testing.T) {
111+
for _, tt := range []struct {
112+
name string
113+
yamlConfig []byte
114+
jsonConfig []byte
115+
wantErr string
116+
}{
117+
{
118+
name: "valid with console exporter",
119+
jsonConfig: []byte(`{"exporter":{"console":{}}}`),
120+
yamlConfig: []byte("exporter:\n console: {}"),
121+
},
122+
{
123+
name: "valid with all fields positive",
124+
jsonConfig: []byte(`{"exporter":{"console":{}},"export_timeout":5000,"max_export_batch_size":512,"max_queue_size":2048,"schedule_delay":1000}`),
125+
yamlConfig: []byte("exporter:\n console: {}\nexport_timeout: 5000\nmax_export_batch_size: 512\nmax_queue_size: 2048\nschedule_delay: 1000"),
126+
},
127+
{
128+
name: "valid with zero export_timeout",
129+
jsonConfig: []byte(`{"exporter":{"console":{}},"export_timeout":0}`),
130+
yamlConfig: []byte("exporter:\n console: {}\nexport_timeout: 0"),
131+
},
132+
{
133+
name: "valid with zero schedule_delay",
134+
jsonConfig: []byte(`{"exporter":{"console":{}},"schedule_delay":0}`),
135+
yamlConfig: []byte("exporter:\n console: {}\nschedule_delay: 0"),
136+
},
137+
{
138+
name: "missing required exporter field",
139+
jsonConfig: []byte(`{}`),
140+
yamlConfig: []byte("{}"),
141+
wantErr: "field exporter in BatchSpanProcessor: required",
142+
},
143+
{
144+
name: "invalid data",
145+
jsonConfig: []byte(`{:2000}`),
146+
yamlConfig: []byte("exporter:\n console: {}\nexport_timeout: !!str str"),
147+
wantErr: "unmarshaling error BatchSpanProcessor",
148+
},
149+
{
150+
name: "invalid export_timeout negative",
151+
jsonConfig: []byte(`{"exporter":{"console":{}},"export_timeout":-1}`),
152+
yamlConfig: []byte("exporter:\n console: {}\nexport_timeout: -1"),
153+
wantErr: "field export_timeout: must be >= 0",
154+
},
155+
{
156+
name: "invalid max_export_batch_size zero",
157+
jsonConfig: []byte(`{"exporter":{"console":{}},"max_export_batch_size":0}`),
158+
yamlConfig: []byte("exporter:\n console: {}\nmax_export_batch_size: 0"),
159+
wantErr: "field max_export_batch_size: must be > 0",
160+
},
161+
{
162+
name: "invalid max_export_batch_size negative",
163+
jsonConfig: []byte(`{"exporter":{"console":{}},"max_export_batch_size":-1}`),
164+
yamlConfig: []byte("exporter:\n console: {}\nmax_export_batch_size: -1"),
165+
wantErr: "field max_export_batch_size: must be > 0",
166+
},
167+
{
168+
name: "invalid max_queue_size zero",
169+
jsonConfig: []byte(`{"exporter":{"console":{}},"max_queue_size":0}`),
170+
yamlConfig: []byte("exporter:\n console: {}\nmax_queue_size: 0"),
171+
wantErr: "field max_queue_size: must be > 0",
172+
},
173+
{
174+
name: "invalid max_queue_size negative",
175+
jsonConfig: []byte(`{"exporter":{"console":{}},"max_queue_size":-1}`),
176+
yamlConfig: []byte("exporter:\n console: {}\nmax_queue_size: -1"),
177+
wantErr: "field max_queue_size: must be > 0",
178+
},
179+
{
180+
name: "invalid schedule_delay negative",
181+
jsonConfig: []byte(`{"exporter":{"console":{}},"schedule_delay":-1}`),
182+
yamlConfig: []byte("exporter:\n console: {}\nschedule_delay: -1"),
183+
wantErr: "field schedule_delay: must be >= 0",
184+
},
185+
} {
186+
t.Run(tt.name, func(t *testing.T) {
187+
bsp := BatchSpanProcessor{}
188+
err := bsp.UnmarshalJSON(tt.jsonConfig)
189+
if tt.wantErr != "" {
190+
require.Error(t, err)
191+
require.Contains(t, err.Error(), tt.wantErr)
192+
} else {
193+
require.NoError(t, err)
194+
}
195+
bsp = BatchSpanProcessor{}
196+
err = yaml.Unmarshal(tt.yamlConfig, &bsp)
197+
if tt.wantErr != "" {
198+
require.Error(t, err)
199+
require.Contains(t, err.Error(), tt.wantErr)
200+
} else {
201+
require.NoError(t, err)
202+
}
203+
})
204+
}
205+
}
206+
13207
func TestUnmarshalCardinalityLimits(t *testing.T) {
14208
for _, tt := range []struct {
15209
name string

otelconf/config_yaml.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,48 @@ import (
99
"go.yaml.in/yaml/v3"
1010
)
1111

12+
// UnmarshalYAML implements yaml.Unmarshaler.
13+
func (j *BatchLogRecordProcessor) UnmarshalYAML(node *yaml.Node) error {
14+
var raw map[string]any
15+
if err := node.Decode(&raw); err != nil {
16+
return errors.Join(errors.New("unmarshaling error BatchLogRecordProcessor"))
17+
}
18+
if _, ok := raw["exporter"]; raw != nil && !ok {
19+
return errors.New("field exporter in BatchLogRecordProcessor: required")
20+
}
21+
type Plain BatchLogRecordProcessor
22+
var plain Plain
23+
if err := node.Decode(&plain); err != nil {
24+
return errors.Join(errors.New("unmarshaling error BatchLogRecordProcessor"))
25+
}
26+
if err := validateBatchLogRecordProcessor((*BatchLogRecordProcessor)(&plain)); err != nil {
27+
return err
28+
}
29+
*j = BatchLogRecordProcessor(plain)
30+
return nil
31+
}
32+
33+
// UnmarshalYAML implements yaml.Unmarshaler.
34+
func (j *BatchSpanProcessor) UnmarshalYAML(node *yaml.Node) error {
35+
var raw map[string]any
36+
if err := node.Decode(&raw); err != nil {
37+
return errors.Join(errors.New("unmarshaling error BatchSpanProcessor"))
38+
}
39+
if _, ok := raw["exporter"]; raw != nil && !ok {
40+
return errors.New("field exporter in BatchSpanProcessor: required")
41+
}
42+
type Plain BatchSpanProcessor
43+
var plain Plain
44+
if err := node.Decode(&plain); err != nil {
45+
return errors.Join(errors.New("unmarshaling error BatchSpanProcessor"))
46+
}
47+
if err := validateBatchSpanProcessor((*BatchSpanProcessor)(&plain)); err != nil {
48+
return err
49+
}
50+
*j = BatchSpanProcessor(plain)
51+
return nil
52+
}
53+
1254
// UnmarshalYAML implements yaml.Unmarshaler.
1355
func (j *CardinalityLimits) UnmarshalYAML(node *yaml.Node) error {
1456
type Plain CardinalityLimits

0 commit comments

Comments
 (0)