diff --git a/CHANGELOG.md b/CHANGELOG.md index 65e49247..94966969 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# v0.14.0 ( unreleased ) + +### New Features + +* add Kafka as available Backend - on backend and UI +* add internal buffer to store metrics and improved management/control/writes to the backend, based on time (see breaking change) +* add new dropped series metric to selfmon stats + +### Fixes + +* fix #343 + +### Breaking Changes + +* refactor metrics sender engine with new output/backend refactor. The metrics are now sent in batches based on an interval and it +* UI: new Output component and InfluxDB is moved as an Output backend +* SNMPCollector OutDB refers now to an Output instead of an InfluxDB Server, a migration script is done to migrate current InfluxDBServers to Outputs + # v0.13.1 ( 2022-11-15 ) ### New Features diff --git a/README.md b/README.md index 5e0ceb1d..cb3e5754 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SnmpCollector [![Go Report Card](https://goreportcard.com/badge/github.com/toni-moreno/snmpcollector)](https://goreportcard.com/report/github.com/toni-moreno/snmpcollector) -SnmpCollector is a full featured Generic SNMP data collector with Web Administration Interface Open Source tool which has as main goal simplify the configuration for getting data from any device which snmp protocol support and send resulting data to an influxdb backend. +SnmpCollector is a full featured Generic SNMP data collector with Web Administration Interface Open Source tool which has as main goal simplify the configuration for getting data from any device which snmp protocol support and send resulting data to an InfluxDB and Kafka backends. For complete information on installation from binary package and configuration you could read the [snmpcollector wiki](https://github.com/toni-moreno/snmpcollector/wiki). @@ -13,6 +13,7 @@ If you want to build a package yourself, or contribute. Here is a guide for how - Go 1.5 for snmpcollector < 0.8 - Go 1.11 for snmpcollector >= 0.8 +- Go 1.17 for snmpcollector >= 0.13 - NodeJS >=6.2.1 ### Get Code and setup example config diff --git a/go.mod b/go.mod index 06ad7b36..a4737e31 100644 --- a/go.mod +++ b/go.mod @@ -4,47 +4,84 @@ go 1.17 require ( github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible + github.com/Shopify/sarama v1.36.0 github.com/go-macaron/binding v1.1.1 github.com/go-macaron/session v1.0.2 github.com/go-macaron/toolbox v0.0.0-20200329073429-4401f4ce0f55 github.com/go-sql-driver/mysql v1.6.0 - github.com/google/go-cmp v0.5.3 - github.com/gosnmp/gosnmp v1.32.0 + github.com/gofrs/uuid v4.2.0+incompatible + github.com/google/go-cmp v0.5.9 + github.com/gosnmp/gosnmp v1.34.0 github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab + github.com/influxdata/telegraf v1.24.2 github.com/kelseyhightower/envconfig v1.4.0 github.com/lib/pq v1.10.2 github.com/mattn/go-sqlite3 v1.14.8 github.com/pkg/errors v0.9.1 - github.com/sirupsen/logrus v1.4.2 + github.com/sirupsen/logrus v1.9.0 github.com/spf13/viper v1.2.1 gopkg.in/macaron.v1 v1.4.0 xorm.io/xorm v1.2.5 ) require ( + github.com/blues/jsonata-go v1.5.4 // indirect + github.com/caio/go-tdigest v3.1.0+incompatible // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/eapache/go-resiliency v1.3.0 // indirect + github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect + github.com/eapache/queue v1.1.0 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/go-macaron/inject v0.0.0-20200308113650-138e5925c53b // indirect + github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.7.4 // indirect - github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/json-iterator/go v1.1.11 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect + github.com/jcmturner/aescts/v2 v2.0.0 // indirect + github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect + github.com/jcmturner/gofork v1.7.6 // indirect + github.com/jcmturner/gokrb5/v8 v8.4.3 // indirect + github.com/jcmturner/rpc/v2 v2.0.3 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.15.10 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect github.com/kr/pretty v0.3.0 // indirect - github.com/magiconair/properties v1.8.0 // indirect - github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/magiconair/properties v1.8.6 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml v1.8.1 // indirect - github.com/spf13/afero v1.1.2 // indirect + github.com/philhofer/fwd v1.1.1 // indirect + github.com/pierrec/lz4/v4 v4.1.15 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/prometheus v1.8.2-0.20210430082741-2a4b8e12bbf2 // indirect + github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect + github.com/spf13/afero v1.2.2 // indirect github.com/spf13/cast v1.3.0 // indirect github.com/spf13/jwalterweatherman v1.0.0 // indirect - github.com/spf13/pflag v1.0.3 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect + github.com/tinylib/msgp v1.1.6 // indirect github.com/unknwon/com v1.0.1 // indirect - golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect - golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect - golang.org/x/text v0.3.6 // indirect - gopkg.in/ini.v1 v1.62.0 // indirect - gopkg.in/yaml.v2 v2.3.0 // indirect + github.com/wavefronthq/wavefront-sdk-go v0.10.4 // indirect + github.com/xdg/scram v1.0.5 // indirect + github.com/xdg/stringprep v1.0.3 // indirect + golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect + golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect + golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect xorm.io/builder v0.3.9 // indirect ) diff --git a/go.sum b/go.sum index 010dfe4a..89b4e780 100644 --- a/go.sum +++ b/go.sum @@ -1,44 +1,141 @@ 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= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0= gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727/go.mod h1:h0OwsgcpJLSYtHcM5+Xciw9OEeuxi6ty4HDiO8C7aIY= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= +github.com/Azure/azure-sdk-for-go v52.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= +github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/HdrHistogram/hdrhistogram-go v1.0.1/go.mod h1:BWJ+nMSHY3L41Zj7CA3uXnloDp7xxV0YvstAE7nKTaM= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/sarama v1.36.0 h1:0OJs3eCcnezkWniVjwBbCJVaa0B1k7ImCRS3WN6NsSk= +github.com/Shopify/sarama v1.36.0/go.mod h1:9glG3eX83tgVYJ5aVtrjVUnEsOPqQIBGx1BWfN+X51I= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/Shopify/toxiproxy/v2 v2.4.0/go.mod h1:3ilnjng821bkozDRxNoo64oI/DKqM+rOyJzb564+bvg= 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/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= 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/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= 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-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 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 v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= +github.com/aws/aws-sdk-go v1.38.3/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= 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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/blues/jsonata-go v1.5.4 h1:XCsXaVVMrt4lcpKeJw6mNJHqQpWU751cnHdCFUq3xd8= +github.com/blues/jsonata-go v1.5.4/go.mod h1:uns2jymDrnI7y+UFYCqsRTEiAH22GyHnNXrkupAVFWI= +github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= +github.com/caio/go-tdigest v3.1.0+incompatible h1:uoVMJ3Q5lXmVLCCqaMGHLBWnbGoN6Lpu7OAUPR60cds= +github.com/caio/go-tdigest v3.1.0+incompatible/go.mod h1:sHQM/ubZStBUmF1WbB8FAm8q9GjDajLC5T7ydxE3JHI= 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/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 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/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= 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/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -50,35 +147,77 @@ github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67/go.mod h1:BQwMFl 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/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY= +github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= 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/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/digitalocean/godo v1.58.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v20.10.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= 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-resiliency v1.3.0 h1:RRL0nge+cWGlxXbUzJ7yMcq6w2XBEr19dCN6HECGaT0= +github.com/eapache/go-resiliency v1.3.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= 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/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 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-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 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-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-macaron/binding v1.1.1 h1:agcYYn3FDj5YQ43CkVRMZ74M2BdqP/X1ut5+hVi8anI= github.com/go-macaron/binding v1.1.1/go.mod h1:dJU/AtPKG0gUiFra1K5TTGduFGMNxMvfJzV/zmXwyGM= github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191/go.mod h1:VFI2o2q9kYsC4o7VP1HrEVosiZZTd+MVT3YZx4gqvJw= @@ -88,48 +227,219 @@ github.com/go-macaron/session v1.0.2 h1:mP6K4FweVzq6VpQpHbMhNWd1si80HJwFZhxNVpqa github.com/go-macaron/session v1.0.2/go.mod h1:kEa+q2raCWCvvaN8pMbyCtp9Mg/37J6zKah2iOKEass= github.com/go-macaron/toolbox v0.0.0-20200329073429-4401f4ce0f55 h1:aDDZ2v9fvO9xGIw+WkmOm65f4jImmRgv7QKqLe8u5bU= github.com/go-macaron/toolbox v0.0.0-20200329073429-4401f4ce0f55/go.mod h1:YFNJ/JT4yLnpuIXTFef30SZkxGHUczjGZGFaZpPcdn0= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ= +github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk= +github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.4/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI= +github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= +github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY= +github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= +github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= +github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4= +github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo= +github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98= +github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= +github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= +github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ= +github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= +github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M= +github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= +github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= +github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8= +github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4= +github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI= +github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= +github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0= github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-json v0.7.4 h1:B44qRUFwz/vxPKPISQ1KhvzRi9kZ28RAf6YtjriBZ5k= github.com/goccy/go-json v0.7.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= +github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 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/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= 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/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/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/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= 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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210323184331-8eee2492667d/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 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/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gophercloud/gophercloud v0.16.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= @@ -137,24 +447,45 @@ github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORR 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/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosnmp/gosnmp v1.32.0 h1:gctewmZx5qFI0oHMzRnjETqIZ093d9NgZy9TQr3V0iA= github.com/gosnmp/gosnmp v1.32.0/go.mod h1:EIp+qkEpXoVsyZxXKy0AmXQx0mCHMMcIhXXvNDMpgF0= +github.com/gosnmp/gosnmp v1.34.0 h1:p96iiNTTdL4ZYspPC3leSKXiHfE1NiIYffMu9100p5E= +github.com/gosnmp/gosnmp v1.34.0/go.mod h1:QWTRprXN9haHFof3P96XTDYc46boCGAh5IXp0DniEx4= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= 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/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/api v1.8.1/go.mod h1:sDjTOq0yUyv5G4h+BqSea7Fn6BU+XbolEz1952UB+mk= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.7.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.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-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= 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-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= 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-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/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= @@ -163,14 +494,31 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 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/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hetznercloud/hcloud-go v1.24.0/go.mod h1:3YmyK8yaZZ48syie6xpm3dt26rtB6s65AisBHylXYFA= 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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/flux v0.65.1/go.mod h1:J754/zds0vvpfwuq7Gc2wRdVwEodfpCFM7mYlOw2LqY= +github.com/influxdata/influxdb v1.8.4/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= +github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= +github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= +github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= +github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= +github.com/influxdata/telegraf v1.24.2 h1:Jc5fz1oS4DVwE2SSqUnrUHqbpRJy5X/46i/EmDcZz44= +github.com/influxdata/telegraf v1.24.2/go.mod h1:p69ITNfx09r9dj4iEzCGIdfM2KIeROCDYnRWloomTwo= +github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -218,35 +566,79 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.3 h1:iTonLeSJOn7MVUtyMT+arAn5AKAPrkilzhGw8wE/Tq8= +github.com/jcmturner/gokrb5/v8 v8.4.3/go.mod h1:dqRwJGXznQrzw6cWmyo6kH+E7jksEQG/CyVWsJEsJO0= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +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/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +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.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 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/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo= +github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= +github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 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.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/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/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 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/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -258,42 +650,87 @@ github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0U github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 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-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= 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/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 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.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 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 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 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= @@ -301,90 +738,160 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE 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/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 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/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 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/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M= github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= 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.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= 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/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= 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/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= +github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 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/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= 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/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/alertmanager v0.21.0/go.mod h1:h7tJ81NA0VLWvWEayi1QltevFkLF3KxmC/malTcT8Go= 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.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= 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.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.23.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/exporter-toolkit v0.5.1/go.mod h1:OCkM4805mmisBhLmVFw858QYi3v0wKdY6/UxrT0pZVg= 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.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/prometheus v1.8.2-0.20210430082741-2a4b8e12bbf2 h1:AHi2TGs09Mv4v688/bjcY2PfAcu9+p4aPvsgVQ4nYDk= +github.com/prometheus/prometheus v1.8.2-0.20210430082741-2a4b8e12bbf2/go.mod h1:5aBj+GpLB+V5MCnrKm5+JAqEJwzDiLugOmDhgt7sDec= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 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/ryanuber/columnize v2.1.0+incompatible/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/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210223165440-c65ae3540d44/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= +github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY= github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg= github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= 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.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= @@ -395,18 +902,24 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK 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/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.2.1 h1:bIcUwXqLseLF3BDAZduuNfekWG87ibtFxi59Bq+oI9M= github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -415,6 +928,8 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J 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/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -422,28 +937,72 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw= +github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= 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/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= +github.com/wavefronthq/wavefront-sdk-go v0.10.4 h1:O8jNBlz21GUDN12YiEFwJWRs25C2zTGt1HEf2I2cs+g= +github.com/wavefronthq/wavefront-sdk-go v0.10.4/go.mod h1:oKJ9Y0y36n+szFm2NiivXI+UubZe3lwfWnN1p+mFNDw= +github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/scram v1.0.5 h1:TuS0RFmt5Is5qm9Tm2SoD89OPqe4IRiFtyFY4iwWXsw= +github.com/xdg/scram v1.0.5/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xdg/stringprep v1.0.3 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4= +github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/xlab/treeprint v1.0.0/go.mod h1:IoImgRak9i3zJyuxOKUP1v4UZd1tMoKkq/Cimt1uhCg= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= 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.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= 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/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -454,33 +1013,77 @@ 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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 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-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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-20181005035420-146acd28ed58/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= @@ -489,26 +1092,82 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r 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-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210324051636-2c4c8ecb7826/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220725212005-46097bf591d3/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM= +golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 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/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 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-20190412183630-56d357773e84/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/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/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= @@ -520,60 +1179,184 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h 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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/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-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/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-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/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-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210314195730-07df6a141424/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/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-20180525024113-a5b4c53f6e8b/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-20181030221726-6c7e314b6563/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-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/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-20190312151545-0bb0c0a6e846/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-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190805222050-c5a2fd39b72a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 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-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200513201620-d5fe73897c97/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 h1:M8tBwCtWD/cZV9DZpFYRUgaymAYAr+aIUTWzDaM3uPs= golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -581,43 +1364,140 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= +gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.42.0/go.mod h1:+Oj4s6ch2SEGtPjGqfUfZonBH0GjQH89gTeKKAEGZKI= 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/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 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-20190418145605-e7d98fc518a7/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-20190502173448-54afdca5d873/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-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210312152112-fc591d9ea70f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 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.21.1/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.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 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.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/macaron.v1 v1.3.4/go.mod h1:/RoHTdC8ALpyJ3+QR36mKjwnT1F1dyYtsGM9Ate6ZFI= gopkg.in/macaron.v1 v1.3.5/go.mod h1:uMZCFccv9yr5TipIalVOyAyZQuOH3OkmXvgcWwhJuP4= gopkg.in/macaron.v1 v1.4.0 h1:RJHC09fAnQ8tuGUiZNjG0uyL1BWSdSWd9SpufIcEArQ= @@ -629,43 +1509,79 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN 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.3/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.2.8/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= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= 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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/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= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU= +k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= +k8s.io/client-go v0.21.0/go.mod h1:nNBytTF9qPFDEhoqgEPaarobC8QPae13bElIVHzIglA= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU= lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.33.6 h1:r63dgSzVzRxUpAJFPQWHy1QeZeY1ydNENUDaBx1GqYc= modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g= +modernc.org/cc/v3 v3.36.0 h1:0kmRkTmqNidmu3c7BNDSdVHCxXCkWLmWmCIVX4LUboo= modernc.org/ccgo/v3 v3.9.5 h1:dEuUSf8WN51rDkprFuAqjfchKEzN0WttP/Py3enBwjk= modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60= +modernc.org/ccgo/v3 v3.16.6 h1:3l18poV+iUemQ98O3X5OMr97LOqlzis+ytivU4NqGhA= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= modernc.org/libc v1.9.11 h1:QUxZMs48Ahg2F7SN41aERvMfGLY2HU/ADnB9DC4Yts8= modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q= +modernc.org/libc v1.16.7 h1:qzQtHhsZNpVPpeCu+aMIQldXeV1P0vRhSqCL0nOIJOA= modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.0 h1:GCjoRaBew8ECCKINQA2nYjzvufFW9YiEuuB+rQ9bn2E= modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU= modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sqlite v1.11.2 h1:ShWQpeD3ag/bmx6TqidBlIWonWmQaSQKls3aenCbt+w= modernc.org/sqlite v1.11.2/go.mod h1:+mhs/P1ONd+6G7hcAs6irwDi/bjTQ7nLW6LHRBsEa3A= +modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI= modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/tcl v1.5.5/go.mod h1:ADkaTUuwukkrlhqwERyq0SM8OvyXo7+TjFz7yAF56EI= modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= xorm.io/builder v0.3.9 h1:Sd65/LdWyO7LR8+Cbd+e7mm3sK/7U9k0jS3999IDHMc= xorm.io/builder v0.3.9/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= diff --git a/package.json b/package.json index 01be72d4..888e89fc 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Toni Inc" }, "name": "snmpcollector", - "version": "0.13.1", + "version": "0.14.0", "repository": { "type": "git", "url": "http://github.com/toni-moreno/snmpcollector.git" diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index 9cf1182a..97546b34 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -8,6 +8,7 @@ import ( "github.com/toni-moreno/snmpcollector/pkg/agent/bus" "github.com/toni-moreno/snmpcollector/pkg/agent/device" "github.com/toni-moreno/snmpcollector/pkg/agent/output" + "github.com/toni-moreno/snmpcollector/pkg/agent/output/backend" "github.com/toni-moreno/snmpcollector/pkg/agent/selfmon" "github.com/toni-moreno/snmpcollector/pkg/config" "github.com/toni-moreno/snmpcollector/pkg/data/stats" @@ -70,8 +71,8 @@ var ( mutex sync.RWMutex // devices is the runtime snmp devices map devices map[string]*device.SnmpDevice - // influxdb is the runtime devices output db map - influxdb map[string]*output.InfluxDB + // outputs is the runtime devices output db map + outputs map[string]*output.SinkDB selfmonProc *selfmon.SelfMon // gatherWg synchronizes device specific goroutines @@ -113,23 +114,62 @@ func CheckAndUnSetReloadProcess() bool { return retval } -// PrepareInfluxDBs initializes all configured output DBs in the SQL database. +// PrepareOutputs initializes all configured output DBs in the SQL database. // If there is no "default" key, creates a dummy output db which does nothing. -func PrepareInfluxDBs() map[string]*output.InfluxDB { - idb := make(map[string]*output.InfluxDB) - - var defFound bool - for k, c := range DBConfig.Influxdb { - if k == "default" { - defFound = true +func PrepareOutputs() map[string]*output.SinkDB { + outtemps := make(map[string]*output.SinkDB) + var err error + + // Outputs finally follows his own lifecycle and has relation with backend as 1:1 + // we need to retrieve all available outputs and retrieve the backend configuration based on map + // the influx/kafka params should be retrieved with special DBConfig as an interface + for id, out := range DBConfig.Outputs { + // load config + sinkDB := &output.SinkDB{ + Cfg: out, + } + // supported outputs - kafka, influxdb + switch out.BackendType { + case "kafka": + if bcfg, ok := DBConfig.Kafka[out.Backend]; ok { + sinkDB.Backend, err = backend.NewNotInitKafka(bcfg) + if err != nil { + log.Errorf("Unable to initialize Kafka backend - %s", out.Backend) + continue + } + } else { + log.Errorf("Kafka server not found, output %s not registered", out.ID) + continue + } + case "influxdb": + if bcfg, ok := DBConfig.Influxdb[out.Backend]; ok { + sinkDB.Backend = backend.NewNotInitInfluxDB(bcfg) + } else { + log.Errorf("Influx server not found, output %s not registered", out.ID) + continue + } } - idb[k] = output.NewNotInitInfluxDB(c) + // register on temp outputs + outtemps[id] = sinkDB } - if defFound == false { - log.Warn("No Output default found influxdb devices found !!") - idb["default"] = output.DummyDB + + if _, ok := outtemps["default"]; !ok { + log.Warn("No Output default devices found") + cfg := config.OutputCfg{ + ID: "dummy", + BackendType: "dummy", + Backend: "dummy", + Active: false, + EnqueueOnError: false, + BufferSize: 0, + } + outtemps["default"] = &output.SinkDB{ + Cfg: &cfg, + Backend: backend.NewNotInitDummyDB(), + } } - return idb + //return idb + return outtemps } // GetDevice returns the snmp device with the given id. @@ -137,14 +177,14 @@ func PrepareInfluxDBs() map[string]*output.InfluxDB { func GetDevice(id string) (*device.SnmpDevice, error) { var dev *device.SnmpDevice var ok bool - if CheckReloadProcess() == true { + if CheckReloadProcess() { log.Warning("There is a reload process running while trying to get device info") - return nil, fmt.Errorf("There is a reload process running.... please wait until finished ") + return nil, fmt.Errorf("there is a reload process running.... please wait until finished ") } mutex.RLock() defer mutex.RUnlock() if dev, ok = devices[id]; !ok { - return nil, fmt.Errorf("There is not any device with id %s running", id) + return nil, fmt.Errorf("there is not any device with id %s running", id) } return dev, nil } @@ -154,9 +194,9 @@ func GetDevice(id string) (*device.SnmpDevice, error) { func GetDeviceJSONInfo(id string) ([]byte, error) { var dev *device.SnmpDevice var ok bool - if CheckReloadProcess() == true { - log.Warning("There is a reload process running while trying to get device info") - return nil, fmt.Errorf("There is a reload process running.... please wait until finished ") + if CheckReloadProcess() { + log.Warnf("There is a reload process running while trying to get device info") + return nil, fmt.Errorf("there is a reload process running.... please wait until finished ") } mutex.RLock() defer mutex.RUnlock() @@ -177,28 +217,28 @@ func GetDevStats() map[string]*stats.GatherStats { return devstats } -// StopInfluxOut stops sending data to output influxDB servers. -func StopInfluxOut(idb map[string]*output.InfluxDB) { +// StopInfluxOut stops sending data to output outputs servers. +func StopOutputs(idb map[string]*output.SinkDB) { for k, v := range idb { - log.Infof("Stopping Influxdb out %s", k) + log.Infof("Stopping output %s", k) v.StopSender() } } -// ReleaseInfluxOut closes the influxDB connections and releases the associated resources. -func ReleaseInfluxOut(idb map[string]*output.InfluxDB) { +// ReleaseOutputs closes the output connections and releases the associated resources. +func ReleaseOutputs(idb map[string]*output.SinkDB) { for k, v := range idb { - log.Infof("Release Influxdb resources %s", k) + log.Infof("Release output resources %s", k) v.End() } } -// DeviceProcessStop stops all device polling goroutines +// DeviceProcessStop stops all device polling goroutines. func DeviceProcessStop() { Bus.Broadcast(&bus.Message{Type: bus.Exit}) } -// DeviceProcessStart starts all device polling goroutines +// DeviceProcessStart starts all device polling goroutines. func DeviceProcessStart() { mutex.Lock() devices = make(map[string]*device.SnmpDevice) @@ -213,26 +253,34 @@ func init() { go Bus.Start() } -func initSelfMonitoring(idb map[string]*output.InfluxDB) { - log.Debugf("INFLUXDB2: %+v", idb) +// initSelfMonitoring initialize the selfmon gourutine. +func initSelfMonitoring(outdbs map[string]*output.SinkDB) { + // create new selfmon selfmonProc = selfmon.NewNotInit(&MainConfig.Selfmon) + // if its enabled, try to find the default backend if MainConfig.Selfmon.Enabled { - if val, ok := idb["default"]; ok { - // only executed if a "default" influxdb exist - val.Init() - val.StartSender(&senderWg) - + if outdb, ok := outdbs["default"]; ok { + // initialize the related SinkDB and related sender + // declare our sinkdb as a container for generic output + err := outdb.Init() + if err != nil { + log.Errorf("Unable to init output - %s", err) + return + } + outdb.StartSender(&senderWg) + + // initialize the selfmon proc and attach all the outputs selfmonProc.Init() - selfmonProc.SetOutDB(idb) - selfmonProc.SetOutput(val) + selfmonProc.SetOutDB(outdbs) + selfmonProc.SetOutput(outdb) log.Printf("SELFMON enabled %+v", MainConfig.Selfmon) // Begin the statistic reporting selfmonProc.StartGather(&gatherWg) } else { MainConfig.Selfmon.Enabled = false - log.Errorf("SELFMON disabled becaouse of no default db found !!! SELFMON[ %+v ] INFLUXLIST[ %+v]\n", MainConfig.Selfmon, idb) + log.Errorf("SELFMON disabled, no default db found. Selfmon: [%+v] OutDB list: [%+v]\n", MainConfig.Selfmon, outdbs) } } else { log.Printf("SELFMON disabled %+v\n", MainConfig.Selfmon) @@ -251,12 +299,12 @@ func IsDeviceInRuntime(id string) bool { // DeleteDeviceInRuntime removes the device `id` from the runtime array. func DeleteDeviceInRuntime(id string) error { - // Avoid modifications to devices while deleting device + // Avoid modifications to devices while deleting device. mutex.Lock() defer mutex.Unlock() if dev, ok := devices[id]; ok { // Stop all device processes and its measurements. Once finished they will be removed - // from the bus and node closed (snmp connections for measurements will be closed) + // from the bus and node closed (snmp connections for measurements will be closed). dev.StopGather() log.Debugf("Bus retuned from the exit message to the ID device %s", id) delete(devices, id) @@ -268,20 +316,28 @@ func DeleteDeviceInRuntime(id string) error { // AddDeviceInRuntime initializes each SNMP device and puts the pointer to the global device map. func AddDeviceInRuntime(k string, cfg *config.SnmpDeviceCfg) { - // Initialize each SNMP device and put pointer to the global map devices + // Initialize each SNMP device and put pointer to the global map devices. dev := device.New(cfg) dev.AttachToBus(Bus) dev.InitCatalogVar(DBConfig.VarCatalog) dev.SetSelfMonitoring(selfmonProc) // send a db map to initialize each one its own db if needed - outdb, _ := dev.GetOutSenderFromMap(influxdb) - outdb.Init() + outdb, err := dev.GetOutSenderFromMap(outputs) + if err != nil { + log.Errorf("Unable to retrieve output from map - %s", err) + return + } + // declare our sinkdb as a container for generic output + err = outdb.Init() + if err != nil { + log.Errorf("Unable to init output - %s", err) + return + } outdb.StartSender(&senderWg) - mutex.Lock() devices[k] = dev - // Start gather goroutine for device and add it to the wait group for gather goroutines + // Start gather goroutine for device and add it to the wait group for gather goroutines. gatherWg.Add(1) go func() { defer gatherWg.Done() @@ -297,10 +353,10 @@ func AddDeviceInRuntime(k string, cfg *config.SnmpDeviceCfg) { // LoadConf loads the DB conf and initializes the device metric config. func LoadConf() { MainConfig.Database.LoadDbConfig(&DBConfig) - influxdb = PrepareInfluxDBs() + outputs = PrepareOutputs() // begin self monitoring process if needed, before all goroutines - initSelfMonitoring(influxdb) + initSelfMonitoring(outputs) config.InitMetricsCfg(&DBConfig) } @@ -329,11 +385,11 @@ func End() (time.Duration, error) { // log.Info("DEBUG Gather WAIT %+v", GatherWg) // log.Info("DEBUG SENDER WAIT %+v", senderWg) // stop all Output Emitter - StopInfluxOut(influxdb) + StopOutputs(outputs) log.Info("END: waiting for all Sender goroutines stop..") senderWg.Wait() log.Info("END: releasing Sender Resources") - ReleaseInfluxOut(influxdb) + ReleaseOutputs(outputs) log.Infof("END: Finished from %s to %s [Duration : %s]", start.String(), time.Now().String(), time.Since(start).String()) return time.Since(start), nil } @@ -341,9 +397,9 @@ func End() (time.Duration, error) { // ReloadConf stops the polling, reloads all configuration and restart the polling. func ReloadConf() (time.Duration, error) { start := time.Now() - if CheckAndSetReloadProcess() == true { + if CheckAndSetReloadProcess() { log.Warnf("RELOADCONF: There is another reload process running while trying to reload at %s ", start.String()) - return time.Since(start), fmt.Errorf("There is another reload process running.... please wait until finished ") + return time.Since(start), fmt.Errorf("there is another reload process running.... please wait until finished ") } log.Infof("RELOADCONF INIT: begin device Gather processes stop... at %s", start.String()) diff --git a/pkg/agent/device/snmpdevice.go b/pkg/agent/device/snmpdevice.go index f407569c..cb05cfd7 100644 --- a/pkg/agent/device/snmpdevice.go +++ b/pkg/agent/device/snmpdevice.go @@ -53,7 +53,7 @@ type SnmpDevice struct { // Variable map VarMap map[string]interface{} // Influx client shared between measurement goroutines to send data and stats to the backend - Influx *output.InfluxDB `json:"-"` + SinkDB *output.SinkDB `json:"-"` // LastError time.Time // Runtime stats stats stats.GatherStats // Runtime Internal statistic @@ -161,21 +161,21 @@ func (d *SnmpDevice) getBasicStats() *stats.GatherStats { } // GetOutSenderFromMap to get info about the sender will use -func (d *SnmpDevice) GetOutSenderFromMap(influxdb map[string]*output.InfluxDB) (*output.InfluxDB, error) { +func (d *SnmpDevice) GetOutSenderFromMap(outputs map[string]*output.SinkDB) (*output.SinkDB, error) { if len(d.cfg.OutDB) == 0 { d.Warnf("No OutDB configured on the device") } var ok bool name := d.cfg.OutDB - if d.Influx, ok = influxdb[name]; !ok { + if d.SinkDB, ok = outputs[name]; !ok { // we assume there is always a default db - if d.Influx, ok = influxdb["default"]; !ok { + if d.SinkDB, ok = outputs["default"]; !ok { // but - return nil, fmt.Errorf("No influx config for snmp device: %s", d.cfg.ID) + return nil, fmt.Errorf("no output config for snmp device: %s", d.cfg.ID) } } - return d.Influx, nil + return d.SinkDB, nil } // ForceGather send message to force a data gather execution @@ -533,7 +533,7 @@ func (d *SnmpDevice) StartGather() { } // Start the loop that will gather metrics and handle signals - m.GatherLoop(node, snmpClient, d.Freq, d.cfg.UpdateFltFreq, d.VarMap, d.TagMap, d.cfg.SystemOIDs, d.Influx, gatherLock) + m.GatherLoop(node, snmpClient, d.Freq, d.cfg.UpdateFltFreq, d.VarMap, d.TagMap, d.cfg.SystemOIDs, d.SinkDB, gatherLock) // If measurement exists, remove it from the bus, close the created node and the snmp connection deviceControlBus.Leave(node) diff --git a/pkg/agent/output/backend/backend.go b/pkg/agent/output/backend/backend.go new file mode 100644 index 00000000..91258f40 --- /dev/null +++ b/pkg/agent/output/backend/backend.go @@ -0,0 +1,12 @@ +package backend + +import "github.com/sirupsen/logrus" + +var ( + log *logrus.Logger +) + +// SetLogger adds a logger to this module +func SetLogger(l *logrus.Logger) { + log = l +} diff --git a/pkg/agent/output/backend/dummy.go b/pkg/agent/output/backend/dummy.go new file mode 100644 index 00000000..d77a3af2 --- /dev/null +++ b/pkg/agent/output/backend/dummy.go @@ -0,0 +1,24 @@ +package backend + +import "github.com/influxdata/telegraf" + +// Declare DummyBackend +type DummyBackend struct { + ID string +} + +func NewNotInitDummyDB() *DummyBackend { + return &DummyBackend{ID: "dummy"} +} + +func (db *DummyBackend) Write([]telegraf.Metric) error { + return nil +} + +func (db *DummyBackend) Connect() error { + return nil +} + +func (db *DummyBackend) Close() error { + return nil +} diff --git a/pkg/agent/output/backend/influx.go b/pkg/agent/output/backend/influx.go new file mode 100644 index 00000000..dc35bb6e --- /dev/null +++ b/pkg/agent/output/backend/influx.go @@ -0,0 +1,182 @@ +package backend + +import ( + "fmt" + "net/http" + "time" + + client "github.com/influxdata/influxdb1-client/v2" + "github.com/influxdata/telegraf" + "github.com/toni-moreno/snmpcollector/pkg/config" + "github.com/toni-moreno/snmpcollector/pkg/data/utils" +) + +/*InfluxDB database export */ +type InfluxDB struct { + cfg *config.InfluxCfg + dummy bool + client client.Client +} + +// DummyDB a BD struct needed if no database configured +var DummyDB = &InfluxDB{ + cfg: nil, + dummy: true, + // iChan: nil, + // chExit: nil, + client: nil, +} + +// BP create a Batch point influx object +func (db *InfluxDB) BP() (*client.BatchPoints, error) { + if db.dummy { + bp, _ := client.NewBatchPoints(client.BatchPointsConfig{ + Database: "dbdummy", + RetentionPolicy: "dbretention", + Precision: "ns", //Default precision for Time lib + }) + return &bp, nil + } + // + if len(db.cfg.Retention) == 0 { + db.cfg.Retention = "autogen" + } + bp, err := client.NewBatchPoints(client.BatchPointsConfig{ + Database: db.cfg.DB, + RetentionPolicy: db.cfg.Retention, + Precision: db.cfg.Precision, //Default precision for Time lib + }) + if err != nil { + log.Errorf("Error on create BatchPoint: %s", err) + return nil, err + } + return &bp, err +} + +// Ping InfluxDB Server +func Ping(cfg *config.InfluxCfg) (client.Client, time.Duration, string, error) { + + var conf client.HTTPConfig + if cfg.EnableSSL { + tls, err := utils.GetTLSConfig(cfg.SSLCert, cfg.SSLKey, cfg.SSLCA, cfg.InsecureSkipVerify) + if err != nil { + log.Errorf("Error on Create TLS config: %s", err) + return nil, 0, "", err + } + conf = client.HTTPConfig{ + Addr: fmt.Sprintf("https://%s:%d", cfg.Host, cfg.Port), + Username: cfg.User, + Password: cfg.Password, + UserAgent: cfg.UserAgent, + Timeout: time.Duration(cfg.Timeout) * time.Second, + TLSConfig: tls, + Proxy: http.ProxyFromEnvironment, + } + } else { + + conf = client.HTTPConfig{ + Addr: fmt.Sprintf("http://%s:%d", cfg.Host, cfg.Port), + Username: cfg.User, + Password: cfg.Password, + UserAgent: cfg.UserAgent, + Timeout: time.Duration(cfg.Timeout) * time.Second, + Proxy: http.ProxyFromEnvironment, + } + } + cli, err := client.NewHTTPClient(conf) + + if err != nil { + return cli, 0, "", err + } + elapsed, message, err := cli.Ping(time.Duration(cfg.Timeout) * time.Second) + log.Infof("PING Influx Database %s : Elapsed ( %s ) : MSG : %s", cfg.ID, elapsed.String(), message) + return cli, elapsed, message, err +} + +// NewNotInitInfluxDB Create Object in memory but not initialized until ready connection needed +func NewNotInitInfluxDB(c *config.InfluxCfg) *InfluxDB { + return &InfluxDB{ + cfg: c, + dummy: false, + } +} + +// TimeWriteRetry time wait +const TimeWriteRetry = 10 + +// Init initialies runtime info +func (db *InfluxDB) Connect() error { + var err error + + if len(db.cfg.UserAgent) == 0 { + db.cfg.UserAgent = "snmpCollector-" + db.cfg.ID + } + + log.Infof("Initializing influxdb with id = [ %s ]. Connecting to: %s", db.cfg.ID, db.cfg.Host) + db.client, _, _, err = Ping(db.cfg) + if err != nil { + log.Errorf("failed connecting to: %s - error: %s", db.cfg.Host, err) + return err + } + + log.Infof("successfully connected to %s", db.cfg.Host) + return nil +} + +// End release DB connection +func (db *InfluxDB) Close() error { + err := db.client.Close() + return err +} + +func (db *InfluxDB) Write(metrics []telegraf.Metric) error { + var ptarray []*client.Point + // do we create a batchpoint and keep with the same process??... + for _, dm := range metrics { + pt, err := client.NewPoint(dm.Name(), dm.Tags(), dm.Fields(), dm.Time()) + if err != nil { + log.Errorf("Cannot create influx point - %s", err) + } + ptarray = append(ptarray, pt) + } + // create batchpoints + bpts, err := db.BP() + if err != nil { + log.Errorf("Cannot create batchpoints - %s", err) + return err + } + if bpts != nil { + (*bpts).AddPoints(ptarray) + // send data + err := db.WriteMetrics(bpts, true) + if err != nil { + return err + } + } + return nil +} + +func (db *InfluxDB) WriteMetrics(data *client.BatchPoints, enqueueonerror bool) error { + //number points + np := len((*data).Points()) + //number of total fields + nf := 0 + for _, v := range (*data).Points() { + fields, err := v.Fields() + if err != nil { + log.Debug("Some error happened when trying to get fields for point... ") + } else { + nf += len(fields) + } + } + // keep trying until we get it (don't drop the data) + startSend := time.Now() + err := db.client.Write(*data) + elapsedSend := time.Since(startSend) + if err != nil { + log.Errorf("ERROR on Write batchPoint in DB %s (%d points) | elapsed : %s | Error: %s ", db.cfg.ID, np, elapsedSend.String(), err) + return err + } + log.Debugf("OK on Write batchPoint in DB %s (%d points) | elapsed : %s ", db.cfg.ID, np, elapsedSend.String()) + return err +} diff --git a/pkg/agent/output/backend/kafka.go b/pkg/agent/output/backend/kafka.go new file mode 100644 index 00000000..5ad39023 --- /dev/null +++ b/pkg/agent/output/backend/kafka.go @@ -0,0 +1,316 @@ +package backend + +// The MIT License (MIT) +// Copyright (c) 2015-2020 InfluxData Inc. + +import ( + _ "embed" + "fmt" + "strings" + "time" + + "github.com/Shopify/sarama" + "github.com/gofrs/uuid" + "github.com/sirupsen/logrus" + "github.com/toni-moreno/snmpcollector/pkg/config" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/common/kafka" + "github.com/influxdata/telegraf/plugins/common/proxy" + "github.com/influxdata/telegraf/plugins/common/tls" + "github.com/influxdata/telegraf/plugins/serializers" +) + +var sampleConfig string + +var ValidTopicSuffixMethods = []string{ + "", + "measurement", + "tags", +} + +var zeroTime = time.Unix(0, 0) + +func NewNotInitKafka(c *config.KafkaCfg) (*Kafka, error) { + + sc := &serializers.Config{TimestampUnits: 1 * time.Second} + sc.DataFormat = "json" + ssc, err := serializers.NewSerializer(sc) + if err != nil { + log.Errorf("unable to set serializer: %s", err) + return nil, err + } + + // build own telegraf kafka config from plain struct + tlsconf := tls.ClientConfig{ + TLSCA: c.TLSCA, + TLSCert: c.TLSCert, + TLSKey: c.TLSKey, + TLSKeyPwd: c.TLSKeyPwd, + TLSMinVersion: c.TLSMinVersion, + InsecureSkipVerify: c.InsecureSkipVerify, + ServerName: c.ServerName, + } + conf := kafka.Config{ + ClientConfig: tlsconf, + Version: c.Version, + CompressionCodec: c.CompressionCodec, + EnableTLS: &c.EnableTLS, + MetadataFull: &c.MetadataFull, + } + + tsuffix := TopicSuffix{ + Method: c.Method, + Keys: c.Keys, + Separator: c.Separator, + } + + kafkaserver := &Kafka{ + Brokers: c.Brokers, + Topic: c.Topic, + ExcludeTopicTag: c.ExcludeTopicTag, + TopicTag: c.TopicTag, + RoutingTag: c.RoutingTag, + RoutingKey: c.RoutingKey, + TopicSuffix: tsuffix, + WriteConfig: kafka.WriteConfig{ + Config: conf, + MaxRetry: c.MaxRetry, + RequiredAcks: c.RequiredAcks, + }, + producerFunc: sarama.NewSyncProducer, + Log: *log, + } + kafkaserver.SetSerializer(ssc) + return kafkaserver, nil +} + +type Kafka struct { + Brokers []string + Topic string + TopicTag string + ExcludeTopicTag bool + TopicSuffix TopicSuffix + RoutingTag string + RoutingKey string + + proxy.Socks5ProxyConfig + + // Legacy TLS config options + // TLS client certificate + Certificate string + // TLS client key + Key string + // TLS certificate authority + CA string + + kafka.WriteConfig + + Log logrus.Logger + + saramaConfig *sarama.Config + producerFunc func(addrs []string, config *sarama.Config) (sarama.SyncProducer, error) + producer sarama.SyncProducer + + serializer serializers.Serializer +} + +type TopicSuffix struct { + Method string + Keys []string + Separator string +} + +// DebugLogger logs messages from sarama at the debug level. +type DebugLogger struct { + Log *logrus.Logger +} + +func (l *DebugLogger) Print(v ...interface{}) { + args := make([]interface{}, 0, len(v)+1) + args = append(append(args, "[sarama] "), v...) + log.Debug(args...) +} + +func (l *DebugLogger) Printf(format string, v ...interface{}) { + log.Debugf("[sarama] "+format, v...) +} + +func (l *DebugLogger) Println(v ...interface{}) { + l.Print(v) +} + +func ValidateTopicSuffixMethod(method string) error { + for _, validMethod := range ValidTopicSuffixMethods { + if method == validMethod { + return nil + } + } + return fmt.Errorf("unknown topic suffix method provided: %s", method) +} + +func (*Kafka) SampleConfig() string { + return sampleConfig +} + +func (k *Kafka) GetTopicName(metric telegraf.Metric) (telegraf.Metric, string) { + topic := k.Topic + if k.TopicTag != "" { + if t, ok := metric.GetTag(k.TopicTag); ok { + topic = t + + // If excluding the topic tag, a copy is required to avoid modifying + // the metric buffer. + if k.ExcludeTopicTag { + metric = metric.Copy() + metric.Accept() + metric.RemoveTag(k.TopicTag) + } + } + } + + var topicName string + switch k.TopicSuffix.Method { + case "measurement": + topicName = topic + k.TopicSuffix.Separator + metric.Name() + case "tags": + var topicNameComponents []string + topicNameComponents = append(topicNameComponents, topic) + for _, tag := range k.TopicSuffix.Keys { + tagValue := metric.Tags()[tag] + if tagValue != "" { + topicNameComponents = append(topicNameComponents, tagValue) + } + } + topicName = strings.Join(topicNameComponents, k.TopicSuffix.Separator) + default: + topicName = topic + } + return metric, topicName +} + +func (k *Kafka) SetSerializer(serializer serializers.Serializer) { + k.serializer = serializer +} + +func (k *Kafka) Init() error { + sarama.Logger = &DebugLogger{Log: log} + + err := ValidateTopicSuffixMethod(k.TopicSuffix.Method) + if err != nil { + return err + } + config := sarama.NewConfig() + + if err := k.SetConfig(config); err != nil { + return err + } + + k.saramaConfig = config + + // Legacy support ssl config + if k.Certificate != "" { + k.TLSCert = k.Certificate + k.TLSCA = k.CA + k.TLSKey = k.Key + } + + if k.Socks5ProxyEnabled { + config.Net.Proxy.Enable = true + + dialer, err := k.Socks5ProxyConfig.GetDialer() + if err != nil { + return fmt.Errorf("connecting to proxy server failed: %s", err) + } + config.Net.Proxy.Dialer = dialer + } + + return nil +} + +func (k *Kafka) Connect() error { + k.Init() + producer, err := k.producerFunc(k.Brokers, k.saramaConfig) + if err != nil { + return err + } + k.producer = producer + return nil +} + +func (k *Kafka) Close() error { + return k.producer.Close() +} + +func (k *Kafka) routingKey(metric telegraf.Metric) (string, error) { + if k.RoutingTag != "" { + key, ok := metric.GetTag(k.RoutingTag) + if ok { + return key, nil + } + } + + if k.RoutingKey == "random" { + u, err := uuid.NewV4() + if err != nil { + return "", err + } + return u.String(), nil + } + + return k.RoutingKey, nil +} + +func (k *Kafka) Write(metrics []telegraf.Metric) error { + msgs := make([]*sarama.ProducerMessage, 0, len(metrics)) + for _, metric := range metrics { + metric, topic := k.GetTopicName(metric) + + buf, err := k.serializer.Serialize(metric) + if err != nil { + log.Debugf("Could not serialize metric: %v", err) + continue + } + + m := &sarama.ProducerMessage{ + Topic: topic, + Value: sarama.ByteEncoder(buf), + } + + // Negative timestamps are not allowed by the Kafka protocol. + if !metric.Time().Before(zeroTime) { + m.Timestamp = metric.Time() + } + + key, err := k.routingKey(metric) + if err != nil { + return fmt.Errorf("could not generate routing key: %v", err) + } + + if key != "" { + m.Key = sarama.StringEncoder(key) + } + msgs = append(msgs, m) + } + + err := k.producer.SendMessages(msgs) + if err != nil { + // We could have many errors, return only the first encountered. + if errs, ok := err.(sarama.ProducerErrors); ok { + for _, prodErr := range errs { + if prodErr.Err == sarama.ErrMessageSizeTooLarge { + k.Log.Error("Message too large, consider increasing `max_message_bytes`; dropping batch") + return nil + } + if prodErr.Err == sarama.ErrInvalidTimestamp { + k.Log.Error("The timestamp of the message is out of acceptable range, consider increasing broker `message.timestamp.difference.max.ms`; dropping batch") + return nil + } + return prodErr //nolint:staticcheck // Return first error encountered + } + } + return err + } + + return nil +} diff --git a/pkg/agent/output/influx.go b/pkg/agent/output/influx.go deleted file mode 100644 index 6f13f4b3..00000000 --- a/pkg/agent/output/influx.go +++ /dev/null @@ -1,355 +0,0 @@ -package output - -import ( - "fmt" - "math/rand" - "net/http" - "strings" - "sync" - "time" - - client "github.com/influxdata/influxdb1-client/v2" - "github.com/sirupsen/logrus" - "github.com/toni-moreno/snmpcollector/pkg/config" - "github.com/toni-moreno/snmpcollector/pkg/data/utils" -) - -var ( - log *logrus.Logger -) - -// SetLogger adds a logger to this module -func SetLogger(l *logrus.Logger) { - log = l -} - -/*InfluxDB database export */ -type InfluxDB struct { - cfg *config.InfluxCfg - stats InfluxStats - initialized bool - imutex sync.Mutex - started bool - smutex sync.Mutex - - dummy bool - iChan chan *client.BatchPoints - chExit chan bool - client client.Client -} - -// DummyDB a BD struct needed if no database configured -var DummyDB = &InfluxDB{ - cfg: nil, - initialized: false, - started: false, - dummy: true, - iChan: nil, - chExit: nil, - client: nil, -} - -// GetResetStats return outdb stats and reset its counters -func (db *InfluxDB) GetResetStats() *InfluxStats { - if db.dummy == true { - log.Debug("Reseting Influxstats for DUMMY DB ") - return &InfluxStats{} - } - log.Debugf("Reseting Influxstats for DB %s", db.cfg.ID) - return db.stats.GetResetStats() -} - -//BP create a Batch point influx object -func (db *InfluxDB) BP() (*client.BatchPoints, error) { - if db.dummy == true { - bp, _ := client.NewBatchPoints(client.BatchPointsConfig{ - Database: "dbdummy", - RetentionPolicy: "dbretention", - Precision: "ns", //Default precision for Time lib - }) - return &bp, nil - } - // - if len(db.cfg.Retention) == 0 { - db.cfg.Retention = "autogen" - } - bp, err := client.NewBatchPoints(client.BatchPointsConfig{ - Database: db.cfg.DB, - RetentionPolicy: db.cfg.Retention, - Precision: db.cfg.Precision, //Default precision for Time lib - }) - if err != nil { - log.Errorf("Error on create BatchPoint: %s", err) - return nil, err - } - return &bp, err -} - -// Ping InfluxDB Server -func Ping(cfg *config.InfluxCfg) (client.Client, time.Duration, string, error) { - - var conf client.HTTPConfig - if cfg.EnableSSL { - tls, err := utils.GetTLSConfig(cfg.SSLCert, cfg.SSLKey, cfg.SSLCA, cfg.InsecureSkipVerify) - if err != nil { - log.Errorf("Error on Create TLS config: %s", err) - return nil, 0, "", err - } - conf = client.HTTPConfig{ - Addr: fmt.Sprintf("https://%s:%d", cfg.Host, cfg.Port), - Username: cfg.User, - Password: cfg.Password, - UserAgent: cfg.UserAgent, - Timeout: time.Duration(cfg.Timeout) * time.Second, - TLSConfig: tls, - Proxy: http.ProxyFromEnvironment, - } - } else { - - conf = client.HTTPConfig{ - Addr: fmt.Sprintf("http://%s:%d", cfg.Host, cfg.Port), - Username: cfg.User, - Password: cfg.Password, - UserAgent: cfg.UserAgent, - Timeout: time.Duration(cfg.Timeout) * time.Second, - Proxy: http.ProxyFromEnvironment, - } - } - cli, err := client.NewHTTPClient(conf) - - if err != nil { - return cli, 0, "", err - } - elapsed, message, err := cli.Ping(time.Duration(cfg.Timeout) * time.Second) - log.Infof("PING Influx Database %s : Elapsed ( %s ) : MSG : %s", cfg.ID, elapsed.String(), message) - return cli, elapsed, message, err -} - -//Connect to influxdb -func (db *InfluxDB) Connect() error { - if db.dummy == true { - return nil - } - var err error - db.client, _, _, err = Ping(db.cfg) - return err -} - -// CheckAndSetStarted check if this thread is already working and set if not -func (db *InfluxDB) CheckAndSetStarted() bool { - db.smutex.Lock() - defer db.smutex.Unlock() - retval := db.started - db.started = true - return retval -} - -// CheckAndUnSetStarted check if this thread is already working and unset if not -func (db *InfluxDB) CheckAndUnSetStarted() bool { - db.smutex.Lock() - defer db.smutex.Unlock() - retval := db.started - db.started = false - return retval -} - -// IsStarted check if this thread is already working -func (db *InfluxDB) IsStarted() bool { - db.smutex.Lock() - defer db.smutex.Unlock() - return db.started -} - -// SetStartedAs change started state -func (db *InfluxDB) SetStartedAs(st bool) { - db.smutex.Lock() - defer db.smutex.Unlock() - db.started = st -} - -// CheckAndSetInitialized check if this thread is already working and set if not -func (db *InfluxDB) CheckAndSetInitialized() bool { - db.imutex.Lock() - defer db.imutex.Unlock() - retval := db.initialized - db.initialized = true - return retval -} - -// CheckAndUnSetInitialized check if this thread is already working and set if not -func (db *InfluxDB) CheckAndUnSetInitialized() bool { - db.imutex.Lock() - defer db.imutex.Unlock() - retval := db.initialized - db.initialized = false - return retval -} - -// NewNotInitInfluxDB Create Object in memory but not initialized until ready connection needed -func NewNotInitInfluxDB(c *config.InfluxCfg) *InfluxDB { - return &InfluxDB{ - cfg: c, - dummy: false, - started: false, - } -} - -// TimeWriteRetry time wait -const TimeWriteRetry = 10 - -//Init initialies runtime info -func (db *InfluxDB) Init() { - if db.dummy == true { - return - } - - if db.CheckAndSetInitialized() == true { - log.Infof("Sender thread to : %s already Initialized (skipping Initialization)", db.cfg.ID) - return - } - - if len(db.cfg.UserAgent) == 0 { - db.cfg.UserAgent = "snmpCollector-" + db.cfg.ID - } - - log.Infof("Initializing influxdb with id = [ %s ]", db.cfg.ID) - - log.Infof("Connecting to: %s", db.cfg.Host) - db.iChan = make(chan *client.BatchPoints, db.cfg.BufferSize) - db.chExit = make(chan bool) - if err := db.Connect(); err != nil { - log.Errorln("failed connecting to: ", db.cfg.Host) - log.Errorln("error: ", err) - //if no connection done started = false and it will try to test again later?? - return - } - - log.Infof("Connected to: %s", db.cfg.Host) -} - -// End release DB connection -func (db *InfluxDB) End() { - if db.dummy == true { - return - } - if db.CheckAndUnSetInitialized() == true { - close(db.iChan) - close(db.chExit) - db.client.Close() - } -} - -// StopSender finalize sender goroutines -func (db *InfluxDB) StopSender() { - if db.dummy == true { - return - } - - if db.IsStarted() == true { - db.chExit <- true - return - } - - log.Infof("Can not stop Sender [%s] becaouse of it is already stopped", db.cfg.ID) -} - -//Send send data -func (db *InfluxDB) Send(bps *client.BatchPoints) { - if db.dummy == true { - return - } - db.iChan <- bps -} - -//Hostname get hostname -func (db *InfluxDB) Hostname() string { - return strings.Split(db.cfg.Host, ":")[0] -} - -// StartSender begins sender loop -func (db *InfluxDB) StartSender(wg *sync.WaitGroup) { - if db.dummy == true { - return - } - if db.CheckAndSetStarted() == true { - log.Infof("Sender thread to : %s already started (skipping Goroutine creation)", db.cfg.ID) - return - } - wg.Add(1) - go db.startSenderGo(rand.Int(), wg) -} - -func (db *InfluxDB) sendBatchPoint(data *client.BatchPoints, enqueueonerror bool) { - var bufferPercent float32 - //number points - np := len((*data).Points()) - //number of total fields - nf := 0 - for _, v := range (*data).Points() { - fields, err := v.Fields() - if err != nil { - log.Debug("Some error happened when trying to get fields for point... ") - } else { - nf += len(fields) - } - } - // keep trying until we get it (don't drop the data) - startSend := time.Now() - err := db.client.Write(*data) - elapsedSend := time.Since(startSend) - - bufferPercent = (float32(len(db.iChan)) * 100.0) / float32(db.cfg.BufferSize) - if err != nil { - db.stats.WriteErrUpdate(elapsedSend, bufferPercent) - log.Errorf("ERROR on Write batchPoint in DB %s (%d points) | elapsed : %s | Error: %s ", db.cfg.ID, np, elapsedSend.String(), err) - // If the queue is not full we will resend after a while - if enqueueonerror { - log.Debug("queing data again...") - if len(db.iChan) < db.cfg.BufferSize { - db.iChan <- data - time.Sleep(TimeWriteRetry * time.Second) - } - } - } else { - log.Debugf("OK on Write batchPoint in DB %s (%d points) | elapsed : %s ", db.cfg.ID, np, elapsedSend.String()) - db.stats.WriteOkUpdate(int64(np), int64(nf), elapsedSend, bufferPercent) - } -} - -func (db *InfluxDB) startSenderGo(r int, wg *sync.WaitGroup) { - defer wg.Done() - - time.Sleep(5) - log.Infof("beginning Influx Sender thread: [%s]", db.cfg.ID) - for { - select { - case <-db.chExit: - //need to flush all data - - chanlen := len(db.iChan) // get number of entries in the batchpoint channel - log.Infof("Flushing %d batchpoints of data in OutDB %s ", chanlen, db.cfg.ID) - for i := 0; i < chanlen; i++ { - //flush them - data := <-db.iChan - //this process only will work if backend is running ok elsewhere points will be lost - db.sendBatchPoint(data, false) - } - - log.Infof("EXIT from Influx sender process for device [%s] ", db.cfg.ID) - db.SetStartedAs(false) - return - case data := <-db.iChan: - if data == nil { - log.Warn("null influx input") - continue - } - if db.client == nil { - log.Warn("db Client not initialized yet!!!!!") - continue - } - - db.sendBatchPoint(data, true) - - } - } -} diff --git a/pkg/agent/output/sinkdb.go b/pkg/agent/output/sinkdb.go new file mode 100644 index 00000000..609e306b --- /dev/null +++ b/pkg/agent/output/sinkdb.go @@ -0,0 +1,379 @@ +package output + +import ( + "fmt" + "math/rand" + "sync" + "time" + + "github.com/influxdata/telegraf" + models "github.com/influxdata/telegraf/models" + "github.com/sirupsen/logrus" + "github.com/toni-moreno/snmpcollector/pkg/config" +) + +var ( + log *logrus.Logger +) + +// SetLogger adds a logger to this module +func SetLogger(l *logrus.Logger) { + log = l +} + +// Output interface defines the required methods to be considered as an valid snmpcollector output +type Backend interface { + Write([]telegraf.Metric) error + Connect() error + Close() error +} + +type SinkDB struct { + Cfg *config.OutputCfg + stats SinkDBStats + tick *time.Ticker + chExit chan bool + buffer *models.Buffer + Backend Backend + // statuses + initialized bool + started bool + connected bool + // need to define special closed status due to several calls on exit + // produces a panic on already closed channels (internal backend logic) + closed bool + imutex sync.Mutex + smutex sync.Mutex + cmutex sync.Mutex + ccmutex sync.Mutex + // special dummy state... + dummy bool +} + +// GetResetStats return outdb stats and reset its counters +func (db *SinkDB) GetResetStats() *SinkDBStats { + if db.dummy { + return &SinkDBStats{} + } + log.Infof("%s [%s-%s] - Stats: Reseting SinkDBStats for DB %s", db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType, db.Cfg.ID) + stt := db.stats.GetResetStats() + log.Infof("%s [%s-%s] - Stats: stats added to buffer: %+v", db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType, stt) + return stt +} + +// Init initializes the needed SinkDB resources and tries the backend connection +// it registers the different sinkdb statuses: +// - initialized - init has been called, no more inits should be used, even if the connection is unsuccessfull +// - started - the sender is being called and threaded within a goroutine, as it is called from external funcionts +// - connected - the output has been succesfully connected and it is registered to be able to write metrics +func (db *SinkDB) Init() error { + + if db.Backend == nil { + return fmt.Errorf("no output found") + } + // enforces the initialization to guarantee that init is called once + chset := db.CheckAndSetInitialized() + if chset { + log.Infof("%s [%s-%s] - Initialized - Skipping, sender thread already initialized", db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType) + return nil + } + // create only once the buffer, buffer registers internal stats based on the touple - id + backend name + // the ID of the SinkDB config is unique, so it is guaranteed + db.buffer = models.NewBuffer(db.Cfg.ID, db.Cfg.Backend, db.Cfg.BufferSize) + + // create needed channels and initialize the ticker + // channels to interact with the go sender + db.chExit = make(chan bool) + if db.Cfg.FlushInterval == 0 { + log.Warnf("retrieved a flushinterval=0, forcing to 30s") + db.Cfg.FlushInterval = 30 + } + db.tick = time.NewTicker(time.Duration(db.Cfg.FlushInterval) * time.Second) + + // tries to connect based on generic method and mark the connected status based on the result + err := db.Backend.Connect() + if err != nil { + log.Errorf("Unable to connect with backend %s - %s", db.Cfg.Backend, db.Cfg.BackendType) + db.CheckAndUnSetConnected() + // the retrieved error should be passed to top, as we will try to reconnect during the flush interval ticker + return nil + } + db.CheckAndSetConnected() + return nil +} + +// End release DB connection +func (db *SinkDB) End() { + if db.CheckAndUnSetInitialized() { + close(db.chExit) + db.tick.Stop() + if !db.IsClosed() { + err := db.Backend.Close() + if err != nil { + log.Errorf("unable to close output %s, error: %s", db.Cfg.ID, err) + } + db.CheckAndSetClosed() + } + } +} + +// CheckAndSetConnected forces the backend status to connected and returns the last status +func (db *SinkDB) CheckAndSetConnected() bool { + log.Debugf("Connected set as true") + db.cmutex.Lock() + defer db.cmutex.Unlock() + retval := db.connected + db.connected = true + return retval +} + +// CheckAndUnSetConnected force the backend status to unconnected and returns the last status +func (db *SinkDB) CheckAndUnSetConnected() bool { + db.cmutex.Lock() + defer db.cmutex.Unlock() + retval := db.connected + db.connected = false + return retval +} + +// IsConnected check if the backend backend is already connected +func (db *SinkDB) IsConnected() bool { + db.cmutex.Lock() + defer db.cmutex.Unlock() + return db.connected +} + +// CheckAndSetInitialized forces the backend status to initialized and returns the last status +func (db *SinkDB) CheckAndSetInitialized() bool { + db.imutex.Lock() + defer db.imutex.Unlock() + retval := db.initialized + db.initialized = true + return retval +} + +// CheckAndUnSetInitialized forces the backend status to non-initialized and returns the last status +func (db *SinkDB) CheckAndUnSetInitialized() bool { + db.imutex.Lock() + defer db.imutex.Unlock() + retval := db.initialized + db.initialized = false + return retval +} + +// IsInitialized check if the backend backend is already initialized +func (db *SinkDB) IsInitialized() bool { + db.imutex.Lock() + defer db.imutex.Unlock() + return db.initialized +} + +// CheckAndSetInitialized forces the backend status to started and returns the last status +func (db *SinkDB) CheckAndSetStarted() bool { + db.smutex.Lock() + defer db.smutex.Unlock() + retval := db.started + db.started = true + return retval +} + +// CheckAndSetInitialized forces the backend status to not-started and returns the last status +func (db *SinkDB) CheckAndUnSetStarted() bool { + db.smutex.Lock() + defer db.smutex.Unlock() + retval := db.started + db.started = false + return retval +} + +// IsStarted check if the backend backend is already started +func (db *SinkDB) IsStarted() bool { + db.smutex.Lock() + defer db.smutex.Unlock() + return db.started +} + +// CheckAndSetInitialized forces the backend status to started and returns the last status +func (db *SinkDB) CheckAndSetClosed() bool { + db.ccmutex.Lock() + defer db.ccmutex.Unlock() + retval := db.closed + db.closed = true + return retval +} + +// CheckAndSetInitialized forces the backend status to not-started and returns the last status +func (db *SinkDB) CheckAndUnSetClosed() bool { + db.ccmutex.Lock() + defer db.ccmutex.Unlock() + retval := db.closed + db.closed = false + return retval +} + +// IsStarted check if the backend backend is already started +func (db *SinkDB) IsClosed() bool { + db.ccmutex.Lock() + defer db.ccmutex.Unlock() + return db.started +} + +// StopSender finalize sender goroutines +func (db *SinkDB) StopSender() { + if db.IsStarted() { + db.chExit <- true + return + } + log.Infof("Cannot stop sender [%s] - it's already stopped", db.Cfg.ID) +} + +// SendToBuffer retrieves the telegraf metrics fom the measurements or selfmon to add them into the buffer +func (db *SinkDB) SendToBuffer(metrics []telegraf.Metric, origin string) (int, error) { + // check for dummy and skip buffer send + if db.Cfg.BackendType == "dummy" { + return 0, nil + } + // check for enqueueonerror and active + // if active = false and enqueueonerror = false, we should not add metrics to the buffer + // metrics are considered as discarded + if !db.Cfg.Active && !db.Cfg.EnqueueOnError { + log.Warnf("%s - Skipped metrics, SinkDB is disabled and EnqueueOnError is set to false", origin) + return len(metrics), nil + } + log.Debugf("Sending %d metrics from [%s] to the buffer", len(metrics), origin) + dropped := db.buffer.Add(metrics...) + if dropped > 0 { + // need to replicate the log to appear on stdout and on specific device log + log.Errorf("unable to add metrics to the buffer, dropped %d of total %d metrics from %s", dropped, len(metrics), origin) + return dropped, fmt.Errorf("unable to add metrics to the buffer, dropped %d metrics of total %d from %s", dropped, len(metrics), origin) + } + + return len(metrics), nil +} + +// StartSender starts the sender goroutine only once, this method is called on each device initialization +func (db *SinkDB) StartSender(wg *sync.WaitGroup) { + if db.CheckAndSetStarted() { + log.Infof("%s [%s-%s] - Started - Skipping, sender thread already started", db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType) + return + } + wg.Add(1) + go db.startSenderGo(rand.Int(), wg) +} + +func (db *SinkDB) startSenderGo(r int, wg *sync.WaitGroup) { + defer wg.Done() + + log.Infof("%s [%s-%s] - Beginning Sender thread", db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType) + + // start the loop + for { + select { + case <-db.chExit: + if !db.IsConnected() { + log.Infof("Output - %s is not connected, try to connect again", db.Cfg.ID) + if err := db.Backend.Connect(); err == nil { + db.CheckAndSetConnected() + log.Infof("Output - %s successfully connected!", db.Cfg.ID) + } else { + log.Errorf("Output - %s failed trying to connect on this iteration, exitting without flushing data: %s", db.Cfg.ID, err) + return + } + } + // TODO: pending to force a full flush without batches + // single batch with full size? + t := time.Now() + nBuffer := db.buffer.Len() + nBatches := nBuffer/db.Cfg.BufferSize + 1 + for i := 0; i < nBatches; i++ { + mett := db.buffer.Batch(db.Cfg.MetricBatchSize) + log.Debugf("%s [%s-%s] - Selected Batchsize of - %d [%s]", db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType, db.buffer.Len(), t) + // avoid to write 0 metrics + if len(mett) == 0 { + break + } + err := db.Backend.Write(mett) + if err != nil { + // if error happens, we should discard the metrics and remove them, forcing and accept + log.Errorf("%s [%s-%s] Error trying to write metrics %d / %d, dropped, error: %s", db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType, len(mett), db.buffer.Len(), err) + } else { + log.Infof("%s [%s-%s] Succesfull write metrics %d / %d", db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType, len(mett), db.buffer.Len()) + } + db.buffer.Accept(mett) + } + //need to flush all data + log.Infof("EXIT from Influx sender process for device [%s] ", db.Cfg.ID) + return + case t := <-db.tick.C: + // declare the status needed vars + var ps, pd, fs, we, ws int64 + var bl float32 + var wt time.Duration + + // as the buffer is entirely flushed we need to retrieve the buffer length prior to the write process + // the bufferlength stat should be computed as the max of all ticks on a gather freq + bl = float32(db.buffer.Len()*100.0) / float32(db.Cfg.BufferSize) + + // if db is not active we should not try to send/connect to the output + if !db.Cfg.Active { + log.Debugf("Output - %s with backend %s is disabled, not sending metrics", db.Cfg.ID, db.Cfg.Backend) + // as the buffersize can change due to enqueueonerror, we should update as if they are reseted + db.stats.FillStats(0, 0, 0, 0, 0, 0, bl) + continue + } + + // we check if the output is already connected, if it isn't, try it again + // metrics keep being stored on + if !db.IsConnected() { + log.Infof("Output - %s with backend %s is not connected, try to connect again", db.Cfg.ID, db.Cfg.Backend) + if err := db.Backend.Connect(); err == nil { + db.CheckAndSetConnected() + log.Infof("Output - %s successfully connected!", db.Cfg.ID) + } else { + log.Errorf("Output - %s failed trying to connect on this iteration, error: %s", db.Cfg.ID, err) + continue + } + } + // TODO: apply jitter, just before the write process + // flush_jitter + // end jitter + nBuffer := db.buffer.Len() + nBatches := nBuffer/db.Cfg.MetricBatchSize + 1 + for i := 0; i < nBatches; i++ { + mett := db.buffer.Batch(db.Cfg.MetricBatchSize) + log.Debugf("%s [%s-%s] - Selected Batchsize of - %d [%s]", db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType, db.buffer.Len(), t) + // avoid to write 0 metrics + if len(mett) == 0 { + break + } + st := time.Now() + err := db.Backend.Write(mett) + wt += time.Since(st) + if err != nil { + // if error happens, we should reject those metrics and they are inserted again in the buffer + if db.Cfg.EnqueueOnError { + db.buffer.Reject(mett) + log.Warnf("%s [%s-%s] Error trying to write metrics %d / %d, enqueud, error: %s", db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType, len(mett), db.buffer.Len(), err) + } else { + db.buffer.Accept(mett) + pd += int64(len(mett)) + log.Warnf("%s [%s-%s] Error trying to write metrics %d / %d, dropped, error: %s", db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType, len(mett), db.buffer.Len(), err) + } + we++ + } else { + log.Debugf("%s [%s-%s] Succesfull write metrics %d / %d", db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType, len(mett), db.buffer.Len()) + db.buffer.Accept(mett) + ws++ + ps += int64(len(mett)) + for _, met := range mett { + fs += int64(len(met.FieldList())) + } + } + } + // batches are finished, we update the stats with the gathered + log.Debugf("%s [%s-%s] - After %d batches - Stats: PointSent: %d, PointsDropped: %d, FieldSent: %d, WriteSent %d, WriteSentError %d, BufferLen: %f, WriteTime: %v", + db.Cfg.ID, db.Cfg.Backend, db.Cfg.BackendType, nBatches, ps, pd, fs, ws, we, bl, wt) + db.stats.FillStats(ps, pd, fs, ws, we, wt, bl) + } + } +} diff --git a/pkg/agent/output/stats.go b/pkg/agent/output/sinkdbstats.go similarity index 66% rename from pkg/agent/output/stats.go rename to pkg/agent/output/sinkdbstats.go index f5b29fe1..710e4ed4 100644 --- a/pkg/agent/output/stats.go +++ b/pkg/agent/output/sinkdbstats.go @@ -5,8 +5,8 @@ import ( "time" ) -// InfluxStats get stats -type InfluxStats struct { +// SinkDBStats get stats +type SinkDBStats struct { // Fields Sent FieldSent int64 // Field Sent the max @@ -15,9 +15,11 @@ type InfluxStats struct { PSent int64 // PSentMax the max PSentMax int64 + // Points dropped + PDropped int64 // WriteSent BatchPoints sent WriteSent int64 - // WriteErrors BatchPoints with errors + // WriteErrors BatchPoints with errors WriteErrors int64 // WriteTime WriteTime time.Duration @@ -29,14 +31,15 @@ type InfluxStats struct { } // GetResetStats get stats for this InfluxStats Output -func (is *InfluxStats) GetResetStats() *InfluxStats { +func (is *SinkDBStats) GetResetStats() *SinkDBStats { is.mutex.Lock() defer is.mutex.Unlock() - retstat := &InfluxStats{ + retstat := &SinkDBStats{ FieldSent: is.FieldSent, FieldSentMax: is.FieldSentMax, PSent: is.PSent, PSentMax: is.PSentMax, + PDropped: is.PDropped, WriteSent: is.WriteSent, WriteErrors: is.WriteErrors, WriteTime: is.WriteTime, @@ -46,6 +49,7 @@ func (is *InfluxStats) GetResetStats() *InfluxStats { is.FieldSent = 0 is.FieldSentMax = 0 is.PSent = 0 + is.PDropped = 0 is.PSentMax = 0 is.WriteSent = 0 is.WriteErrors = 0 @@ -55,35 +59,37 @@ func (is *InfluxStats) GetResetStats() *InfluxStats { return retstat } -// WriteOkUpdate update stats on write ok -func (is *InfluxStats) WriteOkUpdate(ps int64, fs int64, wt time.Duration, bufferPercent float32) { +// FillStats updates in threadsafe the current SinkDBStats +func (is *SinkDBStats) FillStats(ps, pd, fs, ws, we int64, wt time.Duration, bpUsed float32) { is.mutex.Lock() defer is.mutex.Unlock() + // PointSent if is.PSentMax < ps { is.PSentMax = ps } + is.PSent += ps + + // PDropped + is.PDropped += pd + + // Write Time if is.WriteTimeMax < wt { is.WriteTimeMax = wt } + is.WriteTime += wt + + // FieldSent if is.FieldSentMax < fs { is.FieldSentMax = fs } - is.WriteSent++ is.FieldSent += fs - is.PSent += ps - is.WriteTime += wt - is.BufferPercentUsed = bufferPercent -} -// WriteErrUpdate update stats on write error -func (is *InfluxStats) WriteErrUpdate(wt time.Duration, bufferPercent float32) { - is.mutex.Lock() - defer is.mutex.Unlock() + // WriteSent, WriteErrors + is.WriteSent += ws + is.WriteErrors += we - if is.WriteTimeMax < wt { - is.WriteTimeMax = wt + // BufferPercentUsed + if is.BufferPercentUsed < bpUsed { + is.BufferPercentUsed = bpUsed } - is.WriteErrors++ - is.WriteTime += wt - is.BufferPercentUsed = bufferPercent } diff --git a/pkg/agent/selfmon/selfmon.go b/pkg/agent/selfmon/selfmon.go index 22125611..d4b9f023 100644 --- a/pkg/agent/selfmon/selfmon.go +++ b/pkg/agent/selfmon/selfmon.go @@ -7,7 +7,8 @@ import ( "sync" "time" - client "github.com/influxdata/influxdb1-client/v2" + "github.com/influxdata/telegraf" + metric "github.com/influxdata/telegraf/metric" "github.com/sirupsen/logrus" "github.com/toni-moreno/snmpcollector/pkg/agent/output" "github.com/toni-moreno/snmpcollector/pkg/config" @@ -21,20 +22,24 @@ func SetLogger(l *logrus.Logger) { } // SelfMon configuration for self monitoring +// selfmon allows to gather data from all outputs and send metrics to a definid output +// selfmon should run within a goroutine and it should send metrics using the default backend +// that should be already defined as a SinkDB with an attached backend type SelfMon struct { cfg *config.SelfMonConfig - Influx *output.InfluxDB - OutDBs map[string]*output.InfluxDB // needed to get statistics + Output *output.SinkDB + OutDBs map[string]*output.SinkDB // needed to get statistics runtimeStatsRunning bool TagMap map[string]string - bps *client.BatchPoints - chExit chan bool - mutex sync.Mutex - RtMeasName string // devices measurement name - GvmMeasName string // Self agent GoVirtualMachine measurement name - OutMeasName string // Output DB's measurement name - initialized bool - imutex sync.Mutex + tick *time.Ticker + + chExit chan bool + mutex sync.Mutex + RtMeasName string // devices measurement name + GvmMeasName string // Self agent GoVirtualMachine measurement name + OutMeasName string // Output DB's measurement name + initialized bool + imutex sync.Mutex // memory for GVM data colletion lastSampleTime time.Time lastPauseNs uint64 @@ -48,12 +53,12 @@ func NewNotInit(c *config.SelfMonConfig) *SelfMon { // Init Initialize the Object data and check for consistence func (sm *SelfMon) Init() { - if sm.CheckAndSetInitialized() == true { + if sm.CheckAndSetInitialized() { log.Info("Self monitoring thread already Initialized (skipping Initialization)") return } - sm.OutDBs = make(map[string]*output.InfluxDB) + // declare all the available outputs to gather metrics from // Init extra tags if len(sm.cfg.ExtraTags) > 0 { sm.TagMap = make(map[string]string) @@ -82,10 +87,18 @@ func (sm *SelfMon) Init() { } sm.chExit = make(chan bool) + // initialize ticker with the current time + // we should review if we should sync with the output ticker as it can send metrics witha freq delay + // if the output sent ticks before the selfmon ticker + if sm.cfg.Freq <= 0 { + log.Infof("No freq defined, defaulting to 60s") + sm.cfg.Freq = 60 + } + sm.tick = time.NewTicker(time.Duration(sm.cfg.Freq) * time.Second) } // SetOutDB set the output devices for query its statistics -func (sm *SelfMon) SetOutDB(odb map[string]*output.InfluxDB) { +func (sm *SelfMon) SetOutDB(odb map[string]*output.SinkDB) { sm.OutDBs = odb } @@ -115,28 +128,10 @@ func (sm *SelfMon) IsInitialized() bool { } // SetOutput set out data -func (sm *SelfMon) SetOutput(val *output.InfluxDB) { +func (sm *SelfMon) SetOutput(val *output.SinkDB) { sm.mutex.Lock() defer sm.mutex.Unlock() - sm.Influx = val - // Creating a bachpoint to begin writing data - sm.bps, _ = sm.Influx.BP() -} - -func (sm *SelfMon) sendData() { - sm.mutex.Lock() - defer sm.mutex.Unlock() - sm.Influx.Send(sm.bps) - // BatchPoint Init again - sm.bps, _ = sm.Influx.BP() -} - -func (sm *SelfMon) addDataPoint(pt *client.Point) { - sm.mutex.Lock() - defer sm.mutex.Unlock() - if sm.bps != nil { - (*sm.bps).AddPoint(pt) - } + sm.Output = val } // AddDeviceMetrics add data from devices @@ -146,6 +141,8 @@ func (sm *SelfMon) AddMetrics(t string, id string, fields map[string]interface{} } sm.mutex.Lock() defer sm.mutex.Unlock() + var tmetrics []telegraf.Metric + // Selfmon tags tagMap := make(map[string]string) for k, v := range sm.TagMap { @@ -172,17 +169,17 @@ func (sm *SelfMon) AddMetrics(t string, id string, fields map[string]interface{} tagMap["type"] = t now := time.Now() - pt, err := client.NewPoint( - sm.RtMeasName, - tagMap, - fields, - now) - if err != nil { - log.Warnf("Error on compute Stats data Point %+v for %s : %s: Error:%s", fields, t, id, err) + tmetric := metric.New(sm.RtMeasName, tagMap, fields, now) + // Validate that at least len of fields > 0 and len of tags > 0 + if len(fields) == 0 { + log.Warnf("error, empty fields: [%d]", len(fields)) return } - - (*sm.bps).AddPoint(pt) + tmetrics = append(tmetrics, tmetric) + lmet, err := sm.Output.SendToBuffer(tmetrics, "selfmon"+sm.RtMeasName) + if err != nil { + log.Errorf("unable to send metrics to the buffer - dropped metrics: %d, err: %s", lmet, err) + } } // End Release the SelMon Object @@ -204,7 +201,6 @@ func (sm *SelfMon) StartGather(wg *sync.WaitGroup) { } sm.runtimeStatsRunning = true - go sm.reportStats(wg) } @@ -215,24 +211,27 @@ func (sm *SelfMon) StopGather() { } } +// getOutDBStats gather stats from all outputs, we need to query them and add the metrics to the output buffer func (sm *SelfMon) getOutDBStats() { now := time.Now() + var tmetrics []telegraf.Metric - for dbname, db := range sm.OutDBs { - stats := db.GetResetStats() + for dbname, sinkdb := range sm.OutDBs { + stats := sinkdb.GetResetStats() - tm := make(map[string]string) + tags := make(map[string]string) fields := make(map[string]interface{}) for k, v := range sm.TagMap { - tm[k] = v + tags[k] = v } - tm["outdb"] = dbname + tags["outdb"] = dbname fields["fields_sent"] = stats.FieldSent fields["fields_sent_max"] = stats.FieldSentMax fields["points_sent"] = stats.PSent + fields["points_dropped"] = stats.PDropped fields["points_sent_max"] = stats.PSentMax fields["write_sent"] = stats.WriteSent @@ -248,18 +247,21 @@ func (sm *SelfMon) getOutDBStats() { fields["write_time_avg"] = sec / float64(stats.WriteSent) } - pt, err := client.NewPoint(sm.OutMeasName, tm, fields, now) - if err != nil { - log.Warnf("Error on compute Stats data Point %+v for database %s: Error:%s", fields, dbname, err) - return + tmetric := metric.New(sm.OutMeasName, tags, fields, now) + // Validate that at least len of fields > 0 and len of tags > 0 + if len(tags) == 0 || len(fields) == 0 { + log.Warnf("error, empty tags [%d] or fields: [%d]", len(tags), len(fields)) + continue } - - // add data to the batchpoint - sm.addDataPoint(pt) + tmetrics = append(tmetrics, tmetric) } + sm.Output.SendToBuffer(tmetrics, "selfmon"+sm.OutMeasName) } func (sm *SelfMon) getRuntimeStats() { + + var tmetrics []telegraf.Metric + nsInMs := float64(time.Millisecond) memStats := &runtime.MemStats{} runtime.ReadMemStats(memStats) @@ -310,22 +312,15 @@ func (sm *SelfMon) getRuntimeStats() { fields["gc.gc_per_interval"] = diff } sm.lastNumGc = memStats.NumGC - sm.lastSampleTime = now - pt, err := client.NewPoint( - sm.GvmMeasName, - sm.TagMap, - fields, - now, - ) - if err != nil { - log.Warnf("Error on compute Stats data Point %+v for GVB : Error:%s", fields, err) - return + tmetric := metric.New(sm.GvmMeasName, sm.TagMap, fields, now) + // Validate that at least len of fields > 0 and len of tags > 0 + if len(fields) == 0 { + log.Warnf("error, empty fields: [%d]", len(fields)) } - - // add data to the batchpoint - sm.addDataPoint(pt) + tmetrics = append(tmetrics, tmetric) + sm.Output.SendToBuffer(tmetrics, "selfmon"+sm.GvmMeasName) } func (sm *SelfMon) reportStats(wg *sync.WaitGroup) { @@ -333,27 +328,17 @@ func (sm *SelfMon) reportStats(wg *sync.WaitGroup) { wg.Add(1) log.Info("SELFMON: Beginning selfmonitor process for device") - s := time.Tick(time.Duration(sm.cfg.Freq) * time.Second) sm.lastSampleTime = time.Now() for { - // Get BVM stats - sm.getRuntimeStats() - // - sm.getOutDBStats() - // BatchPoint Send - sm.sendData() - - LOOP: - for { - select { - case <-s: - // log.Infof("SELFMON: breaking LOOP ") - break LOOP - case <-sm.chExit: - log.Infof("SELFMON: EXIT from SelfMonitoring Gather process ") - sm.runtimeStatsRunning = false - return - } + select { + case <-sm.tick.C: + // Get BVM stats + sm.getRuntimeStats() + sm.getOutDBStats() + case <-sm.chExit: + log.Infof("SELFMON: EXIT from SelfMonitoring Gather process ") + sm.runtimeStatsRunning = false + return } } } diff --git a/pkg/config/database.go b/pkg/config/database.go index d3b9600a..016e3b71 100644 --- a/pkg/config/database.go +++ b/pkg/config/database.go @@ -118,46 +118,55 @@ func (dbc *DatabaseCfg) InitDB() error { // Sync tables if err = dbc.x.Sync(new(VarCatalogCfg)); err != nil { - log.Fatalf("Fail to sync database VarCatalogCfg: %v\n", err) + log.Fatalf("Fail to sync database VarCatalogCfg: %v", err) + } + if err = dbc.x.Sync(new(OutputCfg)); err != nil { + log.Fatalf("Fail to sync database OutputCfg: %v", err) } if err = dbc.x.Sync(new(InfluxCfg)); err != nil { - log.Fatalf("Fail to sync database InfluxCfg: %v\n", err) + log.Fatalf("Fail to sync database InfluxCfg: %v", err) + } + if err = dbc.x.Sync(new(KafkaCfg)); err != nil { + log.Fatalf("Fail to sync database KafkaCfg: %v", err) + } + if err = dbc.x.Sync(new(OutputBackends)); err != nil { + log.Fatalf("Fail to sync database OutputBackends: %v", err) } if err = dbc.x.Sync(new(SnmpDeviceCfg)); err != nil { - log.Fatalf("Fail to sync database SnmpDeviceCfg: %v\n", err) + log.Fatalf("Fail to sync database SnmpDeviceCfg: %v", err) } if err = dbc.x.Sync(new(SnmpMetricCfg)); err != nil { - log.Fatalf("Fail to sync database SnmpMetricCfg: %v\n", err) + log.Fatalf("Fail to sync database SnmpMetricCfg: %v", err) } if err = dbc.x.Sync(new(MeasurementCfg)); err != nil { - log.Fatalf("Fail to sync database MeasurementCfg: %v\n", err) + log.Fatalf("Fail to sync database MeasurementCfg: %v", err) } if err = dbc.x.Sync(new(MeasFilterCfg)); err != nil { - log.Fatalf("Fail to sync database MeasurementFilterCfg : %v\n", err) + log.Fatalf("Fail to sync database MeasurementFilterCfg : %v", err) } if err = dbc.x.Sync(new(MeasurementFieldCfg)); err != nil { - log.Fatalf("Fail to sync database MeasurementFieldCfg: %v\n", err) + log.Fatalf("Fail to sync database MeasurementFieldCfg: %v", err) } if err = dbc.x.Sync(new(MGroupsCfg)); err != nil { - log.Fatalf("Fail to sync database MGroupCfg: %v\n", err) + log.Fatalf("Fail to sync database MGroupCfg: %v", err) } if err = dbc.x.Sync(new(MGroupsMeasurements)); err != nil { - log.Fatalf("Fail to sync database MGroupsMeasurements: %v\n", err) + log.Fatalf("Fail to sync database MGroupsMeasurements: %v", err) } if err = dbc.x.Sync(new(SnmpDevMGroups)); err != nil { - log.Fatalf("Fail to sync database SnmpDevMGroups: %v\n", err) + log.Fatalf("Fail to sync database SnmpDevMGroups: %v", err) } if err = dbc.x.Sync(new(SnmpDevFilters)); err != nil { - log.Fatalf("Fail to sync database SnmpDevFilters: %v\n", err) + log.Fatalf("Fail to sync database SnmpDevFilters: %v", err) } if err = dbc.x.Sync(new(CustomFilterCfg)); err != nil { - log.Fatalf("Fail to sync database CustomFilterCfg: %v\n", err) + log.Fatalf("Fail to sync database CustomFilterCfg: %v", err) } if err = dbc.x.Sync(new(CustomFilterItems)); err != nil { - log.Fatalf("Fail to sync database CustomFilterItems: %v\n", err) + log.Fatalf("Fail to sync database CustomFilterItems: %v", err) } if err = dbc.x.Sync(new(OidConditionCfg)); err != nil { - log.Fatalf("Fail to sync database OidConditionCfg: %v\n", err) + log.Fatalf("Fail to sync database OidConditionCfg: %v", err) } return nil } @@ -191,12 +200,25 @@ func CatalogVar2Map(cv map[string]*VarCatalogCfg) map[string]interface{} { // LoadDbConfig get data from database func (dbc *DatabaseCfg) LoadDbConfig(cfg *DBConfig) { var err error + + // migration + dbc.StartMigration() + // Load Global Variables VarCatalog := make(map[string]*VarCatalogCfg) VarCatalog, err = dbc.GetVarCatalogCfgMap("") if err != nil { log.Warningf("Some errors on get Global variables :%v", err) } + + // err = dbc.StartMigration() + // if err != nil { + // log.Errorf("ERRORRRR: %+v", err) + // } + + // load backends as interface + //cfg.Backends = dbc.GetBackendsCfgMap("") + cfg.VarCatalog = make(map[string]interface{}, len(VarCatalog)) cfg.VarCatalog = CatalogVar2Map(VarCatalog) @@ -206,6 +228,11 @@ func (dbc *DatabaseCfg) LoadDbConfig(cfg *DBConfig) { log.Warningf("Some errors on get Influx db's :%v", err) } + cfg.Kafka, err = dbc.GetKafkaCfgMap("") + if err != nil { + log.Warningf("Some errors on get Kafka :%v", err) + } + // Load metrics cfg.Metrics, err = dbc.GetSnmpMetricCfgMap("") if err != nil { @@ -237,5 +264,11 @@ func (dbc *DatabaseCfg) LoadDbConfig(cfg *DBConfig) { if err != nil { log.Warningf("Some errors on get SnmpDeviceConf :%v", err) } + + cfg.Outputs, err = dbc.GetOutputCfgMap("") + if err != nil { + log.Warningf("Some errors on get Outputs :%v", err) + } + dbc.resetChanges() } diff --git a/pkg/config/dbconfig.go b/pkg/config/dbconfig.go index e6b2d58e..7f40cbd9 100644 --- a/pkg/config/dbconfig.go +++ b/pkg/config/dbconfig.go @@ -48,6 +48,58 @@ type SnmpDeviceCfg struct { MeasFilters []string `xorm:"-"` } +// OutputCfg is the main configuration for any supported backend type +// the relation will be 1:1 to the backend ID +// swagger:model OutputCfg + +type OutputCfg struct { + ID string `xorm:"'id' unique" binding:"Required"` + BackendType string `xorm:"-" binding:"Required;In(kafka,influxdb)"` + Active bool `xorm:"'active' default TRUE"` + EnqueueOnError bool `xorm:"'enqueue_on_error' default TRUE"` + BufferSize int `xorm:"'buffer_size' default 131070" binding:"Required;IntegerNotZero"` + MetricBatchSize int `xorm:"'metric_batch_size' default 13000" binding:"Required;IntegerNotZero"` + FlushInterval int `xorm:"'flush_interval' default 60" binding:"Required;IntegerNotZero"` + Backend string `xorm:"-"` + Description string `xorm:"description"` +} + +// KafkaCfg is the main configuration for any Kafka +// swagger:model KafkaCfg +type KafkaCfg struct { + ID string `xorm:"'id' unique" binding:"Required"` + Brokers []string `xorm:"brokers" binding:"Required"` + Topic string `xorm:"topic"` + Method string `xorm:"topic_suffix_method" binding:"Required;In(measurement,tags)"` + Keys []string `xorm:"topic_suffix_keys"` + Separator string `xorm:"topic_suffix_separator"` + ExcludeTopicTag bool `xorm:"exclude_topic_tag"` + TopicTag string `xorm:"topic_tag"` + RoutingTag string `xorm:"routing_tag"` + RoutingKey string `xorm:"routing_key"` + Description string `xorm:"description"` + RequiredAcks int `xorm:"'required_acks' default -1"` + MaxRetry int `xorm:"'max_retry' default 3"` + MaxMessageBytes int `xorm:"max_message_bytes"` + IdempotentWrites bool `xorm:"idempotent_writes"` + TLSCA string `xorm:"tls_ca"` + TLSCert string `xorm:"tls_cert"` + TLSKey string `xorm:"tls_key"` + TLSKeyPwd string `xorm:"tls_key_pwd"` + TLSMinVersion string `xorm:"tls_min_version"` + InsecureSkipVerify bool `xorm:"insecure_skip_verify"` + ServerName string `xorm:"tls_server_name"` + Version string `xorm:"version"` + ClientID string `xorm:"client_id"` + CompressionCodec int `xorm:"compression_codec"` + EnableTLS bool `xorm:"enable_tls"` + Socks5ProxyEnabled bool `xorm:"socks5_enabled"` + Socks5ProxyAddress string `xorm:"socks5_address"` + Socks5ProxyUsername string `xorm:"socks5_username"` + Socks5ProxyPassword string `xorm:"socks5_password"` + MetadataFull bool `xorm:"metadata_full"` +} + // InfluxCfg is the main configuration for any InfluxDB TSDB // swagger:model InfluxCfg type InfluxCfg struct { @@ -66,7 +118,7 @@ type InfluxCfg struct { SSLCert string `xorm:"ssl_cert"` SSLKey string `xorm:"ssl_key"` InsecureSkipVerify bool `xorm:"insecure_skip_verify"` - BufferSize int `xorm:"'buffer_size' default 65535"` + BufferSize int `xorm:"-" json:"-"` Description string `xorm:"description"` } @@ -136,6 +188,17 @@ type SnmpDevMGroups struct { IDMGroupCfg string `xorm:"id_mgroup_cfg"` } +// create unique entry based on output_id+backend_id,backend_type so we allow to define multiple +// backends with the same name and they differ on backend_type +// backend_type allow us to distinguish between different supported backends +// and allow them to be extended with custom ui +// even if we support multiple outputs, the relations are still ok +type OutputBackends struct { + IDOutput string `xorm:"'id_output'"` + IDBackend string `xorm:"'id_backend'"` + BackendType string `xorm:"'backend_type'"` +} + // DBConfig read from DB type DBConfig struct { Metrics map[string]*SnmpMetricCfg @@ -144,7 +207,11 @@ type DBConfig struct { GetGroups map[string]*MGroupsCfg SnmpDevice map[string]*SnmpDeviceCfg Influxdb map[string]*InfluxCfg - VarCatalog map[string]interface{} + Kafka map[string]*KafkaCfg + // NEW + Outputs map[string]*OutputCfg + + VarCatalog map[string]interface{} } /* diff --git a/pkg/config/influxcfg.go b/pkg/config/influxcfg.go index cad95f08..de41a5ba 100644 --- a/pkg/config/influxcfg.go +++ b/pkg/config/influxcfg.go @@ -20,7 +20,7 @@ func (dbc *DatabaseCfg) GetInfluxCfgByID(id string) (InfluxCfg, error) { return InfluxCfg{}, err } if len(cfgarray) > 1 { - return InfluxCfg{}, fmt.Errorf("Error %d results on get SnmpDeviceCfg by id %s", len(cfgarray), id) + return InfluxCfg{}, fmt.Errorf("Error %d results on get InfluxCfg by id %s", len(cfgarray), id) } if len(cfgarray) == 0 { return InfluxCfg{}, fmt.Errorf("Error no values have been returned with this id %s in the influx config table", id) @@ -86,7 +86,7 @@ func (dbc *DatabaseCfg) AddInfluxCfg(dev InfluxCfg) (int64, error) { /*DelInfluxCfg for deleting influx databases from ID*/ func (dbc *DatabaseCfg) DelInfluxCfg(id string) (int64, error) { - var affecteddev, affected int64 + var affectedouts, affected int64 var err error session := dbc.x.NewSession() @@ -95,9 +95,8 @@ func (dbc *DatabaseCfg) DelInfluxCfg(id string) (int64, error) { return 0, err } defer session.Close() - // deleting references in SnmpDevCfg - affecteddev, err = session.Where("outdb='" + id + "'").Cols("outdb").Update(&SnmpDeviceCfg{}) + affectedouts, err = session.Where("id_backend='" + id + "' and backend_type = 'influxdb'").Cols("outdb").Delete(&OutputBackends{}) if err != nil { session.Rollback() return 0, fmt.Errorf("Error on Delete Device with id on delete SnmpDevCfg with id: %s, error: %s", id, err) @@ -113,8 +112,8 @@ func (dbc *DatabaseCfg) DelInfluxCfg(id string) (int64, error) { if err != nil { return 0, err } - log.Infof("Deleted Successfully influx db with ID %s [ %d Devices Affected ]", id, affecteddev) - dbc.addChanges(affected + affecteddev) + log.Infof("Deleted Successfully influx db with ID %s [ %d Outputs Affected ]", id, affectedouts) + dbc.addChanges(affected + affectedouts) return affected, nil } @@ -128,13 +127,14 @@ func (dbc *DatabaseCfg) UpdateInfluxCfg(id string, dev InfluxCfg) (int64, error) return 0, err } defer session.Close() + if id != dev.ID { // ID has been changed - affecteddev, err = session.Where("outdb='" + id + "'").Cols("outdb").Update(&SnmpDeviceCfg{OutDB: dev.ID}) + affecteddev, err = session.Where("id_backend='" + id + "' and backend_type='influxdb'").Cols("id_backend").Update(&OutputBackends{IDBackend: dev.ID}) if err != nil { session.Rollback() - return 0, fmt.Errorf("Error on Update InfluxConfig on update id(old) %s with (new): %s, error: %s", id, dev.ID, err) + return 0, fmt.Errorf("Error on Update InfluxConfig on update id(old) %s with (new): %s, error: %s", id, dev.ID, err) } - log.Infof("Updated Influx Config to %d devices ", affecteddev) + log.Infof("Updated Influx Config to %d outputs ", affecteddev) } affected, err = session.Where("id='" + id + "'").UseBool().AllCols().Update(dev) @@ -154,19 +154,19 @@ func (dbc *DatabaseCfg) UpdateInfluxCfg(id string, dev InfluxCfg) (int64, error) /*GetInfluxCfgAffectOnDel for deleting devices from ID*/ func (dbc *DatabaseCfg) GetInfluxCfgAffectOnDel(id string) ([]*DbObjAction, error) { - var devices []*SnmpDeviceCfg + var outputs []*OutputBackends var obj []*DbObjAction - if err := dbc.x.Where("outdb='" + id + "'").Find(&devices); err != nil { - log.Warnf("Error on Get Outout db id %s for devices , error: %s", id, err) + if err := dbc.x.Where("id_backend='" + id + "' and backend_type = 'influxdb'").Find(&outputs); err != nil { + log.Warnf("Error on Get Output id %s for devices , error: %s", id, err) return nil, err } - for _, val := range devices { + for _, val := range outputs { obj = append(obj, &DbObjAction{ - Type: "snmpdevicecfg", - TypeDesc: "SNMP Devices", - ObID: val.ID, - Action: "Reset InfluxDB Server from SNMPDevice to 'default' InfluxDB Server", + Type: "outputcfg", + TypeDesc: "Outputs", + ObID: val.IDOutput, + Action: "Remove InfluxDB Server from Outputs", }) } return obj, nil diff --git a/pkg/config/kafkacfg.go b/pkg/config/kafkacfg.go new file mode 100644 index 00000000..87c2c0df --- /dev/null +++ b/pkg/config/kafkacfg.go @@ -0,0 +1,173 @@ +package config + +import "fmt" + +/*************************** + Kafka DB backends + -GetKafkaCfgCfgByID(struct) + -GetKafkaCfgMap (map - for interna config use + -GetKafkaCfgArray(Array - for web ui use ) + -AddKafkaCfg + -DelKafkaCfg + -UpdateKafkaCfg + -GetKafkaCfgAffectOnDel +***********************************/ + +/*GetKafkaCfgByID get device data by id*/ +func (dbc *DatabaseCfg) GetKafkaCfgByID(id string) (KafkaCfg, error) { + cfgarray, err := dbc.GetKafkaCfgArray("id='" + id + "'") + if err != nil { + return KafkaCfg{}, err + } + if len(cfgarray) > 1 { + return KafkaCfg{}, fmt.Errorf("Error %d results on get KafkaCfg by id %s", len(cfgarray), id) + } + if len(cfgarray) == 0 { + return KafkaCfg{}, fmt.Errorf("Error no values have been returned with this id %s in the Kafka config table", id) + } + return *cfgarray[0], nil +} + +/*GetKafkaCfgMap return data in map format*/ +func (dbc *DatabaseCfg) GetKafkaCfgMap(filter string) (map[string]*KafkaCfg, error) { + cfgarray, err := dbc.GetKafkaCfgArray(filter) + cfgmap := make(map[string]*KafkaCfg) + for _, val := range cfgarray { + cfgmap[val.ID] = val + log.Debugf("%+v", *val) + } + return cfgmap, err +} + +/*GetKafkaCfgArray generate an array of devices with all its information */ +func (dbc *DatabaseCfg) GetKafkaCfgArray(filter string) ([]*KafkaCfg, error) { + var err error + var devices []*KafkaCfg + // Get Only data for selected devices + if len(filter) > 0 { + if err = dbc.x.Where(filter).Find(&devices); err != nil { + log.Warnf("Fail to get KafkaCfg data filteter with %s : %v\n", filter, err) + return nil, err + } + } else { + if err = dbc.x.Find(&devices); err != nil { + log.Warnf("Fail to get Kafkacfg data: %v\n", err) + return nil, err + } + } + return devices, nil +} + +/*AddKafkaCfg for adding new devices*/ +func (dbc *DatabaseCfg) AddKafkaCfg(dev KafkaCfg) (int64, error) { + var err error + var affected int64 + session := dbc.x.NewSession() + if err := session.Begin(); err != nil { + // if returned then will rollback automatically + return 0, err + } + defer session.Close() + + affected, err = session.Insert(dev) + if err != nil { + session.Rollback() + return 0, err + } + // no other relation + err = session.Commit() + if err != nil { + return 0, err + } + log.Infof("Added new Kafka backend Successfully with id %s ", dev.ID) + dbc.addChanges(affected) + return affected, nil +} + +/*DelKafkaCfg for deleting Kafka databases from ID*/ +func (dbc *DatabaseCfg) DelKafkaCfg(id string) (int64, error) { + var affectedouts, affected int64 + var err error + + session := dbc.x.NewSession() + if err := session.Begin(); err != nil { + // if returned then will rollback automatically + return 0, err + } + defer session.Close() + // deleting references in SnmpDevCfg + + affectedouts, err = session.Where("id_backend='" + id + "' and backend_type = 'kafka'").Cols("outdb").Delete(&OutputBackends{}) + if err != nil { + session.Rollback() + return 0, fmt.Errorf("Error on Delete Device with id on delete SnmpDevCfg with id: %s, error: %s", id, err) + } + + affected, err = session.Where("id='" + id + "'").Delete(&KafkaCfg{}) + if err != nil { + session.Rollback() + return 0, err + } + + err = session.Commit() + if err != nil { + return 0, err + } + log.Infof("Deleted Successfully Kafka db with ID %s [ %d Outputs Affected ]", id, affectedouts) + dbc.addChanges(affected + affectedouts) + return affected, nil +} + +/*UpdateKafkaCfg for adding new Kafka*/ +func (dbc *DatabaseCfg) UpdateKafkaCfg(id string, dev KafkaCfg) (int64, error) { + var affecteddev, affected int64 + var err error + session := dbc.x.NewSession() + if err := session.Begin(); err != nil { + // if returned then will rollback automatically + return 0, err + } + defer session.Close() + if id != dev.ID { // ID has been changed + affecteddev, err = session.Where("id_backend='" + id + "' and backend_type='kafka'").Cols("id_backend").Update(&OutputBackends{IDBackend: dev.ID}) + if err != nil { + session.Rollback() + return 0, fmt.Errorf("Error on Update KafkaConfig on update id(old) %s with (new): %s, error: %s", id, dev.ID, err) + } + log.Infof("Updated Kafka Config to %d outputs ", affecteddev) + } + + affected, err = session.Where("id='" + id + "'").UseBool().AllCols().Update(dev) + if err != nil { + session.Rollback() + return 0, err + } + err = session.Commit() + if err != nil { + return 0, err + } + + log.Infof("Updated Kafka Config Successfully with id %s and data:%+v, affected", id, dev) + dbc.addChanges(affected + affecteddev) + return affected, nil +} + +/*GetKafkaCfgAffectOnDel for deleting devices from ID*/ +func (dbc *DatabaseCfg) GetKafkaCfgAffectOnDel(id string) ([]*DbObjAction, error) { + var outputs []*OutputBackends + var obj []*DbObjAction + if err := dbc.x.Where("id_backend='" + id + "' and backend_type = 'kafka'").Find(&outputs); err != nil { + log.Warnf("Error on Get Output id %s for devices , error: %s", id, err) + return nil, err + } + + for _, val := range outputs { + obj = append(obj, &DbObjAction{ + Type: "outputcfg", + TypeDesc: "Outputs", + ObID: val.IDOutput, + Action: "Remove Kafka Server from Outputs", + }) + } + return obj, nil +} diff --git a/pkg/config/migration.go b/pkg/config/migration.go new file mode 100644 index 00000000..8f7b499b --- /dev/null +++ b/pkg/config/migration.go @@ -0,0 +1,135 @@ +package config + +import ( + "fmt" + "strconv" +) + +func (dbc *DatabaseCfg) StartMigration() error { + // snmpcollector - 0.14.0 - ensure new db table exists and its filled with current outputs + ok, err := dbc.ValidateAndGenerateOutputTable() + if err != nil { + return err + } + if ok { + log.Infof("MIGRATION: Migration succed, new outputs have been created") + return nil + } + log.Infof("MIGRATION: Migration was skipped") + return nil + +} + +func (dbc *DatabaseCfg) MigrateInfluxToOutputsDevs() error { + session := dbc.x.NewSession() + if err := session.Begin(); err != nil { + // if returned then will rollback automatically + return err + } + defer session.Close() + + // retrieve all devices: + _, err := dbc.GetSnmpDeviceCfgArray("") + if err != nil { + session.Rollback() + return err + } + + return nil +} + +func (dbc *DatabaseCfg) ValidateAndGenerateOutputTable() (bool, error) { + log.Infof("MIGRATION: trying to migrate current InfluxDB servers to Outputs...") + session := dbc.x.NewSession() + if err := session.Begin(); err != nil { + // if returned then will rollback automatically + return false, err + } + defer session.Close() + // check if new output_cfg table exists + exists, err := session.IsTableExist("output_cfg") + if err != nil { + return false, err + } + if !exists { + log.Infof("MIGRATION: failed to sync with output_cfg, skipping migration") + return false, nil + } + // validate again to avoid some action on table + n, err := dbc.GetOutputCfgArray("") + if err != nil { + return false, nil + } + if len(n) > 0 { + log.Infof("MIGRATION: output_cfg exists and contains data, skipping migration") + return false, nil + } + // retrieve the current influxdb configured servers + icfgs, err := dbc.GetInfluxCfgArray("") + if err != nil { + return false, err + } + + // retrieve the current influxdb configured servers with raw query + // we need to retrieve the deprecated buffersize + ricfgs, err := session.Query("select * from influx_cfg;") + if err != nil { + return false, err + } + var mod int + for _, ricfg := range ricfgs { + for iicfg, icfg := range icfgs { + if icfg.ID == string(ricfg["id"]) { + icfg.BufferSize, err = strconv.Atoi(string(ricfg["buffer_size"])) + if err != nil { + log.Errorf("Failed to retrieve buffer size, skipping output") + //return false, err + } + icfgs[iicfg] = icfg + mod++ + } + } + } + + if mod == 0 { + return false, fmt.Errorf("no influxdb severs have been retrieved or legacy, skipping") + } + + // influxdb outputs exists, so we need to update the current relation + if len(n) == 0 && len(icfgs) > 0 { + log.Infof("MIGRATION: Trying to create %d outputs based on influxdb servers", len(icfgs)) + for _, icfg := range icfgs { + ocfg := &OutputCfg{ + ID: icfg.ID, + Active: true, + Backend: icfg.ID, + BufferSize: icfg.BufferSize, + FlushInterval: 60, + MetricBatchSize: 6500, + EnqueueOnError: true, + } + _, err = session.Insert(ocfg) + if err != nil { + session.Rollback() + return false, err + } + + // add relations + obktruct := OutputBackends{ + IDOutput: icfg.ID, + IDBackend: icfg.ID, + BackendType: "influxdb", + } + _, err := session.Insert(&obktruct) + if err != nil { + session.Rollback() + return false, err + } + } + } + err = session.Commit() + if err != nil { + return false, err + } + return true, nil +} diff --git a/pkg/config/outputcfg.go b/pkg/config/outputcfg.go new file mode 100644 index 00000000..a86cb467 --- /dev/null +++ b/pkg/config/outputcfg.go @@ -0,0 +1,226 @@ +package config + +import "fmt" + +/*************************** + Kafka DB backends + -GetOutputCfgCfgByID(struct) + -GetOutputCfgMap (map - for interna config use + -GetOutputCfgArray(Array - for web ui use ) + -AddOutputCfg + -DelOutputCfg + -UpdateOutputCfg + -GetOutputCfgAffectOnDel +***********************************/ + +/*GetOutputCfgByID get device data by id*/ +func (dbc *DatabaseCfg) GetOutputCfgByID(id string) (OutputCfg, error) { + cfgarray, err := dbc.GetOutputCfgArray("id='" + id + "'") + if err != nil { + return OutputCfg{}, err + } + if len(cfgarray) > 1 { + return OutputCfg{}, fmt.Errorf("Error %d results on get SnmpDeviceCfg by id %s", len(cfgarray), id) + } + if len(cfgarray) == 0 { + return OutputCfg{}, fmt.Errorf("Error no values have been returned with this id %s in the Kafka config table", id) + } + return *cfgarray[0], nil +} + +/*GetOutputCfgMap return data in map format*/ +func (dbc *DatabaseCfg) GetOutputCfgMap(filter string) (map[string]*OutputCfg, error) { + cfgarray, err := dbc.GetOutputCfgArray(filter) + cfgmap := make(map[string]*OutputCfg) + for _, val := range cfgarray { + cfgmap[val.ID] = val + log.Debugf("%+v", *val) + } + return cfgmap, err +} + +/*GetOutputCfgArray generate an array of devices with all its information */ +func (dbc *DatabaseCfg) GetOutputCfgArray(filter string) ([]*OutputCfg, error) { + var err error + var devices []*OutputCfg + // Get Only data for selected devices + if len(filter) > 0 { + if err = dbc.x.Where(filter).Find(&devices); err != nil { + log.Warnf("Fail to get OutputCfg data filteter with %s : %v\n", filter, err) + return nil, err + } + } else { + if err = dbc.x.Find(&devices); err != nil { + log.Warnf("Fail to get OutputCfg data: %v\n", err) + return nil, err + } + } + + // Asign Groups to devices. + var outputkafkabackends []*OutputBackends + if err = dbc.x.Find(&outputkafkabackends); err != nil { + log.Warnf("Fail to get Output and Backends relationship data: %v\n", err) + return devices, err + } + + for _, kVal := range devices { + for _, bk := range outputkafkabackends { + if bk.IDOutput == kVal.ID { + kVal.Backend = bk.IDBackend + kVal.BackendType = bk.BackendType + } + } + } + + // Load backends based on type + + return devices, nil +} + +/*AddOutputCfg for adding new devices*/ +func (dbc *DatabaseCfg) AddOutputCfg(dev OutputCfg) (int64, error) { + var err error + var affected int64 + session := dbc.x.NewSession() + if err := session.Begin(); err != nil { + // if returned then will rollback automatically + return 0, err + } + defer session.Close() + + affected, err = session.Insert(dev) + if err != nil { + session.Rollback() + return 0, err + } + + // Backends + obktruct := OutputBackends{ + IDOutput: dev.ID, + IDBackend: dev.Backend, + BackendType: dev.BackendType, + } + newback, err := session.Insert(&obktruct) + if err != nil { + session.Rollback() + return 0, err + } + + // no other relation + err = session.Commit() + if err != nil { + return 0, err + } + + log.Infof("Added new Output backend successfully with id %s ", dev.ID) + dbc.addChanges(affected + newback) + return affected, nil +} + +/*DelOutputCfg for deleting Kafka databases from ID*/ +func (dbc *DatabaseCfg) DelOutputCfg(id string) (int64, error) { + var affecteddev, affected int64 + var err error + + session := dbc.x.NewSession() + if err := session.Begin(); err != nil { + // if returned then will rollback automatically + return 0, err + } + defer session.Close() + // deleting references in SnmpDevCfg + + affecteddev, err = session.Where("outdb='" + id + "'").Cols("outdb").Update(&SnmpDeviceCfg{}) + if err != nil { + session.Rollback() + return 0, fmt.Errorf("error on delete Device with id on delete SnmpDevCfg with id: %s, error: %s", id, err) + } + + affected, err = session.Where("id='" + id + "'").Delete(&OutputCfg{}) + if err != nil { + session.Rollback() + return 0, err + } + + err = session.Commit() + if err != nil { + return 0, err + } + log.Infof("Deleted Successfully Kafka db with ID %s [ %d Devices Affected ]", id, affecteddev) + dbc.addChanges(affected + affecteddev) + return affected, nil +} + +/*UpdateOutputCfg for adding new Kafka*/ +func (dbc *DatabaseCfg) UpdateOutputCfg(id string, dev OutputCfg) (int64, error) { + var affectedouts, affecteddev, affected int64 + var err error + session := dbc.x.NewSession() + if err := session.Begin(); err != nil { + // if returned then will rollback automatically + return 0, err + } + defer session.Close() + + if id != dev.ID { // ID has been changed + affecteddev, err = session.Where("outdb='" + id + "'").Cols("outdb").Update(&SnmpDeviceCfg{OutDB: dev.ID}) + if err != nil { + session.Rollback() + return 0, fmt.Errorf("error Update OutputBackend id(old) %s with (new): %s, error: %s", id, dev.ID, err) + } + log.Infof("Updated Outputs to %d SNMPDevices ", affecteddev) + } + + // Remove all outputs in group. + _, err = session.Where("id_output='" + id + "'").Delete(&OutputBackends{}) + if err != nil { + session.Rollback() + return 0, fmt.Errorf("error on delete old output with id: %s, error: %s", id, err) + } + // adding again + // Backends + obktruct := OutputBackends{ + IDOutput: dev.ID, + IDBackend: dev.Backend, + BackendType: dev.BackendType, + } + newback, err := session.Insert(&obktruct) + if err != nil { + session.Rollback() + return 0, err + } + + affected, err = session.Where("id='" + id + "'").UseBool().AllCols().Update(dev) + if err != nil { + session.Rollback() + return 0, err + } + err = session.Commit() + if err != nil { + return 0, err + } + + log.Infof("Updated Output Config Successfully with id %s and data:%+v, affected", id, dev) + dbc.addChanges(affected + affectedouts + affecteddev + newback) + return affected, nil +} + +/*GetOutputCfgAffectOnDel for deleting devices from ID*/ +func (dbc *DatabaseCfg) GetOutputCfgAffectOnDel(id string) ([]*DbObjAction, error) { + var devices []*SnmpDeviceCfg + var obj []*DbObjAction + if err := dbc.x.Where("outdb='" + id + "'").Find(&devices); err != nil { + log.Warnf("Error on Get Outout db id %s for devices , error: %s", id, err) + return nil, err + } + + for _, val := range devices { + obj = append(obj, &DbObjAction{ + Type: "snmpdevicecfg", + TypeDesc: "SNMP Devices", + ObID: val.ID, + Action: "Remove Output from SNMPDevice", + }) + } + return obj, nil +} diff --git a/pkg/data/impexp/exportdata.go b/pkg/data/impexp/exportdata.go index 62193c1d..3b4290a2 100644 --- a/pkg/data/impexp/exportdata.go +++ b/pkg/data/impexp/exportdata.go @@ -111,7 +111,7 @@ func (e *ExportData) UpdateTmpObject() { e.tmpObjects = nil } -// Export exports data +// Export exports data func (e *ExportData) Export(ObjType string, id string, recursive bool, level int) error { switch ObjType { case "snmpdevicecfg": @@ -130,7 +130,20 @@ func (e *ExportData) Export(ObjType string, id string, recursive bool, level int for _, val := range v.MeasFilters { e.Export("measfiltercfg", val, recursive, level+1) } - e.Export("influxcfg", v.OutDB, recursive, level+1) + e.Export("outputcfg", v.OutDB, recursive, level+1) + case "outputcfg": + v, err := dbc.GetOutputCfgByID(id) + if err != nil { + return err + } + e.PrependObject(&ExportObject{ObjectTypeID: "outputcfg", ObjectID: id, ObjectCfg: v}) + switch v.BackendType { + case "influxdb": + e.Export("influxcfg", v.Backend, recursive, level+1) + case "kafka": + e.Export("kafkacfg", v.Backend, recursive, level+1) + default: + } case "influxcfg": // contains sensible probable v, err := dbc.GetInfluxCfgByID(id) @@ -138,6 +151,13 @@ func (e *ExportData) Export(ObjType string, id string, recursive bool, level int return err } e.PrependObject(&ExportObject{ObjectTypeID: "influxcfg", ObjectID: id, ObjectCfg: v}) + case "kafkacfg": + // contains sensible probable + v, err := dbc.GetKafkaCfgByID(id) + if err != nil { + return err + } + e.PrependObject(&ExportObject{ObjectTypeID: "kafkacfg", ObjectID: id, ObjectCfg: v}) case "measfiltercfg": v, err := dbc.GetMeasFilterCfgByID(id) if err != nil { diff --git a/pkg/data/impexp/import.go b/pkg/data/impexp/import.go index aeca9c23..c7ca2df9 100644 --- a/pkg/data/impexp/import.go +++ b/pkg/data/impexp/import.go @@ -22,11 +22,13 @@ func (e *ExportData) ImportCheck() (*ExportData, error) { o.Error = fmt.Sprintf("Error inconsistent data not ObjectCfg found on Imported data for id: %s", o.ObjectID) return nil, errors.New(o.Error) } + // try to marshal into a generic object raw, err := json.Marshal(o.ObjectCfg) if err != nil { o.Error = fmt.Sprintf("error on reformating object %s: error: %s ", o.ObjectID, err) return nil, errors.New(o.Error) } + // iterate over all known types switch o.ObjectTypeID { case "snmpdevicecfg": data := config.SnmpDeviceCfg{} @@ -58,6 +60,36 @@ func (e *ExportData) ImportCheck() (*ExportData, error) { o.Error = fmt.Sprintf("Duplicated object %s in the database", o.ObjectID) duplicated = append(duplicated, o) } + case "kafkacfg": + data := config.KafkaCfg{} + json.Unmarshal(raw, &data) + ers := binding.RawValidate(data) + if ers.Len() > 0 { + e, _ := json.Marshal(ers) + o.Error = string(e) + duplicated = append(duplicated, o) + break + } + _, err := dbc.GetKafkaCfgByID(o.ObjectID) + if err == nil { + o.Error = fmt.Sprintf("Duplicated object %s in the database", o.ObjectID) + duplicated = append(duplicated, o) + } + case "outputcfg": + data := config.OutputCfg{} + json.Unmarshal(raw, &data) + ers := binding.RawValidate(data) + if ers.Len() > 0 { + e, _ := json.Marshal(ers) + o.Error = string(e) + duplicated = append(duplicated, o) + break + } + _, err := dbc.GetOutputCfgByID(o.ObjectID) + if err == nil { + o.Error = fmt.Sprintf("Duplicated object %s in the database", o.ObjectID) + duplicated = append(duplicated, o) + } case "measfiltercfg": data := config.MeasFilterCfg{} json.Unmarshal(raw, &data) @@ -219,7 +251,50 @@ func (e *ExportData) Import(overwrite bool, autorename bool) error { if err != nil { return err } - + case "outputcfg": + log.Debugf("Importing outputcfg : %+v", o.ObjectCfg) + data := config.OutputCfg{} + json.Unmarshal(raw, &data) + var err error + _, err = dbc.GetOutputCfgByID(o.ObjectID) + if err == nil { // value exist already in the database + if overwrite == true { + _, err2 := dbc.UpdateOutputCfg(o.ObjectID, data) + if err2 != nil { + return fmt.Errorf("Error on overwrite object [%s] %s : %s", o.ObjectTypeID, o.ObjectID, err2) + } + break + } + } + if autorename == true { + data.ID = data.ID + suffix + } + _, err = dbc.AddOutputCfg(data) + if err != nil { + return err + } + case "kafkacfg": + log.Debugf("Importing kafkacfg : %+v", o.ObjectCfg) + data := config.KafkaCfg{} + json.Unmarshal(raw, &data) + var err error + _, err = dbc.GetKafkaCfgByID(o.ObjectID) + if err == nil { // value exist already in the database + if overwrite == true { + _, err2 := dbc.UpdateKafkaCfg(o.ObjectID, data) + if err2 != nil { + return fmt.Errorf("Error on overwrite object [%s] %s : %s", o.ObjectTypeID, o.ObjectID, err2) + } + break + } + } + if autorename == true { + data.ID = data.ID + suffix + } + _, err = dbc.AddKafkaCfg(data) + if err != nil { + return err + } case "influxcfg": log.Debugf("Importing influxcfg : %+v", o.ObjectCfg) data := config.InfluxCfg{} diff --git a/pkg/data/measurement/measurement.go b/pkg/data/measurement/measurement.go index bda1f210..c713ec5b 100644 --- a/pkg/data/measurement/measurement.go +++ b/pkg/data/measurement/measurement.go @@ -261,7 +261,7 @@ func (m *Measurement) BuildMultiIndexLabels() (map[string]string, []string, erro // Process dependencies | si: sort indexed | index: index info for si, index := range allindex { // Load on index Dependency all dependency params - m.Log.Debugf("[%s] - GOT INDEX MULTIPARAMS --> %+v", index.Label, index.Dependency) + m.Log.Debugf("[%s] - Got Index multiparams: %+v", index.Label, index.Dependency) if index.Dependency != nil { if index.Dependency.Index > len(allindex)-1 { @@ -768,7 +768,7 @@ func (m *Measurement) loadIndexedLabels() (map[string]string, error) { // i := strings.LastIndex(pdu.Name, ".") suffix := pdu.Name[m.curIdxPos+1:] - if m.cfg.IndexAsValue == true { + if m.cfg.IndexAsValue { allindex[suffix] = suffix return nil } @@ -943,7 +943,7 @@ func (m *Measurement) GatherLoop( varMap map[string]interface{}, tagMap map[string]string, systemOIDs []string, - influxClient *output.InfluxDB, + output *output.SinkDB, gatherLock *sync.Mutex, ) { m.Log.Info("MeasurementLoop Fist Check....") @@ -1066,7 +1066,7 @@ func (m *Measurement) GatherLoop( // compute next gather time ( needed to show in the UI ) m.stats.SetGatherNextTime(time.Now().Add(time.Duration(gatherFreq) * time.Second).Unix()) // Gather - err := m.GatherOnce(gatherLock, varMap, tagMap, influxClient) + err := m.GatherOnce(gatherLock, varMap, tagMap, output) if err != nil { // if error is because of no response from any metric // we can suppose the connection has been dropped @@ -1088,7 +1088,7 @@ func (m *Measurement) GatherLoop( m.filterUpdate() case bus.ForceGather: m.stats.ResetCounters() - m.GatherOnce(gatherLock, varMap, tagMap, influxClient) + m.GatherOnce(gatherLock, varMap, tagMap, output) case bus.Enabled: active, ok := val.Data.(bool) if !ok { @@ -1220,7 +1220,7 @@ func (m *Measurement) GatherOnce( gatherLock *sync.Mutex, varMap map[string]interface{}, tagMap map[string]string, - influxClient *output.InfluxDB, + output *output.SinkDB, ) error { start := time.Now() // Do not gather data if measurement is disabled or it doesn't have a connection or measurement is not initialized @@ -1263,22 +1263,18 @@ func (m *Measurement) GatherOnce( m.ComputeEvaluatedMetrics(varMap) - // prepare batchpoint - metSent, metError, measSent, measError, points := m.GetInfluxPoint(tagMap) - m.stats.AddMeasStats(metSent, metError, measSent, measError) - + // prepare metrics + metSent, metError, measSent, measError, metrics := m.GenMetrics(tagMap) + var measDrop int sentStats := time.Now() - // check if the influxClient is nil and skip the send process - if influxClient != nil { - bpts, _ := influxClient.BP() - if bpts != nil { - (*bpts).AddPoints(points) - // send data - influxClient.Send(bpts) - } else { - m.Log.Warnf("Can not send data to the output DB because of batchpoint creation error") + if output != nil { + tm, err := output.SendToBuffer(metrics, m.ID) + if err != nil { + measDrop = tm + m.Log.Errorf("unable to send metrics to the buffer - dropped metrics: %d, err: %s", measDrop, err) } } + m.stats.AddMeasStats(metSent, metError, measSent, measError, int64(measDrop)) elapsedSentStats := time.Since(sentStats) m.stats.AddSentDuration(sentStats, elapsedSentStats) @@ -1289,7 +1285,7 @@ func (m *Measurement) GatherOnce( m.Stats = m.getBasicStats() m.statsData.Unlock() if nProcs == 0 { - return fmt.Errorf("Device not connected") + return fmt.Errorf("device not connected") } return nil } diff --git a/pkg/data/measurement/measurement_test.go b/pkg/data/measurement/measurement_test.go index 68d148e3..6aefa1dd 100644 --- a/pkg/data/measurement/measurement_test.go +++ b/pkg/data/measurement/measurement_test.go @@ -59,16 +59,16 @@ func OrderMapByKey(m map[string]string) string { return buffer.String() } -func GetOutputInfluxMetrics(m *Measurement) { - m.Log.Infof("GOT MEAS --> %+v", m) +func GetOutputGenericMetrics(m *Measurement) { + m.Log.Infof("Processing measurement %+v", m) - metSent, metError, measSent, measError, ptarray := m.GetInfluxPoint(map[string]string{}) + metSent, metError, measSent, measError, ptarray := m.GenMetrics(map[string]string{}) m.Log.Infof("METRIC SENT[%d],METRIC ERROR[%d],MEAS SENT[%d], MEAS ERROR[%d]", metSent, metError, measSent, measError) for _, v := range ptarray { m.Log.Infof("GOT V %+v", v) - fields, _ := v.Fields() + fields := v.Fields() tags := OrderMapByKey(v.Tags()) for fname, fvalue := range fields { @@ -200,7 +200,7 @@ func Example_Measurement_GetMode_Value() { } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:test_name Tags:{} Field:metric_2_name ValueType:int64 Value:52 @@ -390,7 +390,7 @@ func Example_Measurement_GetMode_Indexed() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ portName:eth1 } Field:input ValueType:int64 Value:51 @@ -529,7 +529,7 @@ func Example_Measurement_GetMode_Indexed_Indirect() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ portName:eth1 } Field:input ValueType:int64 Value:51 @@ -697,7 +697,7 @@ func Example_Measurement_GetMode_Indexed_Multi_Indirect() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ portName:eth1 } Field:input ValueType:int64 Value:51 @@ -864,7 +864,7 @@ func Example_Measurement_GetMode_Indexed_MultiIndex_() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ portAlias:myPort2, portDesc:Not defined, portName:Not defined } Field:input ValueType:int64 Value:52 @@ -1025,7 +1025,7 @@ func Example_Measurement_GetMode_Indexed_MultiIndexDIM2() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ portDesc:port1, portName:eth1 } Field:input ValueType:int64 Value:51 @@ -1183,7 +1183,7 @@ func Example_Measurement_GetMode_Indexed_MultiIndex_DIM2_SKIP() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ portDesc:port1, portName:eth1 } Field:input ValueType:int64 Value:51 @@ -1337,7 +1337,7 @@ func Example_Measurement_GetMode_Indexed_MultiIndex_DIM2_FILLNONE() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ portDesc:port1, portName:eth1 } Field:input ValueType:int64 Value:51 @@ -1495,7 +1495,7 @@ func Example_Measurement_GetMode_Indexed_MultiIndex_DIM2_FILLSTRING() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ portDesc:port1, portName:eth1 } Field:input ValueType:int64 Value:51 @@ -1653,7 +1653,7 @@ func Example_Measurement_GetMode_Indexed_MultiIndex_DIM2_SKIP_CUSTOMRESULT() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ portDesc:port1, portName:eth1 } Field:input ValueType:int64 Value:51 @@ -1818,7 +1818,7 @@ func Example_Measurement_GetMode_Indexed_MultiIndex_DIM2_SKIP_CUSTOMRESULT_COMPL return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ ifType:5, portDesc:port1, portName:eth1 } Field:input ValueType:int64 Value:51 @@ -1972,6 +1972,7 @@ func Example_Measurement_GetMode_Indexed_MultiIndex_DIM2_FILLNONE_CUSTOMRESULT_C // 6.- MEASUREMENT ENGINE SETUP + l.SetLevel(logrus.DebugLevel) m := New(cfg, []string{}, map[string]*config.MeasFilterCfg{}, true, l) m.SetSNMPClient(cli) @@ -1983,7 +1984,7 @@ func Example_Measurement_GetMode_Indexed_MultiIndex_DIM2_FILLNONE_CUSTOMRESULT_C return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ ifType:5, portDesc:port1, portName:eth1 } Field:input ValueType:int64 Value:51 @@ -1994,6 +1995,10 @@ func Example_Measurement_GetMode_Indexed_MultiIndex_DIM2_FILLNONE_CUSTOMRESULT_C // Measurement:interfaces_data Tags:{ ifType:5, portDesc:port3 } Field:output ValueType:int64 Value:23 // Measurement:interfaces_data Tags:{ ifType:6, portDesc:port4 } Field:input ValueType:int64 Value:54 // Measurement:interfaces_data Tags:{ ifType:6, portDesc:port4 } Field:output ValueType:int64 Value:24 + + // GetOutputGenericMetricsInflux(m) + // // Unordered Output: + } func Example_Measurement_GetMode_Indexed_MultiIndex_QOS_CMSTATS() { @@ -2204,7 +2209,7 @@ func Example_Measurement_GetMode_Indexed_MultiIndex_QOS_CMSTATS() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ cmInfo:3, cmName:NonLocal, ifName:FastEthernet0/0, policyDirection:1, policyMapName:CPP } Field:cbQosCMPrePolicyByte64 ValueType:int64 Value:69858 @@ -2333,8 +2338,8 @@ func Example_Measurement_GetMode_Indexed_MultiIndex_QOS_MATCH_NAME() { metrics := map[string]*config.SnmpMetricCfg{ "cisco_cbQosMatchPrePolicyPkt64": { - ID: "cisco_cbQosMatchPrePolicyPkt64", - FieldName: "cbQosMatchPrePolicyPkt64 ", + ID: "cisco_cbQosMatchPrePolicyPkt64", + FieldName: "cbQosMatchPrePolicyPkt64 ", Description: "", BaseOID: ".1.3.6.1.4.1.9.9.166.1.16.1.1.3", DataSrcType: "Counter64", @@ -2448,7 +2453,7 @@ func Example_Measurement_GetMode_Indexed_MultiIndex_QOS_MATCH_NAME() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ cmName:ICMP, ifName:FastEthernet0/0, matchStmtName:CLASS_BACKUP, policyDirection:2, policyMapName:LAN_Out } Field:cbQosMatchPrePolicyPkt64 ValueType:int64 Value:8 @@ -2600,7 +2605,7 @@ func Example_Measurement_value_STRINGEVAL() { } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:test_name Tags:{} Field:metric_2_name ValueType:int64 Value:52 @@ -2742,7 +2747,7 @@ func Example_Measurement_Indexed_STRINGEVAL() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ portName:eth2 } Field:input ValueType:int64 Value:52 @@ -2898,7 +2903,7 @@ func Example_Measurement_Indexed_Indirect_STRINGEVAL() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ portName:eth2 } Field:input ValueType:int64 Value:52 @@ -3068,7 +3073,7 @@ func Example_Measurement_Indexed_Multi_Indirect_STRINGEVAL() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ portName:eth2 } Field:input ValueType:int64 Value:52 @@ -3206,7 +3211,7 @@ func Example_Measurement_GetMode_Indexed_MULTISTRINGPARSER_SKIPFIELD() { return } - GetOutputInfluxMetrics(m) + GetOutputGenericMetrics(m) // Unordered Output: // Measurement:interfaces_data Tags:{ myValue:value2, portName:eth2 } Field:output ValueType:int64 Value:22 diff --git a/pkg/data/measurement/influxpoint.go b/pkg/data/measurement/metricpoint.go similarity index 56% rename from pkg/data/measurement/influxpoint.go rename to pkg/data/measurement/metricpoint.go index a508f18f..0b98ef51 100644 --- a/pkg/data/measurement/influxpoint.go +++ b/pkg/data/measurement/metricpoint.go @@ -4,16 +4,17 @@ import ( "strings" "time" - client "github.com/influxdata/influxdb1-client/v2" + "github.com/influxdata/telegraf" + metric "github.com/influxdata/telegraf/metric" ) // GetInfluxPoint get points from measuremnetsl -func (m *Measurement) GetInfluxPoint(hostTags map[string]string) (int64, int64, int64, int64, []*client.Point) { +func (m *Measurement) GenMetrics(hostTags map[string]string) (int64, int64, int64, int64, []telegraf.Metric) { var metSent int64 var metError int64 var measSent int64 var measError int64 - var ptarray []*client.Point + var tmetrics []telegraf.Metric switch m.cfg.GetMode { case "value": @@ -29,7 +30,7 @@ func (m *Measurement) GetInfluxPoint(hostTags map[string]string) (int64, int64, me := vMtr.ImportFieldsAndTags(m.cfg.ID, Fields, Tags) metError += me // check again if metric is valid - if vMtr.Valid == true { + if vMtr.Valid { t = vMtr.CurTime } else { m.Log.Debugf("SKIPPING TS due to invalid %s metric %s", m.cfg.ID, vMtr.CurTime) @@ -38,17 +39,26 @@ func (m *Measurement) GetInfluxPoint(hostTags map[string]string) (int64, int64, metSent += int64(len(Fields)) m.Log.Debugf("FIELDS:%+v", Fields) - pt, err := client.NewPoint(m.cfg.Name, Tags, Fields, t) - if err != nil { - m.Log.Warnf("error in influx point building:%s", err) + tmetric := metric.New(m.cfg.Name, Tags, Fields, t) + + // Validate that at least len of fields > 0 + if len(Fields) == 0 { + m.Log.Warnf("error, empty fields: [%d]", len(Tags), len(Fields)) measError++ - } else { - m.Log.Debugf("GENERATED INFLUX POINT[%s] value: %+v", m.cfg.Name, pt) - ptarray = append(ptarray, pt) - measSent++ - k.Valid = true + break } + // the SNMPCollector can generate emtpy tags in certains configurations (i.e: fill() on multiindex measurements) + // and it is not allowed on IPL. The retrieve tmetric can return an empty tag value, + // so we should ensure that this is removed from the final metric + for tk, tv := range tmetric.Tags() { + if tv == "" { + tmetric.RemoveTag(tk) + } + } + measSent++ + k.Valid = true + tmetrics = append(tmetrics, tmetric) case "indexed", "indexed_it", "indexed_mit", "indexed_multiple": var t time.Time for idx, vIdx := range m.MetricTable.Row { @@ -66,7 +76,7 @@ func (m *Measurement) GetInfluxPoint(hostTags map[string]string) (int64, int64, stags = strings.Split(idx, "|") if len(stags) != len(m.TagName) { m.Log.Errorf("Tags %+v - doesn't match with generated tags %+v. Error in generating point", m.TagName, stags) - return metSent, metError, measSent, measError, ptarray + return metSent, metError, measSent, measError, tmetrics } } for k, v := range m.TagName { @@ -78,7 +88,7 @@ func (m *Measurement) GetInfluxPoint(hostTags map[string]string) (int64, int64, me := vMtr.ImportFieldsAndTags(m.cfg.ID, Fields, Tags) metError += me // check again if metric is valid - if vMtr.Valid == true { + if vMtr.Valid { t = vMtr.CurTime } else { m.Log.Debugf("SKIPPING TS due to invalid %s metric %s", m.cfg.ID, vMtr.CurTime) @@ -87,19 +97,26 @@ func (m *Measurement) GetInfluxPoint(hostTags map[string]string) (int64, int64, metSent += int64(len(Fields)) // here we can chek Fields names prior to send data m.Log.Debugf("FIELDS:%+v TAGS:%+v", Fields, Tags) - pt, err := client.NewPoint(m.cfg.Name, Tags, Fields, t) - if err != nil { - m.Log.Warnf("error in influx point creation :%s", err) + tmetric := metric.New(m.cfg.Name, Tags, Fields, t) + + // Validate that at least len of fields > 0 + if len(Fields) == 0 { + m.Log.Warnf("error, empty fields: [%d]", len(Tags), len(Fields)) measError++ - } else { - m.Log.Debugf("GENERATED INFLUX POINT[%s] index [%s]: %+v", m.cfg.Name, idx, pt) - ptarray = append(ptarray, pt) - measSent++ - vIdx.Valid = true + continue } + // the SNMPCollector can generate emtpy tags in certains configurations (i.e: fill() on multiindex measurements) + // and it is not allowed on IPL. The retrieve tmetric can return an empty tag value, + // so we should ensure that this is removed from the final metric + for tk, tv := range tmetric.Tags() { + if tv == "" { + tmetric.RemoveTag(tk) + } + } + measSent++ + vIdx.Valid = true + tmetrics = append(tmetrics, tmetric) } - } - - return metSent, metError, measSent, measError, ptarray + return metSent, metError, measSent, measError, tmetrics } diff --git a/pkg/data/stats/gatherstats.go b/pkg/data/stats/gatherstats.go index 7b6deec7..ae343ae6 100644 --- a/pkg/data/stats/gatherstats.go +++ b/pkg/data/stats/gatherstats.go @@ -59,8 +59,10 @@ const ( DeviceActive = 21 // DeviceConnected 1 if connected 0 if not DeviceConnected = 22 + // MetricsDropped all metrics that are discarded due to full buffer + MeasurementDropped = 23 // DevStatTypeSize special value to set the last stat position - DevStatTypeSize = 23 + DevStatTypeSize = 24 ) // GatherStats minimal info to show users @@ -126,6 +128,7 @@ func (s *GatherStats) Init(t string, id string, tm map[string]string, l utils.Lo s.Counters[BackEndSentDuration] = 0.0 s.Counters[DeviceActive] = 0 s.Counters[DeviceConnected] = 0 + s.Counters[MeasurementDropped] = 0 } func (s *GatherStats) reset() { @@ -218,6 +221,7 @@ func (s *GatherStats) getMetricFields() map[string]interface{} { /*12*/ "metric_sent_errors": s.Counters[MetricSentErrors], /*13*/ "measurement_sent": s.Counters[MeasurementSent], /*14*/ "measurement_sent_errors": s.Counters[MeasurementSentErrors], + /*23*/ "measurement_dropped": s.Counters[MeasurementDropped], /*15*/ "cycle_gather_start_time": s.Counters[CycleGatherStartTime], /*16*/ "cycle_gather_duration": s.Counters[CycleGatherDuration], /*17*/ "filter_start_time": s.Counters[FilterStartTime], @@ -290,7 +294,7 @@ func (s *GatherStats) Send() { connectedTag = "true" s.log.Infof("[%s] STATS SNMP GET: snmp polling took [%f seconds] SNMP: Gets [%d] , Processed [%d], Errors [%d]", s.Type, s.Counters[CycleGatherDuration], s.Counters[SnmpOIDGetAll], s.Counters[SnmpOIDGetProcessed], s.Counters[SnmpOIDGetErrors]) s.log.Infof("[%s] STATS SNMP FILTER: filter polling took [%f seconds] ", s.Type, s.Counters[FilterDuration]) - s.log.Infof("[%s] STATS INFLUX: influx send took [%f seconds]", s.Type, s.Counters[BackEndSentDuration]) + s.log.Infof("[%s] STATS BUFFER: buffer send took [%f seconds]", s.Type, s.Counters[BackEndSentDuration]) fields = s.getMetricFields() case s.Active && !s.Connected: activeTag = "true" @@ -345,6 +349,7 @@ func (s *GatherStats) Combine(sc *GatherStats) { s.Counters[MetricSentErrors] = s.Counters[MetricSentErrors].(int) + sc.Counters[MetricSentErrors].(int) s.Counters[MeasurementSent] = s.Counters[MeasurementSent].(int) + sc.Counters[MeasurementSent].(int) s.Counters[MeasurementSentErrors] = s.Counters[MeasurementSentErrors].(int) + sc.Counters[MeasurementSentErrors].(int) + s.Counters[MeasurementDropped] = s.Counters[MeasurementDropped].(int) + sc.Counters[MeasurementDropped].(int) // Snmp Stats s.Counters[SnmpOIDGetAll] = s.Counters[SnmpOIDGetAll].(int) + sc.Counters[SnmpOIDGetAll].(int) s.Counters[SnmpOIDGetProcessed] = s.Counters[SnmpOIDGetProcessed].(int) + sc.Counters[SnmpOIDGetProcessed].(int) @@ -361,13 +366,15 @@ func (s *GatherStats) Combine(sc *GatherStats) { } // AddMeasStats add measurement stats to the device stats object -func (s *GatherStats) AddMeasStats(mets int64, mete int64, meass int64, mease int64) { +func (s *GatherStats) AddMeasStats(mets int64, mete int64, meass int64, mease int64, measd int64) { s.mutex.Lock() defer s.mutex.Unlock() s.Counters[MetricSent] = s.Counters[MetricSent].(int) + int(mets) s.Counters[MetricSentErrors] = s.Counters[MetricSentErrors].(int) + int(mete) s.Counters[MeasurementSent] = s.Counters[MeasurementSent].(int) + int(meass) s.Counters[MeasurementSentErrors] = s.Counters[MeasurementSentErrors].(int) + int(mease) + s.Counters[MeasurementDropped] = s.Counters[MeasurementDropped].(int) + int(measd) + } // UpdateSnmpGetStats update snmp statistics diff --git a/pkg/main.go b/pkg/main.go index 5a277a7c..db193799 100644 --- a/pkg/main.go +++ b/pkg/main.go @@ -20,6 +20,7 @@ import ( "github.com/toni-moreno/snmpcollector/pkg/agent/bus" "github.com/toni-moreno/snmpcollector/pkg/agent/device" "github.com/toni-moreno/snmpcollector/pkg/agent/output" + "github.com/toni-moreno/snmpcollector/pkg/agent/output/backend" "github.com/toni-moreno/snmpcollector/pkg/agent/selfmon" "github.com/toni-moreno/snmpcollector/pkg/config" "github.com/toni-moreno/snmpcollector/pkg/data/impexp" @@ -170,6 +171,8 @@ func init() { snmp.SetLogDir(logDir) output.SetLogger(log) + backend.SetLogger(log) + selfmon.SetLogger(log) // devices needs access to all db loaded data device.SetDBConfig(&agent.DBConfig) diff --git a/pkg/webui/apicfg-influxerver.go b/pkg/webui/apicfg-influxerver.go index 1164422d..a59b13dc 100644 --- a/pkg/webui/apicfg-influxerver.go +++ b/pkg/webui/apicfg-influxerver.go @@ -5,7 +5,7 @@ import ( "github.com/go-macaron/binding" "github.com/toni-moreno/snmpcollector/pkg/agent" - "github.com/toni-moreno/snmpcollector/pkg/agent/output" + "github.com/toni-moreno/snmpcollector/pkg/agent/output/backend" "github.com/toni-moreno/snmpcollector/pkg/config" "gopkg.in/macaron.v1" ) @@ -267,7 +267,7 @@ func PingInfluxServer(ctx *Context, cfg config.InfluxCfg) { // "$ref": "#/responses/idOfStringResp" log.Infof("trying to ping influx server %s : %+v", cfg.ID, cfg) - _, elapsed, message, err := output.Ping(&cfg) + _, elapsed, message, err := backend.Ping(&cfg) type result struct { Result string Elapsed time.Duration diff --git a/pkg/webui/apicfg-kafkaserver.go b/pkg/webui/apicfg-kafkaserver.go new file mode 100644 index 00000000..edc0f4d0 --- /dev/null +++ b/pkg/webui/apicfg-kafkaserver.go @@ -0,0 +1,236 @@ +package webui + +import ( + "github.com/go-macaron/binding" + "github.com/toni-moreno/snmpcollector/pkg/agent" + "github.com/toni-moreno/snmpcollector/pkg/config" + "gopkg.in/macaron.v1" +) + +// NewAPICfgKafkaServer KafkaServer API REST creator +func NewAPICfgKafkaServer(m *macaron.Macaron) error { + bind := binding.Bind + + m.Group("/api/cfg/kafkaservers", func() { + m.Get("/", reqSignedIn, GetKafkaServer) + m.Get("/:id", reqSignedIn, GetKafkaServerByID) + m.Post("/", reqSignedIn, bind(config.KafkaCfg{}), AddKafkaServer) + m.Put("/:id", reqSignedIn, bind(config.KafkaCfg{}), UpdateKafkaServer) + m.Delete("/:id", reqSignedIn, DeleteKafkaServer) + m.Get("/checkondel/:id", reqSignedIn, GetKafkaAffectOnDel) + }) + + return nil +} + +// GetKafkaServer Return Server Array +func GetKafkaServer(ctx *Context) { + // swagger:operation GET /cfg/kafkaservers Config_KafkaServers GetKafkaServer + //--- + // summary: Get All Kafka Servers Config Items from DB + // description: Get All Kafka Servers config Items as an array from DB + // tags: + // - "Kafka Servers Config" + // + // responses: + // '200': + // description: "OK" + // schema: + // "$ref": "#/responses/idOfArrayKafkaCfgResp" + // '404': + // description: unexpected error + // schema: + // "$ref": "#/responses/idOfStringResp" + cfgarray, err := agent.MainConfig.Database.GetKafkaCfgArray("") + if err != nil { + ctx.JSON(404, err.Error()) + log.Errorf("Error on get Kafka db :%+s", err) + return + } + ctx.JSON(200, &cfgarray) + log.Debugf("Getting DEVICEs %+v", &cfgarray) +} + +// GetKafkaServerByID --pending-- +func GetKafkaServerByID(ctx *Context) { + // swagger:operation GET /cfg/kafkaservers/{id} Config_KafkaServers GetKafkaServerByID + //--- + // summary: Get KafkaServer Config from DB + // description: Get KafkaServers config info by ID from DB + // tags: + // - "Kafka Servers Config" + // + // parameters: + // - name: id + // in: path + // description: KafkaServer to get + // required: true + // type: string + // responses: + // '200': + // description: "OK" + // schema: + // "$ref": "#/definitions/KafkaCfg" + // '404': + // description: unexpected error + // schema: + // "$ref": "#/responses/idOfStringResp" + id := ctx.Params(":id") + dev, err := agent.MainConfig.Database.GetKafkaCfgByID(id) + if err != nil { + log.Warningf("Error on get Kafka db data for device %s , error: %s", id, err) + ctx.JSON(404, err.Error()) + } else { + ctx.JSON(200, &dev) + } +} + +// AddKafkaServer Insert new measurement groups to de internal BBDD --pending-- +func AddKafkaServer(ctx *Context, dev config.KafkaCfg) { + // swagger:operation POST /cfg/kafkaservers Config_KafkaServers AddKafkaServer + //--- + // summary: Add new Kafka Server Config + // description: Add KafkaServer from Data + // tags: + // - "Kafka Servers Config" + // + // parameters: + // - name: KafkaCfg + // in: body + // description: KafkaConfig to add + // required: true + // schema: + // "$ref": "#/definitions/KafkaCfg" + // + // responses: + // '200': + // description: "OK" + // schema: + // "$ref": "#/definitions/KafkaCfg" + // '404': + // description: unexpected error + // schema: + // "$ref": "#/responses/idOfStringResp" + log.Printf("ADDING Kafka Backend %+v", dev) + affected, err := agent.MainConfig.Database.AddKafkaCfg(dev) + if err != nil { + log.Warningf("Error on insert new Backend %s , affected : %+v , error: %s", dev.ID, affected, err) + ctx.JSON(404, err.Error()) + } else { + // TODO: review if needed return data or affected + ctx.JSON(200, &dev) + } +} + +// UpdateKafkaServer --pending-- +func UpdateKafkaServer(ctx *Context, dev config.KafkaCfg) { + // swagger:operation PUT /cfg/kafkaservers/{id} Config_KafkaServers UpdateKafkaServer + //--- + // summary: Update Kafka Server Config + // description: Update KafkaServer from Data with specified ID + // tags: + // - "Kafka Servers Config" + // + // parameters: + // - name: id + // in: path + // description: Kafka Config ID to update + // required: true + // type: string + // - name: KafkaCfg + // in: body + // description: Metric to add + // required: true + // schema: + // "$ref": "#/definitions/KafkaCfg" + // + // responses: + // '200': + // description: "OK" + // schema: + // "$ref": "#/definitions/KafkaCfg" + // '404': + // description: unexpected error + // schema: + // "$ref": "#/responses/idOfStringResp" + id := ctx.Params(":id") + log.Debugf("Tying to update: %+v", dev) + affected, err := agent.MainConfig.Database.UpdateKafkaCfg(id, dev) + if err != nil { + log.Warningf("Error on update Kafka db %s , affected : %+v , error: %s", dev.ID, affected, err) + } else { + // TODO: review if needed return device data + ctx.JSON(200, &dev) + } +} + +// DeleteKafkaServer --pending-- +func DeleteKafkaServer(ctx *Context) { + // swagger:operation DELETE /cfg/kafkaservers/{id} Config_KafkaServers DeleteKafkaServer + //--- + // summary: Delete Kafka Server Config on DB + // description: Delete Kafka Server on DB with specified ID + // tags: + // - "Kafka Servers Config" + // + // parameters: + // - name: id + // in: path + // description: Measurement Group ID to delete + // required: true + // type: string + // + // responses: + // '200': + // description: "OK" + // schema: + // "$ref": "#/responses/idOfStringResp" + // '404': + // description: unexpected error + // schema: + // "$ref": "#/responses/idOfStringResp" + id := ctx.Params(":id") + log.Debugf("Tying to delete: %+v", id) + affected, err := agent.MainConfig.Database.DelKafkaCfg(id) + if err != nil { + log.Warningf("Error on delete influx db %s , affected : %+v , error: %s", id, affected, err) + ctx.JSON(404, err.Error()) + } else { + ctx.JSON(200, "deleted") + } +} + +// GetKafkaAffectOnDel --pending-- +func GetKafkaAffectOnDel(ctx *Context) { + // swagger:operation GET /cfg/kafkaservers/checkondel/{id} Config_KafkaServers GetKafkaAffectOnDel + //--- + // summary: Check affected sources. + // description: Get all existing Objects affected when deleted the KafkaServer. + // tags: + // - "Kafka Servers Config" + // + // parameters: + // - name: id + // in: path + // description: The measurement Group ID to check + // required: true + // type: string + // + // responses: + // '200': + // description: Object Array + // schema: + // "$ref": "#/responses/idOfCheckOnDelResp" + // '404': + // description: unexpected error + // schema: + // "$ref": "#/responses/idOfStringResp" + id := ctx.Params(":id") + obarray, err := agent.MainConfig.Database.GetKafkaCfgAffectOnDel(id) + if err != nil { + log.Warningf("Error on get object array for influx device %s , error: %s", id, err) + ctx.JSON(404, err.Error()) + } else { + ctx.JSON(200, &obarray) + } +} diff --git a/pkg/webui/apicfg-outputs.go b/pkg/webui/apicfg-outputs.go new file mode 100644 index 00000000..8a5eac41 --- /dev/null +++ b/pkg/webui/apicfg-outputs.go @@ -0,0 +1,236 @@ +package webui + +import ( + "github.com/go-macaron/binding" + "github.com/toni-moreno/snmpcollector/pkg/agent" + "github.com/toni-moreno/snmpcollector/pkg/config" + "gopkg.in/macaron.v1" +) + +// NewAPICfgOutput Output API REST creator +func NewAPICfgOutput(m *macaron.Macaron) error { + bind := binding.Bind + + m.Group("/api/cfg/output", func() { + m.Get("/", reqSignedIn, GetOutput) + m.Get("/:id", reqSignedIn, GetOutputByID) + m.Post("/", reqSignedIn, bind(config.OutputCfg{}), AddOutput) + m.Put("/:id", reqSignedIn, bind(config.OutputCfg{}), UpdateOutput) + m.Delete("/:id", reqSignedIn, DeleteOutput) + m.Get("/checkondel/:id", reqSignedIn, GetOutputAffectOnDel) + }) + + return nil +} + +// GetOutput Return Server Array +func GetOutput(ctx *Context) { + // swagger:operation GET /cfg/output Config_Outputs GetOutput + //--- + // summary: Get All Output Config Items from DB + // description: Get All Output Config Items as an array from DB + // tags: + // - "Output Config" + // + // responses: + // '200': + // description: "OK" + // schema: + // "$ref": "#/responses/idOfArrayOutputCfgResp" + // '404': + // description: unexpected error + // schema: + // "$ref": "#/responses/idOfStringResp" + cfgarray, err := agent.MainConfig.Database.GetOutputCfgArray("") + if err != nil { + ctx.JSON(404, err.Error()) + log.Errorf("Error on get Output :%+s", err) + return + } + ctx.JSON(200, &cfgarray) + log.Debugf("Getting DEVICEs %+v", &cfgarray) +} + +// GetOutputByID --pending-- +func GetOutputByID(ctx *Context) { + // swagger:operation GET /cfg/output/{id} Config_Outputs GetOutputByID + //--- + // summary: Get Output Config from DB + // description: Get Outputs config info by ID from DB + // tags: + // - "Output Config" + // + // parameters: + // - name: id + // in: path + // description: Output to get + // required: true + // type: string + // responses: + // '200': + // description: "OK" + // schema: + // "$ref": "#/definitions/OutputCfg" + // '404': + // description: unexpected error + // schema: + // "$ref": "#/responses/idOfStringResp" + id := ctx.Params(":id") + dev, err := agent.MainConfig.Database.GetOutputCfgByID(id) + if err != nil { + log.Warningf("Error on get Output with id %s, error: %s", id, err) + ctx.JSON(404, err.Error()) + } else { + ctx.JSON(200, &dev) + } +} + +// AddOutput Insert new measurement groups to de internal BBDD --pending-- +func AddOutput(ctx *Context, dev config.OutputCfg) { + // swagger:operation POST /cfg/output Config_Outputs AddOutput + //--- + // summary: Add new Output Config + // description: Add Output from Data + // tags: + // - "Output Config" + // + // parameters: + // - name: OutputCfg + // in: body + // description: OutputConfig to add + // required: true + // schema: + // "$ref": "#/definitions/OutputCfg" + // + // responses: + // '200': + // description: "OK" + // schema: + // "$ref": "#/definitions/OutputCfg" + // '404': + // description: unexpected error + // schema: + // "$ref": "#/responses/idOfStringResp" + log.Printf("ADDING Output Backend %+v", dev) + affected, err := agent.MainConfig.Database.AddOutputCfg(dev) + if err != nil { + log.Warningf("Error on insert new Backend %s, affected: %+v , error: %s", dev.ID, affected, err) + ctx.JSON(404, err.Error()) + } else { + // TODO: review if needed return data or affected + ctx.JSON(200, &dev) + } +} + +// UpdateOutput --pending-- +func UpdateOutput(ctx *Context, dev config.OutputCfg) { + // swagger:operation PUT /cfg/output/{id} Config_Outputs UpdateOutput + //--- + // summary: Update Output Config + // description: Update Output from Data with specified ID + // tags: + // - "Output Config" + // + // parameters: + // - name: id + // in: path + // description: Output Config ID to update + // required: true + // type: string + // - name: OutputCfg + // in: body + // description: Metric to add + // required: true + // schema: + // "$ref": "#/definitions/OutputCfg" + // + // responses: + // '200': + // description: "OK" + // schema: + // "$ref": "#/definitions/OutputCfg" + // '404': + // description: unexpected error + // schema: + // "$ref": "#/responses/idOfStringResp" + id := ctx.Params(":id") + log.Debugf("Tying to update: %+v", dev) + affected, err := agent.MainConfig.Database.UpdateOutputCfg(id, dev) + if err != nil { + log.Warningf("Error on update Output %s, affected: %+v , error: %s", dev.ID, affected, err) + } else { + // TODO: review if needed return device data + ctx.JSON(200, &dev) + } +} + +// DeleteOutput --pending-- +func DeleteOutput(ctx *Context) { + // swagger:operation DELETE /cfg/output/{id} Config_Outputs DeleteOutput + //--- + // summary: Delete Output Config on DB + // description: Delete Output on DB with specified ID + // tags: + // - "Output Config" + // + // parameters: + // - name: id + // in: path + // description: The Output ID to delete + // required: true + // type: string + // + // responses: + // '200': + // description: "OK" + // schema: + // "$ref": "#/responses/idOfStringResp" + // '404': + // description: unexpected error + // schema: + // "$ref": "#/responses/idOfStringResp" + id := ctx.Params(":id") + log.Debugf("Tying to delete: %+v", id) + affected, err := agent.MainConfig.Database.DelOutputCfg(id) + if err != nil { + log.Warningf("Error on delete Output %s, affected : %+v, error: %s", id, affected, err) + ctx.JSON(404, err.Error()) + } else { + ctx.JSON(200, "deleted") + } +} + +// GetOutputAffectOnDel --pending-- +func GetOutputAffectOnDel(ctx *Context) { + // swagger:operation GET /cfg/output/checkondel/{id} Config_Outputs GetOutputAffectOnDel + //--- + // summary: Check affected sources. + // description: Get all existing Objects affected when deleted the Output. + // tags: + // - "Output Config" + // + // parameters: + // - name: id + // in: path + // description: The Output ID to check + // required: true + // type: string + // + // responses: + // '200': + // description: Object Array + // schema: + // "$ref": "#/responses/idOfCheckOnDelResp" + // '404': + // description: unexpected error + // schema: + // "$ref": "#/responses/idOfStringResp" + id := ctx.Params(":id") + obarray, err := agent.MainConfig.Database.GetOutputCfgAffectOnDel(id) + if err != nil { + log.Warningf("Error on get object array for Outputs %s, error: %s", id, err) + ctx.JSON(404, err.Error()) + } else { + ctx.JSON(200, &obarray) + } +} diff --git a/pkg/webui/webserver.go b/pkg/webui/webserver.go index 71b92110..a41a5686 100644 --- a/pkg/webui/webserver.go +++ b/pkg/webui/webserver.go @@ -191,6 +191,10 @@ func WebServer(publicPath string, httpListen string, cfg *config.HTTPConfig, id NewAPICfgInfluxServer(m) + NewAPICfgKafkaServer(m) + + NewAPICfgOutput(m) + NewAPICfgSnmpDevice(m) NewAPICfgCustomFilter(m) diff --git a/src/common/dataservice/export-file-modal.ts b/src/common/dataservice/export-file-modal.ts index b13c24bb..2d52de05 100644 --- a/src/common/dataservice/export-file-modal.ts +++ b/src/common/dataservice/export-file-modal.ts @@ -16,6 +16,8 @@ import { MeasFilterService } from '../../measfilter/measfiltercfg.service'; import { CustomFilterService } from '../../customfilter/customfilter.service'; import { VarCatalogService } from '../../varcatalog/varcatalogcfg.service'; import { Subscription } from 'rxjs'; +import { KafkaServerService } from 'kafkaserver/kafkaservercfg.service'; +import { OutputService } from 'output/outputcfg.service'; @Component({ selector: 'export-file-modal', @@ -149,7 +151,7 @@ import { Subscription } from 'rxjs'; `, styleUrls: ['./import-modal-styles.css'], - providers: [ExportServiceCfg, InfluxServerService, SnmpDeviceService, SnmpMetricService, MeasurementService, OidConditionService,MeasGroupService, MeasFilterService, CustomFilterService, VarCatalogService, TreeView] + providers: [ExportServiceCfg, KafkaServerService, OutputService, InfluxServerService, SnmpDeviceService, SnmpMetricService, MeasurementService, OidConditionService,MeasGroupService, MeasFilterService, CustomFilterService, VarCatalogService, TreeView] }) export class ExportFileModal { @@ -173,6 +175,7 @@ export class ExportFileModal { public mySubscriber: Subscription; constructor(builder: FormBuilder, public exportServiceCfg : ExportServiceCfg, + public kafkaServerService: KafkaServerService, public outputService: OutputService, public influxServerService: InfluxServerService, public metricMeasService: SnmpMetricService, public measurementService: MeasurementService, public oidConditionService : OidConditionService, public snmpDeviceService: SnmpDeviceService, public measGroupService: MeasGroupService, @@ -198,15 +201,17 @@ export class ExportFileModal { //Single Object public colorsObject : Object = { - "snmpdevicecfg" : 'danger', - "influxcfg" : 'info', - "measfiltercfg": 'warning', - "oidconditioncfg" : 'success', - "customfiltercfg" : 'default', - "measurementcfg" : 'primary', - "snmpmetriccfg" : 'warning', - "measgroupcfg" : 'success', - "varcatalogcfg" : 'default' + "snmpdevicecfg" : 'danger', + "outputcfg" : 'success', + "influxcfg" : 'info', + "kafkacfg" : 'primary', + "measfiltercfg": 'warning', + "oidconditioncfg" : 'success', + "customfiltercfg" : 'default', + "measurementcfg" : 'primary', + "snmpmetriccfg" : 'warning', + "measgroupcfg" : 'success', + "varcatalogcfg" : 'default' }; //Control to load exported result @@ -234,7 +239,9 @@ export class ExportFileModal { //Bulk Objects public objectTypes : any = [ {'Type':"snmpdevicecfg", 'Class' : 'danger', 'Visible': false}, + {'Type':"outputcfg" ,'Class' : 'success', 'Visible': false}, {'Type':"influxcfg" ,'Class' : 'info', 'Visible': false}, + {'Type':"kafkacfg" ,'Class' : 'primary', 'Visible': false}, {'Type':"measfiltercfg", 'Class' : 'warning','Visible': false}, {'Type':"oidconditioncfg", 'Class' : 'success', 'Visible': false}, {'Type':"customfiltercfg", 'Class' : 'default', 'Visible': false}, @@ -436,6 +443,21 @@ export class ExportFileModal { ); break; + case 'outputcfg': + this.mySubscriber = this.outputService.getOutput(filter) + .subscribe( + data => { + this.dataArray=data; + this.resultArray = this.dataArray; + for (let i in this.dataArray[0]) { + this.listFilterProp.push({ 'id': i, 'name': i }); + } + }, + err => {console.log(err)}, + () => {console.log("DONE")} + ); + break; + case 'influxcfg': this.mySubscriber = this.influxServerService.getInfluxServer(filter) .subscribe( @@ -450,6 +472,20 @@ export class ExportFileModal { () => {console.log("DONE")} ); break; + case 'kafkacfg': + this.mySubscriber = this.kafkaServerService.getKafkaServer(filter) + .subscribe( + data => { + this.dataArray=data; + this.resultArray = this.dataArray; + for (let i in this.dataArray[0]) { + this.listFilterProp.push({ 'id': i, 'name': i }); + } + }, + err => {console.log(err)}, + () => {console.log("DONE")} + ); + break; case 'oidconditioncfg': this.mySubscriber = this.oidConditionService.getConditions(filter) .subscribe( diff --git a/src/common/dataservice/export.service.ts b/src/common/dataservice/export.service.ts index d3d3dc33..11aec9ad 100644 --- a/src/common/dataservice/export.service.ts +++ b/src/common/dataservice/export.service.ts @@ -15,7 +15,6 @@ export class ExportServiceCfg { // return an observable return this.httpAPI.get('/api/cfg/export/'+type+'/'+id) .map((res) => { - //return new Blob([res.arrayBuffer()],{type: "application/octet-stream" }) return [new Blob([res['_body']],{type: "application/json"}),res.json()]; }) } @@ -36,16 +35,4 @@ export class ExportServiceCfg { return [new Blob([res['_body']],{type: "application/json"}),res.json()]; }) } -} - - -/*this.exportServiceCfg.exportRecursive(item.exportType, item.row.ID).subscribe( -data => { - console.log(data); - saveAs(data[0], data[1].Info.FileName || item.row.ID + ".json"); -}, -err => console.error(err), -() => console.log("DONE"), -) -console.log(item); -*/ +} \ No newline at end of file diff --git a/src/common/dataservice/treeview.ts b/src/common/dataservice/treeview.ts index 415857e5..9b6dbdfe 100644 --- a/src/common/dataservice/treeview.ts +++ b/src/common/dataservice/treeview.ts @@ -56,7 +56,9 @@ export class TreeView { public colorsObject : Object = { "snmpdevicecfg" : 'danger', + "outputcfg" : 'success', "influxcfg" : 'info', + "kafkacfg" : 'primary', "measfiltercfg": 'warning', "oidconditioncfg" : 'success', "customfiltercfg" : 'default', @@ -68,7 +70,7 @@ export class TreeView { }; recursive : boolean; - show() { + show() { console.log("SHOWN"); } constructor(){ @@ -80,7 +82,6 @@ export class TreeView { this.visible = !this.visible; } - public addItem(ObjectID: any, ObjectTypeID: any): void { this.addClicked.emit({ ObjectID, ObjectTypeID, "Options" : {'Recursive': false }}); } diff --git a/src/common/multiselect-dropdown.ts b/src/common/multiselect-dropdown.ts index 953b3464..f908001e 100644 --- a/src/common/multiselect-dropdown.ts +++ b/src/common/multiselect-dropdown.ts @@ -11,7 +11,7 @@ import { CommonModule } from '@angular/common'; import { Observable } from 'rxjs/Rx'; import { FormsModule, NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'; -declare var _:any; +declare var _: any; const MULTISELECT_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, @@ -23,6 +23,7 @@ export interface IMultiSelectOption { id: any; name: string; badge?: string; + parent?: boolean; } export interface IMultiSelectSettings { @@ -37,6 +38,8 @@ export interface IMultiSelectSettings { dynamicTitleMaxItems?: number; maxHeight?: string; singleSelect?: boolean; + uniqueSelect?: boolean; + returnOption?: boolean; } export interface IMultiSelectTexts { @@ -98,7 +101,7 @@ export class MultiSelectSearchFilter { - | + | {{ option.name }} {{option.badge}} @@ -111,6 +114,7 @@ export class MultiselectDropdown implements OnInit, DoCheck, ControlValueAccesso @Input() options: Array; @Input() settings: IMultiSelectSettings; @Input() texts: IMultiSelectTexts; + @Input() extravar: any; @Output() selectionLimitReached = new EventEmitter(); @HostListener('document: click', ['$event.target']) onClick(target) { @@ -126,8 +130,8 @@ export class MultiselectDropdown implements OnInit, DoCheck, ControlValueAccesso } } - protected onModelChange: Function = (_: any) => {}; - protected onModelTouched: Function = () => {}; + protected onModelChange: Function = (_: any) => { }; + protected onModelTouched: Function = () => { }; protected model: any[]; public title: string; protected differ: any; @@ -169,7 +173,7 @@ export class MultiselectDropdown implements OnInit, DoCheck, ControlValueAccesso this.title = this.texts.defaultTitle; } - writeValue(value: any) : void { + writeValue(value: any): void { if (value !== undefined) { this.model = value; } @@ -188,7 +192,7 @@ export class MultiselectDropdown implements OnInit, DoCheck, ControlValueAccesso } ngDoCheck() { - if(this.model){ + if (this.model) { if (this.settings.singleSelect) { this.updateNumSelected(); this.updateTitle(); @@ -210,15 +214,23 @@ export class MultiselectDropdown implements OnInit, DoCheck, ControlValueAccesso } isSelected(option: IMultiSelectOption): boolean { - if (this.settings.singleSelect === true) return (this.model === option.id); + if (this.settings.singleSelect === true && !this.settings.uniqueSelect) { + return (this.model === option.id); + } else if (this.settings.singleSelect === true && this.settings.uniqueSelect) { + let uniqesel: any = (option.id + ".." + option.badge) + return this.model === uniqesel + } return this.model && this.model.indexOf(option.id) > -1; } setSelected(event: Event, option: IMultiSelectOption) { - - if (this.settings.singleSelect === true) { + if (this.settings.singleSelect === true && !this.settings.uniqueSelect) { this.model = option.id; - } else { + } else if (this.settings.singleSelect === true && this.settings.uniqueSelect === true) { + let uniqsel: any = option.id + ".." + option.badge + this.model = uniqsel; + } + else { if (!this.model) this.model = []; var index = this.model.indexOf(option.id); if (index > -1) { @@ -233,9 +245,15 @@ export class MultiselectDropdown implements OnInit, DoCheck, ControlValueAccesso } } if (this.settings.closeOnSelect) { - this.toggleDropdown(); + this.toggleDropdown(); } + // if (this.settings.returnOption === true) { + // console.log("this.model: ", this.model, option) + // this.onModelChange(option); + // return + // } + this.onModelChange(this.model); } @@ -245,18 +263,32 @@ export class MultiselectDropdown implements OnInit, DoCheck, ControlValueAccesso } updateTitle() { + let tmpmodel: any = this.model + if (this.settings.uniqueSelect) { + if (tmpmodel.split('..').length == 1) { + let t: any = tmpmodel + ".." + this.extravar + this.model = t + } + } if (this.numSelected === 0) { this.title = this.texts.defaultTitle; } else if (this.settings.dynamicTitleMaxItems >= this.numSelected) { - if (this.settings.singleSelect === true) { + if (this.settings.singleSelect === true && !this.settings.uniqueSelect) { + this.title = this.options + .filter((option) => this.model === option.id) + .map((fil) => fil.name).toString(); + } else if (this.settings.singleSelect === true && this.settings.uniqueSelect) { this.title = this.options - .filter((option) => this.model === option.id) - .map((fil) => fil.name).toString(); + .filter((option) => { + let uniqueselect: any = option.id + ".." + option.badge; + if (this.model === uniqueselect) return true + }) + .map((fil) => fil.name).toString(); } else { this.title = this.options - .filter((option: IMultiSelectOption) => this.model && this.model.indexOf(option.id) > -1) - .map((option: IMultiSelectOption) => option.name) - .join(', '); + .filter((option: IMultiSelectOption) => this.model && this.model.indexOf(option.id) > -1) + .map((option: IMultiSelectOption) => option.name) + .join(', '); } } else { this.title = this.numSelected + ' ' + (this.numSelected === 1 ? this.texts.checked : this.texts.checkedPlural); @@ -264,8 +296,8 @@ export class MultiselectDropdown implements OnInit, DoCheck, ControlValueAccesso } checkAll() { - if(!this.model) this.model = []; - let newEntries = _.differenceWith(new MultiSelectSearchFilter().transform(this.options,this.searchFilterText).map(option => option.id),this.model,_.isEqual); + if (!this.model) this.model = []; + let newEntries = _.differenceWith(new MultiSelectSearchFilter().transform(this.options, this.searchFilterText).map(option => option.id), this.model, _.isEqual); this.model = newEntries.concat(this.model); this.onModelChange(this.model); } diff --git a/src/common/table-available-actions.ts b/src/common/table-available-actions.ts index dc45a6b0..f410296c 100644 --- a/src/common/table-available-actions.ts +++ b/src/common/table-available-actions.ts @@ -16,6 +16,10 @@ export class AvailableTableActions { return this.getMetricAvailableActions(); case 'influxcfg': return this.getInfluxServersAvailableActions(); + case 'kafkacfg': + return this.getKafkaServersAvailableActions(); + case 'outputcfg': + return this.getOutputAvailableActions(); case 'oidconditioncfg': return this.getOIDConditionsAvailableActions(); case 'measgroupcfg': @@ -193,6 +197,35 @@ export class AvailableTableActions { return tableAvailableActions; } + getOutputAvailableActions(data?: any): any { + let tableAvailableActions = [ + //Remove Action + { + 'title': 'Remove', 'content': + { 'type': 'button', 'action': 'RemoveAllSelected' } + }, + //Change Property Action + { + 'title': 'Change property', 'content': + { + 'type': 'selector', 'action': 'ChangeProperty', 'options': [ + { + 'title': 'Active', 'type': 'boolean', 'options': [ + 'true', 'false' + ] + }, + {'title': 'BufferSize','type':'input', 'options': + new FormGroup({ + formControl : new FormControl(0, Validators.required) + }) + }, + ] + }, + } + ]; + return tableAvailableActions; + } + getMeasGroupsAvailableActions (data ? : any) : any { let tableAvailableActions = [ //Remove Action @@ -203,6 +236,16 @@ export class AvailableTableActions { return tableAvailableActions; } + getKafkaServersAvailableActions (data ? : any) : any { + let tableAvailableActions = [ + //Remove Action + {'title': 'Remove', 'content' : + {'type' : 'button','action' : 'RemoveAllSelected'} + } + ]; + return tableAvailableActions; + } + getCustomFiltersAvailableActions (data ? : any) : any { let tableAvailableActions = [ //Remove Action diff --git a/src/home/home.css b/src/home/home.css index d0a1fa4d..eb79bc15 100644 --- a/src/home/home.css +++ b/src/home/home.css @@ -1,3 +1,8 @@ .body { background-color: #C1BFBF; } + +.child { + padding-left: 8px; + font-size: 12px; +} \ No newline at end of file diff --git a/src/home/home.html b/src/home/home.html index efea3368..8475a492 100644 --- a/src/home/home.html +++ b/src/home/home.html @@ -54,7 +54,7 @@ Configuration
  • - {{menuItem.title}} + {{menuItem.parent ? '- ' : ''}}{{menuItem.title}}
  • @@ -93,7 +93,13 @@
    -

    +

    + + + + + + diff --git a/src/home/home.ts b/src/home/home.ts index b20cbd2d..d933eede 100644 --- a/src/home/home.ts +++ b/src/home/home.ts @@ -29,11 +29,13 @@ export class Home { nativeWindow: any response: string; api: string; - item_type: string; + item_type: {'item': string, 'parent': string} = {'item': '', 'parent': 'none'}; version: RInfo; configurationItems : Array = [ {'title': 'Variable Catalog', 'selector' : 'varcatalog'}, - {'title': 'Influx Servers', 'selector' : 'influxserver'}, + {'title': 'Outputs', 'selector': 'output'}, + {'title': 'Kafka Servers', 'selector' : 'kafkaserver', 'parent': 'output'}, + {'title': 'Influx Servers', 'selector' : 'influxserver', 'parent': 'output'}, {'title': 'OID Conditions', 'selector' : 'oidcondition'}, {'title': 'SNMP Metrics', 'selector' : 'snmpmetric'}, {'title': 'Measurements', 'selector' : 'measurement'}, @@ -56,7 +58,7 @@ export class Home { constructor(private winRef: WindowRef,public router: Router, public httpAPI: HttpService, private _blocker: BlockUIService, public homeService: HomeService) { this.nativeWindow = winRef.nativeWindow; this.getFooterInfo(); - this.item_type= "runtime"; + this.item_type.item= "runtime"; } link(url: string) { @@ -80,9 +82,9 @@ export class Home { this.mode = !this.mode } - clickMenu(selected : string) : void { - this.item_type = ""; - this.item_type = selected; + clickMenu(selected : string, parent: string = 'none') : void { + this.item_type.item = selected; + this.item_type.parent = parent } showImportModal() { diff --git a/src/influxserver/influxservercfg.component.ts b/src/influxserver/influxservercfg.component.ts index 03e16ebc..3f7e27c5 100644 --- a/src/influxserver/influxservercfg.component.ts +++ b/src/influxserver/influxservercfg.component.ts @@ -92,7 +92,6 @@ export class InfluxServerCfgComponent { SSLCert: [this.influxserverForm ? this.influxserverForm.value.SSLCert : ''], SSLKey: [this.influxserverForm ? this.influxserverForm.value.SSLKey : ''], InsecureSkipVerify: [this.influxserverForm ? this.influxserverForm.value.InsecureSkipVerify : 'true'], - BufferSize: [this.influxserverForm ? this.influxserverForm.value.BufferSize : 65535, Validators.compose([Validators.required, ValidationService.uintegerNotZeroValidator])], Description: [this.influxserverForm ? this.influxserverForm.value.Description : ''] }); } diff --git a/src/influxserver/influxservercfg.data.ts b/src/influxserver/influxservercfg.data.ts index 581a0dd3..818680db 100644 --- a/src/influxserver/influxservercfg.data.ts +++ b/src/influxserver/influxservercfg.data.ts @@ -1,6 +1,6 @@ export const InfluxServerCfgComponentConfig: any = { - 'name' : 'Influx Server', + 'name' : 'Outputs > InfluxDB Server', 'table-columns' : [ { title: 'ID', name: 'ID' }, { title: 'Host', name: 'Host' }, @@ -11,7 +11,6 @@ export const InfluxServerCfgComponentConfig: any = { title: 'Retention', name: 'Retention' }, { title: 'Precision', name: 'Precision' }, { title: 'Timeout', name: 'Timeout' }, - { title: 'Buffer Size', name: 'BufferSize' }, { title: 'User Agent', name: 'UserAgent' } ], 'slug' : 'influxcfg' diff --git a/src/influxserver/influxservereditor.html b/src/influxserver/influxservereditor.html index 2f4c4ee2..5d1e9d3c 100644 --- a/src/influxserver/influxservereditor.html +++ b/src/influxserver/influxservereditor.html @@ -180,14 +180,6 @@

    -
    - - -
    - - -
    -
    diff --git a/src/kafkaserver/kafkaservercfg.component.ts b/src/kafkaserver/kafkaservercfg.component.ts new file mode 100644 index 00000000..562b9d57 --- /dev/null +++ b/src/kafkaserver/kafkaservercfg.component.ts @@ -0,0 +1,391 @@ +import { Component, ChangeDetectionStrategy, ViewChild } from '@angular/core'; +import { FormBuilder, Validators} from '@angular/forms'; +import { FormArray, FormGroup, FormControl} from '@angular/forms'; + +import { KafkaServerService } from './kafkaservercfg.service'; +import { ValidationService } from '../common/validation.service' +import { ExportServiceCfg } from '../common/dataservice/export.service' + +import { GenericModal } from '../common/generic-modal'; +import { ExportFileModal } from '../common/dataservice/export-file-modal'; +import { Observable } from 'rxjs/Rx'; + +import { ItemsPerPageOptions } from '../common/global-constants'; +import { TableActions } from '../common/table-actions'; +import { AvailableTableActions } from '../common/table-available-actions'; + +import { TableListComponent } from '../common/table-list.component'; +import { KafkaServerCfgComponentConfig, TableRole, OverrideRoleActions } from './kafkaservercfg.data'; + +declare var _:any; + +@Component({ + selector: 'kafkaservers', + providers: [KafkaServerService, ValidationService], + templateUrl: './kafkaservereditor.html', + styleUrls: ['../css/component-styles.css'] +}) + +export class KafkaServerCfgComponent { + @ViewChild('viewModal') public viewModal: GenericModal; + @ViewChild('viewModalDelete') public viewModalDelete: GenericModal; + @ViewChild('exportFileModal') public exportFileModal : ExportFileModal; + + itemsPerPageOptions : any = ItemsPerPageOptions; + editmode: string; //list , create, modify + kafkaservers: Array; + filter: string; + kafkaserverForm: any; + myFilterValue: any; + alertHandler : any = null; + + + //Initialization data, rows, colunms for Table + private data: Array = []; + public rows: Array = []; + public tableAvailableActions : any; + + selectedArray : any = []; + public defaultConfig : any = KafkaServerCfgComponentConfig; + public tableRole : any = TableRole; + public overrideRoleActions: any = OverrideRoleActions; + public isRequesting : boolean; + public counterItems : number = null; + public counterErrors: any = []; + + public page: number = 1; + public itemsPerPage: number = 20; + public maxSize: number = 5; + public numPages: number = 1; + public length: number = 0; + private builder; + private oldID : string; + + //Set config + public config: any = { + paging: true, + sorting: { columns: this.defaultConfig['table-columns'] }, + filtering: { filterString: '' }, + className: ['table-striped', 'table-bordered'] + }; + + constructor(public kafkaServerService: KafkaServerService, public exportServiceCfg : ExportServiceCfg, builder: FormBuilder) { + this.editmode = 'list'; + this.reloadData(); + this.builder = builder; + } + + createStaticForm() { + this.kafkaserverForm = this.builder.group({ + ID: [this.kafkaserverForm ? this.kafkaserverForm.value.ID : '', Validators.required], + Brokers: [this.kafkaserverForm ? this.kafkaserverForm.value.Brokers : '', Validators.required], + Topic: [this.kafkaserverForm ? this.kafkaserverForm.value.Topic : ''], + TopicTag: [this.kafkaserverForm ? this.kafkaserverForm.value.TopicTag : ''], + ExcludeTopicTag: [this.kafkaserverForm ? this.kafkaserverForm.value.ExcludeTopicTag : false], + Version: [this.kafkaserverForm ? this.kafkaserverForm.value.Version : ''], + ClientID: [this.kafkaserverForm ? this.kafkaserverForm.value.ClientID : 'Telegraf'], + CompressionCodec: [this.kafkaserverForm ? this.kafkaserverForm.value.CompressionCodec : 0], + Method: [this.kafkaserverForm ? this.kafkaserverForm.value.Method : 'measurement', Validators.required], + Keys: [this.kafkaserverForm ? this.kafkaserverForm.value.Keys : ''], + Separator: [this.kafkaserverForm ? this.kafkaserverForm.value.Separator : '_'], + RoutingTag: [this.kafkaserverForm ? this.kafkaserverForm.value.RoutingTag : ''], + RoutingKey: [this.kafkaserverForm ? this.kafkaserverForm.value.RoutingKey : ''], + RequiredAcks: [this.kafkaserverForm ? this.kafkaserverForm.value.RequiredAcks : -1, Validators.compose([Validators.required, ValidationService.uintegerAndLessOneValidator])], + MaxRetry: [this.kafkaserverForm ? this.kafkaserverForm.value.MaxRetry : 3, Validators.compose([Validators.required, ValidationService.uintegerNotZeroValidator])], + MaxMessageBytes: [this.kafkaserverForm ? this.kafkaserverForm.value.MaxMessageBytes : 0, ValidationService.integerValidator], + IdempotentWrites: [this.kafkaserverForm ? this.kafkaserverForm.value.IdempotentWrites : false], + EnableTLS: [this.kafkaserverForm ? this.kafkaserverForm.value.EnableTLS : false], + Socks5ProxyEnabled: [this.kafkaserverForm ? this.kafkaserverForm.value.Socks5ProxyEnabled : false], + MetadataFull: [this.kafkaserverForm ? this.kafkaserverForm.value.MetadataFull : false], + Description: [this.kafkaserverForm ? this.kafkaserverForm.value.Description : ''] + }); + } + + + createDynamicForm(fieldsArray: any) : void { + + //Generates the static form: + //Saves the actual to check later if there are shared values + let tmpform : any; + if (this.kafkaserverForm) tmpform = this.kafkaserverForm.value; + this.createStaticForm(); + //Set new values and check if we have to mantain the value! + for (let entry of fieldsArray) { + let value = entry.defVal; + //Check if there are common values from the previous selected item + if (tmpform) { + if (tmpform[entry.ID] && entry.override !== true) { + value = tmpform[entry.ID]; + } + } + //Set different controls: + this.kafkaserverForm.addControl(entry.ID, new FormControl(value, entry.Validators)); + } +} + +checkFields(tls, socks5) { + let dyn = '' + if (tls == true || tls == 'true') { + dyn = 'tls' + } + if (socks5 == true || socks5 == 'true') { + dyn += 'socks5' + } + if (dyn == '') { + dyn = null + } + return dyn + } + +setDynamicFields (field : any, override? : boolean) : void { + //Saves on the array all values to push into formGroup + let controlArray : Array = []; + switch (field) { + case 'tls': + controlArray.push({'ID': 'ServerName', 'defVal' : '', 'Validators' : Validators.required }); + controlArray.push({'ID': 'TLSCA', 'defVal' : '', 'Validators' : Validators.nullValidator }); + controlArray.push({'ID': 'TLSCert', 'defVal' : '', 'Validators' : Validators.nullValidator }); + controlArray.push({'ID': 'TLSKey', 'defVal' : '', 'Validators' : Validators.required }); + controlArray.push({'ID': 'TLSMinVersion', 'defVal' : '', 'Validators' : Validators.required }); + controlArray.push({'ID': 'InsecureSkipVerify', 'defVal' : false, 'Validators' : Validators.required }); + break; + case 'socks5': + controlArray.push({'ID': 'Socks5ProxyAddress', 'defVal' : '', 'Validators' : Validators.required }); + controlArray.push({'ID': 'Socks5ProxyUsername', 'defVal' : '', 'Validators' : Validators.nullValidator }); + controlArray.push({'ID': 'Socks5ProxyPassword', 'defVal' : '', 'Validators' : Validators.nullValidator }); + break; + case 'tlssocks5': + controlArray.push({'ID': 'ServerName', 'defVal' : '', 'Validators' : Validators.required }); + controlArray.push({'ID': 'TLSCA', 'defVal' : '', 'Validators' : Validators.nullValidator }); + controlArray.push({'ID': 'TLSCert', 'defVal' : '', 'Validators' : Validators.nullValidator }); + controlArray.push({'ID': 'TLSKey', 'defVal' : '', 'Validators' : Validators.required }); + controlArray.push({'ID': 'TLSMinVersion', 'defVal' : '', 'Validators' : Validators.required }); + controlArray.push({'ID': 'InsecureSkipVerify', 'defVal' : false, 'Validators' : Validators.required }); + controlArray.push({'ID': 'Socks5ProxyAddress', 'defVal' : '', 'Validators' : Validators.required }); + controlArray.push({'ID': 'Socks5ProxyUsername', 'defVal' : '', 'Validators' : Validators.nullValidator }); + controlArray.push({'ID': 'Socks5ProxyPassword', 'defVal' : '', 'Validators' : Validators.nullValidator }); + } + + //Reload the formGroup with new values saved on controlArray + console.log("GOT CONTROL ARRAY:", controlArray) + this.createDynamicForm(controlArray); +} + + + reloadData() { + //In order to avoid issues with the array we clean it every time we refresh data + this.selectedArray = []; + this.isRequesting = true; + // now it's a simple subscription to the observable + this.kafkaServerService.getKafkaServer(null) + .subscribe( + data => { + this.isRequesting = false; + this.kafkaservers = data + this.data = data; + }, + err => console.error(err), + () => console.log('DONE') + ); + } + + applyAction(test : any, data? : Array) : void { + this.selectedArray = data || []; + switch(test.action) { + case "RemoveAllSelected": { + this.removeAllSelectedItems(this.selectedArray); + break; + } + case "ChangeProperty": { + this.updateAllSelectedItems(this.selectedArray,test.field,test.value) + break; + } + case "AppendProperty": { + this.updateAllSelectedItems(this.selectedArray,test.field,test.value,true); + } + default: { + break; + } + } + } + + customActions(action : any) { + switch (action.option) { + case 'export' : + this.exportItem(action.event); + break; + case 'new' : + this.newKafkaServer() + case 'view': + this.viewItem(action.event); + break; + case 'edit': + this.editKafkaServer(action.event); + break; + case 'remove': + this.removeItem(action.event); + break; + case 'tableaction': + this.applyAction(action.event, action.data); + break; + } + } + + viewItem(id) { + console.log('view', id); + this.viewModal.parseObject(id); + } + + exportItem(item : any) : void { + this.exportFileModal.initExportModal(item); + } + + removeAllSelectedItems(myArray) { + let obsArray = []; + this.counterItems = 0; + this.isRequesting = true; + for (let i in myArray) { + console.log("Removing ",myArray[i].ID) + this.deleteKafkaServer(myArray[i].ID,true); + obsArray.push(this.deleteKafkaServer(myArray[i].ID,true)); + } + this.genericForkJoin(obsArray); + } + + removeItem(row) { + let id = row.ID; + console.log('remove', id); + this.kafkaServerService.checkOnDeleteKafkaServer(id) + .subscribe( + data => { + console.log(data); + let temp = data; + this.viewModalDelete.parseObject(temp) + }, + err => console.error(err), + () => { } + ); + } + newKafkaServer() { + //No hidden fields, so create fixed Form + if (this.kafkaserverForm) { + this.setDynamicFields(this.checkFields(this.kafkaserverForm.value.EnableTLS,this.kafkaserverForm.value.Socks5ProxyEnabled)); + } else { + this.setDynamicFields(null); + } + this.editmode = "create"; + } + + editKafkaServer(row) { + let id = row.ID; + this.kafkaServerService.getKafkaServerById(id) + .subscribe(data => { + this.kafkaserverForm = {}; + this.kafkaserverForm.value = data; + this.oldID = data.ID + this.setDynamicFields(this.checkFields(row.EnableTLS, row.Socks5ProxyEnabled)) + this.editmode = "modify"; + }, + err => console.error(err) + ); + } + + deleteKafkaServer(id, recursive?) { + if (!recursive) { + this.kafkaServerService.deleteKafkaServer(id) + .subscribe(data => { }, + err => console.error(err), + () => { this.viewModalDelete.hide(); this.editmode = "list"; this.reloadData() } + ); + } else { + return this.kafkaServerService.deleteKafkaServer(id, true) + .do( + (test) => { this.counterItems++}, + (err) => { this.counterErrors.push({'ID': id, 'error' : err})} + ); + } + } + + cancelEdit() { + this.editmode = "list"; + this.reloadData(); + } + + saveKafkaServer() { + if (this.kafkaserverForm.valid) { + this.kafkaServerService.addKafkaServer(this.kafkaserverForm.value) + .subscribe(data => { console.log(data) }, + err => { + console.log(err); + }, + () => { this.editmode = "list"; this.reloadData() } + ); + } + } + + updateAllSelectedItems(mySelectedArray,field,value, append?) { + let obsArray = []; + this.counterItems = 0; + this.isRequesting = true; + if (!append) + for (let component of mySelectedArray) { + component[field] = value; + obsArray.push(this.updateKafkaServer(true,component)); + } else { + let tmpArray = []; + if(!Array.isArray(value)) value = value.split(','); + console.log(value); + for (let component of mySelectedArray) { + console.log(value); + //check if there is some new object to append + let newEntries = _.differenceWith(value,component[field],_.isEqual); + tmpArray = newEntries.concat(component[field]) + console.log(tmpArray); + component[field] = tmpArray; + obsArray.push(this.updateKafkaServer(true,component)); + } + } + this.genericForkJoin(obsArray); + //Make sync calls and wait the result + this.counterErrors = []; + } + + updateKafkaServer(recursive?, component?) { + if(!recursive) { + if (this.kafkaserverForm.valid) { + var r = true; + if (this.kafkaserverForm.value.ID != this.oldID) { + r = confirm("Changing Kafka Server ID from " + this.oldID + " to " + this.kafkaserverForm.value.ID + ". Proceed?"); + } + if (r == true) { + this.kafkaServerService.editKafkaServer(this.kafkaserverForm.value, this.oldID, true) + .subscribe(data => { console.log(data) }, + err => console.error(err), + () => { this.editmode = "list"; this.reloadData() } + ); + } + } + } else { + return this.kafkaServerService.editKafkaServer(component, component.ID) + .do( + (test) => { this.counterItems++ }, + (err) => { this.counterErrors.push({'ID': component['ID'], 'error' : err['_body']})} + ) + .catch((err) => { + return Observable.of({'ID': component.ID , 'error': err['_body']}) + }) + } + } + + genericForkJoin(obsArray: any) { + Observable.forkJoin(obsArray) + .subscribe( + data => { + this.selectedArray = []; + this.reloadData() + }, + err => console.error(err), + ); + } + +} diff --git a/src/kafkaserver/kafkaservercfg.data.ts b/src/kafkaserver/kafkaservercfg.data.ts new file mode 100644 index 00000000..ee229d8c --- /dev/null +++ b/src/kafkaserver/kafkaservercfg.data.ts @@ -0,0 +1,79 @@ +export const KafkaServerCfgComponentConfig: any = + { + 'name' : 'Outputs > Kafka Server', + 'table-columns' : [ + { title: 'ID', name: 'ID' }, + { title: 'Brokers', name: 'Brokers' }, + { title: 'Enable TLS',name:'EnableTLS'}, + { title: 'Enable Socks5',name:'Socks5ProxyEnabled'}, + { title: 'Topic', name: 'Topic' }, + { title: 'Method', name: 'Method' }, + { title: 'RoutingKey', name: 'RoutingKey' }, + { title: 'MaxRetry', name:'MaxRetry'} + + + + // type KafkaCfg struct { + // ID string `xorm:"'id' unique" binding:"Required"` + // Brokers []string `xorm:"brokers" binding:"Required"` + // Topic string `xorm:"topic" binding:"Required;IntegerNotZero"` + // Method string `xorm:"topic_suffix_method"` + // Keys []string `xorm:"topic_suffix_keys"` + // Separator string `xorm:"topic_suffix_separator"` + // // RoutingTag string `xorm:"routing_tag" binding:"Required"` + // RoutingKey string `xorm:"routing_key" binding:"Required"` + // Description string `xorm:"description"` + // RequiredAcks int `xorm:"'required_acks' default -1"` + // MaxRetry int `xorm:"'max_retry' default 3"` + // MaxMessageBytes int `xorm:"max_message_bytes"` + // IdempotentWrites bool `xorm:"idempotent_writes"` + // TLSCA string `xorm:"tls_ca"` + // TLSCert string `xorm:"tls_cert"` + // TLSKey string `xorm:"tls_key"` + // TLSKeyPwd string `xorm:"tls_key_pwd"` + // TLSMinVersion string `xorm:"tls_min_version"` + // InsecureSkipVerify bool `xorm:"insecure_skip_verify"` + // ServerName string `xorm:"tls_server_name"` + // Version string `xorm:"version"` + // ClientID string `xorm:"client_id"` + // CompressionCodec int `xorm:"compression_codec"` + // EnableTLS *bool `xorm:"enable_tls"` + // // Disable full metadata fetching + // MetadataFull *bool `xorm:"metadata_full"` + + + + // { title: 'Enable SSL',name:'EnableSSL'}, + // { title: 'DB', name: 'DB' }, + // { title: 'User', name: 'User' }, + // { title: 'Retention', name: 'Retention' }, + // { title: 'Precision', name: 'Precision' }, + // { title: 'Timeout', name: 'Timeout' }, + // { title: 'Buffer Size', name: 'BufferSize' }, + // { title: 'User Agent', name: 'UserAgent' } + ], + 'slug' : 'kafkacfg' + }; + + + // type KafkaCfg struct { + // ID string `xorm:"'id' unique" binding:"Required"` + // Brokers []string `xorm:"brokers" binding:"Required"` + // Topic string `xorm:"topic" binding:"Required;IntegerNotZero"` + // TopicSuffix TopicSuffix `xorm:"extends" binding:"Required"` + // // RoutingTag string `xorm:"routing_tag" binding:"Required"` + // RoutingKey string `xorm:"routing_key" binding:"Required"` + // SSLCA string `xorm:"ssl_ca"` + // SSLCert string `xorm:"ssl_cert"` + // SSLKey string `xorm:"ssl_key"` + // BufferSize int `xorm:"'buffer_size' default 65535"` + // Description string `xorm:"description"` + // } + + export const TableRole : string = 'fulledit'; + export const OverrideRoleActions : Array = [ + {'name':'export', 'type':'icon', 'icon' : 'glyphicon glyphicon-download-alt text-default', 'tooltip': 'Export item'}, + {'name':'view', 'type':'icon', 'icon' : 'glyphicon glyphicon-eye-open text-success', 'tooltip': 'View item'}, + {'name':'edit', 'type':'icon', 'icon' : 'glyphicon glyphicon-edit text-warning', 'tooltip': 'Edit item'}, + {'name':'remove', 'type':'icon', 'icon' : 'glyphicon glyphicon glyphicon-remove text-danger', 'tooltip': 'Remove item'} + ] \ No newline at end of file diff --git a/src/kafkaserver/kafkaservercfg.service.ts b/src/kafkaserver/kafkaservercfg.service.ts new file mode 100644 index 00000000..a8c609f0 --- /dev/null +++ b/src/kafkaserver/kafkaservercfg.service.ts @@ -0,0 +1,111 @@ +import { Injectable } from '@angular/core'; +import { HttpService } from '../core/http.service'; +import { Observable } from 'rxjs/Observable'; + +declare var _:any; + +@Injectable() +export class KafkaServerService { + + constructor(public httpAPI: HttpService) { + } + + parseJSON(key,value) { + if ( key == 'RequiredAcks' || + key == 'MaxRetry' || + key == 'CompressionCodec' || + key == 'MaxMessageBytes') { + return parseInt(value); + } + if ( key == 'EnableTLS' || + key == 'Socks5ProxyEnabled' || + key == 'InsecureSkipVerify' || + key == 'ExcludeTopicTag') { + return ( value === "true" || value === true); + } + if ( key == 'Brokers' || key == 'Keys') + return String(value).split(','); + + return value; + } + + addKafkaServer(dev) { + return this.httpAPI.post('/api/cfg/kafkaservers',JSON.stringify(dev,this.parseJSON)) + .map( (responseData) => responseData.json()); + + } + + editKafkaServer(dev, id, hideAlert?) { + return this.httpAPI.put('/api/cfg/kafkaservers/'+id,JSON.stringify(dev,this.parseJSON),null,hideAlert) + .map( (responseData) => responseData.json()); + } + + getKafkaServer(filter_s: string) { + // return an observable + return this.httpAPI.get('/api/cfg/kafkaservers') + .map( (responseData) => { + return responseData.json(); + }) + .map((kafkaservers) => { + console.log("MAP SERVICE",kafkaservers); + let result = []; + if (kafkaservers) { + _.forEach(kafkaservers,function(value,key){ + console.log("FOREACH LOOP",value,value.ID); + if(filter_s && filter_s.length > 0 ) { + console.log("maching: "+value.ID+ "filter: "+filter_s); + var re = new RegExp(filter_s, 'gi'); + if (value.ID.match(re)){ + result.push(value); + } + console.log(value.ID.match(re)); + } else { + result.push(value); + } + }); + } + return result; + }); + } + getKafkaServerById(id : string) { + // return an observable + console.log("ID: ",id); + return this.httpAPI.get('/api/cfg/kafkaservers/'+id) + .map( (responseData) => + responseData.json() + )}; + + checkOnDeleteKafkaServer(id : string){ + return this.httpAPI.get('/api/cfg/kafkaservers/checkondel/'+id) + .map( (responseData) => + responseData.json() + ).map((deleteobject) => { + console.log("MAP SERVICE",deleteobject); + let result : any = {'ID' : id}; + _.forEach(deleteobject,function(value,key){ + result[value.TypeDesc] = []; + }); + _.forEach(deleteobject,function(value,key){ + result[value.TypeDesc].Description=value.Action; + result[value.TypeDesc].push(value.ObID); + }); + return result; + }); + }; + + testKafkaServer(kafkaserver,hideAlert?) { + // return an observable + return this.httpAPI.post('/api/cfg/kafkaservers/ping/',JSON.stringify(kafkaserver,this.parseJSON), null, hideAlert) + .map((responseData) => responseData.json()); + }; + + deleteKafkaServer(id : string, hideAlert?) { + // return an observable + console.log("ID: ",id); + console.log("DELETING"); + return this.httpAPI.delete('/api/cfg/kafkaservers/'+id, null, hideAlert) + .map( (responseData) => + responseData.json() + ); + }; +} diff --git a/src/kafkaserver/kafkaservereditor.html b/src/kafkaserver/kafkaservereditor.html new file mode 100644 index 00000000..18a51631 --- /dev/null +++ b/src/kafkaserver/kafkaservereditor.html @@ -0,0 +1,396 @@ +

    {{defaultConfig.name}}

    + + + + + + + + + +
    + +
    +

    + {{ editmode | uppercase}} +

    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + Connection Settings + +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    +
    +
    + {{alertHandler.result}} - Ping elapsed: {{alertHandler.elapsed / 1000000 }} ms +

    {{alertHandler.msg}}

    +
    +
    +
    +
    +
    + Kafka Settings +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + +
    +
    +
    + + +
    + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + {{'{tag::'+kafkaserverForm.value.TopicTag+'.value}'}}{{kafkaserverForm.value.Separator}}{{'{measurement.name}'}} + {{kafkaserverForm.value.Topic}}{{kafkaserverForm.value.Separator}}{{'{measurement.name}'}} + + + {{'{tag::'+kafkaserverForm.value.TopicTag+'.value}'}}{{kafkaserverForm.value.Separator}} + + {{'{tag::'+tkey+'.value}'}}{{li === false ? kafkaserverForm.value.Separator : ''}} + + + {{kafkaserverForm.value.Topic}}{{kafkaserverForm.value.Separator}} + + {{'{tag::'+tkey+'.value}'}}{{li === false ? kafkaserverForm.value.Separator : ''}} + + + +
    +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + +
    +
    +
    +
    + TLS Settings +
    + + +
    + +
    +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    +
    +
    + Socks5 Settings +
    + + +
    + +
    +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    +
    +
    + Extra Settings +
    + + +
    + + +
    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/src/main.module.ts b/src/main.module.ts index 836ff8d1..9697b8d6 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -29,6 +29,8 @@ import { SnmpMetricCfgComponent } from './snmpmetric/snmpmetriccfg.component'; import { MeasurementCfgComponent } from './measurement/measurementcfg.component'; import { MeasGroupCfgComponent } from './measgroup/measgroupcfg.component'; import { MeasFilterCfgComponent } from './measfilter/measfiltercfg.component'; +import { OutputCfgComponent } from './output/outputcfg.component'; +import { KafkaServerCfgComponent } from './kafkaserver/kafkaservercfg.component'; import { InfluxServerCfgComponent } from './influxserver/influxservercfg.component'; import { RuntimeComponent } from './runtime/runtime.component'; import { CustomFilterCfgComponent } from './customfilter/customfiltercfg.component'; @@ -81,6 +83,8 @@ import { SwaggerUiComponent} from './swagger-ui/swagger-ui.component'; MeasurementCfgComponent, MeasGroupCfgComponent, MeasFilterCfgComponent, + OutputCfgComponent, + KafkaServerCfgComponent, InfluxServerCfgComponent, CustomFilterCfgComponent, VarCatalogCfgComponent, diff --git a/src/output/outputcfg.component.ts b/src/output/outputcfg.component.ts new file mode 100644 index 00000000..9bf9c54d --- /dev/null +++ b/src/output/outputcfg.component.ts @@ -0,0 +1,364 @@ +import { Component, ChangeDetectionStrategy, ViewChild } from '@angular/core'; +import { FormBuilder, Validators} from '@angular/forms'; +import { FormArray, FormGroup, FormControl} from '@angular/forms'; + +import { KafkaServerService } from '../kafkaserver/kafkaservercfg.service'; +import { InfluxServerService } from '../influxserver/influxservercfg.service'; + + +import { OutputService } from './outputcfg.service'; +import { ValidationService } from '../common/validation.service' +import { ExportServiceCfg } from '../common/dataservice/export.service' + +import { GenericModal } from '../common/generic-modal'; +import { ExportFileModal } from '../common/dataservice/export-file-modal'; +import { Observable } from 'rxjs/Rx'; + +import { ItemsPerPageOptions } from '../common/global-constants'; +import { TableActions } from '../common/table-actions'; +import { AvailableTableActions } from '../common/table-available-actions'; + +import { TableListComponent } from '../common/table-list.component'; +import { OutputCfgComponentConfig, TableRole, OverrideRoleActions } from './outputcfg.data'; + +import { IMultiSelectOption, IMultiSelectSettings, IMultiSelectTexts } from '../common/multiselect-dropdown'; + +declare var _:any; + +@Component({ + selector: 'outputs', + providers: [OutputService, ValidationService, KafkaServerService, InfluxServerService], + templateUrl: './outputeditor.html', + styleUrls: ['../css/component-styles.css'] +}) + +export class OutputCfgComponent { + @ViewChild('viewModal') public viewModal: GenericModal; + @ViewChild('viewModalDelete') public viewModalDelete: GenericModal; + @ViewChild('exportFileModal') public exportFileModal : ExportFileModal; + + itemsPerPageOptions : any = ItemsPerPageOptions; + editmode: string; //list , create, modify + outputs: Array; + filter: string; + outputForm: any; + myFilterValue: any; + alertHandler : any = null; + + private mySettings: IMultiSelectSettings = { + singleSelect: true, + returnOption: true, + uniqueSelect: true, +}; + + + //Initialization data, rows, colunms for Table + private data: Array = []; + public rows: Array = []; + public tableAvailableActions : any; + + selectedArray : any = []; + public defaultConfig : any = OutputCfgComponentConfig; + public tableRole : any = TableRole; + public overrideRoleActions: any = OverrideRoleActions; + public isRequesting : boolean; + public counterItems : number = null; + public counterErrors: any = []; + + public page: number = 1; + public itemsPerPage: number = 20; + public maxSize: number = 5; + public numPages: number = 1; + public length: number = 0; + private builder; + private oldID : string; + + backends: Array; + selectbackends: IMultiSelectOption[] = []; + + + //Set config + public config: any = { + paging: true, + sorting: { columns: this.defaultConfig['table-columns'] }, + filtering: { filterString: '' }, + className: ['table-striped', 'table-bordered'] + }; + + constructor(public outputService: OutputService, public influxServerService: InfluxServerService, public kafkaServerService: KafkaServerService, public exportServiceCfg : ExportServiceCfg, builder: FormBuilder) { + this.editmode = 'list'; + this.reloadData(); + this.builder = builder; + } + + createStaticForm() { + this.outputForm = this.builder.group({ + ID: [this.outputForm ? this.outputForm.value.ID : '', Validators.required], + BackendType: [this.outputForm ? this.outputForm.value.BackendType : '', Validators.required], + Active: [this.outputForm ? this.outputForm.value.Active : 'true', Validators.required], + EnqueueOnError: [this.outputForm ? this.outputForm.value.EnqueueOnError : 'true', Validators.required], + BufferSize: [this.outputForm ? this.outputForm.value.BufferSize : 131070, Validators.compose([Validators.required, ValidationService.uintegerNotZeroValidator])], + MetricBatchSize: [this.outputForm ? this.outputForm.value.MetricBatchSize : 15000, Validators.compose([Validators.required, ValidationService.uintegerNotZeroValidator])], + FlushInterval: [this.outputForm ? this.outputForm.value.FlushInterval : 60, Validators.compose([Validators.required, ValidationService.uintegerNotZeroValidator])], + Backend: [this.outputForm ? this.outputForm.value.Backend : '', Validators.required], + Description: [this.outputForm ? this.outputForm.value.Description : ''] + }); + } + + reloadData() { + // now it's a simple subscription to the observable + this.alertHandler = null; + this.outputService.getOutput(null) + .subscribe( + data => { + this.isRequesting = false; + this.outputs = data + this.data = data; + }, + err => console.error(err), + () => console.log('DONE') + ); + } + + applyAction(test : any, data? : Array) : void { + this.selectedArray = data || []; + switch(test.action) { + case "RemoveAllSelected": { + this.removeAllSelectedItems(this.selectedArray); + break; + } + case "ChangeProperty": { + this.updateAllSelectedItems(this.selectedArray,test.field,test.value) + break; + } + case "AppendProperty": { + this.updateAllSelectedItems(this.selectedArray,test.field,test.value,true); + } + default: { + break; + } + } + } + + customActions(action : any) { + switch (action.option) { + case 'export' : + this.exportItem(action.event); + break; + case 'new' : + this.newOutput() + case 'view': + this.viewItem(action.event); + break; + case 'edit': + this.editOutput(action.event); + break; + case 'remove': + this.removeItem(action.event); + break; + case 'tableaction': + this.applyAction(action.event, action.data); + break; + } + } + + viewItem(id) { + console.log('view', id); + this.viewModal.parseObject(id); + } + + exportItem(item : any) : void { + this.exportFileModal.initExportModal(item); + } + + removeAllSelectedItems(myArray) { + let obsArray = []; + this.counterItems = 0; + this.isRequesting = true; + for (let i in myArray) { + console.log("Removing ",myArray[i].ID) + this.deleteOutput(myArray[i].ID,true); + obsArray.push(this.deleteOutput(myArray[i].ID,true)); + } + this.genericForkJoin(obsArray); + } + + removeItem(row) { + let id = row.ID; + console.log('remove', id); + this.outputService.checkOnDeleteOutput(id) + .subscribe( + data => { + console.log(data); + let temp = data; + this.viewModalDelete.parseObject(temp) + }, + err => console.error(err), + () => { } + ); + } + newOutput() { + //No hidden fields, so create fixed Form + this.createStaticForm(); + this.getBackendsforOutput(); + this.editmode = "create"; + } + + + editOutput(row) { + let id = row.ID; + this.getBackendsforOutput() + this.outputService.getOutputById(id) + .subscribe(data => { + this.outputForm = {}; + this.outputForm.value = data; + this.oldID = data.ID + this.createStaticForm(); + this.editmode = "modify"; + }, + err => console.error(err) + ); + } + + deleteOutput(id, recursive?) { + if (!recursive) { + this.outputService.deleteOutput(id) + .subscribe(data => { }, + err => console.error(err), + () => { this.viewModalDelete.hide(); this.editmode = "list"; this.reloadData() } + ); + } else { + return this.outputService.deleteOutput(id, true) + .do( + (test) => { this.counterItems++}, + (err) => { this.counterErrors.push({'ID': id, 'error' : err})} + ); + } + } + + cancelEdit() { + this.editmode = "list"; + this.reloadData(); + } + + saveOutput() { + if (this.outputForm.valid) { + console.log(this.outputForm) + this.outputService.addOutput(this.outputForm.value) + .subscribe(data => { console.log(data) }, + err => { + console.log(err); + }, + () => { this.editmode = "list"; this.reloadData() } + ); + } + } + + updateAllSelectedItems(mySelectedArray,field,value, append?) { + let obsArray = []; + this.counterItems = 0; + this.isRequesting = true; + if (!append) + for (let component of mySelectedArray) { + component[field] = value; + obsArray.push(this.updateOutput(true,component)); + } else { + let tmpArray = []; + if(!Array.isArray(value)) value = value.split(','); + console.log(value); + for (let component of mySelectedArray) { + console.log(value); + //check if there is some new object to append + let newEntries = _.differenceWith(value,component[field],_.isEqual); + tmpArray = newEntries.concat(component[field]) + console.log(tmpArray); + component[field] = tmpArray; + obsArray.push(this.updateOutput(true,component)); + } + } + this.genericForkJoin(obsArray); + //Make sync calls and wait the result + this.counterErrors = []; + } + + updateOutput(recursive?, component?) { + if(!recursive) { + if (this.outputForm.valid) { + var r = true; + if (this.outputForm.value.ID != this.oldID) { + r = confirm("Changing Output ID from " + this.oldID + " to " + this.outputForm.value.ID + ". Proceed?"); + } + if (r == true) { + this.outputService.editOutput(this.outputForm.value, this.oldID, true) + .subscribe(data => { console.log(data) }, + err => console.error(err), + () => { this.editmode = "list"; this.reloadData() } + ); + } + } + } else { + return this.outputService.editOutput(component, component.ID) + .do( + (test) => { this.counterItems++ }, + (err) => { this.counterErrors.push({'ID': component['ID'], 'error' : err['_body']})} + ) + .catch((err) => { + return Observable.of({'ID': component.ID , 'error': err['_body']}) + }) + } + } + + selectBackend(event: any) { + let composename = event.split("..") + if (event != "" && composename.length > 1) { + for (let entry of this.selectbackends) { + if (composename[0] == entry["id"] && composename[1] == entry["badge"]) { + this.outputForm.controls["BackendType"].setValue(composename[1]) + } + } + } + } + + getBackendsforOutput() { + this.selectbackends = []; + this.backends = []; + + this.influxServerService.getInfluxServer(null) + .subscribe( + data => { + this.backends.push(data); + for (let entry of data) { + console.log(entry) + this.selectbackends.push({ 'id': entry.ID, 'name': entry.ID, 'badge': "influxdb", 'parent': true }); + } + }, + err => console.error(err), + () => console.log('DONE') + ); + this.kafkaServerService.getKafkaServer(null) + .subscribe( + data => { + this.backends.push(data); + for (let entry of data) { + console.log(entry) + this.selectbackends.push({ 'id': entry.ID, 'name': entry.ID, 'badge': "kafka", 'parent': true }); + } + }, + err => console.error(err), + () => console.log('DONE') + ); + } + + + genericForkJoin(obsArray: any) { + Observable.forkJoin(obsArray) + .subscribe( + data => { + this.selectedArray = []; + this.reloadData() + }, + err => console.error(err), + ); + } + +} diff --git a/src/output/outputcfg.data.ts b/src/output/outputcfg.data.ts new file mode 100644 index 00000000..f6af74dd --- /dev/null +++ b/src/output/outputcfg.data.ts @@ -0,0 +1,23 @@ +export const OutputCfgComponentConfig: any = + { + 'name' : 'Outputs', + 'table-columns' : [ + { title: 'ID', name: 'ID' }, + { title: 'Active', name: 'Active' }, + { title: 'Enq. On Error', name: 'EnqueueOnError' }, + { title: 'BackendType', name: 'BackendType' }, + { title: 'BufferSize', name: 'BufferSize' }, + { title: 'MetricBatchSize', name: 'MetricBatchSize' }, + { title: 'FlushInterval', name: 'FlushInterval' }, + { title: 'Backend', name: 'Backend'} + ], + 'slug' : 'outputcfg' + }; + + export const TableRole : string = 'fulledit'; + export const OverrideRoleActions : Array = [ + {'name':'export', 'type':'icon', 'icon' : 'glyphicon glyphicon-download-alt text-default', 'tooltip': 'Export item'}, + {'name':'view', 'type':'icon', 'icon' : 'glyphicon glyphicon-eye-open text-success', 'tooltip': 'View item'}, + {'name':'edit', 'type':'icon', 'icon' : 'glyphicon glyphicon-edit text-warning', 'tooltip': 'Edit item'}, + {'name':'remove', 'type':'icon', 'icon' : 'glyphicon glyphicon glyphicon-remove text-danger', 'tooltip': 'Remove item'} + ] \ No newline at end of file diff --git a/src/output/outputcfg.service.ts b/src/output/outputcfg.service.ts new file mode 100644 index 00000000..acb48d7f --- /dev/null +++ b/src/output/outputcfg.service.ts @@ -0,0 +1,106 @@ +import { Injectable } from '@angular/core'; +import { HttpService } from '../core/http.service'; +import { Observable } from 'rxjs/Observable'; + +declare var _:any; + +@Injectable() +export class OutputService { + + constructor(public httpAPI: HttpService) { + } + + parseJSON(key,value) { + if ( key == 'BufferSize'|| + key == 'FlushInterval' || + key == 'MetricBatchSize' ) { + return parseInt(value); + } + if (key == 'Backend') { + return value.split('..')[0] + } + if ( key == 'Active' || + key == 'EnqueueOnError') return ( value === "true" || value === true); + return value; + } + + addOutput(dev) { + return this.httpAPI.post('/api/cfg/output',JSON.stringify(dev,this.parseJSON)) + .map( (responseData) => responseData.json()); + + } + + editOutput(dev, id, hideAlert?) { + return this.httpAPI.put('/api/cfg/output/'+id,JSON.stringify(dev,this.parseJSON),null,hideAlert) + .map( (responseData) => responseData.json()); + } + + getOutput(filter_s: string) { + // return an observable + return this.httpAPI.get('/api/cfg/output') + .map( (responseData) => { + return responseData.json(); + }) + .map((output) => { + console.log("MAP SERVICE",output); + let result = []; + if (output) { + _.forEach(output,function(value,key){ + console.log("FOREACH LOOP",value,value.ID); + if(filter_s && filter_s.length > 0 ) { + console.log("maching: "+value.ID+ "filter: "+filter_s); + var re = new RegExp(filter_s, 'gi'); + if (value.ID.match(re)){ + result.push(value); + } + console.log(value.ID.match(re)); + } else { + result.push(value); + } + }); + } + return result; + }); + } + getOutputById(id : string) { + // return an observable + console.log("ID: ",id); + return this.httpAPI.get('/api/cfg/output/'+id) + .map( (responseData) => + responseData.json() + )}; + + checkOnDeleteOutput(id : string){ + return this.httpAPI.get('/api/cfg/output/checkondel/'+id) + .map( (responseData) => + responseData.json() + ).map((deleteobject) => { + console.log("MAP SERVICE",deleteobject); + let result : any = {'ID' : id}; + _.forEach(deleteobject,function(value,key){ + result[value.TypeDesc] = []; + }); + _.forEach(deleteobject,function(value,key){ + result[value.TypeDesc].Description=value.Action; + result[value.TypeDesc].push(value.ObID); + }); + return result; + }); + }; + + testOutput(output,hideAlert?) { + // return an observable + return this.httpAPI.post('/api/cfg/output/ping/',JSON.stringify(output,this.parseJSON), null, hideAlert) + .map((responseData) => responseData.json()); + }; + + deleteOutput(id : string, hideAlert?) { + // return an observable + console.log("ID: ",id); + console.log("DELETING"); + return this.httpAPI.delete('/api/cfg/output/'+id, null, hideAlert) + .map( (responseData) => + responseData.json() + ); + }; +} diff --git a/src/output/outputeditor.html b/src/output/outputeditor.html new file mode 100644 index 00000000..48742cc3 --- /dev/null +++ b/src/output/outputeditor.html @@ -0,0 +1,114 @@ +

    {{defaultConfig.name}}

    + + + + + + + + + +
    + +
    +

    + {{ editmode | uppercase}} +

    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + Output Settings + +
    + + +
    + + +
    +
    +
    + + +
    + + + +
    +
    +
    + + +
    + + +
    +
    +
    +
    + Buffer Settings +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    + + +
    + + +
    +
    +
    +
    + + Extra Settings + +
    + + +
    + + +
    +
    +
    +
    +
    +
    +
    diff --git a/src/runtime/runtime.data.ts b/src/runtime/runtime.data.ts index 2d2668af..c70d11d2 100644 --- a/src/runtime/runtime.data.ts +++ b/src/runtime/runtime.data.ts @@ -87,7 +87,8 @@ export const MeasurementCounterDef: CounterType[] = [ { show: false, source: "counters", id: "FilterStartTime", idx: 17, label: "Filter update Start Time", type: "time", tooltip: "Last Filter time" }, { show: true, source: "counters", id: "FilterDuration", idx: 18, label: "Filter update Duration", type: "duration", tooltip: "Elapsed time taken to compute all applicable filters on the device" }, { show: false, source: "counters", id: "BackEndSentStartTime", idx: 19, label: "BackEnd DB Sent Start Time", type: "time", tooltip: "Last sent time" }, - { show: true, source: "counters", id: "BackEndSentDuration", idx:20, label: "BackEnd DB Sent Duration", type: "duration", tooltip: "Elapsed time taken to send data to the db backend" }, + { show: true, source: "counters", id: "MeasurementDropped", idx:23, label: "Series dropped", type: "counter", tooltip: "Series dropped due to full buffer" }, + { show: false, source: "counters", id: "BackEndSentDuration", idx:20, label: "BackEnd DB Sent Duration", type: "duration", tooltip: "Elapsed time taken to send data to the db backend" }, { show: true, source: "stats", id: "GatherFreq", label: "Gather Frequency", type: "duration", tooltip: "Gather frequency" }, { show: true, source: "counters", id: "CycleGatherStartTime", idx: 15, label: "Cycle Gather Start Time", type: "time", tooltip: "Last gather time" }, { show: true, source: "counters", id: "CycleGatherDuration", idx: 16, label: "Cycle Gather Duration", type: "duration", tooltip: "Elapsed time taken to get all measurement info" }, diff --git a/src/snmpdevice/snmpdevicecfg.component.ts b/src/snmpdevice/snmpdevicecfg.component.ts index 222cf0bb..a2ca1721 100644 --- a/src/snmpdevice/snmpdevicecfg.component.ts +++ b/src/snmpdevice/snmpdevicecfg.component.ts @@ -2,7 +2,7 @@ import { Component, ChangeDetectionStrategy, ViewChild,ViewContainerRef } from ' import { FormBuilder, Validators} from '@angular/forms'; import { IMultiSelectOption, IMultiSelectSettings, IMultiSelectTexts } from '../common/multiselect-dropdown'; import { SnmpDeviceService } from '../snmpdevice/snmpdevicecfg.service'; -import { InfluxServerService } from '../influxserver/influxservercfg.service'; +import { OutputService } from '../output/outputcfg.service'; import { MeasGroupService } from '../measgroup/measgroupcfg.service'; import { MeasFilterService } from '../measfilter/measfiltercfg.service'; import { VarCatalogService } from '../varcatalog/varcatalogcfg.service'; @@ -30,7 +30,7 @@ declare var _:any; @Component({ selector: 'snmpdevs', - providers: [SnmpDeviceService, InfluxServerService, MeasGroupService, MeasFilterService, VarCatalogService,BlockUIService], + providers: [SnmpDeviceService, OutputService, MeasGroupService, MeasFilterService, VarCatalogService,BlockUIService], templateUrl: './snmpdeviceeditor.html', styleUrls: ['../css/component-styles.css'] }) @@ -63,7 +63,7 @@ export class SnmpDeviceCfgComponent { filteroptions: any; selectgroups: IMultiSelectOption[] = []; selectfilters: IMultiSelectOption[] = []; - selectinfluxservers: IMultiSelectOption[] = []; + selectoutputs: IMultiSelectOption[] = []; selectvarcatalogs: IMultiSelectOption[] = []; private mySettingsInflux: IMultiSelectSettings = { singleSelect: true, @@ -98,7 +98,7 @@ export class SnmpDeviceCfgComponent { selectedVars: Array = []; public extraActions: any = ExtraActions; - constructor(public snmpDeviceService: SnmpDeviceService, public varCatalogService: VarCatalogService, public influxserverDeviceService: InfluxServerService, public measgroupsDeviceService: MeasGroupService, public measfiltersDeviceService: MeasFilterService, public exportServiceCfg : ExportServiceCfg, builder: FormBuilder, private _blocker: BlockUIService) { + constructor(public snmpDeviceService: SnmpDeviceService, public varCatalogService: VarCatalogService, public outputDeviceService: OutputService, public measgroupsDeviceService: MeasGroupService, public measfiltersDeviceService: MeasFilterService, public exportServiceCfg : ExportServiceCfg, builder: FormBuilder, private _blocker: BlockUIService) { this.editmode = 'list'; this.reloadData(); this.builder = builder; @@ -379,7 +379,7 @@ export class SnmpDeviceCfgComponent { } else { this.setDynamicFields(null); } - this.getInfluxServersforDevices(); + this.getOutputsForDevices(); this.getMeasGroupsforDevices(); this.getMeasFiltersforDevices(); this.getVarCatalogsforDevices(); @@ -389,7 +389,7 @@ export class SnmpDeviceCfgComponent { editDevice(row) { let id = row.ID; //Get select options - this.getInfluxServersforDevices(); + this.getOutputsForDevices(); this.getMeasGroupsforDevices(); this.getMeasFiltersforDevices(); this.getVarCatalogsforDevices(); @@ -560,15 +560,15 @@ updateAllSelectedItems(mySelectedArray,field,value, append?) { ); } - getInfluxServersforDevices() { - this.influxserverDeviceService.getInfluxServer(null) + getOutputsForDevices() { + this.outputDeviceService.getOutput(null) .subscribe( data => { // this.influxservers = data; - this.selectinfluxservers = []; + this.selectoutputs = []; for (let entry of data) { console.log(entry) - this.selectinfluxservers.push({ 'id': entry.ID, 'name': entry.ID }); + this.selectoutputs.push({ 'id': entry.ID, 'name': entry.ID, 'badge': entry.BackendType, 'parent': true }); } }, err => console.error(err), diff --git a/src/snmpdevice/snmpdevicecfg.data.ts b/src/snmpdevice/snmpdevicecfg.data.ts index 0b783025..919347c9 100644 --- a/src/snmpdevice/snmpdevicecfg.data.ts +++ b/src/snmpdevice/snmpdevicecfg.data.ts @@ -12,7 +12,7 @@ export const SnmpDeviceCfgComponentConfig: any = { title: 'Polling Period (sec)', name: 'Freq' }, { title: 'Update Filter (Cycles)', name: 'UpdateFltFreq' }, { title: 'Concurrent Gather', name: 'ConcurrentGather' }, - { title: 'Influx DB', name: 'OutDB' }, + { title: 'Output', name: 'OutDB' }, { title: 'Log Level', name: 'LogLevel' }, { title: 'Disable Snmp Bulk Queries', name: 'DisableBulk' }, { title: 'MaxOids for SNMP GET', name: 'MaxOids' }, diff --git a/src/snmpdevice/snmpdeviceeditor.html b/src/snmpdevice/snmpdeviceeditor.html index c717de76..d70ca3e9 100644 --- a/src/snmpdevice/snmpdeviceeditor.html +++ b/src/snmpdevice/snmpdeviceeditor.html @@ -326,10 +326,10 @@

    Data Settings
    - - + +
    - +