Skip to content

Commit 4422dae

Browse files
authored
Add component config validation (#30)
Prior to this commit, the config validation wasn't being performed by the playground, and invalid configurations were run as it's. This PR changed the config parsing logic to also call the `xconfmap.Validator.Validate` function, making it closer to a real collector and avoiding running config with invalid config values.
1 parent ffdd018 commit 4422dae

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)