dbb420f79e
Co-authored-by: AlteredCoder Co-authored-by: erenJag
383 lines
8 KiB
Go
383 lines
8 KiB
Go
package acquisition
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
|
log "github.com/sirupsen/logrus"
|
|
"github.com/stretchr/testify/assert"
|
|
tomb "gopkg.in/tomb.v2"
|
|
)
|
|
|
|
func TestAcquisCat(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
cfg DataSourceCfg
|
|
//tombState
|
|
config_error string
|
|
read_error string
|
|
tomb_error string
|
|
lines int
|
|
}{
|
|
{ //missing filename(s)
|
|
cfg: DataSourceCfg{
|
|
Mode: CAT_MODE,
|
|
},
|
|
config_error: "no filename or filenames",
|
|
},
|
|
{ //forbiden file
|
|
cfg: DataSourceCfg{
|
|
Mode: CAT_MODE,
|
|
Filename: "/etc/shadow",
|
|
},
|
|
config_error: "unable to open /etc/shadow : permission denied",
|
|
},
|
|
{ //bad regexp
|
|
cfg: DataSourceCfg{
|
|
Filename: "[a-",
|
|
Mode: CAT_MODE,
|
|
},
|
|
config_error: "while globbing [a-: syntax error in pattern",
|
|
},
|
|
{ //inexisting file
|
|
cfg: DataSourceCfg{
|
|
Filename: "/does/not/exists",
|
|
Mode: CAT_MODE,
|
|
},
|
|
config_error: "no files to read for [/does/not/exists]",
|
|
},
|
|
{ //ok file
|
|
cfg: DataSourceCfg{
|
|
Filename: "./tests/test.log",
|
|
Mode: CAT_MODE,
|
|
},
|
|
lines: 1,
|
|
},
|
|
{ //invalid gz
|
|
cfg: DataSourceCfg{
|
|
Filename: "./tests/badlog.gz",
|
|
Mode: CAT_MODE,
|
|
},
|
|
lines: 0,
|
|
tomb_error: "failed to read gz ./tests/badlog.gz: EOF",
|
|
},
|
|
{ //good gz
|
|
cfg: DataSourceCfg{
|
|
Filename: "./tests/test.log.gz",
|
|
Mode: CAT_MODE,
|
|
},
|
|
lines: 1,
|
|
},
|
|
}
|
|
|
|
for tidx, test := range tests {
|
|
fileSrc := new(FileSource)
|
|
err := fileSrc.Configure(test.cfg)
|
|
if test.config_error != "" {
|
|
assert.Contains(t, fmt.Sprintf("%s", err), test.config_error)
|
|
log.Infof("expected config error ok : %s", test.config_error)
|
|
continue
|
|
} else {
|
|
if err != nil {
|
|
t.Fatalf("%d/%d unexpected config error %s", tidx, len(tests), err)
|
|
}
|
|
}
|
|
|
|
out := make(chan types.Event)
|
|
tomb := tomb.Tomb{}
|
|
count := 0
|
|
|
|
err = fileSrc.StartReading(out, &tomb)
|
|
if test.read_error != "" {
|
|
assert.Contains(t, fmt.Sprintf("%s", err), test.read_error)
|
|
log.Infof("expected read error ok : %s", test.read_error)
|
|
continue
|
|
} else {
|
|
if err != nil {
|
|
t.Fatalf("%d/%d unexpected read error %s", tidx, len(tests), err)
|
|
}
|
|
}
|
|
|
|
READLOOP:
|
|
for {
|
|
select {
|
|
case <-out:
|
|
count++
|
|
case <-time.After(1 * time.Second):
|
|
break READLOOP
|
|
}
|
|
}
|
|
|
|
if count != test.lines {
|
|
t.Fatalf("%d/%d expected %d line read, got %d", tidx, len(tests), test.lines, count)
|
|
}
|
|
|
|
if test.tomb_error != "" {
|
|
assert.Contains(t, fmt.Sprintf("%s", tomb.Err()), test.tomb_error)
|
|
log.Infof("expected tomb error ok : %s", test.read_error)
|
|
continue
|
|
} else {
|
|
if tomb.Err() != nil {
|
|
t.Fatalf("%d/%d unexpected tomb error %s", tidx, len(tests), tomb.Err())
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func TestTailKill(t *testing.T) {
|
|
cfg := DataSourceCfg{
|
|
Filename: "./tests/test.log",
|
|
Mode: TAIL_MODE,
|
|
}
|
|
|
|
fileSrc := new(FileSource)
|
|
err := fileSrc.Configure(cfg)
|
|
if err != nil {
|
|
t.Fatalf("unexpected config error %s", err)
|
|
}
|
|
|
|
out := make(chan types.Event)
|
|
tb := tomb.Tomb{}
|
|
|
|
err = fileSrc.StartReading(out, &tb)
|
|
if err != nil {
|
|
t.Fatalf("unexpected read error %s", err)
|
|
}
|
|
time.Sleep(1 * time.Second)
|
|
if tb.Err() != tomb.ErrStillAlive {
|
|
t.Fatalf("unexpected tomb error %s (should be alive)", tb.Err())
|
|
}
|
|
//kill it :>
|
|
tb.Kill(nil)
|
|
time.Sleep(1 * time.Second)
|
|
if tb.Err() != nil {
|
|
t.Fatalf("unexpected tomb error %s (should be dead)", tb.Err())
|
|
}
|
|
|
|
}
|
|
|
|
func TestTailKillBis(t *testing.T) {
|
|
cfg := DataSourceCfg{
|
|
Filename: "./tests/test.log",
|
|
Mode: TAIL_MODE,
|
|
}
|
|
|
|
fileSrc := new(FileSource)
|
|
err := fileSrc.Configure(cfg)
|
|
if err != nil {
|
|
t.Fatalf("unexpected config error %s", err)
|
|
}
|
|
|
|
out := make(chan types.Event)
|
|
tb := tomb.Tomb{}
|
|
|
|
err = fileSrc.StartReading(out, &tb)
|
|
if err != nil {
|
|
t.Fatalf("unexpected read error %s", err)
|
|
}
|
|
time.Sleep(1 * time.Second)
|
|
if tb.Err() != tomb.ErrStillAlive {
|
|
t.Fatalf("unexpected tomb error %s (should be alive)", tb.Err())
|
|
}
|
|
//kill the underlying tomb of tailer
|
|
fileSrc.tails[0].Kill(fmt.Errorf("ratata"))
|
|
time.Sleep(1 * time.Second)
|
|
//it can be two errors :
|
|
if !strings.Contains(fmt.Sprintf("%s", tb.Err()), "dead reader for ./tests/test.log") &&
|
|
!strings.Contains(fmt.Sprintf("%s", tb.Err()), "tail for ./tests/test.log is empty") {
|
|
t.Fatalf("unexpected error : %s", tb.Err())
|
|
}
|
|
|
|
}
|
|
|
|
func TestTailRuntime(t *testing.T) {
|
|
//log.SetLevel(log.TraceLevel)
|
|
|
|
cfg := DataSourceCfg{
|
|
Filename: "./tests/test.log",
|
|
Mode: TAIL_MODE,
|
|
}
|
|
|
|
fileSrc := new(FileSource)
|
|
err := fileSrc.Configure(cfg)
|
|
if err != nil {
|
|
t.Fatalf("unexpected config error %s", err)
|
|
}
|
|
|
|
out := make(chan types.Event)
|
|
tb := tomb.Tomb{}
|
|
count := 0
|
|
|
|
err = fileSrc.StartReading(out, &tb)
|
|
if err != nil {
|
|
t.Fatalf("unexpected read error %s", err)
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
//write data
|
|
f, err := os.OpenFile(cfg.Filename, os.O_APPEND|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for i := 0; i < 5; i++ {
|
|
_, err := f.WriteString(fmt.Sprintf("ratata%d\n", i))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
f.Close()
|
|
|
|
READLOOP:
|
|
for {
|
|
select {
|
|
case <-out:
|
|
count++
|
|
case <-time.After(1 * time.Second):
|
|
break READLOOP
|
|
}
|
|
}
|
|
|
|
if count != 5 {
|
|
t.Fatalf("expected %d line read, got %d", 5, count)
|
|
}
|
|
|
|
if tb.Err() != tomb.ErrStillAlive {
|
|
t.Fatalf("unexpected tomb error %s", tb.Err())
|
|
}
|
|
|
|
/*reset the file*/
|
|
f, err = os.OpenFile(cfg.Filename, os.O_CREATE|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = f.WriteString("one log line\n")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
f.Close()
|
|
}
|
|
|
|
func TestAcquisTail(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
cfg DataSourceCfg
|
|
//tombState
|
|
config_error string
|
|
read_error string
|
|
tomb_error string
|
|
lines int
|
|
}{
|
|
{ //missing filename(s)
|
|
cfg: DataSourceCfg{
|
|
Mode: TAIL_MODE,
|
|
},
|
|
config_error: "no filename or filenames",
|
|
},
|
|
{ //forbiden file
|
|
cfg: DataSourceCfg{
|
|
Mode: TAIL_MODE,
|
|
Filename: "/etc/shadow",
|
|
},
|
|
config_error: "unable to open /etc/shadow : permission denied",
|
|
},
|
|
{ //bad regexp
|
|
cfg: DataSourceCfg{
|
|
Filename: "[a-",
|
|
Mode: TAIL_MODE,
|
|
},
|
|
config_error: "while globbing [a-: syntax error in pattern",
|
|
},
|
|
{ //inexisting file
|
|
cfg: DataSourceCfg{
|
|
Filename: "/does/not/exists",
|
|
Mode: TAIL_MODE,
|
|
},
|
|
config_error: "no files to read for [/does/not/exists]",
|
|
},
|
|
{ //ok file
|
|
cfg: DataSourceCfg{
|
|
Filename: "./tests/test.log",
|
|
Mode: TAIL_MODE,
|
|
},
|
|
lines: 0,
|
|
tomb_error: "still alive",
|
|
},
|
|
{ //invalid gz
|
|
cfg: DataSourceCfg{
|
|
Filename: "./tests/badlog.gz",
|
|
Mode: TAIL_MODE,
|
|
},
|
|
lines: 0,
|
|
tomb_error: "still alive",
|
|
},
|
|
{ //good gz
|
|
cfg: DataSourceCfg{
|
|
Filename: "./tests/test.log.gz",
|
|
Mode: TAIL_MODE,
|
|
},
|
|
lines: 0,
|
|
tomb_error: "still alive",
|
|
},
|
|
}
|
|
|
|
for tidx, test := range tests {
|
|
fileSrc := new(FileSource)
|
|
err := fileSrc.Configure(test.cfg)
|
|
if test.config_error != "" {
|
|
assert.Contains(t, fmt.Sprintf("%s", err), test.config_error)
|
|
log.Infof("expected config error ok : %s", test.config_error)
|
|
continue
|
|
} else {
|
|
if err != nil {
|
|
t.Fatalf("%d/%d unexpected config error %s", tidx, len(tests), err)
|
|
}
|
|
}
|
|
|
|
out := make(chan types.Event)
|
|
tomb := tomb.Tomb{}
|
|
count := 0
|
|
|
|
err = fileSrc.StartReading(out, &tomb)
|
|
if test.read_error != "" {
|
|
assert.Contains(t, fmt.Sprintf("%s", err), test.read_error)
|
|
log.Infof("expected read error ok : %s", test.read_error)
|
|
continue
|
|
} else {
|
|
if err != nil {
|
|
t.Fatalf("%d/%d unexpected read error %s", tidx, len(tests), err)
|
|
}
|
|
}
|
|
|
|
READLOOP:
|
|
for {
|
|
select {
|
|
case <-out:
|
|
count++
|
|
case <-time.After(1 * time.Second):
|
|
break READLOOP
|
|
}
|
|
}
|
|
|
|
if count != test.lines {
|
|
t.Fatalf("%d/%d expected %d line read, got %d", tidx, len(tests), test.lines, count)
|
|
}
|
|
|
|
if test.tomb_error != "" {
|
|
assert.Contains(t, fmt.Sprintf("%s", tomb.Err()), test.tomb_error)
|
|
log.Infof("expected tomb error ok : %s", test.read_error)
|
|
continue
|
|
} else {
|
|
if tomb.Err() != nil {
|
|
t.Fatalf("%d/%d unexpected tomb error %s", tidx, len(tests), tomb.Err())
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|