Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dependency updates and tests #49

Merged
merged 39 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
f56bb60
Use go 0.19 and add prometheus server
sbs2001 Aug 25, 2022
aec1259
Update default conf
sbs2001 Aug 25, 2022
557c19c
Add func tests
sbs2001 Oct 3, 2022
ee5f5cb
Fix bin path in tests
sbs2001 Oct 3, 2022
d42cff9
log=info for tests
sbs2001 Oct 3, 2022
3d6eca9
Track stream init error
sbs2001 Oct 3, 2022
c4b5a1d
Fix default config
sbs2001 Oct 4, 2022
0caeed8
Fix merge
sbs2001 Dec 14, 2022
cb9b00a
Func tests without sudo
sbs2001 Jan 4, 2023
eea3b27
use pipenv/pytest
mmetc Feb 22, 2023
8d0cd32
Merge branch 'main' into mm-func-tests
mmetc Feb 22, 2023
fc60c9f
Log error if Prometheus can't shutdown
mmetc Feb 22, 2023
984e787
Bump GitHub actions, added golangci-lint
mmetc Feb 22, 2023
e2b7e1f
Build before test
mmetc Feb 22, 2023
0214e36
Remove unused vars
mmetc Feb 22, 2023
4c20bf2
Lint
mmetc Feb 22, 2023
1acd395
wip
mmetc Feb 22, 2023
122ee5c
Merge branch 'main' into mm-func-tests-docker
mmetc Feb 23, 2023
bd2ab1b
wip
mmetc Feb 23, 2023
fe011aa
Use pytest-cs
mmetc Feb 23, 2023
5057d7a
Refactor tests; added bin_args option
mmetc Feb 23, 2023
0f1ac27
Added tests with real lapi
mmetc Feb 28, 2023
29e9129
wip
mmetc Feb 28, 2023
7aaaac1
Test non-blocking behavior with updated go-cs-bouncer; removed mock lapi
mmetc Mar 3, 2023
3616e31
Skip unused error return
mmetc Mar 3, 2023
46b331e
Added test envvars for pytest
mmetc Mar 3, 2023
5e9a905
CI: fetch all git commits to set version
mmetc Mar 3, 2023
3a75ea7
-dev tag only has full flavor
mmetc Mar 3, 2023
c245f03
create docker network for tests
mmetc Mar 3, 2023
cc0a514
use errgroup; go 1.20
mmetc Mar 3, 2023
b5b99fd
Test with both api_key='' and undefined
mmetc Mar 6, 2023
2f5701e
Refactor fixtures for reuse
mmetc Mar 6, 2023
4fcba8b
Bump go-cs-bouncer and deps
mmetc Mar 7, 2023
de328e0
use go 1.20.1
mmetc Mar 7, 2023
207d49c
Merge branch 'main' into mm-func-tests-docker
mmetc Mar 7, 2023
8051a9a
Bump pytest-cs dependency, set timeout in CI
mmetc Mar 8, 2023
6f279ce
Unified build/test workflow, renamed test directory
mmetc Mar 14, 2023
c2914fe
Update systemd dependency
mmetc Mar 14, 2023
0e18fa3
Add bin_args to default config
mmetc Mar 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions .github/workflows/functional_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,33 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:

- name: Set up Go 1.19
uses: actions/setup-go@v3
with:
go-version: 1.19
id: go

- name: Check out code into the Go module directory
uses: actions/checkout@v3
- id: cache-pipenv
with:
fetch-depth: 0

- name: Cache virtualenvs
id: cache-pipenv
uses: actions/cache@v3
with:
path: ~/.local/share/virtualenvs
key: ${{ runner.os }}-pipenv-${{ hashFiles('**/Pipfile.lock') }}

- name: Install deps for tests
run: |
sudo apt install -y nftables iptables ipset
python3 -m pip install --upgrade pipenv wheel
docker network create net-test

- name: Run tests
run: |
make build
pipenv install --deploy
pipenv run pytest
pipenv run pytest --durations=0 --color=yes
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2020 crowdsecurity
Copyright (c) 2020-2023 crowdsecurity

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
5 changes: 1 addition & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ export LD_OPTS=-ldflags "-s -w -X github.com/crowdsecurity/cs-custom-bouncer/pkg

RELDIR = "crowdsec-custom-bouncer-${BUILD_VERSION}"

PYTHON=python3
PIP=pip

all: clean test build
all: clean build test

static: clean
$(GOBUILD) $(LD_OPTS) -o $(BINARY_NAME) -v -a -tags netgo -ldflags '-w -extldflags "-static"'
Expand Down
3 changes: 2 additions & 1 deletion Pipfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[packages]
pytest = "*"
pytest-dotenv = "*"
flask = "*"
pytimeparse = "*"
psutil = "*"
pytest-cs = {ref = "main", git = "https://github.com/crowdsecurity/pytest-cs.git"}

[dev-packages]
gnureadline = "*"
Expand Down
336 changes: 332 additions & 4 deletions Pipfile.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type PrometheusConfig struct {

type bouncerConfig struct {
BinPath string `yaml:"bin_path"` // path to binary
BinArgs []string `yaml:"bin_args"` // arguments for binary
PidDir string `yaml:"piddir"`
UpdateFrequency string `yaml:"update_frequency"`
IncludeScenariosContaining []string `yaml:"include_scenarios_containing"`
Expand Down Expand Up @@ -82,7 +83,7 @@ func newConfig(reader io.Reader) (*bouncerConfig, error) {

/*Configure logging*/
if err := types.SetDefaultLoggerConfig(config.LogMode, config.LogDir, config.LogLevel, config.LogMaxSize, config.LogMaxFiles, config.LogMaxAge, config.CompressLogs, false); err != nil {
log.Fatal(err.Error())
log.Fatal(err)
}
if config.LogMode == "file" {
if config.LogDir == "" {
Expand Down
3 changes: 3 additions & 0 deletions default.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CROWDSEC_TEST_VERSION="dev"
CROWDSEC_TEST_FLAVORS="full"
CROWDSEC_TEST_NETWORK="net-test"
16 changes: 10 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
module github.com/crowdsecurity/cs-custom-bouncer

go 1.19
go 1.20

require (
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/crowdsecurity/crowdsec v1.4.1
github.com/crowdsecurity/go-cs-bouncer v0.0.2
github.com/crowdsecurity/crowdsec v1.4.6
github.com/crowdsecurity/go-cs-bouncer v0.0.3-0.20230302160125-5325225f5785
github.com/go-openapi/swag v0.22.3 // indirect
github.com/prometheus/client_golang v1.14.0
github.com/sirupsen/logrus v1.9.0
golang.org/x/net v0.4.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
)

require (
golang.org/x/sync v0.1.0
gopkg.in/yaml.v2 v2.4.0
)

Expand Down Expand Up @@ -44,5 +47,6 @@ require (
github.com/prometheus/procfs v0.8.0 // indirect
go.mongodb.org/mongo-driver v1.11.1 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
1,811 changes: 12 additions & 1,799 deletions go.sum

Large diffs are not rendered by default.

146 changes: 82 additions & 64 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,17 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus/hooks/writer"
"golang.org/x/sync/errgroup"

"github.com/crowdsecurity/crowdsec/pkg/models"
"github.com/crowdsecurity/cs-custom-bouncer/pkg/version"
csbouncer "github.com/crowdsecurity/go-cs-bouncer"
"gopkg.in/tomb.v2"
)

const (
name = "crowdsec-custom-bouncer"
)

var t tomb.Tomb

func termHandler(sig os.Signal, custom *customBouncer) error {
if err := custom.ShutDown(); err != nil {
return err
Expand Down Expand Up @@ -62,6 +61,38 @@ func HandleSignals(custom *customBouncer) {
os.Exit(code)
}

func deleteDecisions(custom *customBouncer, decisions []*models.Decision) {
if len(decisions) == 1 {
log.Infof("deleting 1 decision")
} else {
log.Infof("deleting %d decisions", len(decisions))
}
for _, d := range decisions {
if err := custom.Delete(d); err != nil {
log.Errorf("unable to delete decision for '%s': %s", *d.Value, err)
continue
}
log.Debugf("deleted '%s'", *d.Value)
}
}


func addDecisions(custom *customBouncer, decisions []*models.Decision) {
if len(decisions) == 1 {
log.Infof("adding 1 decision")
} else {
log.Infof("adding %d decisions", len(decisions))
}
for _, d := range decisions {
if err := custom.Add(d); err != nil {
log.Errorf("unable to insert decision for '%s': %s", *d.Value, err)
continue
}
log.Debugf("Adding '%s' for '%s'", *d.Value, *d.Duration)
}
}


func main() {
var err error
var promServer *http.Server
Expand Down Expand Up @@ -104,31 +135,33 @@ func main() {

custom, err := newCustomBouncer(config)
if err != nil {
log.Fatalf(err.Error())
log.Fatal(err)
}

if err := custom.Init(); err != nil {
log.Fatalf(err.Error())
log.Fatal(err)
}

bouncer := &csbouncer.StreamBouncer{}
bouncer.UserAgent = fmt.Sprintf("%s/%s", name, version.VersionStr())

err = bouncer.ConfigReader(bytes.NewReader(configBytes))
if err != nil {
log.Errorf("unable to configure bouncer: %s", err)
return
log.Fatalf("unable to configure bouncer: %s", err)
}

if err := bouncer.Init(); err != nil {
log.Error(err.Error())
return
log.Fatal(err)
}
cacheResetTicker := time.NewTicker(config.CacheRetentionDuration)
go func() {
bouncer.Run()
t.Kill(fmt.Errorf("stream init failed"))
}()

g, ctx := errgroup.WithContext(context.Background())

g.Go(func() error {
bouncer.Run(ctx)
return fmt.Errorf("stream init failed")
})

if config.PrometheusConfig.Enabled {
listenOn := net.JoinHostPort(
config.PrometheusConfig.ListenAddress,
Expand All @@ -147,49 +180,46 @@ func main() {
go func() {
log.Infof("Serving metrics at %s", listenOn+"/metrics")
log.Error(promServer.ListenAndServe())
// don't need to cancel context here, prometheus is not critical
}()
}
if config.FeedViaStdin {
t.Go(
func() error {
f := func() error {
c := exec.Command(config.BinPath)
s, err := c.StdinPipe()
if err != nil {
return err
}
custom.binaryStdin = s
if err := c.Start(); err != nil {
return err
}

return c.Wait()
g.Go(func() error {
f := func() error {
log.Debugf("Starting binary %s %s", config.BinPath, config.BinArgs)
c := exec.CommandContext(ctx, config.BinPath, config.BinArgs...)
s, err := c.StdinPipe()
if err != nil {
return err
}
var err error
if config.TotalRetries == -1 {
for {
err := f()
log.Errorf("Binary exited: %s", err)
}
} else {
for i := 0; i <= config.TotalRetries; i++ {
err = f()
log.Errorf("Binary exited (retry %d/%d): %s", i, config.TotalRetries, err)
}
custom.binaryStdin = s
if err := c.Start(); err != nil {
return err
}
log.Error("maximum retries exceeded for binary. Exiting")
t.Kill(err)
return err
},
)

return c.Wait()
}
var err error
if config.TotalRetries == -1 {
for {
err := f()
log.Errorf("Binary exited: %s", err)
}
} else {
for i := 1; i <= config.TotalRetries; i++ {
err = f()
log.Errorf("Binary exited (retry %d/%d): %s", i, config.TotalRetries, err)
}
}
return fmt.Errorf("maximum retries exceeded for binary. Exiting")
})
}

t.Go(func() error {
log.Printf("Processing new and deleted decisions . . .")
g.Go(func() error {
log.Infof("Processing new and deleted decisions . . .")
for {
select {
case <-t.Dying():
case <-ctx.Done():
log.Infoln("terminating bouncer process")
if config.PrometheusConfig.Enabled {
log.Infoln("terminating prometheus server")
Expand All @@ -199,23 +229,11 @@ func main() {
}
return nil
case decisions := <-bouncer.Stream:
log.Infof("deleting '%d' decisions", len(decisions.Deleted))
for _, decision := range decisions.Deleted {
if err := custom.Delete(decision); err != nil {
log.Errorf("unable to delete decision for '%s': %s", *decision.Value, err)
} else {
log.Debugf("deleted '%s'", *decision.Value)
}

}
log.Infof("adding '%d' decisions", len(decisions.New))
for _, decision := range decisions.New {
if err := custom.Add(decision); err != nil {
log.Errorf("unable to insert decision for '%s': %s", *decision.Value, err)
} else {
log.Debugf("Adding '%s' for '%s'", *decision.Value, *decision.Duration)
}
if decisions == nil {
continue
}
deleteDecisions(custom, decisions.Deleted)
addDecisions(custom, decisions.New)
case <-cacheResetTicker.C:
custom.ResetCache()
}
Expand All @@ -230,7 +248,7 @@ func main() {
go HandleSignals(custom)
}

if err := t.Wait(); err != nil {
log.Errorf("process return with error: %s", err)
if err := g.Wait(); err != nil {
log.Fatal(err)
}
}
3 changes: 3 additions & 0 deletions pytest-debug.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[pytest]
# drop to pdb on first failure
addopts = --pdb --pdbcls=IPython.terminal.debugger:Pdb
env_files =
.env
default.env
3 changes: 3 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[pytest]
addopts =
env_files =
.env
default.env
Loading