do not send more than group_threshold alerts at once to a notification plugin (#2264)
* do not send more than group_threshold alerts at once to a notification plugin * Use generic Chunks function, updated tests --------- Co-authored-by: Sebastien Blot <sebastien@crowdsec.net>
This commit is contained in:
parent
26e2bbd709
commit
e3cb4ab2c4
|
@ -20,6 +20,7 @@ import (
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"github.com/crowdsecurity/go-cs-lib/pkg/csstring"
|
"github.com/crowdsecurity/go-cs-lib/pkg/csstring"
|
||||||
|
"github.com/crowdsecurity/go-cs-lib/pkg/slicetools"
|
||||||
|
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||||
|
@ -116,8 +117,15 @@ loop:
|
||||||
pb.alertsByPluginName[pluginName] = make([]*models.Alert, 0)
|
pb.alertsByPluginName[pluginName] = make([]*models.Alert, 0)
|
||||||
pluginMutex.Unlock()
|
pluginMutex.Unlock()
|
||||||
go func() {
|
go func() {
|
||||||
if err := pb.pushNotificationsToPlugin(pluginName, tmpAlerts); err != nil {
|
//Chunk alerts to respect group_threshold
|
||||||
log.WithField("plugin:", pluginName).Error(err)
|
threshold := pb.pluginConfigByName[pluginName].GroupThreshold
|
||||||
|
if threshold == 0 {
|
||||||
|
threshold = 1
|
||||||
|
}
|
||||||
|
for _, chunk := range slicetools.Chunks(tmpAlerts, threshold) {
|
||||||
|
if err := pb.pushNotificationsToPlugin(pluginName, chunk); err != nil {
|
||||||
|
log.WithField("plugin:", pluginName).Error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
package csplugin
|
package csplugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -278,21 +280,35 @@ func (s *PluginSuite) TestBrokerRunGroupThreshold() {
|
||||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
// because of group threshold, we shouldn't have data yet
|
// because of group threshold, we shouldn't have data yet
|
||||||
assert.NoFileExists(t, "./out")
|
assert.NoFileExists(t, "./out")
|
||||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
time.Sleep(100 * time.Millisecond)
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
// and now we should
|
// and now we should
|
||||||
content, err := os.ReadFile("./out")
|
content, err := os.ReadFile("./out")
|
||||||
require.NoError(t, err, "Error reading file")
|
require.NoError(t, err, "Error reading file")
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(bytes.NewReader(content))
|
||||||
|
|
||||||
var alerts []models.Alert
|
var alerts []models.Alert
|
||||||
err = json.Unmarshal(content, &alerts)
|
|
||||||
|
// two notifications, one with 4 alerts, one with 2 alerts
|
||||||
|
|
||||||
|
err = decoder.Decode(&alerts)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, alerts, 4)
|
assert.Len(t, alerts, 4)
|
||||||
|
|
||||||
|
err = decoder.Decode(&alerts)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, alerts, 2)
|
||||||
|
|
||||||
|
err = decoder.Decode(&alerts)
|
||||||
|
assert.Equal(t, err, io.EOF)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PluginSuite) TestBrokerRunTimeThreshold() {
|
func (s *PluginSuite) TestBrokerRunTimeThreshold() {
|
||||||
|
@ -346,13 +362,26 @@ func (s *PluginSuite) TestBrokerRunSimple() {
|
||||||
|
|
||||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
time.Sleep(time.Millisecond * 200)
|
// make it wait a bit, CI can be slow
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
content, err := os.ReadFile("./out")
|
content, err := os.ReadFile("./out")
|
||||||
require.NoError(t, err, "Error reading file")
|
require.NoError(t, err, "Error reading file")
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(bytes.NewReader(content))
|
||||||
|
|
||||||
var alerts []models.Alert
|
var alerts []models.Alert
|
||||||
err = json.Unmarshal(content, &alerts)
|
|
||||||
|
// two notifications, one alert each
|
||||||
|
|
||||||
|
err = decoder.Decode(&alerts)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, alerts, 2)
|
assert.Len(t, alerts, 1)
|
||||||
|
|
||||||
|
err = decoder.Decode(&alerts)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, alerts, 1)
|
||||||
|
|
||||||
|
err = decoder.Decode(&alerts)
|
||||||
|
assert.Equal(t, err, io.EOF)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue