crowdsec/pkg/acquisition/modules/journalctl/journalctl_test.go
he2ss 0652e9ed08
feature cscli|crowdsec add additional labels on crowdsec dsn run (#1053)
* feature cscli|crowdsec add additional labels on crowdsec dsn run
2021-11-17 10:08:46 +01:00

288 lines
6.5 KiB
Go

package journalctlacquisition
import (
"os"
"os/exec"
"testing"
"time"
"github.com/crowdsecurity/crowdsec/pkg/types"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
"gopkg.in/tomb.v2"
)
func TestBadConfiguration(t *testing.T) {
tests := []struct {
config string
expectedErr string
}{
{
config: `foobar: asd.log`,
expectedErr: "line 1: field foobar not found in type journalctlacquisition.JournalCtlConfiguration",
},
{
config: `
mode: tail
source: journalctl`,
expectedErr: "journalctl_filter is required",
},
{
config: `
mode: cat
source: journalctl
journalctl_filter:
- _UID=42`,
expectedErr: "",
},
}
subLogger := log.WithFields(log.Fields{
"type": "journalctl",
})
for _, test := range tests {
f := JournalCtlSource{}
err := f.Configure([]byte(test.config), subLogger)
if test.expectedErr != "" && err == nil {
t.Fatalf("Expected err %s but got nil !", test.expectedErr)
}
if test.expectedErr != "" {
assert.Contains(t, err.Error(), test.expectedErr)
}
}
}
func TestConfigureDSN(t *testing.T) {
tests := []struct {
dsn string
expectedErr string
}{
{
dsn: "asd://",
expectedErr: "invalid DSN asd:// for journalctl source, must start with journalctl://",
},
{
dsn: "journalctl://",
expectedErr: "empty journalctl:// DSN",
},
{
dsn: "journalctl://foobar=42",
expectedErr: "unsupported key foobar in journalctl DSN",
},
{
dsn: "journalctl://filters=%ZZ",
expectedErr: "could not parse journalctl DSN : invalid URL escape \"%ZZ\"",
},
{
dsn: "journalctl://filters=_UID=42?log_level=warn",
expectedErr: "",
},
{
dsn: "journalctl://filters=_UID=1000&log_level=foobar",
expectedErr: "unknown level foobar: not a valid logrus Level:",
},
{
dsn: "journalctl://filters=_UID=1000&log_level=warn&since=yesterday",
expectedErr: "",
},
}
subLogger := log.WithFields(log.Fields{
"type": "journalctl",
})
for _, test := range tests {
f := JournalCtlSource{}
err := f.ConfigureByDSN(test.dsn, map[string]string{"type": "testtype"}, subLogger)
if test.expectedErr != "" {
assert.Contains(t, err.Error(), test.expectedErr)
} else {
assert.Equal(t, err, nil)
}
}
}
func TestOneShot(t *testing.T) {
tests := []struct {
config string
expectedErr string
expectedOutput string
expectedLines int
logLevel log.Level
}{
{
config: `
source: journalctl
mode: cat
journalctl_filter:
- "-_UID=42"`,
expectedErr: "",
expectedOutput: "journalctl: invalid option",
logLevel: log.WarnLevel,
expectedLines: 0,
},
{
config: `
source: journalctl
mode: cat
journalctl_filter:
- _SYSTEMD_UNIT=ssh.service`,
expectedErr: "",
expectedOutput: "",
logLevel: log.WarnLevel,
expectedLines: 14,
},
}
for _, ts := range tests {
var logger *log.Logger
var subLogger *log.Entry
var hook *test.Hook
if ts.expectedOutput != "" {
logger, hook = test.NewNullLogger()
logger.SetLevel(ts.logLevel)
subLogger = logger.WithFields(log.Fields{
"type": "journalctl",
})
} else {
subLogger = log.WithFields(log.Fields{
"type": "journalctl",
})
}
tomb := tomb.Tomb{}
out := make(chan types.Event)
j := JournalCtlSource{}
err := j.Configure([]byte(ts.config), subLogger)
if err != nil {
t.Fatalf("Unexpected error : %s", err)
}
actualLines := 0
if ts.expectedLines != 0 {
go func() {
READLOOP:
for {
select {
case <-out:
actualLines++
case <-time.After(1 * time.Second):
break READLOOP
}
}
}()
}
err = j.OneShotAcquisition(out, &tomb)
if ts.expectedErr == "" && err != nil {
t.Fatalf("Unexpected error : %s", err)
} else if ts.expectedErr != "" && err != nil {
assert.Contains(t, err.Error(), ts.expectedErr)
continue
} else if ts.expectedErr != "" && err == nil {
t.Fatalf("Expected error %s, but got nothing !", ts.expectedErr)
}
if ts.expectedLines != 0 {
assert.Equal(t, ts.expectedLines, actualLines)
}
if ts.expectedOutput != "" {
if hook.LastEntry() == nil {
t.Fatalf("Expected log output '%s' but got nothing !", ts.expectedOutput)
}
assert.Contains(t, hook.LastEntry().Message, ts.expectedOutput)
hook.Reset()
}
}
}
func TestStreaming(t *testing.T) {
tests := []struct {
config string
expectedErr string
expectedOutput string
expectedLines int
logLevel log.Level
}{
{
config: `
source: journalctl
mode: cat
journalctl_filter:
- _SYSTEMD_UNIT=ssh.service`,
expectedErr: "",
expectedOutput: "",
logLevel: log.WarnLevel,
expectedLines: 14,
},
}
for _, ts := range tests {
var logger *log.Logger
var subLogger *log.Entry
var hook *test.Hook
if ts.expectedOutput != "" {
logger, hook = test.NewNullLogger()
logger.SetLevel(ts.logLevel)
subLogger = logger.WithFields(log.Fields{
"type": "journalctl",
})
} else {
subLogger = log.WithFields(log.Fields{
"type": "journalctl",
})
}
tomb := tomb.Tomb{}
out := make(chan types.Event)
j := JournalCtlSource{}
err := j.Configure([]byte(ts.config), subLogger)
if err != nil {
t.Fatalf("Unexpected error : %s", err)
}
actualLines := 0
if ts.expectedLines != 0 {
go func() {
READLOOP:
for {
select {
case <-out:
actualLines++
case <-time.After(1 * time.Second):
break READLOOP
}
}
}()
}
err = j.StreamingAcquisition(out, &tomb)
if ts.expectedErr == "" && err != nil {
t.Fatalf("Unexpected error : %s", err)
} else if ts.expectedErr != "" && err != nil {
assert.Contains(t, err.Error(), ts.expectedErr)
continue
} else if ts.expectedErr != "" && err == nil {
t.Fatalf("Expected error %s, but got nothing !", ts.expectedErr)
}
if ts.expectedLines != 0 {
time.Sleep(1 * time.Second)
assert.Equal(t, ts.expectedLines, actualLines)
}
tomb.Kill(nil)
tomb.Wait()
output, _ := exec.Command("pgrep", "-x", "journalctl").CombinedOutput()
if string(output) != "" {
t.Fatalf("Found a journalctl process after killing the tomb !")
}
if ts.expectedOutput != "" {
if hook.LastEntry() == nil {
t.Fatalf("Expected log output '%s' but got nothing !", ts.expectedOutput)
}
assert.Contains(t, hook.LastEntry().Message, ts.expectedOutput)
hook.Reset()
}
}
}
func TestMain(m *testing.M) {
if os.Getenv("USE_SYSTEM_JOURNALCTL") == "" {
os.Setenv("PATH", "./test_files"+":"+os.Getenv("PATH"))
}
os.Exit(m.Run())
}