Skip to content

Commit 44d05d8

Browse files
committed
Add component config validation
1 parent ffdd018 commit 44d05d8

File tree

4 files changed

+88
-13
lines changed

4 files changed

+88
-13
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,4 @@ fmt:
7272
.PHONY: tidy
7373
tidy:
7474
rm -fr go.sum
75-
$(GOCMD) mod tidy -compat=1.22.0
75+
$(GOCMD) mod tidy -compat=1.24.0

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
go.opentelemetry.io/collector/component v1.49.0
1111
go.opentelemetry.io/collector/component/componenttest v0.143.0
1212
go.opentelemetry.io/collector/confmap v1.49.0
13+
go.opentelemetry.io/collector/confmap/xconfmap v0.143.0
1314
go.opentelemetry.io/collector/consumer v1.49.0
1415
go.opentelemetry.io/collector/consumer/xconsumer v0.143.0
1516
go.opentelemetry.io/collector/pdata v1.49.0

internal/config.go

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/go-viper/mapstructure/v2"
2727
"go.opentelemetry.io/collector/component"
2828
"go.opentelemetry.io/collector/confmap"
29+
"go.opentelemetry.io/collector/confmap/xconfmap"
2930
"gopkg.in/yaml.v3"
3031
)
3132

@@ -63,13 +64,8 @@ func parseConfig[C any](id component.ID, yamlConfig string, createDefaultConfig
6364
return parseMultipleConfigs[C](yamlConfig, deserializedConf, createDefaultConfig)
6465
}
6566

66-
configMap := make(map[string]any)
67-
for k, v := range deserializedConf.ToStringMap() {
68-
configMap[k] = escapeDollarSigns(v)
69-
}
70-
7167
defaultConfig := createDefaultConfig()
72-
err = confmap.NewFromStringMap(configMap).Unmarshal(&defaultConfig)
68+
err = unmarshalValidConfig(deserializedConf, defaultConfig)
7369
if err != nil {
7470
return nil, err
7571
}
@@ -89,13 +85,8 @@ func parseMultipleConfigs[C any](yamlConfig string, deserializedConf *confmap.Co
8985
return nil, err
9086
}
9187

92-
subConfigMap := map[string]any{}
93-
for k, v := range sub.ToStringMap() {
94-
subConfigMap[k] = escapeDollarSigns(v)
95-
}
96-
9788
defaultConfig := createDefaultConfig()
98-
err = confmap.NewFromStringMap(subConfigMap).Unmarshal(&defaultConfig)
89+
err = unmarshalValidConfig(sub, defaultConfig)
9990
if err != nil {
10091
return nil, err
10192
}
@@ -106,6 +97,25 @@ func parseMultipleConfigs[C any](yamlConfig string, deserializedConf *confmap.Co
10697
return configs, nil
10798
}
10899

100+
func unmarshalValidConfig[C any](cfg *confmap.Conf, defaultConfig C) error {
101+
subConfigMap := map[string]any{}
102+
for k, v := range cfg.ToStringMap() {
103+
subConfigMap[k] = escapeDollarSigns(v)
104+
}
105+
106+
err := confmap.NewFromStringMap(subConfigMap).Unmarshal(&defaultConfig)
107+
if err != nil {
108+
return err
109+
}
110+
111+
validator, ok := any(defaultConfig).(xconfmap.Validator)
112+
if ok {
113+
return validator.Validate()
114+
}
115+
116+
return nil
117+
}
118+
109119
func sortedConfigKeys(yamlData string) ([]string, error) {
110120
var root yaml.Node
111121
if err := yaml.Unmarshal([]byte(yamlData), &root); err != nil {

internal/config_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package internal
2121

2222
import (
23+
"errors"
2324
"testing"
2425

2526
"github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor"
@@ -52,6 +53,54 @@ func Test_parseConfig_singleConfig(t *testing.T) {
5253
require.NotEmpty(t, pc.MetricStatements)
5354
}
5455

56+
func Test_parseConfig_configValidator(t *testing.T) {
57+
id := component.MustNewID("test")
58+
tests := []struct {
59+
name string
60+
config string
61+
expectErr bool
62+
}{
63+
{
64+
name: "valid single config",
65+
config: "valid: true",
66+
expectErr: false,
67+
},
68+
{
69+
name: "invalid single config",
70+
config: "valid: false",
71+
expectErr: true,
72+
},
73+
{
74+
name: "valid multiple config",
75+
config: "test:\n valid: true\ntest/b:\n valid: true",
76+
expectErr: false,
77+
},
78+
{
79+
name: "invalid multiple config",
80+
config: "test:\n valid: true\ntest/b:\n valid: false",
81+
expectErr: true,
82+
},
83+
}
84+
85+
for _, tt := range tests {
86+
t.Run(tt.name, func(t *testing.T) {
87+
_, err := parseConfig[mockConfig](
88+
id,
89+
tt.config,
90+
func() *mockConfig {
91+
return &mockConfig{Valid: !tt.expectErr}
92+
},
93+
)
94+
95+
if tt.expectErr {
96+
require.ErrorIs(t, err, errInvalidMockConfig)
97+
} else {
98+
require.NoError(t, err)
99+
}
100+
})
101+
}
102+
}
103+
55104
func Test_parseConfig_multipleConfig(t *testing.T) {
56105
config := readTestData(t, configMultiple)
57106
allConfigs, err := parseConfig[transformprocessor.Config](
@@ -87,3 +136,18 @@ func Test_parseConfig_invalidConfig(t *testing.T) {
87136
)
88137
require.ErrorContains(t, err, "cannot be used as a Conf")
89138
}
139+
140+
var (
141+
errInvalidMockConfig = errors.New("config validation failed")
142+
)
143+
144+
type mockConfig struct {
145+
Valid bool `mapstructure:"valid"`
146+
}
147+
148+
func (m *mockConfig) Validate() error {
149+
if m.Valid {
150+
return nil
151+
}
152+
return errInvalidMockConfig
153+
}

0 commit comments

Comments
 (0)