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

Analytics Dashboard #72

Open
wants to merge 104 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
902d6c7
start on admin dashboard
jordanschalm Jan 3, 2017
4236bac
fix imports
jordanschalm Jan 3, 2017
8906068
get websockets working reliably across reloads, add unsubscribe metho…
jordanschalm Jan 4, 2017
7ad2f0c
get logs into dashboard module
jordanschalm Jan 5, 2017
2ae4951
add log hooks to main package
jordanschalm Jan 24, 2017
1515594
giving up on plain js -- setup react
jordanschalm Jan 24, 2017
55eedaa
set up statik file system and move session management to occur over w…
jordanschalm Jan 24, 2017
f0f59f3
set up redux store and hook up to websockets
jordanschalm Jan 30, 2017
2bbfa29
time conversion on fe, and feed from be into charts
jordanschalm Feb 4, 2017
5ed2b25
styling of charts, and fix sync issue with event store
jordanschalm Feb 6, 2017
4da0bbb
add initSess stub
jordanschalm Feb 6, 2017
13191e0
glide.lock auto-update
jordanschalm Feb 6, 2017
31cb581
merge master into dashboard
jordanschalm Feb 6, 2017
dd5094c
add decorator pattern
flashmob Feb 9, 2017
20cf073
Merge branch 'master' into more-backends-refactoring
flashmob Feb 9, 2017
6476239
dummy backend uses the decorator pattern with two decorators: "debugg…
flashmob Feb 9, 2017
44ad353
dummy backend uses the decorator pattern with two decorators: "debugg…
flashmob Feb 9, 2017
b19bac2
more progress with decorators:
flashmob Feb 10, 2017
00dfc08
more progress with decorators:
flashmob Feb 10, 2017
87c442a
refactored Processor initialization and shutdown: now using initializ…
flashmob Feb 13, 2017
9127bc0
- got rid of abstract.go backend
flashmob Feb 14, 2017
e2d7375
- redis: when not compressed, delivery header was not added
flashmob Feb 14, 2017
91d4e66
- synchronise BackendService
flashmob Feb 14, 2017
0c84f5e
- backend logger refactoring
flashmob Feb 14, 2017
35f069b
forgot to set the log level
flashmob Feb 14, 2017
32c1e9e
Fixed tests
flashmob Feb 15, 2017
6c1c48b
- AddProcessor function to give more abstraction into the processor c…
flashmob Feb 16, 2017
0d1e35c
add error checking for backend processor initialization
flashmob Feb 16, 2017
7dbe397
trim whitespace from user part
flashmob Feb 16, 2017
0c160b6
rename process_line to process_stack to better reflect name
flashmob Feb 16, 2017
c4fea83
Send actions over websockets directly as redux actions
jordanschalm Feb 21, 2017
8e1e342
Refactoring
flashmob Feb 22, 2017
7aa52bf
Add ranking telemetry for top clients by helo, ip, domain and create …
jordanschalm Feb 23, 2017
b90508a
test travis
jordanschalm Feb 23, 2017
f56b3c8
test travis
jordanschalm Feb 23, 2017
85d6aef
test travis
jordanschalm Feb 23, 2017
0a4965f
test travis
jordanschalm Feb 23, 2017
462ebb2
test travis
jordanschalm Feb 23, 2017
2107c27
test travis
jordanschalm Feb 23, 2017
5f44d28
test travis
jordanschalm Feb 23, 2017
dfcd016
test travis
jordanschalm Feb 23, 2017
2db6573
test travis
jordanschalm Feb 23, 2017
1d59402
add old backend compatibility
flashmob Feb 23, 2017
5764ae4
old version of guerrilla-db-redis, to remove
flashmob Feb 23, 2017
9c09c57
ported guerrilla_db_redis to new backend system
flashmob Feb 23, 2017
60494c1
remove old deprecated backend system
flashmob Feb 23, 2017
f94e4f4
Reimplement ranking analytics to save (lots of) memory
jordanschalm Feb 23, 2017
f96ebc7
Add config options to dashboard and update sample config and readme
jordanschalm Feb 25, 2017
1da6c64
change enabled key to match rest of config
jordanschalm Feb 25, 2017
f7a2032
- renamed envelope package to mail because *envelope.Envelope didn't …
flashmob Feb 25, 2017
2f62ce7
return ErrProcessorNotFound error if procesor not found
flashmob Feb 25, 2017
98b297c
update readme to include info about the new backend system
flashmob Feb 25, 2017
0966eaa
- debugger processor name, make case insensitive
flashmob Feb 26, 2017
407dcd3
limit helo to 16 characters logged, update README
jordanschalm Feb 26, 2017
2a12a28
fix slice bound error
jordanschalm Feb 26, 2017
bc52714
- tweak debug messages
flashmob Feb 26, 2017
83f98cc
- clean up comments
flashmob Feb 27, 2017
224edf4
fix broken build
flashmob Feb 27, 2017
a0e78f7
Add dashboard deps and build to makefile
jordanschalm Feb 27, 2017
b1fff47
update clean in makefile
jordanschalm Feb 28, 2017
6e9d2ca
update build process in readme
jordanschalm Feb 28, 2017
c359a95
- Backend shutdown process re-written to use channels (workStoppers)…
flashmob Mar 3, 2017
3a31bd2
remove unneeded import from test
flashmob Mar 3, 2017
74b0540
fix format error string
flashmob Mar 3, 2017
7188363
update readme
flashmob Mar 3, 2017
28d7542
change log re-open signal to SIGUSR1 instead of SIGHUP
flashmob Mar 3, 2017
e9878f3
make sure to register to get SIGUSR1 signal
flashmob Mar 3, 2017
df08327
- moved backed config from CmdConfig to AppConfig, including backend …
flashmob Mar 5, 2017
4b9a781
- New API (with default configuration options)
flashmob Mar 7, 2017
7043262
refactor serve.go to use the new API
flashmob Mar 7, 2017
7d6d1fd
* api tests
flashmob Mar 8, 2017
565c3f9
use the makefile to run test
flashmob Mar 8, 2017
d4bcc13
make log public
flashmob Mar 9, 2017
3cb670d
- Envelopes now have their own pool. This is so that if processing ta…
flashmob Mar 10, 2017
a452c06
add testrace to makefile
flashmob Mar 10, 2017
ef7202c
update sample config
flashmob Mar 10, 2017
29db21a
separate process stack for validating recipients - it means it use a …
flashmob Mar 11, 2017
b532c91
- remove /sloonz/go-qprintable and replace with go stdlib equivalent
flashmob Mar 11, 2017
e82cf80
Readme update: edit description and add features list
flashmob Mar 11, 2017
5427265
merge backend refactor into dashboard and fix conflicts. Also fix a b…
jordanschalm Mar 12, 2017
dd8dceb
update gitignore in test
jordanschalm Mar 12, 2017
9e50efb
add dashboard glide deps
jordanschalm Mar 13, 2017
bfe58e2
Merge branch 'master' into dashboard
flashmob Mar 21, 2017
e3b8469
fix merge conflicts
flashmob Mar 21, 2017
8fcb6c2
- add Stop function
flashmob Mar 23, 2017
2ff69cf
- fix deadlock for when the http server for dashboard fails and Stop…
flashmob Mar 23, 2017
abf5280
fix statik build
flashmob Mar 23, 2017
ebf74b8
Fix dashboard target in Makefile
jordanschalm Mar 23, 2017
5c46f53
Fix bug where JS console prints error on INIT message from dashboard …
jordanschalm Mar 23, 2017
f1521b0
add a simulation test for the dashboard
flashmob Mar 24, 2017
b44b3bf
Merge branch 'dashboard' of github.com:flashmob/go-guerrilla into das…
flashmob Mar 24, 2017
ea7149d
remove line limit from dashboard simulation test
flashmob Mar 24, 2017
7e86a4e
- split hook to hook.go
flashmob Mar 25, 2017
21e9d2a
create heap structure for connection records
jordanschalm Apr 11, 2017
96340cd
Merge branch 'master' into dashboard
flashmob May 31, 2018
b1b7061
fix imports
flashmob May 31, 2018
ef7478b
fix deadlock
flashmob May 31, 2018
e1c505b
Merge branch 'master' into dashboard
flashmob Jan 30, 2019
2b4a8cd
- update readme
flashmob Jan 31, 2019
c5ca3e5
fix deadlog caused by debuf message that wasn't removed
flashmob Jan 31, 2019
b314384
fix race condition when incrementing number of clients
flashmob Feb 1, 2019
339f7df
fix tests
flashmob Feb 1, 2019
666750d
update dashboard readme
flashmob Feb 1, 2019
2e7c6db
add screenshot
flashmob Feb 1, 2019
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ goguerrilla.conf
goguerrilla.conf.json
/guerrillad
vendor
go-guerrilla.wiki
go-guerrilla.wiki
statik
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ go:
- 1.10.x
- master

before_install:
- if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi
- nvm install 6 && nvm use 6

install:
- export GO15VENDOREXPERIMENT=1
- go get github.com/rakyll/statik
- go install github.com/rakyll/statik
- cd dashboard/js && npm install && npm run build && cd ../..
- statik -src=dashboard/js/build -dest=dashboard
- go get github.com/Masterminds/glide
- go install github.com/Masterminds/glide
- glide up
Expand Down
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,17 @@ help:

clean:
rm -f guerrillad
rm -rf dashboard/js/node_modules
rm -rf dashboard/js/build

dependencies:
$(GO_VARS) $(GO) list -f='{{ join .Deps "\n" }}' $(ROOT)/cmd/guerrillad | grep -v $(ROOT) | tr '\n' ' ' | $(GO_VARS) xargs $(GO) get -u -v
$(GO_VARS) $(GO) list -f='{{ join .Deps "\n" }}' $(ROOT)/cmd/guerrillad | grep -v $(ROOT) | tr '\n' ' ' | $(GO_VARS) xargs $(GO) install -v
cd dashboard/js && npm install && cd ../..

dashboard: dashboard/*.go */*/*/*.js */*/*/*/*.js
cd dashboard/js && npm run build && cd ../..
statik -src=dashboard/js/build -dest=dashboard

guerrillad: *.go */*.go */*/*.go
$(GO_VARS) $(GO) build -o="guerrillad" -ldflags="$(LD_FLAGS)" $(ROOT)/cmd/guerrillad
Expand All @@ -41,4 +48,4 @@ testrace: *.go */*.go */*/*.go
$(GO_VARS) $(GO) test -v ./tests -race
$(GO_VARS) $(GO) test -v ./cmd/guerrillad -race
$(GO_VARS) $(GO) test -v ./response -race
$(GO_VARS) $(GO) test -v ./backends -race
$(GO_VARS) $(GO) test -v ./backends -race
5 changes: 3 additions & 2 deletions backends/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import (
"sync"
"time"

"runtime/debug"
"strings"

"github.com/flashmob/go-guerrilla/log"
"github.com/flashmob/go-guerrilla/mail"
"github.com/flashmob/go-guerrilla/response"
"runtime/debug"
"strings"
)

var ErrProcessorNotFound error
Expand Down
7 changes: 7 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

# Build frontend to `dashboard/js/build`
cd dashboard/js && npm install && cd ../../
cd dashboard/js && npm run build && cd ../../
# Build statik file system in `dashboard/statik`
statik -src=dashboard/js/build -dest=dashboard
91 changes: 91 additions & 0 deletions cmd/guerrillad/backend_test.go.no
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package main

import (
"testing"
"os"
"time"
"io/ioutil"
"github.com/flashmob/go-guerrilla/tests/testcert"
"github.com/flashmob/go-guerrilla/log"
"runtime"
"github.com/spf13/cobra"
"sync"
"strings"
"fmt"
)

func TestBadBackendReload2(t *testing.T) {

testcert.GenerateCert("mail2.guerrillamail.com", "", 365*24*time.Hour, false, 2048, "P256", "../../tests/")
os.Truncate("../../tests/testlog", 0)
//mainlog, _ = log.GetLogger("../../tests/testlog")
mainlog, _ = log.GetLogger("stdout")
mainlog.SetLevel("debug")
mainlog.Info("are u sure")
mainlog.Info("not another word")

select {

case <-time.After(10 * time.Second):
mainlog.Info("paabix")
stacktrace := make([]byte, 8192)
length := runtime.Stack(stacktrace, true)
_ = length
fmt.Fprintf(ioutil.Discard, (string(stacktrace[:length])))

panic("timed out")
}

mainlog.Info("not another word")
sigKill()
ioutil.WriteFile("configJsonA.json", []byte(configJsonA), 0644)
cmd := &cobra.Command{}
configPath = "configJsonA.json"
var serveWG sync.WaitGroup
serveWG.Add(1)
go func() {
mainlog.Info("start serve")
serve(cmd, []string{})
serveWG.Done()
}()
mainlog.Info("after start")
time.Sleep(testPauseDuration)

// change the config file to the one with a broken backend
ioutil.WriteFile("configJsonA.json", []byte(configJsonE), 0644)

// test SIGHUP via the kill command
// Would not work on windows as kill is not available.
// TODO: Implement an alternative test for windows.
if runtime.GOOS != "windows" {
sigHup()
time.Sleep(testPauseDuration) // allow sighup to do its job
// did the pidfile change as expected?
if _, err := os.Stat("./pidfile2.pid"); os.IsNotExist(err) {
t.Error("pidfile not changed after sighup SIGHUP", err)
}
}

// send kill signal and wait for exit
sigKill()
serveWG.Wait()
//time.Sleep(time.Second * 3)
// did backend started as expected?
fd, err := os.Open("../../tests/testlog")
if err != nil {
t.Error(err)
}
if read, err := ioutil.ReadAll(fd); err == nil {
logOutput := string(read)
if i := strings.Index(logOutput, "reverted to old backend config"); i < 0 {
t.Error("did not revert to old backend config")
}
}

// cleanup
//os.Truncate("../../tests/testlog", 0)
os.Remove("configJsonA.json")
os.Remove("./pidfile.pid")
os.Remove("./pidfile2.pid")

}
8 changes: 6 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/flashmob/go-guerrilla/backends"
"github.com/flashmob/go-guerrilla/log"
"os"
"reflect"
"strings"
"time"

"github.com/flashmob/go-guerrilla/backends"
"github.com/flashmob/go-guerrilla/dashboard"
"github.com/flashmob/go-guerrilla/log"
)

// AppConfig is the holder of the configuration of the app
Expand All @@ -30,6 +32,8 @@ type AppConfig struct {
LogLevel string `json:"log_level,omitempty"`
// BackendConfig configures the email envelope processing backend
BackendConfig backends.BackendConfig `json:"backend_config"`
// Dashboard config configures how analytics are gathered and displayed
Dashboard dashboard.Config `json:"dashboard"`
}

// ServerConfig specifies config options for a single server
Expand Down
82 changes: 82 additions & 0 deletions dashboard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# About the Dashboard

The dashboard package gathers data about Guerrilla while it is running
and provides an analytics web dashboard. To activate the dashboard, checkout
the dashboard branch, then build it, then edit your configuration
file as specified in the example configuration.

tl/dr

```
$ git checkout dashboard
$ cd dashboard/js
$ npm install
$ npm run build
$ cd ..
$ statik -src=./js/build
$ cd ..
$ make guerrillad
```

Then see the Config section below how to enable it!

## Screenshot

![](https://cloud.githubusercontent.com/assets/10557821/23277291/233efc0c-f9c2-11e6-8374-f2515f7d868c.png)

## The Backend

The backend is a Go package that collects and stores data from guerrillad,
serves the dashboard to web clients, and updates clients with new analytics data
over WebSockets.

The backend uses [statik](https://github.com/rakyll/statik) to convert the `build`
folder into a http-servable Go package. When deploying, the frontend should be
built first, then the `statik` package should be created.
An example of this process is in the `.travis.yml`.

`To build the statik Go package, cd to the `dashboard` dir, then run

`statik -src=./js/build`

## The Frontend

The front-end is written in React and uses WebSockets to accept data
from the backend and [Victory](https://formidable.com/open-source/victory/) to render charts.
The `js` directory is an NPM module that contains all frontend code.
All commands below should be run within the `js` directory.

To install frontend dependencies:
`npm install`

To build the frontend code:
`npm run build`

To run the HMR development server (serves frontend on port 3000 rather than through `dashboard` package):
`npm start`

## Config

Add `dashboard` to your goguerrilla.json config file

```
"dashboard": {
"is_enabled": true,
"listen_interface": ":8081",
"tick_interval": "5s",
"max_window": "24h",
"ranking_aggregation_interval": "6h"
}
```

## Security considerations

Warning: The dashboard does not have any authentication. It is also served over HTTP.

Assuming that the host will open the dashboard http port only to the local network or VPN.
However, if you need to access the dashboard securely from a remote connection and
don't have a VPN, then maybe an SSH tunnel could do:

`ssh you@example.com -L 8081:127.0.0.1:8081 -N`

Then point your browser to http://127.0.0.1:8081
51 changes: 51 additions & 0 deletions dashboard/conn_record.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package dashboard

import "container/heap"

// Records ranking of one unique connection in domain/ip/helo rankings.
type connRecord struct {
// Number of records of this type
count int
// Name of the record, either the domain, IP, or helo
value string
}

// Contains all ranking of a particular type (domain/ip/helo).
// Tracks ranking ordering by implementing heap.Interface
type connRecordHeap []connRecord

func (crh connRecordHeap) Len() int {
return len(crh)
}

func (crh connRecordHeap) Less(i, j int) bool {
return crh[i].count > crh[j].count
}

func (crh connRecordHeap) Swap(i, j int) {
crh[i], crh[j] = crh[j], crh[i]
}

func (crh *connRecordHeap) Push(x interface{}) {
*crh = append(*crh, x.(connRecord))
}

func (crh *connRecordHeap) Pop() interface{} {
old := *crh
l := len(old)
toPop := old[l-1]
*crh = old[:l-1]
return toPop
}

// Gets N records with the greatest counts, maintaining the state of the heap
func (crh *connRecordHeap) GetN(n int) []connRecord {
nHighest := make([]connRecord, n)
for i := 0; i < n; i++ {
nHighest[i] = heap.Pop(crh).(connRecord)
}
for _, cr := range nHighest {
heap.Push(crh, cr)
}
return nHighest
}
28 changes: 28 additions & 0 deletions dashboard/conn_record_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package dashboard

import (
"container/heap"
"math/rand"
"testing"
)

const (
nConnRecords = 100
)

func TestConnRecord(t *testing.T) {
var crHeap connRecordHeap = make([]connRecord, nConnRecords)
var max connRecord
for i := 0; i < nConnRecords; i++ {
crHeap[i] = connRecord{rand.Int(), "abc"}
if crHeap[i].count > max.count {
max = crHeap[i]
}
}

heap.Init(&crHeap)

if max.count != heap.Pop(&crHeap).(connRecord).count {
t.Error("Pop did not return maximum value")
}
}
Loading