Skip to content

Commit c2fb7e9

Browse files
committed
Add component config validation
1 parent ffdd018 commit c2fb7e9

File tree

4 files changed

+83
-13
lines changed

4 files changed

+83
-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: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,54 @@ func Test_parseConfig_singleConfig(t *testing.T) {
5252
require.NotEmpty(t, pc.MetricStatements)
5353
}
5454

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

0 commit comments

Comments
 (0)