Browse Source

inital commit

master
ixyd 4 years ago
commit
c795f01d8c
  1. 8
      Dockerfile.pskreporter_exporter
  2. 8
      Dockerfile.wsjtx_exporter
  3. 6
      IMPORT.SQL
  4. 6
      IMPORT2.SQL
  5. 151
      Readme.md
  6. 20
      cmd/alltxt2csv/ALL.CSV
  7. 20
      cmd/alltxt2csv/ALL.test
  8. 10
      cmd/alltxt2csv/Dockerfile
  9. 91
      cmd/alltxt2csv/main.go
  10. 163
      cmd/pskreporter_exporter/main.go
  11. 89
      cmd/pskreporter_exporter/mysql.go
  12. 72
      cmd/pskreporter_exporter/prometheus.go
  13. 233
      cmd/pskreporter_exporter/pskreporter.go
  14. 120
      cmd/wsjtx_exporter/main.go
  15. 88
      cmd/wsjtx_exporter/mysql.go
  16. 56
      cmd/wsjtx_exporter/prometheus.go
  17. 21
      deploy.sh
  18. 16
      go.mod
  19. 463
      go.sum
  20. 6
      misc/IMPORT.SQL
  21. 21
      misc/bandswitcher.sh
  22. 9628
      misc/dashboards/prometheus.json
  23. BIN
      screenshot.png
  24. 171
      shared/wsjtx/wsjtx.go

8
Dockerfile.pskreporter_exporter

@ -0,0 +1,8 @@
from golang:1.15.0
RUN mkdir /pskreporter_exporter
ADD . /pskreporter_exporter
WORKDIR /pskreporter_exporter/cmd/pskreporter_exporter
RUN go build
CMD ["/pskreporter_exporter/cmd/pskreporter_exporter/pskreporter_exporter"]

8
Dockerfile.wsjtx_exporter

@ -0,0 +1,8 @@
from golang:1.15.0
RUN mkdir /wsjtx_exporter
ADD . /wsjtx_exporter
WORKDIR /wsjtx_exporter/cmd/wsjtx_exporter
RUN go build
CMD ["/wsjtx_exporter/cmd/wsjtx_exporter/wsjtx_exporter"]

6
IMPORT.SQL

@ -0,0 +1,6 @@
LOAD DATA LOCAL INFILE '/wsjtx/40m.csv'
INTO TABLE wsjtx_all_txt
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
(ts, station, callsign, band, continent, mode, dxcc, geohash, report, rx)

6
IMPORT2.SQL

@ -0,0 +1,6 @@
LOAD DATA LOCAL INFILE '/wsjtx/80m.csv'
INTO TABLE wsjtx_all_txt
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
(ts, station, callsign, band, continent, mode, dxcc, geohash, report, rx)

151
Readme.md

@ -0,0 +1,151 @@
# what is it?
FIXME SCREENSHOT!!!!!!!!!!!!!!!!!!!!!!!!!
* a set of tools to export your personal WSJT-X
* live reception data into prometheus or mysql
* historical reception data (since 2019) into mysql
* a tool to poll pskreporter and export your personal
* live transmission data to prometheus or mysql
every time your wsjtx is/was running you are/were generating nice data points, which can be queried using grafana.
this can be very interesting to evaluate your **personal** hf conditions from the current and the past.
some use cases:
* choose dxcc, band, mode and timeframe in all combinations so see when your were successfully able to receive and transmit, to plan future activities
* use grafana alerts or prometheus alertmanager to send yourself alerts when your stations receives something interesting
* have nice wallboard for contesting or dxpedition
* display rx/tx data as map with custom timespan and filters
* compare input of different station via flexible queries/graphs
* compare hf propagration forecasts to your personal situation
* impress other people with cool dashboards ;)
* ...
sample dashboards are provided.
have fun!
73 de DL3SD
## prerequisites
* ALL.TXT with interesting data and/or running hamradio station to provide live data
* grafana
* prometheus or mysql
## tooling overview
* **pskreporter_exporter**
* polls pskreporter.info for your callsign
* supports prometheus and mysql
* **wsjtx_exporter**
* follows live traffice in ALL.txt
* supports prometheus and mysql
* **alltxt2csv**
* reads whole ALL.txt since 2019 and creates csv-file which can be imported into mysql
* **misc**
* sample dashboards
* shell script to hop between bands via rigctl to allow fractional monitoring of multiple bands
* ready to launch docker-compose setup based on https://github.com/stefanprodan/dockprom
## things you should be aware of..
### what about prometheus and mysql?
show pro/con overview:
**prometheus:**
* + you get nicer graphs with counters and bars if you ask me
* + you can use alertmanager if you want advanced alerting
* + optimized for timeseries data
* - not designed for keeping long timeseries (weeks or some months are fine)
* - not possible to import historical data
* - not as flexible as mysql regarding complex queries
**mysql**:
* + you can import your ALL.txt since 2019
* + you can store a lot of data over a long timeframe
* + you can propably build more complex queries in sql
* - in comparison to prometheus not optimized for timeseries, but still good enough
* - gauge based graphs are not as nice as counter/bars if you ask me
* - you can use *only* graphanas internal alerting
both allow distributed setups with multiple wsjtx instances submitting their data to a central prometheus or mysql service.
you can as well run both in parallel and use prometheus for a live overview and mysql for historical evaluations.
### pskreporter_exporter vs other access/polling of pskreporter like GridTracker
### can it read my whole ALL.txt since from the beginning?
not really. only lines in 'new' format are accepted because the old format doesnt contain the frequency and the date.
lines in incompatible format are silently ignored.
with alltxt2csv you can prepare your ALL.TXT from ca. mid 2019 for import to mysql.
for prometheus it is not possible to import historical data for obivous reasons.
### there are a lot of "cant parse callsign messages" during importing my ALL.txt
thats quite normal as there are propably a lot of strange/broken messages in your ALL.txt ;)
### does it work with wsjt-x 2.3.0.RC2?
no, there is a bug which prevents the ALL.TXT to contain them timestamp, but it should be fixed in the next release.
### what is the state of this?
this works fine for me! but it was just build from boredom during covid lockdown and it is not to be considered as perfect.
performance obviously depends on your hardware and the complexity of your queries.
feedback and pull requests are highly appreciated!
here be dragons:
* callsign lookup performance done with regex in one goroutine is unneccessary slow for alltxt2csv
* grids for ALL.TXT entries are always looked up by dxcc instead if being tracked in the file
* there is a lot of space for improvements for the dashboards
### grafana says too many rows for mysql datasource
choose a bigger interval
### how long does it take to import my data into mysql?
* my ALL.TXT (new format start july 2019) contains ~ 13.7 mio lines and has ~ 850M.
* converting to csv takes ~ 40min on i7-4750HQ (2015) and the result has ~ 1.2G.
* currently this uses only one core, so there is a lot of room for optimization.
* importing the csv to mysql takes ~ 3.5min.
* querying the whole time (~ 1.5 years) in grafana takes some seconds.
### does this need a lot of ressource on my machine?
well, it depends on your queries, but propably yes! ;)
## howtos
there is no special howto on integrating the components into your existing grafana/prometheus/mysql infrastructure,
as you should propably know how to deal with these kind of stuff.. ;)
### howto build binaries/containers
build binaries:
```
go get github.com/denzs/wsjtx_dashboards
```
build docker containers:
```
docker build Dockerfile.wsjtx_exporter .
docker build Dockerfile.pskreporter_exporter .
```
### to be done...
debian/win10@wsl2 + docker-compose (historical data with mysql + alltxt2csv)
raspbian + docker-compose (live data with prometheus only)
alerting
## Kudos to
* WSJT-X, Grafana, Prometheus, MySQL Teams
* Philip Gladstone for pskreporter.info
* Todd Neal for his nice work on https://github.com/tzneal/ham-go
* Stefan Prodan for his nice work on https://github.com/stefanprodan/dockprom

20
cmd/alltxt2csv/ALL.CSV

@ -0,0 +1,20 @@
"2020-11-19 23:52:30","DL3SD","CO8MCL","40m","NA","FT8","Cuba","d5z479cy8fkr","-15","1",
"2020-11-19 23:52:30","DL3SD","EA3IKA","40m","EU","FT8","Spain","ezht8vnkrc2q","-11","1",
"2020-11-19 23:52:30","DL3SD","WP4AZT","40m","NA","FT8","Puerto Rico","de0xmtef0tek","-12","1",
"2020-11-19 23:52:30","DL3SD","IZ5MDD","40m","EU","FT8","Italy","sr8gw70kx4xc","-16","1",
"2020-11-19 23:52:30","DL3SD","YO2BBX","40m","EU","FT8","Romania","u81s6jczzdwc","-5","1",
"2020-11-19 23:52:30","DL3SD","N3MK","40m","NA","FT8","United States","9ywv9bpun5jd","-13","1",
"2020-11-19 23:52:30","DL3SD","UR6QA","40m","EU","FT8","Ukraine","u8vk6wjr4et3","-12","1",
"2020-11-19 23:52:45","DL3SD","EA3AVS","40m","EU","FT8","Spain","ezht8vnkrc2q","0","1",
"2020-11-19 23:52:45","DL3SD","UB1CCL","40m","EU","FT8","European Russia","uct3jqstyugg","2","1",
"2020-11-19 23:52:45","DL3SD","EV1P","40m","EU","FT8","Belarus","u9egjnkuz395","-7","1",
"2020-11-19 23:52:45","DL3SD","EA3IEO","40m","EU","FT8","Spain","ezht8vnkrc2q","3","1",
"2020-11-19 23:52:45","DL3SD","K8FL","40m","NA","FT8","United States","9ywv9bpun5jd","-10","1",
"2020-11-19 23:52:45","DL3SD","E74E","40m","EU","FT8","Bosnia-Herzegovina","srukpu4ndwv9","-5","1",
"2020-11-19 23:52:45","DL3SD","US2YW","40m","EU","FT8","Ukraine","u8vk6wjr4et3","-2","1",
"2020-11-19 23:52:45","DL3SD","AA2T","40m","NA","FT8","United States","9ywv9bpun5jd","-12","1",
"2020-11-19 23:52:45","DL3SD","EA7DAP","40m","EU","FT8","Spain","ezht8vnkrc2q","-12","1",
"2020-11-19 23:52:45","DL3SD","SV3AUW","40m","EU","FT8","Greece","srp6r9exqc3p","-2","1",
"2020-11-19 23:52:45","DL3SD","W1ES","40m","NA","FT8","United States","9ywv9bpun5jd","-16","1",
"2020-11-19 23:52:45","DL3SD","CU7AA","40m","EU","FT8","Azores","equshm5ez4bk","-16","1",
"2020-11-19 23:52:45","DL3SD","EB3DIM","40m","EU","FT8","Spain","ezht8vnkrc2q","-8","1",

20
cmd/alltxt2csv/ALL.test

@ -0,0 +1,20 @@
201119_235230 7.074 Rx FT8 -15 -1.1 1725 CQ CO8MCL FL20
201119_235230 7.074 Rx FT8 -11 0.0 1057 NC6R EA3IKA JN12
201119_235230 7.074 Rx FT8 -12 -1.2 753 CQ WP4AZT FK67
201119_235230 7.074 Rx FT8 -16 0.1 870 K9OW IZ5MDD JN53
201119_235230 7.074 Rx FT8 -5 0.6 400 R8CCC YO2BBX +02
201119_235230 7.074 Rx FT8 -13 0.2 2910 CQ N3MK FM27
201119_235230 7.074 Rx FT8 -12 0.2 461 R8CCC UR6QA -12
201119_235245 7.074 Rx FT8 0 0.7 626 SV1SPJ EA3AVS -18
201119_235245 7.074 Rx FT8 2 0.2 966 PJ4EL UB1CCL -12
201119_235245 7.074 Rx FT8 -7 1.4 1438 MI0GOZ EV1P -09
201119_235245 7.074 Rx FT8 3 0.5 1493 CQ EA3IEO JN01
201119_235245 7.074 Rx FT8 -10 0.1 2166 CO8VWZ K8FL 73
201119_235245 7.074 Rx FT8 -5 -1.7 1237 M7EMH E74E -03
201119_235245 7.074 Rx FT8 -2 0.1 1683 YB1CW US2YW KN28
201119_235245 7.074 Rx FT8 -12 0.1 1127 LX1GQ AA2T R+01
201119_235245 7.074 Rx FT8 -12 0.1 1618 PJ4GR EA7DAP IM86
201119_235245 7.074 Rx FT8 -2 0.2 1949 CQ SV3AUW KM17
201119_235245 7.074 Rx FT8 -16 0.1 1187 WL7CG W1ES FM06
201119_235245 7.074 Rx FT8 -16 0.2 1310 CQ CU7AA HM58
201119_235245 7.074 Rx FT8 -8 0.2 1936 CQ EB3DIM JN11

10
cmd/alltxt2csv/Dockerfile

@ -0,0 +1,10 @@
from golang:1.15.0
RUN mkdir /wsjtx2mysql
ADD . /wsjtx2mysql
WORKDIR /wsjtx2mysql
RUN go build -o wsjtx2mysql .
#CMD ["/wsjtx2mysql/wsjtx2mysql","-debug","-readall"]
#CMD ["/wsjtx2mysql/wsjtx2mysql","-readall"]
CMD ["/wsjtx2mysql/wsjtx2mysql","-back=50000000"]

91
cmd/alltxt2csv/main.go

@ -0,0 +1,91 @@
package main
import (
"fmt"
"github.com/jnovack/flag"
log "github.com/sirupsen/logrus"
// "strings"
// "strconv"
// "time"
"os"
"bufio"
// "github.com/mmcloughlin/geohash"
// "github.com/tzneal/ham-go/dxcc"
"github.com/denzs/wsjtx_dashboards/shared/wsjtx"
)
var station string
var pathin string
var pathout string
var trace bool
func usage() {
fmt.Printf("Usage of %s:\n", os.Args[0])
flag.PrintDefaults()
}
func init() {
flag.StringVar(&pathin, "in", "", "path to wsjt-x ALL.txt")
flag.StringVar(&pathout, "out", "", "path to csv outfile")
flag.StringVar(&station, "station", "localstation", "your callsign or wsjtx instance identifier")
flag.BoolVar(&trace, "trace", false, "log every line... yes really ;)")
flag.Parse()
if pathin == "" || pathout == "" {
usage()
os.Exit(1)
}
formatter := &log.TextFormatter{
FullTimestamp: true,
}
log.SetFormatter(formatter)
if trace {
log.SetLevel(log.TraceLevel)
log.Info("trace logging enabled")
} else {
log.Info("normal logging enabled")
}
}
func main(){
_ , err := os.Stat(pathout)
if !os.IsNotExist(err) {
log.Fatalf("file %s already exists..", pathout)
}
fileout, err := os.OpenFile(pathout,os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
log.Fatal(err)
}
writer := bufio.NewWriter(fileout)
filein, err := os.Open(pathin)
if err != nil {
log.Fatal(err)
}
lines := bufio.NewScanner(filein)
counter := 0
for lines.Scan() {
result, parsed := wsjtx.ScanLine(lines.Text())
if !parsed {
continue
}
counter++
if counter % 1000000 == 0 {
log.Infof("%d lines parsed..", counter)
}
_, err := writer.WriteString(fmt.Sprintf("\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%d\",\"%d\",\n", result.Timestamp.Format("2006-01-02 15:04:05"), station, result.Call, result.Band, result.Ent.Continent, result.Mode, result.Ent.Entity, result.GeoHash, result.Signal, result.Rx))
if err != nil {
log.Warn(err)
}
}
writer.Flush()
fileout.Close()
filein.Close()
log.Info("done..")
}

163
cmd/pskreporter_exporter/main.go

@ -0,0 +1,163 @@
package main
import (
"fmt"
"github.com/namsral/flag"
"net/http"
"time"
"os"
"github.com/prometheus/client_golang/prometheus/promhttp"
log "github.com/sirupsen/logrus"
)
var station string
var debug bool
var reportList = make(map[string]report)
var metricpath string
var mysql_host string
var mysql_db string
var mysql_user string
var mysql_pass string
var mysql_table string
var port int
//var promcalls bool
var trace bool
var useProm bool
var useMysql bool
func usage() {
fmt.Printf("Usage of %s:\n", os.Args[0])
flag.PrintDefaults()
}
func init() {
flag.StringVar(&station, "station", "", "callsign to monitor on pskreporter")
flag.StringVar(&mysql_host, "dbhost", "db", "name/ip of mysql host")
flag.StringVar(&mysql_db, "db", "digimode_stats", "db name")
flag.StringVar(&mysql_user, "dbuser", "wsjtx", "mysql username")
flag.StringVar(&mysql_pass, "dbpass", "secret", "mysql password")
flag.StringVar(&mysql_table, "dbtable", "pskreporter_stats", "mysql table name")
flag.StringVar(&metricpath, "metricpath", "/metrics", "path for prometheus metric endpoint")
flag.IntVar(&port, "port", 2112, "port for prometheus metric endpoint")
flag.BoolVar(&useProm, "prometheus", false, "activate prometheus exporter")
flag.BoolVar(&useMysql, "mysql", false, "activate mysql exporter")
// flag.BoolVar(&promcalls, "promcalls", false, "activate prometheus callsign metrics")
flag.BoolVar(&trace, "trace", false, "log almost everything")
flag.BoolVar(&debug, "debug", false, "enable debug logging")
flag.Parse()
formatter := &log.TextFormatter{
FullTimestamp: true,
}
log.SetFormatter(formatter)
if trace {
log.SetLevel(log.TraceLevel)
log.Info("trace logging enabled")
} else {
log.Info("normal logging enabled")
}
if !useProm && !useMysql {
usage()
log.Fatal("you have to enable at least one exporter. see -mysql and -prometheus flags")
}
if useProm {
log.Info("prometheus exporter enabled..")
}
if useMysql {
log.Info("mysql exporter enabled..")
// wait for stupid mysql container to come up..
_, db_down := dbConn()
for db_down {
log.Info("waiting for db to come up..")
time.Sleep(2 * time.Second)
_, db_down = dbConn()
}
init_db()
}
}
func handleResults(reports chan report) {
for {
select {
case report := <- reports :
_ , seen := reportList[report.CallSign]
// check if receiver is known
if seen {
// check if fetched record is newer than existing one
if report.lastReport > reportList[report.CallSign].lastReport {
log.WithFields(log.Fields{
"Callsign":report.CallSign,
"Report":report.Signal,
"Grid":report.Grid,
"Geohash":report.GeoHash,
"Mode":report.Mode,
"Continent":report.Continent,
"LastReport":report.lastReport,
"ITUZone":fmt.Sprintf("%d",report.ITUZone),
"CQZone":fmt.Sprintf("%d",report.CQZone),
"DXCC":report.Dxcc,
}).Trace("known receiver with fresh report")
if useProm {
handlePrometheus(report)
}
if useMysql {
handleMysql(report)
}
// put in cache
reportList[report.CallSign] = report
} else { // just hit an old entry, no need to store
log.Trace("known receiver with known report")
}
} else {
// FIXME is it safe that lastReport is the last Report from the other station or is it the last Report he reported us?
// if it is the last, the check could be wrong :-/
// -> add debugging output to check!
if report.lastReport > (time.Now().Unix() - 300) {
log.WithFields(log.Fields{
"Callsign":report.CallSign,
"Report":report.Signal,
"Grid":report.Grid,
"Geohash":report.GeoHash,
"Mode":report.Mode,
"Continent":report.Continent,
"LastReport":report.lastReport,
"ITUZone":fmt.Sprintf("%d",report.ITUZone),
"CQZone":fmt.Sprintf("%d",report.CQZone),
"DXCC":report.Dxcc,
}).Trace("unknown receiver with fresh report")
if useProm {
handlePrometheus(report)
}
if useMysql {
handleMysql(report)
}
} else {
log.Trace("unknown receiver with old report")
}
// put in cache
reportList[report.CallSign] = report
}
}
}
}
func main(){
reports := make(chan report)
go fetchReports(reports)
go handleResults(reports)
log.Infof("listening on :%d%s",port, metricpath)
http.Handle(metricpath, promhttp.Handler())
http.ListenAndServe(fmt.Sprintf(":%d",port), nil)
}

89
cmd/pskreporter_exporter/mysql.go

@ -0,0 +1,89 @@
package main
import (
log "github.com/sirupsen/logrus"
"database/sql"
"time"
_ "github.com/go-sql-driver/mysql"
// "github.com/denzs/wsjtx_dashboards/shared/wsjtx"
)
func handleMysql(r report) {
db, dbDown := dbConn()
if dbDown {
log.Fatal("cant reach database..")
}
defer db.Close()
// log.Infof("handleMysql(%+v)",r)
stmt, err := db.Prepare("INSERT INTO " + mysql_table + " (ts, station, callsign, band, mode, report, dxcc, geohash, continent, cqzone, ituzone) VALUES(?,?,?,?,?,?,?,?,?,?,?)")
defer stmt.Close()
_, err = stmt.Exec(time.Unix(r.lastReport, 0), station, r.CallSign, r.Band, r.Mode, r.Signal, r.Dxcc, r.GeoHash, r.Continent, r.CQZone, r.ITUZone)
if err != nil {
log.WithFields(log.Fields{"err":err.Error()}).Warn("error while executing prepared statement..")
}
}
func init_db() {
var cnt int
log.Debug("init_db()")
db, _ := dbConn()
row := db.QueryRow("SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_NAME LIKE '%" + mysql_table + "%';")
switch err := row.Scan(&cnt); err {
case sql.ErrNoRows:
log.Error("something went wrong while scanning for existing table structure..")
panic(err.Error())
case nil:
default:
panic(err)
}
if cnt < 1 {
qry := "CREATE TABLE IF NOT EXISTS " + mysql_table + " (" +
"ts timestamp NOT NULL," +
"station VARCHAR(16) NOT NULL," +
"callsign VARCHAR(16) NOT NULL," +
"band VARCHAR(10) NOT NULL," +
"continent VARCHAR(32) NOT NULL," +
"mode VARCHAR(16) NOT NULL," +
"dxcc VARCHAR(128) NOT NULL," +
"geohash VARCHAR(16) NOT NULL," +
"report TINYINT NOT NULL," +
"cqzone INT NOT NULL," +
"ituzone INT NOT NULL," +
"UNIQUE KEY UC_" + mysql_table + "(ts, station, callsign));"
log.WithFields(log.Fields{"query":qry}).Debug("creating database..")
_, err := db.Exec(qry)
if err != nil {
log.Error("something went wrong while creating table structure..")
panic(err)
}
} else {
log.Info("found existing table")
}
}
func dbConn() (db *sql.DB, err bool){
db, er := sql.Open("mysql", mysql_user+":"+mysql_pass+"@tcp("+mysql_host+")/"+mysql_db)
if er != nil {
log.Debugf("db not reachable: %s",err)
return nil, true
}
pingerr := db.Ping()
if pingerr != nil {
log.Debug("db not pingable..")
return nil, true
}
return db, false
}

72
cmd/pskreporter_exporter/prometheus.go

@ -0,0 +1,72 @@
package main
import (
"fmt"
// log "github.com/sirupsen/logrus"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
// "time"
)
var prt *prometheus.CounterVec
func handlePrometheus(r report) {
if(prt == nil) {
prt = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "pskreporter_receivereports_total",
Help: "PSK Reporter receiveReports",},
[]string{"station","callsign","grid","signal","dxcc","mode","geohash","continent","band","ituzone","cqzone"},)
}
// _ , seen := reportList[r.CallSign]
// // only increase counter if reporter sends update to prevent double counting thru restarts
// if seen { // check if fetched record is newer than existing
// if r.lastReport > reportList[r.CallSign].lastReport {
// log.WithFields(log.Fields{
// "Callsign":r.CallSign,
// "Grid":r.Grid,
// "Geohash":r.GeoHash,
// "Mode":r.Mode,
// "Continent":r.Continent,
// "ITUZone":fmt.Sprintf("%d",r.ITUZone),
// "CQZone":fmt.Sprintf("%d",r.CQZone),
// "DXCC":r.Dxcc,
// }).Debug("new but fresh")
prt.With(prometheus.Labels{"signal":fmt.Sprintf("%d",r.Signal),
"callsign":r.CallSign,
"station":station,
"grid":r.Grid,
"mode":r.Mode,
"geohash":r.GeoHash,
"continent":r.Continent,
"ituzone":fmt.Sprintf("%d",r.ITUZone),
"cqzone":fmt.Sprintf("%d",r.CQZone),
"band":r.Band,
"dxcc":r.Dxcc}).Inc()
// } else { // just hit an old entry, no need to store
// log.Info("old entry!")
// return
// }
// } else {
// log.Info("!seen")
//// log.Infof("reportList:%+v",reportList)
// }
// if r.lastReport > (time.Now().Unix() - 300) {
//
// log.WithFields(log.Fields{
// "Callsign":r.CallSign,
// }).Debug("new but fresh")
//
// prt.With(prometheus.Labels{"signal":fmt.Sprintf("%d",r.Signal),
// "callsign":r.CallSign,
// "station":station,
// "grid":r.Grid,
// "mode":r.Mode,
// "geohash":r.GeoHash,
// "continent": r.Continent,
// "ituzone":fmt.Sprintf("%d",r.ITUZone),
// "cqzone":fmt.Sprintf("%d",r.CQZone),
// "band": r.Band,
// "dxcc":r.Dxcc}).Inc()
// }
// reportList[r.CallSign] = r
}

233
cmd/pskreporter_exporter/pskreporter.go

@ -0,0 +1,233 @@
package main
import (
"encoding/xml"
log "github.com/sirupsen/logrus"
"io/ioutil"
"net/http"
"time"
"fmt"
"github.com/tzneal/ham-go/dxcc"
"github.com/mmcloughlin/geohash"
"github.com/pd0mz/go-maidenhead"
)
type receptionReports struct {
XMLName xml.Name `xml:"receptionReports"`
ActiveReceivers []activeReceiver `xml:"activeReceiver"`
LastSequenceNumber lastSequenceNumber `xml:"lastSequenceNumber"`
MaxFlowStartSeconds maxFlowStartSeconds `xml:"maxFlowStartSeconds"`
ReceptionReport []receptionReport `xml:"receptionReport"`
SenderSearch senderSearch `xml:"senderSearch"`
ActiveCallSign []activeCallSign `xml:"activeCallsign"`
Lotw lotw `xml:"lotw"`
}
type activeReceiver struct {
XMLName xml.Name `xml:"activeReceiver"`
CallSign string `xml:"callsign,attr"`
Locator string `xml:"locator,attr"`
Frequency string `xml:"frequency,attr"`
Region string `xml:"region,attr"`
Dxcc string `xml:"DXCC,attr"`
DecoderSoftware string `xml:"decoderSoftware,attr"`
AntennaInformation string `xml:"antennaInformation,attr"`
Mode string `xml:"mode,attr"`
}
type lastSequenceNumber struct {
XMLName xml.Name `xml:"lastSequenceNumber"`
Value int64 `xml:"value,attr"`
}
type maxFlowStartSeconds struct {
XMLName xml.Name `xml:"maxFlowStartSeconds"`
Value string `xml:"value,attr"`
}
type receptionReport struct {
XMLName xml.Name `xml:"receptionReport"`
ReceiverCallSign string `xml:"receiverCallsign,attr"`
ReceiverLocator string `xml:"receiverLocator,attr"`
SenderCallSign string `xml:"senderCallsign,attr"`
SenderLocator string `xml:"senderLocator,attr"`
Frequency float64 `xml:"frequency,attr"`
FlowStartSeconds int64 `xml:"flowStartSeconds,attr"`
Mode string `xml:"mode,attr"`
IsSender bool `xml:"isSender,attr"`
ReceiverDxcc string `xml:"receiverDXCC,attr"`
ReceiverDxccCode string `xml:"receiverDXCCCode,attr"`
SenderLotwUpload string `xml:"senderLotwUpload,attr"`
Snr int `xml:"sNR,attr"`
}
type senderSearch struct {
XMLName xml.Name `xml:"senderSearch"`
CallSign string `xml:"callsign,attr"`
RecentFlowStartSeconds string `xml:"recentFlowStartSeconds,attr"`
}
type activeCallSign struct {
XMLName xml.Name `xml:"activeCallsign"`
CallSign string `xml:"callsign"`
Reports int `xml:"reports"`
Dxcc string `xml:"DXCC"`
DxccCode string `xml:"DXCCCode"`
frequency int `xml:"frequency"`
}
type lotw struct {
XMLName xml.Name `xml:"lotw"`
Upload string `xml:"upload,attr"`
CallSign string `xml:"callsign,attr"`
}
type report struct {
CallSign string
Grid string
Signal int
Mode string
Dxcc string
GeoHash string
Continent string
lastReport int64
Band string
CQZone int
ITUZone int
}
func GetBand(freq float64) (string){
band := "unknown"
if (freq>1000000 && freq<2000000) {
band = "160m"
}
if (freq>3000000 && freq<4000000) {
band = "80m"
}
if (freq>5000000 && freq<6000000) {
band = "60m"
}
if (freq>7000000 && freq<8000000) {
band = "40m"
}
if (freq>10000000 && freq<11000000) {
band = "30m"
}
if (freq>14000000 && freq<15000000) {
band = "20m"
}
if (freq>18000000 && freq<19000000) {
band = "17m"
}
if (freq>21000000 && freq<22000000) {
band = "15m"
}
if (freq>24000000 && freq<25000000) {
band = "12m"
}
if (freq>28000000 && freq<30000000) {
band = "10m"
}
return band
}
func handleReport(receiver receptionReport) (report, bool) {
var r report
// var i int64
Ent, found := dxcc.Lookup(receiver.ReceiverCallSign)
if !found {
log.Warnf("erro while lookup up %s",receiver.ReceiverCallSign)
return r ,false
}
p, err:= maidenhead.ParseLocator(receiver.ReceiverLocator)
if err != nil {
log.Error("crazy shit happened while grid parsing..")
log.Error(err)
return r, false
}
r.CallSign = receiver.ReceiverCallSign
r.Grid = receiver.ReceiverLocator
r.Signal = receiver.Snr
r.Mode = receiver.Mode
r.Dxcc = receiver.ReceiverDxcc
r.Continent = Ent.Continent
r.CQZone = Ent.CQZone
r.ITUZone = Ent.ITUZone
r.lastReport = receiver.FlowStartSeconds
r.GeoHash = geohash.Encode(p.Latitude, p.Longitude)
r.Band = GetBand(receiver.Frequency)
// i, err := strconv.ParseInt(receiver.FlowStartSeconds, 10, 64)
// if err != nil {
// log.WithFields(log.Fields{"err":err}).Error("something went wrong while parsing the timestamp: ",)
// return r, false
// }
// r.lastReport = time.Unix(i, 0)
return r, true
}
func fetchReports(reports chan report) {
var lastseqno int64
var url string
lastseqno = 0
for {
if lastseqno == 0 {
url = "https://retrieve.pskreporter.info/query?senderCallsign=" + station + "&rronly=true"
} else {
url = "https://retrieve.pskreporter.info/query?senderCallsign=" + station + "&lastseqno=" + fmt.Sprintf("%d",lastseqno) + "&rronly=true"
}
log.WithFields(log.Fields{"url":url}).Debug("fetching reports")
resp, err := http.Get(url)
defer resp.Body.Close()
if err != nil {
log.Error("well.. shit happened.. 1")
log.Error(err)
log.Info("sleeping for 300 seconds..")
time.Sleep(300 * time.Second)
continue
}
body,err := ioutil.ReadAll(resp.Body)
if err !=nil {
log.Error("well.. shit happened.. 2")
log.Error(err)
log.Info("sleeping for 300 seconds..")
time.Sleep(300 * time.Second)
continue
}
// phrt.With(prometheus.Labels{"code": fmt.Sprintf("%d",resp.StatusCode)}).Inc()
if resp.StatusCode >= 200 && resp.StatusCode <= 299 {
log.Info("fetching reports done.")
rr := receptionReports{}
err = xml.Unmarshal(body,&rr)
if err != nil {
log.Error("well.. shit happened.. 3")
log.Error(err)
log.Info("sleeping for 300 seconds..")
time.Sleep(300 * time.Second)
continue
}
for _, receiver := range rr.ReceptionReport {
r, parsed := handleReport(receiver)
if parsed {
reports <- r
}
}
lastseqno = rr.LastSequenceNumber.Value
} else {
log.WithFields(log.Fields{"response":resp.StatusCode,}).Warn("fetching reports was NOT successfully ")
log.Warn(string(body))
}
log.Info("sleeping for 300 seconds..")
time.Sleep(300 * time.Second)
}
}

120
cmd/wsjtx_exporter/main.go

@ -0,0 +1,120 @@
package main
import (
"github.com/jnovack/flag"
"fmt"
log "github.com/sirupsen/logrus"
"net/http"
"github.com/denzs/wsjtx_dashboards/shared/wsjtx"
"github.com/hpcloud/tail"
"github.com/prometheus/client_golang/prometheus/promhttp"
"os"
"time"
)
var station string
var pathin string
var metricpath string
var mysql_host string
var mysql_db string
var mysql_user string
var mysql_pass string
var mysql_table string
var port int
var promcalls bool
var trace bool
var useProm bool
var useMysql bool
func usage() {
fmt.Printf("Usage of %s:\n", os.Args[0])
flag.PrintDefaults()
}
func init() {
flag.StringVar(&station, "station", "localstation", "your callsign or wsjtx instance identifier")
flag.StringVar(&pathin, "pathin", "/wsjtx/ALL.TXT", "path to WSJT-X ALL.TXT")
flag.StringVar(&mysql_host, "dbhost", "db", "name/ip of mysql host")
flag.StringVar(&mysql_db, "db", "digimode_stats", "db name")
flag.StringVar(&mysql_user, "dbuser", "wsjtx", "mysql username")
flag.StringVar(&mysql_pass, "dbpass", "secret", "mysql password")
flag.StringVar(&mysql_table, "dbtable", "wsjtx_all_txt", "mysql table name")
flag.StringVar(&metricpath, "metricpath", "/metrics", "path for prometheus metric endpoint")
flag.IntVar(&port, "port", 2112, "port for prometheus metric endpoint")
flag.BoolVar(&useProm, "prometheus", false, "activate prometheus exporter")
flag.BoolVar(&useMysql, "mysql", false, "activate mysql exporter")
flag.BoolVar(&promcalls, "promcalls", false, "activate prometheus callsign metrics")
flag.BoolVar(&trace, "trace", false, "log almost everything")
flag.Parse()
formatter := &log.TextFormatter{
FullTimestamp: true,
}
log.SetFormatter(formatter)
if trace {
log.SetLevel(log.TraceLevel)
log.Info("trace logging enabled")
} else {
log.Info("normal logging enabled")
}
if !useProm && !useMysql {
usage()
log.Fatal("you have to enable at least one exporter. see -mysql and -prometheus flags")
}
if useProm {
log.Info("prometheus exporter enabled..")
}
if useMysql {
log.Info("mysql exporter enabled..")
// wait for stupid mysql container to come up..
_, db_down := dbConn()
for db_down {
log.Info("waiting for db to come up..")
time.Sleep(2 * time.Second)
_, db_down = dbConn()
}
init_db()
}
}
func followFile(results chan wsjtx.Result) {
log.Printf("start following %s", pathin)
t, _:= tail.TailFile(pathin, tail.Config{Follow: true, Location: &tail.SeekInfo{Offset: 0, Whence: 2},})
for line := range t.Lines {
result, parsed := wsjtx.ScanLine(line.Text)
if parsed {
results <- result
} else {
continue
}
}
}
func handleResults(results chan wsjtx.Result) {
for {
select {
case result := <- results :
handlePrometheus(result)
handleMysql(result)
}
}
}
func main(){
results := make(chan wsjtx.Result)
go followFile(results)
go handleResults(results)
log.Infof("listening on :%d%s",port, metricpath)
http.Handle(metricpath, promhttp.Handler())
http.ListenAndServe(fmt.Sprintf(":%d",port), nil)
}

88
cmd/wsjtx_exporter/mysql.go

@ -0,0 +1,88 @@
package main
import (
log "github.com/sirupsen/logrus"
"database/sql"
_ "github.com/go-sql-driver/mysql"
"github.com/denzs/wsjtx_dashboards/shared/wsjtx"
)
func handleMysql(result wsjtx.Result) {
db, dbDown := dbConn()
if dbDown {
log.Fatal("cant reach database..")
}
defer db.Close()
stmt, err := db.Prepare("INSERT INTO wsjtx_all_txt(ts, station, callsign, band, mode, report, dxcc, geohash, continent, cqzone, ituzone, rx) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)")
defer stmt.Close()
_, err = stmt.Exec(result.Timestamp, station, result.Call, result.Band, result.Mode, result.Signal, result.Ent.Entity, result.GeoHash, result.Ent.Continent, result.Ent.CQZone, result.Ent.ITUZone, result.Rx)
if err != nil {
log.WithFields(log.Fields{"err":err.Error()}).Warn("error while executing prepared statement..")
}
}
func init_db() {
var cnt int
log.Debug("init_db()")
db, _ := dbConn()
row := db.QueryRow("SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_NAME LIKE '%" + mysql_table + "%';")
switch err := row.Scan(&cnt); err {
case sql.ErrNoRows:
log.Error("something went wrong while scanning for existing table structure..")
panic(err.Error())
case nil:
default:
panic(err)
}
if cnt < 1 {
qry := "CREATE TABLE IF NOT EXISTS " + mysql_table + " (" +
"ts timestamp NOT NULL," +
"station VARCHAR(16) NOT NULL," +
"callsign VARCHAR(16) NOT NULL," +
"band VARCHAR(10) NOT NULL," +
"continent VARCHAR(32) NOT NULL," +
"mode VARCHAR(16) NOT NULL," +
"dxcc VARCHAR(128) NOT NULL," +
"geohash VARCHAR(16) NOT NULL," +
"report TINYINT NOT NULL," +
"cqzone INT NOT NULL," +
"ituzone INT NOT NULL," +
"rx TINYINT NOT NULL," +
"PRIMARY KEY UC_" + mysql_table + "(ts, station, callsign))," +
"INDEX idx_dxcc (dxcc);"
log.WithFields(log.Fields{"query":qry}).Debug("creating database..")
_, err := db.Exec(qry)
if err != nil {
log.Error("something went wrong while creating table structure..")
panic(err)
}
} else {
log.Info("found existing table")
}
}
func dbConn() (db *sql.DB, err bool){
db, er := sql.Open("mysql", mysql_user+":"+mysql_pass+"@tcp("+mysql_host+")/"+mysql_db)
if er != nil {
log.Debugf("db not reachable: %s",err)
return nil, true
}
pingerr := db.Ping()
if pingerr != nil {
log.Debug("db not pingable..")
return nil, true
}
return db, false
}

56
cmd/wsjtx_exporter/prometheus.go

@ -0,0 +1,56 @@
package main
import (
"fmt"
"github.com/denzs/wsjtx_dashboards/shared/wsjtx"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
log "github.com/sirupsen/logrus"
)
var wsjtx_received_total *prometheus.CounterVec
var wsjtx_received_call_total *prometheus.CounterVec
func handlePrometheus(result wsjtx.Result) {
incr_wsjtx_received_total(result)
if promcalls {
incr_wsjtx_received_callsigns_total(result)
}
}
func incr_wsjtx_received_total(result wsjtx.Result) {
if(wsjtx_received_total == nil) {
log.Printf("creating wsjtx_received_total...")
wsjtx_received_total = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "wsjtx_received_total", Help: "DXCCs ordery by labels",
}, []string{"num","signal","name","continent","cqzone","ituzone","band","mode","geohash","station"},)
}
wsjtx_received_total.With(prometheus.Labels{"num":fmt.Sprintf("%d",result.Ent.DXCC),"signal":fmt.Sprintf("%d",result.Signal),
"band":result.Band,
"name":result.Ent.Entity,
"continent":result.Ent.Continent,
"cqzone":fmt.Sprintf("%d",result.Ent.CQZone),
"mode":result.Mode,
"geohash":result.GeoHash,
"station": station,
"ituzone":fmt.Sprintf("%d",result.Ent.ITUZone)}).Inc()
}
func incr_wsjtx_received_callsigns_total(result wsjtx.Result) {
if(wsjtx_received_call_total == nil) {
log.Printf("creating wsjtx_received_call_total...")
wsjtx_received_call_total = promauto.NewCounterVec(prometheus.CounterOpts{ Name: "wsjtx_received_call_total", Help: "DXCCs ordery by labels",
}, []string{"num","signal","name","continent","cqzone","ituzone","band","call","mode","geohash","station"},)
}
wsjtx_received_call_total.With(prometheus.Labels{"num":fmt.Sprintf("%d",result.Ent.DXCC),"signal":fmt.Sprintf("%d",result.Signal),
"band":result.Band,
"name":result.Ent.Entity,
"continent":result.Ent.Continent,
"cqzone":fmt.Sprintf("%d",result.Ent.CQZone),
"mode":result.Mode,
"call":result.Call,
"geohash":result.GeoHash,
"station": station,
"ituzone":fmt.Sprintf("%d",result.Ent.ITUZone)}).Inc()
}

21
deploy.sh

@ -0,0 +1,21 @@
#!/bin/bash
for file in Dockerfile*
do
app=$(echo $file | sed 's/Dockerfile.//g')
docker build -f $file . || exit 1
export TAG=$(docker images|grep -v REPO|head -n1|awk '{print $3}')
docker tag $TAG localhost:5000/$app:latest || exit 1
docker push localhost:5000/$app:latest || exit 1
done
docker-compose -f ../dockprom/docker-compose.yml down --remove-orphans || exit 1
for file in Dockerfile*
do
app=$(echo $file | sed 's/Dockerfile.//g')
for i in $(docker images|grep $app|awk '{print $3}') ; do docker rmi $i ; done || exit 1
done
docker-compose -f ../dockprom/docker-compose.yml up -d || exit 1

16
go.mod

@ -0,0 +1,16 @@
module github.com/denzs/wsjtx_dashboards
go 1.14
require (
github.com/go-sql-driver/mysql v1.4.0
github.com/hpcloud/tail v1.0.0
github.com/jnovack/flag v1.15.0
github.com/mmcloughlin/geohash v0.10.0
github.com/namsral/flag v1.7.4-pre
github.com/pd0mz/go-maidenhead v0.0.0-20170221185439-faa09c24425e
github.com/prometheus/client_golang v1.8.0
github.com/robfig/cron v1.2.0
github.com/sirupsen/logrus v1.7.0
github.com/tzneal/ham-go v0.2.0
)

463
go.sum

@ -0,0 +1,463 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cgrates/ltcache v0.0.0-20181016092649-92fb7fa77cca/go.mod h1:q7c996DUu8OrJRnewVSQzM+y/bRcxZAHoo+zCD8bFBo=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dh1tw/goHamlib v0.0.0-20181218214755-d4da0f6f14c2/go.mod h1:tV9TEPFlb00Muc9G1jU9Wh5h9SHtQ87M9J9XxwHlScY=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jnovack/flag v1.15.0 h1:ENYkyIpoKg2u57ME6sK0204om4FdXznbuEeLu5bjfJI=
github.com/jnovack/flag v1.15.0/go.mod h1:k96ecNuM1uCWu77DCU/2ICA05POKE56bl6/KFy3WdME=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mmcloughlin/geohash v0.10.0 h1:9w1HchfDfdeLc+jFEf/04D27KP7E2QmpDu52wPbJWRE=
github.com/mmcloughlin/geohash v0.10.0/go.mod h1:oNZxQo5yWJh0eMQEP/8hwQuVx9Z9tjwFUqcTB1SmG0c=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/namsral/flag v1.7.4-pre h1:b2ScHhoCUkbsq0d2C15Mv+VU8bl8hAXV8arnWiOHNZs=
github.com/namsral/flag v1.7.4-pre/go.mod h1:OXldTctbM6SWH1K899kPZcf65KxJiD7MsceFUpB5yDo=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nsf/termbox-go v0.0.0-20200418040025-38ba6e5628f1/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pd0mz/go-maidenhead v0.0.0-20170221185439-faa09c24425e h1:Wi4WahFJ11u42ofsl3BdI7y7TiAoCH4yDXei/IQuAzw=
github.com/pd0mz/go-maidenhead v0.0.0-20170221185439-faa09c24425e/go.mod h1:4Q+QSDCqWqlabstLGUVm47rAcL06nEEty2d3KzsTNMk=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw=
github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4=
github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tzneal/ham-go v0.2.0 h1:yrR3SXNt7GPct9vTf7fPIzYSlH/8fNgsMS/CIiX6bTg=
github.com/tzneal/ham-go v0.2.0/go.mod h1:vbzwW47cxGdiDsjOjC3NE20TfUxNWbMr97+TaJyL4rs=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

6
misc/IMPORT.SQL

@ -0,0 +1,6 @@
LOAD DATA LOCAL INFILE '/wsjtx/SEBO.CSV'
INTO TABLE wsjtx_all_txt
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
(ts, station, callsign, band, continent, mode, dxcc, geohash, report, rx)

21
misc/bandswitcher.sh

@ -0,0 +1,21 @@
#!/bin/bash
RIGCTL=$(which rigctl)
PARAMETERS="-m 2"
DELAY="60"
BANDS="14074000 21074000 28074000"
# wait for full minute
sleep $(((60 - $(date +%s) % 60)-1))
# start switching
while true
do
for band in $BANDS
do
$RIGCTL $PARAMETERS F $band
sleep $DELAY
done
done

9628
misc/dashboards/prometheus.json

File diff suppressed because it is too large

BIN
screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 KiB

171
shared/wsjtx/wsjtx.go

@ -0,0 +1,171 @@
package wsjtx
import (
"strconv"
"strings"
"time"
"github.com/mmcloughlin/geohash"
"github.com/tzneal/ham-go/dxcc"
log "github.com/sirupsen/logrus"
)
type Row struct {
Time time.Time
Bandf float64
Direction string
Mode string
Strength int
Offset float64
Freq string
First string
Second string
Third string
Fourth string
}
type Result struct {
Ent dxcc.Entity
Call string
Grid string
Band string
Mode string
Signal int
GeoHash string
Timestamp time.Time
Rx int
}
func GetBand(freq float64) (string){
band := "unknown"
if (freq>1 && freq<2) {
band = "160m"
}
if (freq>3 && freq<4) {
band = "80m"
}
if (freq>5 && freq<6) {
band = "60m"
}
if (freq>7.0 && freq<8.0) {
band = "40m"
}
if (freq>10 && freq<11) {
band = "30m"
}
if (freq>14 && freq<15) {
band = "20m"
}
if (freq>18 && freq<19) {
band = "17m"
}
if (freq>21 && freq<22) {
band = "15m"
}
if (freq>24 && freq<25) {
band = "12m"
}
if (freq>28 && freq<30) {
band = "10m"
}
return band
}
func ScanLine(line string) (Result, bool) {
var err error
var tmp string
element := new(Row)
result := new(Result)
found := false
// parse only lines in new format, because old format misses band
if line[6] == '_' && line[15] != 'T' {
dataSlice := strings.Fields(line)
for i, v := range dataSlice {
switch i {
case 0:
element.Time, err = time.Parse("060102_150405",v)
if err != nil {
log.WithFields(log.Fields{"err":err}).Trace("something went wrong while parsing the timestamp: ",v)
continue
}
case 1:
element.Bandf,_ = strconv.ParseFloat(v,10)
case 2:
element.Direction = v
case 3:
element.Mode = v
case 4:
element.Strength,_ = strconv.Atoi(v)
case 5:
element.Offset,_ = strconv.ParseFloat(v,10)
case 6:
element.Freq = v
case 7:
element.First= v
case 8:
element.Second= v
case 9:
element.Third = v
case 10:
element.Fourth = v
default:
log.WithFields(log.Fields{"line":line}).Trace("can't parse line..")
}
}
// check for 4 element sequence like 'CQ DX DL3SD JO31'
if element.Fourth != "" {
result.Call = element.Third
log.WithFields(log.Fields{"line":line,"callsign":result.Call}).Trace("parsed 4 element callsign")
} else {
result.Call = element.Second
}
// ignore 'TNX QSO GL 73' etc.
if result.Call == "73" || result.Call == "<...>" || result.Call == "QSO" || result.Call == "QSY" {
log.WithFields(log.Fields{"line":line,"callsign":result.Call}).Trace("skipping callsign")
return *result, false
}
// take care of Calls like <DL3SD> / FIXME does this actually work?
tmp = strings.Replace(result.Call, "<", "", -1)
result.Call = strings.Replace(tmp, ">", "", -1)
result.Ent, found = dxcc.Lookup(result.Call)
// FIXME result.Grid = qrz.lookup(result.Call) ;) or track in ALL.txt ^^
if(found){
result.Signal = element.Strength
result.Mode = element.Mode
result.Band = GetBand(element.Bandf)
// FIXME
// * get better grid, see above
// * build geohash from better grid with gpsfromgrid(result.Grid)
result.GeoHash = geohash.Encode(result.Ent.Latitude, result.Ent.Longitude)
result.Timestamp = element.Time
if element.Direction[0] == 'R' {
result.Rx = 1
} else {
result.Rx = 0
result.Grid = element.Third
}
log.WithFields(log.Fields{ "call":result.Call,
"signal":result.Signal,
"dxcc":result.Ent.DXCC,
"continent":result.Ent.Continent,
"band":result.Band,
"time":string(result.Timestamp.String()),
"mode":result.Mode,
"geohash":result.GeoHash,
"rx":result.Rx,
}).Trace("successfully parsed line")
return *result, true
} else {
log.WithFields(log.Fields{"line":line,"callsign":element.Second}).Trace("cant parse callsign")
}
} else {
// log.WithFields(log.Fields{"line":line}).Info("found old formated line..")
}
return *result, false
}