Skip to content

Commit 0490986

Browse files
authored
pkg/csplugin: constructors for PluginConfig, PluginConfigList (#3640)
1 parent 1cbb88b commit 0490986

File tree

3 files changed

+49
-33
lines changed

3 files changed

+49
-33
lines changed

cmd/crowdsec-cli/clinotifications/notifications.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,17 @@ func (cli *cliNotifications) getPluginConfigs() (map[string]csplugin.PluginConfi
9393

9494
name := filepath.Join(cfg.ConfigPaths.NotificationDir, info.Name()) // Avoid calling info.Name() twice
9595
if (strings.HasSuffix(name, "yaml") || strings.HasSuffix(name, "yml")) && !(info.IsDir()) {
96-
ts, err := csplugin.ParsePluginConfigFile(name)
96+
fin, err := os.Open(name)
9797
if err != nil {
98-
return fmt.Errorf("loading notifification plugin configuration with %s: %w", name, err)
98+
return fmt.Errorf("opening %s: %w", name, err)
99+
}
100+
101+
ts, err := csplugin.NewPluginConfigList(fin)
102+
if err != nil {
103+
return fmt.Errorf("loading notification plugin configuration with %s: %w", name, err)
99104
}
100105

101106
for _, t := range ts {
102-
csplugin.SetRequiredFields(&t)
103107
pcfgs[t.Name] = t
104108
}
105109
}

pkg/csplugin/broker.go

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,34 @@ type PluginConfig struct {
6868
Config map[string]any `yaml:",inline"` // to keep the plugin-specific config
6969
}
7070

71+
// UnmarshalYAML implements yaml.Unmarshaler.
72+
func (pc *PluginConfig) UnmarshalYAML(unmarshal func(any) error) error {
73+
type raw PluginConfig
74+
aux := raw{}
75+
76+
if err := unmarshal(&aux); err != nil {
77+
return err
78+
}
79+
80+
if aux.Type == "" {
81+
return errors.New("missing required field 'type'")
82+
}
83+
84+
if aux.MaxRetry == 0 {
85+
aux.MaxRetry = 1
86+
}
87+
88+
if aux.TimeOut == 0 {
89+
aux.TimeOut = time.Second * 5
90+
}
91+
92+
*pc = PluginConfig(aux)
93+
return nil
94+
}
95+
96+
type PluginConfigList []PluginConfig
97+
98+
7199
func (pb *PluginBroker) Init(ctx context.Context, pluginCfg *csconfig.PluginCfg, profileConfigs []*csconfig.ProfileCfg, configPaths *csconfig.ConfigurationPaths) error {
72100
pb.PluginChannel = make(chan models.ProfileAlert)
73101
pb.notificationConfigsByPluginType = make(map[string][][]byte)
@@ -195,14 +223,17 @@ func (pb *PluginBroker) loadConfig(path string) error {
195223
continue
196224
}
197225

198-
pluginConfigs, err := ParsePluginConfigFile(configFilePath)
226+
fin, err := os.Open(configFilePath)
199227
if err != nil {
200-
return err
228+
return fmt.Errorf("while opening %s: %w", configFilePath, err)
201229
}
202230

203-
for _, pluginConfig := range pluginConfigs {
204-
SetRequiredFields(&pluginConfig)
231+
pluginConfigs, err := NewPluginConfigList(fin)
232+
if err != nil {
233+
return fmt.Errorf("error in %s: %w", configFilePath, err)
234+
}
205235

236+
for _, pluginConfig := range pluginConfigs {
206237
if _, ok := pb.pluginConfigByName[pluginConfig.Name]; ok {
207238
log.Warningf("notification '%s' is defined multiple times", pluginConfig.Name)
208239
}
@@ -392,15 +423,10 @@ func (pb *PluginBroker) pushNotificationsToPlugin(ctx context.Context, pluginNam
392423
return err
393424
}
394425

395-
func ParsePluginConfigFile(path string) ([]PluginConfig, error) {
396-
parsedConfigs := make([]PluginConfig, 0)
397-
398-
yamlFile, err := os.Open(path)
399-
if err != nil {
400-
return nil, fmt.Errorf("while opening %s: %w", path, err)
401-
}
426+
func NewPluginConfigList(fin io.Reader) (PluginConfigList, error) {
427+
parsedConfigs := make(PluginConfigList, 0)
402428

403-
dec := yaml.NewDecoder(yamlFile)
429+
dec := yaml.NewDecoder(fin)
404430
dec.SetStrict(true)
405431

406432
idx := -1
@@ -410,40 +436,26 @@ func ParsePluginConfigFile(path string) ([]PluginConfig, error) {
410436

411437
idx += 1
412438

413-
err = dec.Decode(&pc)
439+
err := dec.Decode(&pc)
414440
if err != nil {
415441
if errors.Is(err, io.EOF) {
416442
break
417443
}
418444

419-
return nil, fmt.Errorf("while decoding %s got error %s", path, err)
445+
return nil, fmt.Errorf("document %d: %w", idx, err)
420446
}
421447

422448
// if the yaml document is empty, skip
423449
if reflect.DeepEqual(pc, PluginConfig{}) {
424450
continue
425451
}
426452

427-
if pc.Type == "" {
428-
return nil, fmt.Errorf("field 'type' missing in %s (position %d)", path, idx)
429-
}
430-
431453
parsedConfigs = append(parsedConfigs, pc)
432454
}
433455

434456
return parsedConfigs, nil
435457
}
436458

437-
func SetRequiredFields(pluginCfg *PluginConfig) {
438-
if pluginCfg.MaxRetry == 0 {
439-
pluginCfg.MaxRetry++
440-
}
441-
442-
if pluginCfg.TimeOut == time.Second*0 {
443-
pluginCfg.TimeOut = time.Second * 5
444-
}
445-
}
446-
447459
func getUUID() (string, error) {
448460
uuidv4, err := uuid.NewRandom()
449461
if err != nil {

test/bats/72_plugin_badconfig.bats

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ teardown() {
139139
# the slack plugin may fail or not, but we just need the logs
140140
config_set '.common.log_media="stdout"'
141141
rune wait-for \
142-
--err "api server init: plugin broker: loading plugin config" \
142+
--err "api server init: plugin broker: loading config" \
143143
"$CROWDSEC"
144-
assert_stderr --partial "field 'type' missing in $CONFIG_DIR/notifications/http.yaml (position 0)"
144+
assert_stderr --partial "error in $CONFIG_DIR/notifications/http.yaml: document 0: missing required field 'type'"
145145
}

0 commit comments

Comments
 (0)