diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index d318aa15..9b3d5f04 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -31,7 +31,7 @@ jobs:
runs-on: ubuntu-latest
env:
GO111MODULE: on
- GOLANGCI_LINT_VERSION: v1.27.0
+ GOLANGCI_LINT_VERSION: v1.31.0
steps:
- name: Set up Go
diff --git a/.golangci.yml b/.golangci.yml
index 56729ef3..f0333c48 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -24,5 +24,17 @@ issues:
- path: cmd/ghz/main.go
text: "Error return value of `logger.Sync` is not checked"
+ # sync.once copy in pacer test
+ - path: load/pacer_test.go
+ text: "copylocks"
+
+ # TODO fix protobuf deprecated
+ - path: runner/
+ text: "SA1019: package github.com/golang/protobuf"
+
+ # TODO fix protobuf deprecated
+ - path: protodesc/
+ text: "SA1019: package github.com/golang/protobuf"
+
- path: runner/requester.go
text: "SA1019: grpc.WithBalancerName"
\ No newline at end of file
diff --git a/Makefile b/Makefile
index cd3361bd..1bb1a2e7 100644
--- a/Makefile
+++ b/Makefile
@@ -36,10 +36,6 @@ else
OPEN_COVERAGE_HTML :=
endif
-#################################################
-##### Everything below should not be edited #####
-#################################################
-
# UNAME_OS stores the value of uname -s.
UNAME_OS := $(shell uname -s)
# UNAME_ARCH stores the value of uname -m.
@@ -48,25 +44,16 @@ UNAME_ARCH := $(shell uname -m)
# TMP_BASE is the base directory used for TMP.
# Use TMP and not TMP_BASE as the temporary directory.
TMP_BASE := .tmp
-# TMP is the temporary directory used.
-# This is based on UNAME_OS and UNAME_ARCH to make sure there are no issues
-# switching between platform builds.
-TMP := $(TMP_BASE)/$(UNAME_OS)/$(UNAME_ARCH)
-# TMP_BIN is where we install binaries for usage during development.
-TMP_BIN = $(TMP)/bin
# TMP_COVERAGE is where we store code coverage files.
TMP_COVERAGE := $(TMP_BASE)/coverage
# Run all by default when "make" is invoked.
.DEFAULT_GOAL := all
-# Install all the build and lint dependencies
-setup:
- if [ ! -f $(GOPATH)/bin/tparse ]; then go get github.com/mfridman/tparse; fi;
- if [ ! -f $(GOPATH)/bin/goimports ]; then go get golang.org/x/tools/cmd/goimports; fi;
- if [ ! -f $(GOPATH)/bin/golangci-lint ]; then curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOPATH)/bin v1.23.8; fi;
- go mod download
-.PHONY: setup
+# Tools
+.PHONY: tools
+tools:
+ go generate -tags tools tools/tools.go
# All runs the default lint, test, and code coverage targets.
.PHONY: all
@@ -88,11 +75,12 @@ lint:
test:
go test $(GO_TEST_FLAGS) $(GO_PKGS)
-# gofmt and goimports all go files
+# Formats using gofmt and goimports all go files
.PHONY: fmt
fmt:
find . -name '*.go' | while read -r file; do gofmt -w -s "$$file"; goimports -w "$$file"; done
+# Build
.PHONY: build
build:
CGO_ENABLED=0 go build --ldflags="-s -w" -o $(DIST_DIR)/ghz ./cmd/ghz/...
diff --git a/README.md b/README.md
index ad368218..e2b4c9bb 100644
--- a/README.md
+++ b/README.md
@@ -70,16 +70,31 @@ Flags:
--key= File containing client private key, to present to the server. Must also provide -cert option.
--cname= Server name override when validating TLS certificate - useful for self signed certs.
--skipTLS Skip TLS client verification of the server's certificate chain and host name.
- --skipFirst=0 Skip the first X requests from the timing calculations (useful for initial warmup)
--insecure Use plaintext and insecure connection.
--authority= Value to be used as the :authority pseudo-header. Only works if -insecure is used.
- -c, --concurrency=50 Number of requests to run concurrently. Total number of requests cannot be smaller than the concurrency level. Default is 50.
+ --async Make requests asynchronous as soon as possible. Does not wait for request to finish before sending next one.
+ -r, --rps=0 Requests per second (RPS) rate limit for constant load schedule. Default is no rate limit.
+ --load-schedule="const" Specifies the load schedule. Options are const, step, or line. Default is const.
+ --load-start=0 Specifies the RPS load start value for step or line schedules.
+ --load-step=0 Specifies the load step value or slope value.
+ --load-end=0 Specifies the load end value for step or line load schedules.
+ --load-step-duration=0 Specifies the load step duration value for step load schedule.
+ --load-max-duration=0 Specifies the max load duration value for step or line load schedule.
+ -c, --concurrency=50 Number of request workers to run concurrently for const concurrency schedule. Default is 50.
+ --concurrency-schedule="const"
+ Concurrency change schedule. Options are const, step, or line. Default is const.
+ --concurrency-start=0 Concurrency start value for step and line concurrency schedules.
+ --concurrency-end=0 Concurrency end value for step and line concurrency schedules.
+ --concurrency-step=1 Concurrency step / slope value for step and line concurrency schedules.
+ --concurrency-step-duration=0
+ Specifies the concurrency step duration value for step concurrency schedule.
+ --concurrency-max-duration=0
+ Specifies the max concurrency adjustment duration value for step or line concurrency schedule.
-n, --total=200 Number of requests to run. Default is 200.
- -q, --qps=0 Rate limit, in queries per second (QPS). Default is no rate limit.
-t, --timeout=20s Timeout for each request. Default is 20s, use 0 for infinite.
-z, --duration=0 Duration of application to send requests. When duration is reached, application stops and exits. If duration is specified, n is ignored. Examples: -z 10s -z 3m.
-x, --max-duration=0 Maximum duration of application to send requests with n setting respected. If duration is reached before n requests are completed, application stops and exits. Examples: -x 10s -x 3m.
- --duration-stop="close" Specifies how duration stop is reported. Options are close, wait or ignore.
+ --duration-stop="close" Specifies how duration stop is reported. Options are close, wait or ignore. Default is close.
-d, --data= The call data as stringified JSON. If the value is '@' then the request contents are read from stdin.
-D, --data-file= File path for call data JSON file. Examples: /home/user/file.json or ./file.json.
-b, --binary The call data comes as serialized binary message or multiple count-prefixed messages read from stdin.
@@ -90,6 +105,7 @@ Flags:
--reflect-metadata= Reflect metadata as stringified JSON used only for reflection request.
-o, --output= Output path. If none provided stdout is used.
-O, --format= Output format. One of: summary, csv, json, pretty, html, influx-summary, influx-details. Default is summary.
+ --skipFirst=0 Skip the first X requests when doing the results tally.
--connections=1 Number of connections to use. Concurrency is distributed evenly among all the connections. Default is 1.
--connect-timeout=10s Connection timeout for the initial connection dial. Default is 10s.
--keepalive=0 Keepalive time duration. Only used if present and above 0.
diff --git a/cmd/ghz/main.go b/cmd/ghz/main.go
index b5445a16..5ef03ac2 100644
--- a/cmd/ghz/main.go
+++ b/cmd/ghz/main.go
@@ -64,10 +64,6 @@ var (
skipVerify = kingpin.Flag("skipTLS", "Skip TLS client verification of the server's certificate chain and host name.").
Default("false").IsSetByUser(&isSkipSet).Bool()
- isSkipFirstSet = false
- skipFirst = kingpin.Flag("skipFirst", "Skip the first X requests when doing the results tally.").
- Default("0").IsSetByUser(&isSkipFirstSet).Uint()
-
isInsecSet = false
insecure = kingpin.Flag("insecure", "Use plaintext and insecure connection.").
Default("false").IsSetByUser(&isInsecSet).Bool()
@@ -77,18 +73,72 @@ var (
PlaceHolder(" ").IsSetByUser(&isAuthSet).String()
// Run
+ isAsyncSet = false
+ async = kingpin.Flag("async", "Make requests asynchronous as soon as possible. Does not wait for request to finish before sending next one.").
+ Default("false").IsSetByUser(&isAsyncSet).Bool()
+
+ isRPSSet = false
+ rps = kingpin.Flag("rps", "Requests per second (RPS) rate limit for constant load schedule. Default is no rate limit.").
+ Default("0").Short('r').IsSetByUser(&isRPSSet).Uint()
+
+ isScheduleSet = false
+ schedule = kingpin.Flag("load-schedule", "Specifies the load schedule. Options are const, step, or line. Default is const.").
+ Default("const").IsSetByUser(&isScheduleSet).String()
+
+ isLoadStartSet = false
+ loadStart = kingpin.Flag("load-start", "Specifies the RPS load start value for step or line schedules.").
+ Default("0").IsSetByUser(&isLoadStartSet).Uint()
+
+ isLoadStepSet = false
+ loadStep = kingpin.Flag("load-step", "Specifies the load step value or slope value.").
+ Default("0").IsSetByUser(&isLoadStepSet).Int()
+
+ isLoadEndSet = false
+ loadEnd = kingpin.Flag("load-end", "Specifies the load end value for step or line load schedules.").
+ Default("0").IsSetByUser(&isLoadEndSet).Uint()
+
+ isLoadStepDurSet = false
+ loadStepDuration = kingpin.Flag("load-step-duration", "Specifies the load step duration value for step load schedule.").
+ Default("0").IsSetByUser(&isLoadStepDurSet).Duration()
+
+ isLoadMaxDurSet = false
+ loadMaxDuration = kingpin.Flag("load-max-duration", "Specifies the max load duration value for step or line load schedule.").
+ Default("0").IsSetByUser(&isLoadMaxDurSet).Duration()
+
+ // Concurrency
isCSet = false
- c = kingpin.Flag("concurrency", "Number of requests to run concurrently. Total number of requests cannot be smaller than the concurrency level. Default is 50.").
+ c = kingpin.Flag("concurrency", "Number of request workers to run concurrently for const concurrency schedule. Default is 50.").
Short('c').Default("50").IsSetByUser(&isCSet).Uint()
+ isCScheduleSet = false
+ cschdule = kingpin.Flag("concurrency-schedule", "Concurrency change schedule. Options are const, step, or line. Default is const.").
+ Default("const").IsSetByUser(&isCScheduleSet).String()
+
+ isCStartSet = false
+ cStart = kingpin.Flag("concurrency-start", "Concurrency start value for step and line concurrency schedules.").
+ Default("0").IsSetByUser(&isCStartSet).Uint()
+
+ isCEndSet = false
+ cEnd = kingpin.Flag("concurrency-end", "Concurrency end value for step and line concurrency schedules.").
+ Default("0").IsSetByUser(&isCEndSet).Uint()
+
+ isCStepSet = false
+ cstep = kingpin.Flag("concurrency-step", "Concurrency step / slope value for step and line concurrency schedules.").
+ Default("1").IsSetByUser(&isCStepSet).Int()
+
+ isCStepDurSet = false
+ cStepDuration = kingpin.Flag("concurrency-step-duration", "Specifies the concurrency step duration value for step concurrency schedule.").
+ Default("0").IsSetByUser(&isCStepDurSet).Duration()
+
+ isCMaxDurSet = false
+ cMaxDuration = kingpin.Flag("concurrency-max-duration", "Specifies the max concurrency adjustment duration value for step or line concurrency schedule.").
+ Default("0").IsSetByUser(&isCMaxDurSet).Duration()
+
+ // Other
isNSet = false
n = kingpin.Flag("total", "Number of requests to run. Default is 200.").
Short('n').Default("200").IsSetByUser(&isNSet).Uint()
- isQSet = false
- q = kingpin.Flag("qps", "Rate limit, in queries per second (QPS). Default is no rate limit.").
- Default("0").Short('q').IsSetByUser(&isQSet).Uint()
-
isTSet = false
t = kingpin.Flag("timeout", "Timeout for each request. Default is 20s, use 0 for infinite.").
Default("20s").Short('t').IsSetByUser(&isTSet).Duration()
@@ -102,7 +152,7 @@ var (
Short('x').Default("0").IsSetByUser(&isXSet).Duration()
isZStopSet = false
- zstop = kingpin.Flag("duration-stop", "Specifies how duration stop is reported. Options are close, wait or ignore.").
+ zstop = kingpin.Flag("duration-stop", "Specifies how duration stop is reported. Options are close, wait or ignore. Default is close.").
Default("close").IsSetByUser(&isZStopSet).String()
// Data
@@ -147,6 +197,10 @@ var (
format = kingpin.Flag("format", "Output format. One of: summary, csv, json, pretty, html, influx-summary, influx-details. Default is summary.").
Short('O').Default("summary").PlaceHolder(" ").IsSetByUser(&isFormatSet).Enum("summary", "csv", "json", "pretty", "html", "influx-summary", "influx-details")
+ isSkipFirstSet = false
+ skipFirst = kingpin.Flag("skipFirst", "Skip the first X requests when doing the results tally.").
+ Default("0").IsSetByUser(&isSkipFirstSet).Uint()
+
// Connection
isConnSet = false
conns = kingpin.Flag("connections", "Number of connections to use. Concurrency is distributed evenly among all the connections. Default is 1.").
@@ -360,7 +414,7 @@ func createConfigFromArgs(cfg *runner.Config) error {
cfg.CName = *cname
cfg.N = *n
cfg.C = *c
- cfg.QPS = *q
+ cfg.RPS = *rps
cfg.Z = runner.Duration(*z)
cfg.X = runner.Duration(*x)
cfg.Timeout = runner.Duration(*t)
@@ -384,6 +438,19 @@ func createConfigFromArgs(cfg *runner.Config) error {
cfg.ReflectMetadata = rmdMap
cfg.Debug = *debug
cfg.EnableCompression = *enableCompression
+ cfg.LoadSchedule = *schedule
+ cfg.LoadStart = *loadStart
+ cfg.LoadStep = *loadStep
+ cfg.LoadEnd = *loadEnd
+ cfg.LoadStepDuration = runner.Duration(*loadStepDuration)
+ cfg.LoadMaxDuration = runner.Duration(*loadMaxDuration)
+ cfg.Async = *async
+ cfg.CSchedule = *cschdule
+ cfg.CStart = *cStart
+ cfg.CStep = *cstep
+ cfg.CEnd = *cEnd
+ cfg.CStepDuration = runner.Duration(*cStepDuration)
+ cfg.CMaxDuration = runner.Duration(*cMaxDuration)
return nil
}
@@ -393,6 +460,8 @@ func mergeConfig(dest *runner.Config, src *runner.Config) error {
return errors.New("config cannot be nil")
}
+ // proto
+
if isProtoSet {
dest.Proto = src.Proto
}
@@ -405,6 +474,8 @@ func mergeConfig(dest *runner.Config, src *runner.Config) error {
dest.Call = src.Call
}
+ // security
+
if isCACertSet {
dest.RootCert = src.RootCert
}
@@ -437,18 +508,12 @@ func mergeConfig(dest *runner.Config, src *runner.Config) error {
dest.CName = src.CName
}
+ // run
+
if isNSet {
dest.N = src.N
}
- if isCSet {
- dest.C = src.C
- }
-
- if isQSet {
- dest.QPS = src.QPS
- }
-
if isZSet {
dest.Z = src.Z
}
@@ -465,6 +530,8 @@ func mergeConfig(dest *runner.Config, src *runner.Config) error {
dest.ZStop = src.ZStop
}
+ // data
+
if isDataSet {
dest.Data = src.Data
}
@@ -489,6 +556,8 @@ func mergeConfig(dest *runner.Config, src *runner.Config) error {
dest.MetadataPath = src.MetadataPath
}
+ // other
+
if isSISet {
dest.SI = src.SI
}
@@ -541,6 +610,70 @@ func mergeConfig(dest *runner.Config, src *runner.Config) error {
dest.Host = src.Host
}
+ // load
+
+ if isAsyncSet {
+ dest.Async = src.Async
+ }
+
+ if isRPSSet {
+ dest.RPS = src.RPS
+ }
+
+ if isScheduleSet {
+ dest.LoadSchedule = src.LoadSchedule
+ }
+
+ if isLoadStartSet {
+ dest.LoadStart = src.LoadStart
+ }
+
+ if isLoadStepSet {
+ dest.LoadStep = src.LoadStep
+ }
+
+ if isLoadEndSet {
+ dest.LoadEnd = src.LoadEnd
+ }
+
+ if isLoadStepDurSet {
+ dest.LoadStepDuration = src.LoadStepDuration
+ }
+
+ if isLoadMaxDurSet {
+ dest.LoadMaxDuration = src.LoadMaxDuration
+ }
+
+ // concurrency
+
+ if isCSet {
+ dest.C = src.C
+ }
+
+ if isCScheduleSet {
+ dest.CSchedule = src.CSchedule
+ }
+
+ if isCStartSet {
+ dest.CStart = src.CStart
+ }
+
+ if isCStepSet {
+ dest.CStep = src.CStep
+ }
+
+ if isCEndSet {
+ dest.CEnd = src.CEnd
+ }
+
+ if isCStepDurSet {
+ dest.CStepDuration = src.CStepDuration
+ }
+
+ if isCMaxDurSet {
+ dest.CMaxDuration = src.CMaxDuration
+ }
+
return nil
}
diff --git a/go.mod b/go.mod
index e5e5ef35..19877148 100644
--- a/go.mod
+++ b/go.mod
@@ -3,38 +3,31 @@ module github.com/bojand/ghz
go 1.14
require (
- cloud.google.com/go v0.46.3 // indirect
github.com/alecthomas/kingpin v1.3.8-0.20191105203113-8c96d1c22481
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/bojand/hri v1.1.0
- github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.16.0 // indirect
github.com/go-playground/validator v9.30.0+incompatible
github.com/gogo/protobuf v1.3.1
- github.com/golang/protobuf v1.3.2
+ github.com/golang/protobuf v1.4.2
+ github.com/golangci/golangci-lint v1.31.0
github.com/google/uuid v1.1.1
github.com/jhump/protoreflect v1.5.0
github.com/jinzhu/configor v1.1.1
github.com/jinzhu/gorm v1.9.11
- github.com/kr/text v0.2.0 // indirect
github.com/labstack/echo v3.3.10+incompatible
github.com/labstack/gommon v0.3.0
github.com/leodido/go-urn v1.2.0 // indirect
- github.com/mattn/go-colorable v0.1.4 // indirect
- github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
- github.com/pkg/errors v0.8.1
+ github.com/mfridman/tparse v0.8.3
+ github.com/pkg/errors v0.9.1
github.com/rakyll/statik v0.1.6
- github.com/stretchr/testify v1.5.1
+ github.com/stretchr/testify v1.6.1
go.uber.org/multierr v1.3.0
go.uber.org/zap v1.13.0
- golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
- golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e // indirect
- golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 // indirect
- google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a // indirect
+ golang.org/x/net v0.0.0-20200625001655-4c5254603344
+ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
+ golang.org/x/tools v0.0.0-20200812195022-5ae4c3c160a0
google.golang.org/grpc v1.24.0
- gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
- gopkg.in/yaml.v2 v2.2.8 // indirect
- honnef.co/go/tools v0.0.1-2020.1.3 // indirect
)
diff --git a/go.sum b/go.sum
index 857e1173..2d8696a0 100644
--- a/go.sum
+++ b/go.sum
@@ -9,12 +9,23 @@ cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
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/Djarvur/go-err113 v0.0.0-20200511133814-5174e21577d5 h1:XTrzB+F8+SpRmbhAH8HLxhiiG6nYNwaBZjrFps1oWEk=
+github.com/Djarvur/go-err113 v0.0.0-20200511133814-5174e21577d5/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
+github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
+github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
+github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
+github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/kingpin v1.3.8-0.20191105203113-8c96d1c22481 h1:NXM4vkjHeFp3bbp0z/u0AdQRLg6b5LrPeFwgjVHUW58=
github.com/alecthomas/kingpin v1.3.8-0.20191105203113-8c96d1c22481/go.mod h1:b6br6/pDFSfMkBgC96TbpOji05q5pa+v5rIlS0Y6XtI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -22,42 +33,104 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/apache/thrift v0.12.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/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+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/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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bojand/hri v1.1.0 h1:OIv6AtbPjYv9A7qjUqylU11mbcP610JWsWCwvpc3w3U=
github.com/bojand/hri v1.1.0/go.mod h1:qwGosuHpNn1S0nyw/mExN0+WZrDf4bQyWjhWh51y3VY=
+github.com/bombsimon/wsl/v3 v3.1.0 h1:E5SRssoBgtVFPcYWUOFJEcgaySgdtTNYzsSKDOY7ss8=
+github.com/bombsimon/wsl/v3 v3.1.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/daixiang0/gci v0.2.4 h1:BUCKk5nlK2m+kRIsoj+wb/5hazHvHeZieBKWd9Afa8Q=
+github.com/daixiang0/gci v0.2.4/go.mod h1:+AV8KmHTGxxwp/pY84TLQfFKp2vuKXXJVzF3kD/hfR4=
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/denis-tingajkin/go-header v0.3.1 h1:ymEpSiFjeItCy1FOP+x0M2KdCELdEAHUsNa8F+hHc6w=
+github.com/denis-tingajkin/go-header v0.3.1/go.mod h1:sq/2IxMhaZX+RRcgHfCRx/m0M5na0fBt4/CRe7Lrji0=
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA=
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
+github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
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/go-critic/go-critic v0.5.2 h1:3RJdgf6u4NZUumoP8nzbqiiNT8e1tC2Oc7jlgqre/IA=
+github.com/go-critic/go-critic v0.5.2/go.mod h1:cc0+HvdE3lFpqLecgqMaJcvWWH77sLdBp+wLGPM1Yyo=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
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-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-playground/validator v9.30.0+incompatible h1:myhdWhx5UHvLXZ7maUcP0uYxyijMT+smaNAhARBVc9s=
github.com/go-playground/validator v9.30.0+incompatible/go.mod h1:yrEkQXlcI+PugkyDjY2bRrL/UBU4f3rvrgkN3V8JEig=
+github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
+github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
+github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8=
+github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
+github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ=
+github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
+github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k=
+github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
+github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
+github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg=
+github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
+github.com/go-toolsmith/pkgload v1.0.0 h1:4DFWWMXVfbcN5So1sBNW9+yeiMqLFGl1wFLTL5R0Tgg=
+github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
+github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4=
+github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
+github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
+github.com/go-toolsmith/typep v1.0.2 h1:8xdsa1+FSIH/RhEkgnD1j2CJOy5mNllW1Q9tRiYwvlk=
+github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
+github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo=
+github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
+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/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY=
+github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
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 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/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=
@@ -65,27 +138,104 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0=
+github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
+github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
+github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
+github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 h1:YYWNAGTKWhKpcLLt7aSj/odlKrSrelQwlovBpDuf19w=
+github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
+github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw=
+github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
+github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8=
+github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
+github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d h1:pXTK/gkVNs7Zyy7WKgLXmpQ5bHTrq5GDsp8R9Qs67g0=
+github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
+github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks=
+github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
+github.com/golangci/golangci-lint v1.31.0 h1:+m9I3LEmxXLpymkXRPkDQGzOVBmBYm16UtDiXqZxWek=
+github.com/golangci/golangci-lint v1.31.0/go.mod h1:aMQuNCA+NDU5+4jLL5pEuFHoue0IznKE2+/GsFvvs8A=
+github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc h1:gLLhTLMk2/SutryVJ6D4VZCU3CUqr8YloG7FPIBWFpI=
+github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
+github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA=
+github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
+github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
+github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
+github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk=
+github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
+github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us=
+github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
+github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1aiNg8JbBMO2+E1WIQ+j/gL4SQqGPg=
+github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
+github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys=
+github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
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.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
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/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 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/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/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
+github.com/gostaticanalysis/analysisutil v0.0.3 h1:iwp+5/UAyzQSFgQ4uR2sni99sJ8Eo9DEacKWM5pekIg=
+github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/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.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/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/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jhump/protoreflect v1.5.0 h1:NgpVT+dX71c8hZnxHof2M7QDK7QtohIJ7DYycjnkyfc=
github.com/jhump/protoreflect v1.5.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
+github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk=
+github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s=
github.com/jinzhu/configor v1.1.1 h1:gntDP+ffGhs7aJ0u8JvjCDts2OsxsI7bnz3q+jC+hSY=
github.com/jinzhu/configor v1.1.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
github.com/jinzhu/gorm v1.9.11 h1:gaHGvE+UnWGlbWG4Y3FUwY1EcZ5n6S9WtqBA/uySMLE=
@@ -94,93 +244,267 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3 h1:jNYPNLe3d8smommaoQlK7LOA5ESyUJJ+Wf79ZtA7Vp4=
+github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
+github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
+github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+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/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/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kyoh86/exportloopref v0.1.7 h1:u+iHuTbkbTS2D/JP7fCuZDo/t3rBVGo3Hf58Rc+lQVY=
+github.com/kyoh86/exportloopref v0.1.7/go.mod h1:h1rDl2Kdj97+Kwh4gdz3ujE7XHmH51Q0lUiZ1z4NLj8=
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
+github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
+github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/maratori/testpackage v1.0.1 h1:QtJ5ZjqapShm0w5DosRjg0PRlSdAdlx+W6cCKoALdbQ=
+github.com/maratori/testpackage v1.0.1/go.mod h1:ddKdw+XG0Phzhx8BFDTKgpWP4i7MpApTE5fXSKAqwDU=
+github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb h1:RHba4YImhrUVQDHUCe2BNSOz4tVy2yGyXhvYDvxGgeE=
+github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
+github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+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-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mfridman/tparse v0.8.3 h1:DnjEnBXdlUJPo8ShfNPasu7m52iI1ETiST5RvS6b0c4=
+github.com/mfridman/tparse v0.8.3/go.mod h1:LzZWLkqcQrOfgvqZn7LOSBzgZwWnqI5NQsfgQVOT1o8=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/nakabonne/nestif v0.3.0 h1:+yOViDGhg8ygGrmII72nV9B/zGxY188TYpfolntsaPw=
+github.com/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c=
+github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E=
+github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nishanths/exhaustive v0.0.0-20200811152831-6cf413ae40e0 h1:eMV1t2NQRc3r1k3guWiv/zEeqZZP6kPvpUfy6byfL1g=
+github.com/nishanths/exhaustive v0.0.0-20200811152831-6cf413ae40e0/go.mod h1:wBEpHwM2OdmeNpdCvRPUlkEbBuaFmcK4Wv8Q7FuGW3c=
+github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
+github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
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.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.13.0 h1:M76yO2HkZASFjXL0HSoZJ1AYEmQxNJmY41Jx1zNUq1Y=
+github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/gomega v1.4.3/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 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
+github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d h1:CdDQnGF8Nq9ocOS/xlSptM1N3BbrA6/kmaep5ggwaIA=
+github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d/go.mod h1:3OzsM7FXDQlpCiw2j81fOmAwQLnZnLGXVKUzeKQXIAw=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
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/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
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.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
+github.com/quasilyte/go-ruleguard v0.2.0 h1:UOVMyH2EKkxIfzrULvA9n/tO+HtEhqD9mrLSWMr5FwU=
+github.com/quasilyte/go-ruleguard v0.2.0/go.mod h1:2RT/tf0Ce0UDj5y243iWKosQogJd8+1G3Rs2fxmlYnw=
+github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 h1:L8QM9bvf68pVdQ3bCFZMDmnt9yqcMBro1pC7F+IPYMY=
+github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs=
github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rogpeppe/go-internal v1.6.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryancurrah/gomodguard v1.1.0 h1:DWbye9KyMgytn8uYpuHkwf0RHqAYO6Ay/D0TbCpPtVU=
+github.com/ryancurrah/gomodguard v1.1.0/go.mod h1:4O8tr7hBODaGE6VIhfJDHcwzh5GUccKSJBU0UMXJFVM=
+github.com/ryanrolds/sqlclosecheck v0.3.0 h1:AZx+Bixh8zdUBxUA1NxbxVAS78vTPq4rCb8OUZI9xFw=
+github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/securego/gosec/v2 v2.4.0 h1:ivAoWcY5DMs9n04Abc1VkqZBO0FL0h4ShTcVsC53lCE=
+github.com/securego/gosec/v2 v2.4.0/go.mod h1:0/Q4cjmlFDfDUj1+Fib61sc+U5IQb2w+Iv9/C3wPVko=
+github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU=
+github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
+github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
+github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
+github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
+github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
+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/sonatard/noctx v0.0.1 h1:VC1Qhl6Oxx9vvWo3UDgrGXYCeKCe3Wbw7qAWL6FrmTY=
+github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI=
+github.com/sourcegraph/go-diff v0.6.0 h1:WbN9e/jD8ujU+o0vd9IFN5AEwtfB0rn/zM/AANaClqQ=
+github.com/sourcegraph/go-diff v0.6.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
+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/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 v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
+github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
+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 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.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
+github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
+github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+github.com/ssgreg/nlreturn/v2 v2.1.0 h1:6/s4Rc49L6Uo6RLjhWZGBpWWjfzk2yrf1nIW8m4wgVA=
+github.com/ssgreg/nlreturn/v2 v2.1.0/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2 h1:Xr9gkxfOP0KQWXKNqmwe8vEeSUiUj4Rlee9CMVX2ZUQ=
+github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
+github.com/tetafro/godot v0.4.8 h1:h61+hQraWhdI6WYqMwAwZYCE5yxL6a9/Orw4REbabSU=
+github.com/tetafro/godot v0.4.8/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
+github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q=
+github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa h1:RC4maTWLKKwb7p1cnoygsbKIgNlJqSYBeAFON3Ar8As=
+github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA=
+github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
+github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFODYDsg=
+github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
+github.com/uudashr/gocognit v1.0.1 h1:MoG2fZ0b/Eo7NXoIwCVFLG5JED3qgQz5/NEE+rOsjPs=
+github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v1.15.1/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA=
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
+github.com/valyala/quicktemplate v1.6.2/go.mod h1:mtEJpQtUiBV0SHhMX6RtiJtqxncgrfmjcUy5T68X8TM=
+github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+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=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.20.1/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.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
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-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/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/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=
@@ -195,13 +519,19 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
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.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
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/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -209,10 +539,15 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/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 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -221,60 +556,101 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
+golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/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 h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y=
+golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
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/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-20190110163146-51295c7ec13a/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-20190221204921-83362c3779f5/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
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-20190311215038-5c2858a9cfe5/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-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/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-20190606124116-d0a3d012864b/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-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/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-20190910044552-dd2b5c81c578/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-20191112195655-aa38f8e97acc/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-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 h1:M9Fif0OxNji8w+HvmhVQ8KJtiZOsjU9RgslJGhn95XE=
-golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200321224714-0d839f3cf2ed/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
+golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200701041122-1837592efa10/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200812195022-5ae4c3c160a0 h1:SQvH+DjrwqD1hyyQU+K7JegHz1KEZgEwt17p9d6R2eg=
+golang.org/x/tools v0.0.0-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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=
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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
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=
@@ -296,29 +672,56 @@ google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEd
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.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.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
+gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
+gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
-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.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k=
+honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
+mvdan.cc/gofumpt v0.0.0-20200709182408-4fd085cb6d5f h1:gi7cb8HTDZ6q8VqsUpkdoFi3vxwHMneQ6+Q5Ap5hjPE=
+mvdan.cc/gofumpt v0.0.0-20200709182408-4fd085cb6d5f/go.mod h1:9VQ397fNXEnF84t90W4r4TRCQK+pg9f8ugVfyj+S26w=
+mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
+mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
+mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
+mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
+mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4=
+mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
diff --git a/internal/helloworld/greeter_server.go b/internal/helloworld/greeter_server.go
index 7b09323a..73ec43fb 100644
--- a/internal/helloworld/greeter_server.go
+++ b/internal/helloworld/greeter_server.go
@@ -4,6 +4,7 @@ import (
"fmt"
"io"
"math/rand"
+ "strings"
"sync"
"time"
@@ -220,6 +221,37 @@ func (c *HWStatsHandler) GetConnectionCount() int {
return val
}
+// GetCountByWorker gets count of requests by goroutine
+func (s *Greeter) GetCountByWorker(key CallType) map[string]int {
+ s.mutex.Lock()
+ val, ok := s.calls[key]
+ s.mutex.Unlock()
+
+ if !ok {
+ return nil
+ }
+
+ counts := make(map[string]int)
+
+ for _, reqs := range val {
+ for _, req := range reqs {
+ name := req.GetName()
+ if strings.Contains(name, "worker:") {
+ parts := strings.Split(name, ":")
+ wid := parts[len(parts)-1]
+ wc, ok := counts[wid]
+ if !ok {
+ counts[wid] = 0
+ }
+
+ counts[wid] = wc + 1
+ }
+ }
+ }
+
+ return counts
+}
+
// HandleConn handle the connection
func (c *HWStatsHandler) HandleConn(ctx context.Context, cs stats.ConnStats) {
// no-op
diff --git a/load/pacer.go b/load/pacer.go
new file mode 100644
index 00000000..325a9527
--- /dev/null
+++ b/load/pacer.go
@@ -0,0 +1,319 @@
+package load
+
+import (
+ "fmt"
+ "math"
+ "sync"
+ "time"
+)
+
+// nano is the const for number of nanoseconds in a second
+const nano = 1e9
+
+// Pacer defines the interface to control the rate of hit.
+type Pacer interface {
+ // Pace returns the duration the attacker should wait until
+ // making next hit, given an already elapsed duration and
+ // completed hits. If the second return value is true, an attacker
+ // should stop sending hits.
+ Pace(elapsed time.Duration, hits uint64) (wait time.Duration, stop bool)
+
+ // Rate returns a Pacer's instantaneous hit rate (per seconds)
+ // at the given elapsed duration of an attack.
+ Rate(elapsed time.Duration) float64
+}
+
+// A ConstantPacer defines a constant rate of hits.
+type ConstantPacer struct {
+ Freq uint64 // Frequency of hits per second
+ Max uint64 // Optional maximum allowed hits
+}
+
+// String returns a pretty-printed description of the ConstantPacer's behaviour:
+// ConstantPacer{Freq: 1} => Constant{1 hits / 1s}
+func (cp *ConstantPacer) String() string {
+ return fmt.Sprintf("Constant{%d hits / 1s}", cp.Freq)
+}
+
+// Pace determines the length of time to sleep until the next hit is sent.
+func (cp *ConstantPacer) Pace(elapsed time.Duration, hits uint64) (time.Duration, bool) {
+
+ if cp.Max > 0 && hits >= cp.Max {
+ return 0, true
+ }
+
+ if cp.Freq == 0 {
+ return 0, false // Zero value = infinite rate
+ }
+
+ expectedHits := uint64(cp.Freq) * uint64(elapsed/nano)
+ if hits < expectedHits {
+ // Running behind, send next hit immediately.
+ return 0, false
+ }
+
+ interval := uint64(nano / int64(cp.Freq))
+ if math.MaxInt64/interval < hits {
+ // We would overflow delta if we continued, so stop the attack.
+ return 0, true
+ }
+
+ delta := time.Duration((hits + 1) * interval)
+ // Zero or negative durations cause time.Sleep to return immediately.
+ return delta - elapsed, false
+}
+
+// Rate returns a ConstantPacer's instantaneous hit rate (i.e. requests per second)
+// at the given elapsed duration of an attack. Since it's constant, the return
+// value is independent of the given elapsed duration.
+func (cp *ConstantPacer) Rate(elapsed time.Duration) float64 {
+ return cp.hitsPerNs() * 1e9
+}
+
+// hitsPerNs returns the rate in fractional hits per nanosecond.
+func (cp *ConstantPacer) hitsPerNs() float64 {
+ return float64(cp.Freq) / nano
+}
+
+// StepPacer paces an attack by starting at a given request rate
+// and increasing or decreasing with steps at a given step interval and duration.
+type StepPacer struct {
+ Start ConstantPacer // Constant start rate
+ Step int64 // Step value
+ StepDuration time.Duration // Step duration
+ Stop ConstantPacer // Optional constant stop value
+ LoadDuration time.Duration // Optional maximum load duration
+ Max uint64 // Optional maximum allowed hits
+
+ once sync.Once
+ init bool // TOOO improve this
+ constAt time.Duration
+ baseHits uint64
+}
+
+func (p *StepPacer) initialize() {
+
+ if p.StepDuration == 0 {
+ panic("StepPacer.StepDuration cannot be 0")
+ }
+
+ if p.Step == 0 {
+ panic("StepPacer.Step cannot be 0")
+ }
+
+ if p.Start.Freq == 0 {
+ panic("Start.Freq cannot be 0")
+ }
+
+ if p.init {
+ return
+ }
+
+ p.init = true
+
+ if p.LoadDuration > 0 {
+ p.constAt = p.LoadDuration
+
+ if p.Stop.Freq == 0 {
+ steps := p.constAt.Nanoseconds() / p.StepDuration.Nanoseconds()
+
+ p.Stop.Freq = p.Start.Freq + uint64(int64(p.Step)*steps)
+ }
+ } else if p.Stop.Freq > 0 && p.constAt == 0 {
+ stopRPS := float64(p.Stop.Freq)
+
+ if p.Step > 0 {
+ t := time.Duration(0)
+ for {
+ if p.Rate(t) > stopRPS {
+ p.constAt = t
+ break
+ }
+ t = t + p.StepDuration
+ }
+ } else {
+ t := time.Duration(0)
+ for {
+ if p.Rate(t) < stopRPS {
+ p.constAt = t
+ break
+ }
+ t = t + p.StepDuration
+ }
+ }
+ }
+
+ if p.constAt > 0 {
+ p.baseHits = uint64(p.hits(p.constAt))
+ }
+}
+
+// Pace determines the length of time to sleep until the next hit is sent.
+func (p *StepPacer) Pace(elapsed time.Duration, hits uint64) (time.Duration, bool) {
+
+ if p.Max > 0 && hits >= p.Max {
+ return 0, true
+ }
+
+ p.once.Do(p.initialize)
+
+ expectedHits := p.hits(elapsed)
+
+ if hits < uint64(expectedHits) {
+ // Running behind, send next hit immediately.
+ return 0, false
+ }
+
+ // const part
+ if p.constAt > 0 && elapsed >= p.constAt {
+ if p.Stop.Freq == 0 {
+ return 0, true
+ }
+
+ return p.Stop.Pace(elapsed-p.constAt, hits-p.baseHits)
+ }
+
+ rate := p.Rate(elapsed)
+ interval := nano / rate
+
+ if n := uint64(interval); n != 0 && math.MaxInt64/n < hits {
+ // We would overflow wait if we continued, so stop the attack.
+ return 0, true
+ }
+
+ delta := float64(hits+1) - expectedHits
+ wait := time.Duration(interval * delta)
+
+ // if wait > nano {
+ // intervals := elapsed / nano
+ // wait = (intervals+1)*nano - elapsed
+ // }
+
+ return wait, false
+}
+
+// Rate returns a StepPacer's instantaneous hit rate (i.e. requests per second)
+// at the given elapsed duration.
+func (p *StepPacer) Rate(elapsed time.Duration) float64 {
+ p.initialize()
+
+ t := elapsed
+
+ if p.constAt > 0 && elapsed >= p.constAt {
+ return float64(p.Stop.Freq)
+ }
+
+ steps := t.Nanoseconds() / p.StepDuration.Nanoseconds()
+
+ rate := (p.Start.hitsPerNs() + float64(int64(p.Step)*steps)/nano) * 1e9
+
+ if rate < 0 {
+ rate = 0
+ }
+
+ return rate
+}
+
+// hits returns the number of hits that have been sent at elapsed duration t.
+func (p *StepPacer) hits(t time.Duration) float64 {
+ if t < 0 {
+ return 0
+ }
+
+ steps := t.Nanoseconds() / p.StepDuration.Nanoseconds()
+
+ base := p.Start.hitsPerNs() * 1e9
+
+ // first step
+ var s float64
+ if steps > 0 {
+ s = p.StepDuration.Seconds() * base
+ } else {
+ s = t.Seconds() * base
+ }
+
+ // previous steps: 1...n
+ for i := int64(1); i < steps; i++ {
+ d := time.Duration(p.StepDuration.Nanoseconds() * i)
+ r := p.Rate(d)
+ ch := r * p.StepDuration.Seconds()
+ s = s + ch
+ }
+
+ c := float64(0)
+ if steps > 0 {
+ // current step
+ elapsed := time.Duration(t.Nanoseconds() - steps*p.StepDuration.Nanoseconds())
+ c = elapsed.Seconds() * p.Rate(t)
+ }
+
+ return s + c
+}
+
+// String returns a pretty-printed description of the StepPacer's behaviour:
+// StepPacer{Step: 1, StepDuration: 5s} => Step{Step:1 hits / 5s}
+func (p *StepPacer) String() string {
+ return fmt.Sprintf("Step{Step: %d hits / %s}", p.Step, p.StepDuration.String())
+}
+
+// LinearPacer paces the hit rate by starting at a given request rate
+// and increasing linearly with the given slope at 1s interval.
+type LinearPacer struct {
+ Start ConstantPacer // Constant start rate
+ Slope int64 // Slope value to change the rate
+ Stop ConstantPacer // Constant stop rate
+ LoadDuration time.Duration // Total maximum load duration
+ Max uint64 // Maximum number of hits
+
+ once sync.Once
+ sp StepPacer
+}
+
+// initializes the wrapped step pacer
+func (p *LinearPacer) initialize() {
+ if p.Start.Freq == 0 {
+ panic("LinearPacer.Start cannot be 0")
+ }
+
+ if p.Slope == 0 {
+ panic("LinearPacer.Slope cannot be 0")
+ }
+
+ p.once.Do(func() {
+ p.sp = StepPacer{
+ Start: p.Start,
+ Step: p.Slope,
+ StepDuration: time.Second,
+ Stop: p.Stop,
+ LoadDuration: p.LoadDuration,
+ }
+
+ p.sp.initialize()
+ })
+}
+
+// Pace determines the length of time to sleep until the next hit is sent.
+func (p *LinearPacer) Pace(elapsed time.Duration, hits uint64) (time.Duration, bool) {
+ if p.Max > 0 && hits >= p.Max {
+ return 0, true
+ }
+
+ p.initialize()
+
+ return p.sp.Pace(elapsed, hits)
+}
+
+// Rate returns a LinearPacer's instantaneous hit rate (i.e. requests per second)
+// at the given elapsed duration.
+func (p *LinearPacer) Rate(elapsed time.Duration) float64 {
+
+ p.initialize()
+
+ return p.sp.Rate(elapsed)
+}
+
+// String returns a pretty-printed description of the LinearPacer's behaviour:
+// LinearPacer{Slope: 1} => Linear{1 hits / 1s}
+func (p *LinearPacer) String() string {
+ return fmt.Sprintf("Linear{%d hits / 1s}", p.Slope)
+}
diff --git a/load/pacer_test.go b/load/pacer_test.go
new file mode 100644
index 00000000..d9350abd
--- /dev/null
+++ b/load/pacer_test.go
@@ -0,0 +1,1081 @@
+package load
+
+import (
+ "math"
+ "strconv"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestConstantPacer(t *testing.T) {
+ t.Parallel()
+
+ for _, tc := range []struct {
+ freq uint64
+ max uint64
+ elapsed time.Duration
+ hits uint64
+ wait time.Duration
+ stop bool
+ }{
+ // 1 hit/sec, 0 hits sent, 0s elapsed => 1s until next hit
+ {
+ freq: 1,
+ elapsed: 0,
+ hits: 0,
+ wait: 1000000000,
+ stop: false,
+ },
+ // 1 hit/sec, 0 hits sent, 0.1s elapsed => 0.9s until next hit
+ {
+ freq: 1,
+ elapsed: 100 * time.Millisecond,
+ hits: 0,
+ wait: 900 * time.Millisecond,
+ stop: false,
+ },
+ // 1 hit/sec, 0 hits sent, 1s elapsed => 0s until next hit
+ {
+ freq: 1,
+ elapsed: 1 * time.Second,
+ hits: 0,
+ wait: 0,
+ stop: false,
+ },
+ // 1 hit/sec, 0 hits sent, 2s elapsed => 0s (-1s) until next hit
+ {
+ freq: 1,
+ elapsed: 2 * time.Second,
+ hits: 0,
+ wait: 0,
+ stop: false,
+ },
+ // 1 hit/sec, 1 hit sent, 1s elapsed => 1s until next hit
+ {
+ freq: 1,
+ elapsed: 1 * time.Second,
+ hits: 1,
+ wait: 1 * time.Second,
+ stop: false,
+ },
+ // 1 hit/sec, 2 hits sent, 1s elapsed => 2s until next hit
+ {
+ freq: 1,
+ elapsed: 1 * time.Second,
+ hits: 2,
+ wait: 2 * time.Second,
+ stop: false,
+ },
+ // 1 hit/sec, 10 hits sent, 1s elapsed => 10s until next hit
+ {
+ freq: 1,
+ elapsed: 1 * time.Second,
+ hits: 10,
+ wait: 10 * time.Second,
+ stop: false,
+ },
+ // 1 hit/sec, 10 hits sent, 11s elapsed => 0s until next hit
+ {
+ freq: 1,
+ elapsed: 11 * time.Second,
+ hits: 10,
+ wait: 0,
+ stop: false,
+ },
+ // 2 hit/sec, 9 hits sent, 4.9s elapsed => 100ms until next hit
+ {
+ freq: 2,
+ elapsed: 4900 * time.Millisecond,
+ hits: 9,
+ wait: 100 * time.Millisecond,
+ stop: false,
+ },
+ // BAD TESTS
+ // Zero frequency.
+ {
+ freq: 0,
+ elapsed: 0,
+ hits: 0,
+ wait: 0,
+ stop: false,
+ },
+ // Large hits, overflow int64.
+ {
+ freq: 1,
+ elapsed: time.Duration(math.MaxInt64),
+ hits: 2562048,
+ wait: 0,
+ stop: false,
+ },
+ // Max
+ {
+ freq: 1,
+ elapsed: 1 * time.Second,
+ hits: 10,
+ wait: 10 * time.Second,
+ stop: false,
+ max: 0,
+ },
+ {
+ freq: 1,
+ elapsed: 1 * time.Second,
+ hits: 10,
+ wait: 0,
+ stop: true,
+ max: 7,
+ },
+ } {
+ cp := ConstantPacer{Freq: tc.freq, Max: tc.max}
+ wait, stop := cp.Pace(tc.elapsed, tc.hits)
+ assert.Equal(t, tc.wait, wait)
+ assert.Equal(t, tc.stop, stop)
+ }
+}
+
+func TestConstantPacer_Rate(t *testing.T) {
+ t.Parallel()
+
+ for _, tc := range []struct {
+ freq uint64
+ elapsed time.Duration
+ rate float64
+ }{
+ {
+ freq: 60,
+ elapsed: 0,
+ rate: 60,
+ },
+ {
+ freq: 500,
+ elapsed: 5 * time.Second,
+ rate: 500.0,
+ },
+ } {
+ cp := ConstantPacer{Freq: tc.freq}
+ actual, expected := cp.Rate(tc.elapsed), tc.rate
+ assert.True(t, floatEqual(actual, expected), "%s.Rate(_): actual %f, expected %f", cp, actual, expected)
+ }
+}
+
+func TestConstantPacer_String(t *testing.T) {
+ cp := ConstantPacer{Freq: 5}
+ actual := cp.String()
+ assert.Equal(t, "Constant{5 hits / 1s}", actual)
+}
+
+func TestLinearPacer(t *testing.T) {
+
+ t.Parallel()
+
+ for ti, tt := range []struct {
+ // pacer config
+ start uint64
+ slope int64
+ stopDuration time.Duration
+ stopRate uint64
+ // params
+ elapsed time.Duration
+ hits uint64
+ // expected
+ wait time.Duration
+ stop bool
+ }{
+ // slope: 1, start 1
+ {
+ start: 1,
+ slope: 1,
+ elapsed: 0,
+ hits: 0,
+ wait: 1 * time.Second,
+ stop: false,
+ },
+ {
+ start: 1,
+ slope: 1,
+ elapsed: 1 * time.Second,
+ hits: 0,
+ wait: 0 * time.Millisecond,
+ stop: false,
+ },
+ {
+ start: 1,
+ slope: 1,
+ elapsed: 0,
+ hits: 1,
+ wait: 2 * time.Second,
+ stop: false,
+ },
+ {
+ start: 1,
+ slope: 1,
+ elapsed: 1 * time.Second,
+ hits: 1,
+ wait: 500 * time.Millisecond,
+ stop: false,
+ },
+ {
+ start: 1,
+ slope: 1,
+ elapsed: 1 * time.Second,
+ hits: 2,
+ wait: 1000 * time.Millisecond,
+ stop: false,
+ },
+ {
+ start: 1,
+ slope: 1,
+ elapsed: 2 * time.Second,
+ hits: 2,
+ wait: 0 * time.Millisecond,
+ stop: false,
+ },
+ {
+ start: 1,
+ slope: 1,
+ elapsed: 2500 * time.Millisecond,
+ hits: 5,
+ wait: 500 * time.Millisecond,
+ stop: false,
+ },
+ // slope: 1, start 5
+ {
+ start: 5,
+ slope: 1,
+ elapsed: 0,
+ hits: 0,
+ wait: 200 * time.Millisecond,
+ stop: false,
+ },
+ {
+ start: 5,
+ slope: 1,
+ elapsed: 1 * time.Second,
+ hits: 5,
+ wait: 166666 * time.Microsecond,
+ stop: false,
+ },
+ {
+ start: 5,
+ slope: 1,
+ elapsed: 1200 * time.Millisecond,
+ hits: 5,
+ wait: 0 * time.Microsecond,
+ stop: false,
+ },
+ {
+ start: 5,
+ slope: 1,
+ elapsed: 2000 * time.Millisecond,
+ hits: 6,
+ wait: 0,
+ stop: false,
+ },
+ {
+ start: 5,
+ slope: 1,
+ elapsed: 2000 * time.Millisecond,
+ hits: 7,
+ wait: 0,
+ stop: false,
+ },
+ {
+ start: 5,
+ slope: 1,
+ elapsed: 2000 * time.Millisecond,
+ hits: 11,
+ wait: 142857 * time.Microsecond,
+ stop: false,
+ },
+ {
+ start: 5,
+ slope: 1,
+ elapsed: 2000 * time.Millisecond,
+ hits: 12,
+ wait: 285714 * time.Microsecond,
+ stop: false,
+ },
+ // // slope: -1, start 20, various elapsed and hits
+ {
+ start: 20,
+ slope: -1,
+ elapsed: 0,
+ hits: 0,
+ wait: 50 * time.Millisecond,
+ stop: false,
+ },
+ {
+ start: 20,
+ slope: -1,
+ elapsed: 1100 * time.Millisecond,
+ hits: 0,
+ wait: 0,
+ stop: false,
+ },
+ {
+ start: 20,
+ slope: -1,
+ elapsed: 50 * time.Millisecond,
+ hits: 1,
+ wait: 50 * time.Millisecond,
+ stop: false,
+ },
+ {
+ start: 20,
+ slope: -1,
+ elapsed: 50 * time.Millisecond,
+ hits: 19,
+ wait: 950 * time.Millisecond,
+ stop: false,
+ },
+ {
+ start: 20,
+ slope: -1,
+ elapsed: 950 * time.Millisecond,
+ hits: 19,
+ wait: 50 * time.Millisecond,
+ stop: false,
+ },
+ // slope: 1, stop rate
+ {
+ start: 1,
+ slope: 1,
+ elapsed: 0,
+ stopRate: 20,
+ hits: 0,
+ wait: 1 * time.Second,
+ stop: false,
+ },
+ {
+ start: 1,
+ slope: 1,
+ stopRate: 5,
+ elapsed: 5 * time.Second,
+ hits: 0,
+ wait: 0,
+ stop: false,
+ },
+ {
+ start: 1,
+ slope: 1,
+ stopRate: 5,
+ elapsed: 5000 * time.Millisecond,
+ hits: 17,
+ wait: 600 * time.Millisecond,
+ stop: false,
+ },
+ // slope: 1, stop duration
+ {
+ start: 1,
+ slope: 1,
+ elapsed: 0,
+ stopDuration: 5 * time.Second,
+ hits: 0,
+ wait: 1 * time.Second,
+ stop: false,
+ },
+ {
+ start: 1,
+ slope: 1,
+ stopDuration: 5 * time.Second,
+ elapsed: 2 * time.Second,
+ hits: 0,
+ wait: 0,
+ stop: false,
+ },
+ {
+ start: 1,
+ slope: 1,
+ stopDuration: 5 * time.Second,
+ elapsed: 2000 * time.Millisecond,
+ hits: 5,
+ wait: 1 * time.Second,
+ stop: false,
+ },
+ {
+ start: 1,
+ slope: 1,
+ stopDuration: 5 * time.Second,
+ elapsed: 5200 * time.Millisecond,
+ hits: 18,
+ wait: 466666 * time.Microsecond,
+ stop: false,
+ },
+ } {
+ t.Run(strconv.Itoa(ti), func(t *testing.T) {
+ p := LinearPacer{
+ Start: ConstantPacer{Freq: tt.start},
+ Slope: tt.slope,
+ Stop: ConstantPacer{Freq: tt.stopRate},
+ LoadDuration: tt.stopDuration,
+ }
+
+ wait, stop := p.Pace(tt.elapsed, tt.hits)
+
+ assert.True(t, durationEqual(tt.wait, wait),
+ "%d: %+v.Pace(%s, %d) = (%s, %t); expected (%s, %t)", ti, &p, tt.elapsed, tt.hits, wait, stop, tt.wait, tt.stop)
+
+ assert.Equal(t, tt.stop, stop)
+ })
+ }
+}
+
+func TestStepPacer_hits(t *testing.T) {
+ t.Parallel()
+
+ // TODO improve this to have different pacer params
+ p := StepPacer{
+ Start: ConstantPacer{Freq: 10},
+ StepDuration: 4 * time.Second,
+ Step: 10,
+ }
+
+ for _, tc := range []struct {
+ elapsed time.Duration
+ hits float64
+ }{
+ {0, 0},
+ {1 * time.Second, 10},
+ {2 * time.Second, 20},
+ {6 * time.Second, 80},
+ } {
+ actual := p.hits(tc.elapsed)
+ expected := tc.hits
+
+ assert.True(t, floatEqual(actual, expected), "%+v.hits(%v) = %v, expected: %v", p, tc.elapsed, actual, expected)
+ }
+}
+
+func TestStepPacer_Rate(t *testing.T) {
+ t.Parallel()
+
+ for _, tc := range []struct {
+ // pacer config
+ start uint64
+ step int64
+ stepDuration time.Duration
+ stop uint64
+ stopDuration time.Duration
+ // params
+ elapsed time.Duration
+ // expected
+ rate float64
+ }{
+ // step: 5, start: 1
+ {
+ start: 1,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 0,
+ rate: 1,
+ },
+ {
+ start: 1,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 1 * time.Second,
+ rate: 1,
+ },
+ {
+ start: 1,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 3 * time.Second,
+ rate: 1,
+ },
+ {
+ start: 1,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 4 * time.Second,
+ rate: 6,
+ },
+ {
+ start: 1,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 5 * time.Second,
+ rate: 6,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 25,
+ stopDuration: 0,
+ elapsed: 9 * time.Second,
+ rate: 15,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 25,
+ stopDuration: 0,
+ elapsed: 12 * time.Second,
+ rate: 20,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 25,
+ stopDuration: 0,
+ elapsed: 22 * time.Second,
+ rate: 25,
+ },
+ // start: 5, step: 5, stop duration: 25s
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 25 * time.Second,
+ elapsed: 0,
+ rate: 5,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 25 * time.Second,
+ elapsed: 19 * time.Second,
+ rate: 25,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 25 * time.Second,
+ elapsed: 20 * time.Second,
+ rate: 30,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 25 * time.Second,
+ elapsed: 21 * time.Second,
+ rate: 30,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 25 * time.Second,
+ elapsed: 26 * time.Second,
+ rate: 35,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 25 * time.Second,
+ elapsed: 31 * time.Second,
+ rate: 35,
+ },
+ // start: 15, step -5
+ {
+ start: 15,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 0,
+ rate: 15,
+ },
+ {
+ start: 15,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 3 * time.Second,
+ rate: 15,
+ },
+ {
+ start: 15,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 4 * time.Second,
+ rate: 10,
+ },
+ {
+ start: 15,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 5 * time.Second,
+ rate: 10,
+ },
+ {
+ start: 15,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 11 * time.Second,
+ rate: 5,
+ },
+ {
+ start: 15,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 12 * time.Second,
+ rate: 0,
+ },
+ {
+ start: 15,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 17 * time.Second,
+ rate: 0,
+ },
+ // start: 20, step: -5, stop: 5
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 5,
+ stopDuration: 0,
+ elapsed: 0,
+ rate: 20,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 5,
+ stopDuration: 0,
+ elapsed: 11 * time.Second,
+ rate: 10,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 5,
+ stopDuration: 0,
+ elapsed: 12 * time.Second,
+ rate: 5,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 5,
+ stopDuration: 0,
+ elapsed: 13 * time.Second,
+ rate: 5,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 5,
+ stopDuration: 0,
+ elapsed: 22 * time.Second,
+ rate: 5,
+ },
+ // start: 20, step: -5, stop: 10s
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 10 * time.Second,
+ elapsed: 0,
+ rate: 20,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 10 * time.Second,
+ elapsed: 9 * time.Second,
+ rate: 10,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 10 * time.Second,
+ elapsed: 10 * time.Second,
+ rate: 10,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 10 * time.Second,
+ elapsed: 11 * time.Second,
+ rate: 10,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 10 * time.Second,
+ elapsed: 15 * time.Second,
+ rate: 10,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 4 * time.Second,
+ stop: 0,
+ stopDuration: 10 * time.Second,
+ elapsed: 22 * time.Second,
+ rate: 10,
+ },
+ } {
+ p := StepPacer{
+ Start: ConstantPacer{Freq: tc.start},
+ Step: tc.step, StepDuration: tc.stepDuration,
+ LoadDuration: tc.stopDuration, Stop: ConstantPacer{Freq: tc.stop}}
+
+ actual := p.Rate(tc.elapsed)
+ expected := tc.rate
+
+ assert.True(t, floatEqual(actual, expected), "%+v.Rate(%v) = %v, expected: %v", p, tc.elapsed, actual, expected)
+ }
+}
+
+func TestStepPacer(t *testing.T) {
+ t.Parallel()
+
+ for ti, tc := range []struct {
+ // pacer config
+ start uint64
+ step int64
+ stepDuration time.Duration
+ stop uint64
+ stopDuration time.Duration
+ max uint64
+ // params
+ elapsed time.Duration
+ hits uint64
+ // expected
+ wait time.Duration
+ stopResult bool
+ }{
+ // start: 5, step: 5
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 0 * time.Second,
+ elapsed: 0 * time.Second,
+ hits: 0,
+ wait: 200 * time.Millisecond,
+ stopResult: false,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 0 * time.Second,
+ elapsed: 1 * time.Second,
+ hits: 4,
+ wait: 0 * time.Millisecond,
+ stopResult: false,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 0 * time.Second,
+ elapsed: 1 * time.Second,
+ hits: 6,
+ wait: 400 * time.Millisecond,
+ stopResult: false,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 0 * time.Second,
+ elapsed: 4200 * time.Millisecond,
+ hits: 25,
+ wait: 1 * time.Second,
+ stopResult: false,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 0 * time.Second,
+ elapsed: 5000 * time.Millisecond,
+ hits: 25,
+ wait: 100 * time.Millisecond,
+ stopResult: false,
+ },
+ // start: 5, step: 5, stop: 25
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 25,
+ stopDuration: 0 * time.Second,
+ elapsed: 5000 * time.Millisecond,
+ hits: 25,
+ wait: 100 * time.Millisecond,
+ stopResult: false,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 25,
+ stopDuration: 0 * time.Second,
+ elapsed: 20 * time.Second,
+ hits: 250,
+ wait: 40 * time.Millisecond,
+ stopResult: false,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 25,
+ stopDuration: 0 * time.Second,
+ elapsed: 30 * time.Second,
+ hits: 450,
+ wait: 0 * time.Millisecond,
+ stopResult: false,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 25,
+ stopDuration: 0 * time.Second,
+ elapsed: 30 * time.Second,
+ hits: 500,
+ wait: 40 * time.Millisecond,
+ stopResult: false,
+ },
+ // start: 5, step: 5, stop: 20s
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 20 * time.Second,
+ elapsed: 5000 * time.Millisecond,
+ hits: 25,
+ wait: 100 * time.Millisecond,
+ stopResult: false,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 20 * time.Second,
+ elapsed: 19 * time.Second,
+ hits: 25,
+ wait: 0 * time.Millisecond,
+ stopResult: false,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 20 * time.Second,
+ elapsed: 20 * time.Second,
+ hits: 250,
+ wait: 40 * time.Millisecond,
+ stopResult: false,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 20 * time.Second,
+ elapsed: 30 * time.Second,
+ hits: 400,
+ wait: 0 * time.Millisecond,
+ stopResult: false,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 20 * time.Second,
+ elapsed: 30 * time.Second,
+ hits: 500,
+ wait: 40 * time.Millisecond,
+ stopResult: false,
+ },
+ // start: 20, step: -5,
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 0 * time.Millisecond,
+ hits: 0,
+ wait: 50 * time.Millisecond,
+ stopResult: false,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 5000 * time.Millisecond,
+ hits: 100,
+ wait: 66666666 * time.Nanosecond,
+ stopResult: false,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 5000 * time.Millisecond,
+ hits: 100,
+ wait: 66666666 * time.Nanosecond,
+ stopResult: false,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 20 * time.Second,
+ hits: 249,
+ wait: 0,
+ stopResult: false,
+ },
+ {
+ start: 20,
+ step: -5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 0,
+ elapsed: 20 * time.Second,
+ hits: 250,
+ wait: 0,
+ stopResult: true,
+ },
+ {
+ start: 30,
+ step: -5,
+ stepDuration: 5 * time.Second,
+ stop: 0,
+ stopDuration: 20 * time.Second,
+ elapsed: 30 * time.Second,
+ hits: 550,
+ wait: 100 * time.Millisecond,
+ stopResult: false,
+ },
+ // Max
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 25,
+ stopDuration: 0 * time.Second,
+ max: 100,
+ elapsed: 5000 * time.Millisecond,
+ hits: 25,
+ wait: 100 * time.Millisecond,
+ stopResult: false,
+ },
+ {
+ start: 5,
+ step: 5,
+ stepDuration: 5 * time.Second,
+ stop: 25,
+ max: 10,
+ stopDuration: 0 * time.Second,
+ elapsed: 5000 * time.Millisecond,
+ hits: 25,
+ wait: 0,
+ stopResult: true,
+ },
+ } {
+ t.Run(strconv.Itoa(ti), func(t *testing.T) {
+ p := StepPacer{
+ Start: ConstantPacer{Freq: tc.start, Max: tc.max},
+ Max: tc.max,
+ Step: tc.step, StepDuration: tc.stepDuration,
+ LoadDuration: tc.stopDuration, Stop: ConstantPacer{Freq: tc.stop}}
+
+ wait, stop := p.Pace(tc.elapsed, tc.hits)
+
+ assert.Equal(t, tc.wait, wait, "%+v.Pace(%v, %v) = %v, expected: %v", p, tc.elapsed, tc.hits, wait, tc.wait)
+ assert.Equal(t, tc.stopResult, stop, "%+v.Pace(%v, %v) = %v, expected: %v", p, tc.elapsed, tc.hits, stop, tc.stopResult)
+ })
+ }
+}
+
+func TestStepPacer_String(t *testing.T) {
+ p := StepPacer{
+ Start: ConstantPacer{Freq: 5, Max: 100},
+ Max: 100,
+ Step: 2, StepDuration: 5 * time.Second,
+ LoadDuration: 25 * time.Second, Stop: ConstantPacer{Freq: 25}}
+
+ actual := p.String()
+ assert.Equal(t, "Step{Step: 2 hits / 5s}", actual)
+}
+
+// Stolen from https://github.com/google/go-cmp/cmp/cmpopts/equate.go
+// to avoid an unwieldy dependency. Both fraction and margin set at 1e-6.
+func floatEqual(x, y float64) bool {
+ relMarg := 1e-6 * math.Min(math.Abs(x), math.Abs(y))
+ return math.Abs(x-y) <= math.Max(1e-6, relMarg)
+}
+
+// A similar function to the above because SinePacer.Pace has discrete
+// inputs and outputs but uses floats internally, and sometimes the
+// floating point imprecision leaks out :-(
+func durationEqual(x, y time.Duration) bool {
+ diff := x - y
+ if diff < 0 {
+ diff = -diff
+ }
+ return diff <= time.Microsecond
+}
diff --git a/load/worker_ticker.go b/load/worker_ticker.go
new file mode 100644
index 00000000..d72f69ce
--- /dev/null
+++ b/load/worker_ticker.go
@@ -0,0 +1,159 @@
+package load
+
+import (
+ "time"
+)
+
+// WorkerTicker is the interface controlling worker parallelism.
+type WorkerTicker interface {
+ // Ticker returns a channel which sends TickValues
+ // When a value is received the number of workers should be appropriately
+ // increased or decreased given by the delta property.
+ Ticker() <-chan TickValue
+
+ // Run starts the worker ticker
+ Run()
+
+ // Finish closes the channel
+ Finish()
+}
+
+// TickValue is the tick value sent over the ticker channel.
+type TickValue struct {
+ Delta int // Delta value representing worker increase or decrease
+ Done bool // A flag representing whether the ticker is done running. Once true no more values should be received over the ticker channel.
+}
+
+// ConstWorkerTicker represents a constant number of workers.
+// It would send one value for initial number of workers to start.
+type ConstWorkerTicker struct {
+ C chan TickValue // The tick value channel
+ N uint // The number of workers
+}
+
+// Ticker returns the ticker channel.
+func (c *ConstWorkerTicker) Ticker() <-chan TickValue {
+ return c.C
+}
+
+// Run runs the ticker.
+func (c *ConstWorkerTicker) Run() {
+ c.C <- TickValue{Delta: int(c.N), Done: true}
+}
+
+// Finish closes the channel.
+func (c *ConstWorkerTicker) Finish() {
+ close(c.C)
+}
+
+// StepWorkerTicker is the worker ticker that implements step adjustments to worker concurrency.
+type StepWorkerTicker struct {
+ C chan TickValue // The tick value channel
+
+ Start uint // Starting number of workers
+ Step int // Step change
+ StepDuration time.Duration // Duration to apply the step change
+ Stop uint // Final number of workers
+ MaxDuration time.Duration // Maximum duration
+}
+
+// Ticker returns the ticker channel.
+func (c *StepWorkerTicker) Ticker() <-chan TickValue {
+ return c.C
+}
+
+// Run runs the ticker.
+func (c *StepWorkerTicker) Run() {
+
+ stepUp := c.Step > 0
+ wc := int(c.Start)
+ done := make(chan bool)
+
+ ticker := time.NewTicker(c.StepDuration)
+ defer ticker.Stop()
+
+ begin := time.Now()
+
+ c.C <- TickValue{Delta: int(c.Start)}
+
+ go func() {
+ for range ticker.C {
+ // we have load duration and we eclipsed it
+ if c.MaxDuration > 0 && time.Since(begin) >= c.MaxDuration {
+ if stepUp && c.Stop > 0 && c.Stop >= uint(wc) {
+ // if we have step up and stop value is > current count
+ // send the final diff
+ c.C <- TickValue{Delta: int(c.Stop - uint(wc)), Done: true}
+ } else if !stepUp && c.Stop > 0 && c.Stop <= uint(wc) {
+ // if we have step down and stop value is < current count
+ // send the final diff
+ c.C <- TickValue{Delta: int(c.Stop - uint(wc)), Done: true}
+ } else {
+ // send done signal
+ c.C <- TickValue{Delta: 0, Done: true}
+ }
+
+ done <- true
+ return
+ } else if (c.MaxDuration == 0) && ((c.Stop > 0 && stepUp && wc >= int(c.Stop)) ||
+ (!stepUp && wc <= int(c.Stop))) {
+ // we do not have load duration
+ // if we have stop and are step up and current count >= stop
+ // or if we have stop and are step down and current count <= stop
+ // send done signal
+
+ c.C <- TickValue{Delta: 0, Done: true}
+ done <- true
+ return
+ } else {
+ c.C <- TickValue{Delta: c.Step}
+ wc = wc + c.Step
+ }
+ }
+ }()
+
+ <-done
+}
+
+// Finish closes the channel.
+func (c *StepWorkerTicker) Finish() {
+ close(c.C)
+}
+
+// LineWorkerTicker is the worker ticker that implements line adjustments to concurrency.
+// Essentially this is same as step worker with 1s step duration.
+type LineWorkerTicker struct {
+ C chan TickValue // The tick value channel
+
+ Start uint // Starting number of workers
+ Slope int // Slope value to adjust the number of workers
+ Stop uint // Final number of workers
+ MaxDuration time.Duration // Maximum adjustment duration
+
+ stepTicker StepWorkerTicker
+}
+
+// Ticker returns the ticker channel.
+func (c *LineWorkerTicker) Ticker() <-chan TickValue {
+ return c.C
+}
+
+// Run runs the ticker.
+func (c *LineWorkerTicker) Run() {
+
+ c.stepTicker = StepWorkerTicker{
+ C: c.C,
+ Start: c.Start,
+ Step: c.Slope,
+ StepDuration: 1 * time.Second,
+ Stop: c.Stop,
+ MaxDuration: c.MaxDuration,
+ }
+
+ c.stepTicker.Run()
+}
+
+// Finish closes the internal tick value channel.
+func (c *LineWorkerTicker) Finish() {
+ c.stepTicker.Finish()
+}
diff --git a/load/worker_ticker_test.go b/load/worker_ticker_test.go
new file mode 100644
index 00000000..fe61e797
--- /dev/null
+++ b/load/worker_ticker_test.go
@@ -0,0 +1,273 @@
+package load
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestConstWorkerTicker(t *testing.T) {
+ wt := ConstWorkerTicker{N: 5, C: make(chan TickValue)}
+ defer wt.Finish()
+
+ wct := wt.Ticker()
+
+ assert.NotNil(t, wct)
+
+ go func() {
+ wt.Run()
+ }()
+
+ tv := <-wct
+
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, 5, tv.Delta)
+}
+
+func TestStepWorkerTicker(t *testing.T) {
+ t.Parallel()
+
+ t.Run("step increase load duration", func(t *testing.T) {
+ wt := StepWorkerTicker{
+ C: make(chan TickValue),
+ Start: 5,
+ Step: 2,
+ Stop: 0,
+ StepDuration: 2 * time.Second,
+ MaxDuration: 5 * time.Second,
+ }
+
+ defer wt.Finish()
+
+ wct := wt.Ticker()
+
+ assert.NotNil(t, wct)
+
+ go func() {
+ wt.Run()
+ }()
+
+ tv := <-wct
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, 5, tv.Delta)
+ assert.False(t, tv.Done)
+
+ start := time.Now()
+ tv = <-wct
+ end := time.Since(start)
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, 2, tv.Delta)
+ assert.False(t, tv.Done)
+ expected := 2 * time.Second
+ assert.True(t, durationEqual(expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+
+ tv = <-wct
+ end = time.Since(start)
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, 2, tv.Delta)
+ assert.False(t, tv.Done)
+ assert.True(t, durationEqual(2*expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+
+ tv = <-wct
+ assert.Equal(t, 0, tv.Delta)
+ assert.True(t, tv.Done)
+ })
+
+ t.Run("step increase load duration with stop", func(t *testing.T) {
+ wt := StepWorkerTicker{
+ C: make(chan TickValue),
+ Start: 5,
+ Step: 2,
+ Stop: 15,
+ StepDuration: 2 * time.Second,
+ MaxDuration: 5 * time.Second,
+ }
+
+ defer wt.Finish()
+
+ wct := wt.Ticker()
+
+ assert.NotNil(t, wct)
+
+ go func() {
+ wt.Run()
+ }()
+
+ tv := <-wct
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, 5, tv.Delta)
+ assert.False(t, tv.Done)
+
+ start := time.Now()
+ tv = <-wct
+ end := time.Since(start)
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, 2, tv.Delta)
+ assert.False(t, tv.Done)
+ expected := 2 * time.Second
+ assert.True(t, durationEqual(expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+
+ tv = <-wct
+ end = time.Since(start)
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, 2, tv.Delta)
+ assert.False(t, tv.Done)
+ assert.True(t, durationEqual(2*expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+
+ tv = <-wct
+ end = time.Since(start)
+ assert.Equal(t, 6, tv.Delta)
+ assert.True(t, tv.Done)
+ assert.True(t, durationEqual(3*expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+ })
+
+ t.Run("step decrease load duration", func(t *testing.T) {
+ wt := StepWorkerTicker{
+ C: make(chan TickValue),
+ Start: 10,
+ Step: -2,
+ Stop: 0,
+ StepDuration: 2 * time.Second,
+ MaxDuration: 5 * time.Second,
+ }
+
+ defer wt.Finish()
+
+ wct := wt.Ticker()
+
+ assert.NotNil(t, wct)
+
+ go func() {
+ wt.Run()
+ }()
+
+ tv := <-wct
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, 10, tv.Delta)
+ assert.False(t, tv.Done)
+
+ start := time.Now()
+ tv = <-wct
+ end := time.Since(start)
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, -2, tv.Delta)
+ assert.False(t, tv.Done)
+ expected := 2 * time.Second
+ assert.True(t, durationEqual(expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+
+ tv = <-wct
+ end = time.Since(start)
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, -2, tv.Delta)
+ assert.False(t, tv.Done)
+ assert.True(t, durationEqual(2*expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+
+ tv = <-wct
+ assert.Equal(t, 0, tv.Delta)
+ assert.True(t, tv.Done)
+ })
+
+ t.Run("step decrease with stop", func(t *testing.T) {
+ wt := StepWorkerTicker{
+ C: make(chan TickValue),
+ Start: 10,
+ Step: -2,
+ Stop: 4,
+ StepDuration: 2 * time.Second,
+ MaxDuration: 0,
+ }
+
+ defer wt.Finish()
+
+ wct := wt.Ticker()
+
+ assert.NotNil(t, wct)
+
+ go func() {
+ wt.Run()
+ }()
+
+ tv := <-wct
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, 10, tv.Delta)
+ assert.False(t, tv.Done)
+
+ start := time.Now()
+ tv = <-wct
+ end := time.Since(start)
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, -2, tv.Delta)
+ assert.False(t, tv.Done)
+ expected := 2 * time.Second
+ assert.True(t, durationEqual(expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+
+ tv = <-wct
+ end = time.Since(start)
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, -2, tv.Delta)
+ assert.False(t, tv.Done)
+ assert.True(t, durationEqual(2*expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+
+ tv = <-wct
+ end = time.Since(start)
+ assert.Equal(t, -2, tv.Delta)
+ assert.False(t, tv.Done)
+ assert.True(t, durationEqual(3*expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+
+ tv = <-wct
+ end = time.Since(start)
+ assert.Equal(t, 0, tv.Delta)
+ assert.True(t, tv.Done)
+ assert.True(t, durationEqual(4*expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+ })
+
+ t.Run("step decrease with stop and load duration", func(t *testing.T) {
+
+ wt := StepWorkerTicker{
+ C: make(chan TickValue),
+ Start: 12,
+ Step: -2,
+ Stop: 3,
+ StepDuration: 2 * time.Second,
+ MaxDuration: 5 * time.Second,
+ }
+
+ defer wt.Finish()
+
+ wct := wt.Ticker()
+
+ assert.NotNil(t, wct)
+
+ go func() {
+ wt.Run()
+ }()
+
+ tv := <-wct
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, 12, tv.Delta)
+ assert.False(t, tv.Done)
+
+ start := time.Now()
+ tv = <-wct
+ end := time.Since(start)
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, -2, tv.Delta)
+ assert.False(t, tv.Done)
+ expected := 2 * time.Second
+ assert.True(t, durationEqual(expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+
+ tv = <-wct
+ end = time.Since(start)
+ assert.NotEmpty(t, tv)
+ assert.Equal(t, -2, tv.Delta)
+ assert.False(t, tv.Done)
+ assert.True(t, durationEqual(2*expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+
+ tv = <-wct
+ end = time.Since(start)
+ assert.Equal(t, -5, tv.Delta)
+ assert.True(t, tv.Done)
+ assert.True(t, durationEqual(3*expected, end.Round(time.Second)), "expected %s to equal %s", expected, end)
+ })
+}
diff --git a/printer/printer.go b/printer/printer.go
index a7b6dcf5..9740524f 100644
--- a/printer/printer.go
+++ b/printer/printer.go
@@ -144,8 +144,29 @@ func (rp *ReportPrinter) getInfluxTags(addErrors bool) string {
s = append(s, fmt.Sprintf(`call="%v"`, options.Call))
s = append(s, fmt.Sprintf(`host="%v"`, options.Host))
s = append(s, fmt.Sprintf("n=%v", options.Total))
- s = append(s, fmt.Sprintf("c=%v", options.Concurrency))
- s = append(s, fmt.Sprintf("qps=%v", options.QPS))
+
+ if options.CSchedule == "const" {
+ s = append(s, fmt.Sprintf("c=%v", options.Concurrency))
+ } else {
+ s = append(s, fmt.Sprintf("concurrency-schedule=%v", options.CSchedule))
+ s = append(s, fmt.Sprintf("concurrency-start=%v", options.CStart))
+ s = append(s, fmt.Sprintf("concurrency-end=%v", options.CEnd))
+ s = append(s, fmt.Sprintf("concurrency-step=%v", options.CStep))
+ s = append(s, fmt.Sprintf("concurrency-step-duration=%v", options.CStepDuration))
+ s = append(s, fmt.Sprintf("concurrency-max-duration=%v", options.CMaxDuration))
+ }
+
+ if options.LoadSchedule == "const" {
+ s = append(s, fmt.Sprintf("rps=%v", options.RPS))
+ } else {
+ s = append(s, fmt.Sprintf("load-schedule=%v", options.LoadSchedule))
+ s = append(s, fmt.Sprintf("load-start=%v", options.LoadStart))
+ s = append(s, fmt.Sprintf("load-end=%v", options.LoadEnd))
+ s = append(s, fmt.Sprintf("load-step=%v", options.LoadStep))
+ s = append(s, fmt.Sprintf("load-step-duration=%v", options.LoadStepDuration))
+ s = append(s, fmt.Sprintf("load-max-duration=%v", options.LoadMaxDuration))
+ }
+
s = append(s, fmt.Sprintf("z=%v", options.Duration.Nanoseconds()))
s = append(s, fmt.Sprintf("timeout=%v", options.Timeout.Seconds()))
s = append(s, fmt.Sprintf("dial_timeout=%v", options.DialTimeout.Seconds()))
@@ -374,424 +395,3 @@ func cleanInfluxString(input string) string {
input = strings.Replace(input, "=", "\\=", -1)
return input
}
-
-var (
- defaultTmpl = `
-Summary:
-{{ if .Name }} Name: {{ .Name }}
-{{ end }} Count: {{ .Count }}
- Total: {{ formatNanoUnit .Total }}
- Slowest: {{ formatNanoUnit .Slowest }}
- Fastest: {{ formatNanoUnit .Fastest }}
- Average: {{ formatNanoUnit .Average }}
- Requests/sec: {{ formatSeconds .Rps }}
-
-Response time histogram:
-{{ histogram .Histogram }}
-Latency distribution:{{ range .LatencyDistribution }}
- {{ .Percentage }} % in {{ formatNanoUnit .Latency }} {{ end }}
-
-{{ if gt (len .StatusCodeDist) 0 }}Status code distribution:
-{{ formatStatusCode .StatusCodeDist }}{{ end }}
-{{ if gt (len .ErrorDist) 0 }}Error distribution:
-{{ formatErrorDist .ErrorDist }}{{ end }}
-`
-
- csvTmpl = `
-duration (ms),status,error{{ range $i, $v := .Details }}
-{{ formatMilli .Latency.Seconds }},{{ .Status }},{{ .Error }}{{ end }}
-`
-
- htmlTmpl = `
-
-
-
-
-
- ghz{{ if .Name }} - {{ .Name }}{{end}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ if .Name }}
-
{{ .Name }}
- {{ end }}
- {{ if .Date }}
- {{ formatDate .Date }}
- {{ end }}
-
-
-
-
-
- {{ if gt (len .Tags) 0 }}
-
-
-
-
- {{ range $tag, $val := .Tags }}
-
-
-
- {{ $tag }}
- {{ $val }}
-
-
-
- {{ end }}
-
-
-
-
- {{ end }}
-
-
-
-
-
-
- Summary
-
-
-
-
- Count |
- {{ .Count }} |
-
-
- Total |
- {{ formatNanoUnit .Total }} |
-
-
- Slowest |
- {{ formatNanoUnit .Slowest }} |
-
-
- Fastest |
- {{ formatNanoUnit .Fastest }} |
-
-
- Average |
- {{ formatNanoUnit .Average }} |
-
-
- Requests / sec |
- {{ formatSeconds .Rps }} |
-
-
-
-
-
-
-
-
- Options
-
-
-
-
{{ jsonify .Options true }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Latency distribution
-
-
-
-
- {{ range .LatencyDistribution }}
- {{ .Percentage }} % |
- {{ end }}
-
-
-
-
- {{ range .LatencyDistribution }}
- {{ formatNanoUnit .Latency }} |
- {{ end }}
-
-
-
-
-
-
-
-
-
-
-
-
- Status distribution
-
-
-
-
- Status |
- Count |
- % of Total |
-
-
-
- {{ range $code, $num := .StatusCodeDist }}
-
- {{ $code }} |
- {{ $num }} |
- {{ formatPercent $num $.Count }} % |
-
- {{ end }}
-
-
-
-
-
-
-
- {{ if gt (len .ErrorDist) 0 }}
-
-
-
-
-
-
-
- Errors
-
-
-
-
- Error |
- Count |
- % of Total |
-
-
-
- {{ range $err, $num := .ErrorDist }}
-
- {{ $err }} |
- {{ $num }} |
- {{ formatPercent $num $.Count }} % |
-
- {{ end }}
-
-
-
-
-
-
-
- {{ end }}
-
-
-
-
-
-
-
-
- Generated by ghz
-
-
-
-
-
-
-
-
-
-
-
-
-
-