Repository: knative/pkg
Pull request #1811 (2020-10-15 11:09:49)

Update vegeta to a version supporting go modules
This allows us to drop the explicit overrides in downstream repositories and it also looks so much nicer :).

I'd hope the change is non-breaking to downstream repositories, given the interface is strictly widened here.

/assign @julz @vagababov 

knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/pkg/pull/1811#" title="Author self-approved">markusthoemmes</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative%2Fpkg).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/knative/pkg/blob/master/OWNERS)~~ [markusthoemmes]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
codecov[bot]: # [Codecov](https://codecov.io/gh/knative/pkg/pull/1811?src=pr&el=h1) Report
> Merging [#1811](https://codecov.io/gh/knative/pkg/pull/1811?src=pr&el=desc) into [master](https://codecov.io/gh/knative/pkg/commit/9e655c6b02f668013cc6d38bd69668be45a709d2?el=desc) will **decrease** coverage by `0.02%`.
> The diff coverage is `0.00%`.

[![Impacted file tree graph](https://codecov.io/gh/knative/pkg/pull/1811/graphs/tree.svg?width=650&height=150&src=pr&token=rlqpDnfxbO)](https://codecov.io/gh/knative/pkg/pull/1811?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1811      +/-   ##
==========================================
- Coverage   67.82%   67.80%   -0.03%     
==========================================
  Files         222      222              
  Lines        9434     9438       +4     
==========================================
  Hits         6399     6399              
- Misses       2712     2716       +4     
  Partials      323      323              
```


| [Impacted Files](https://codecov.io/gh/knative/pkg/pull/1811?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [test/vegeta/pacers/combined\_pacer.go](https://codecov.io/gh/knative/pkg/pull/1811/diff?src=pr&el=tree#diff-dGVzdC92ZWdldGEvcGFjZXJzL2NvbWJpbmVkX3BhY2VyLmdv) | `85.00% <0.00%> (-6.90%)` | :arrow_down: |
| [test/vegeta/pacers/steady\_up\_pacer.go](https://codecov.io/gh/knative/pkg/pull/1811/diff?src=pr&el=tree#diff-dGVzdC92ZWdldGEvcGFjZXJzL3N0ZWFkeV91cF9wYWNlci5nbw==) | `81.57% <0.00%> (-2.21%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/knative/pkg/pull/1811?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/knative/pkg/pull/1811?src=pr&el=footer). Last update [9e655c6...26a1080](https://codecov.io/gh/knative/pkg/pull/1811?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/go.mod b/go.mod
index 01e005cef..8b288850c 100644
--- a/go.mod
+++ b/go.mod
@@ -31,7 +31,7 @@ require (
 	github.com/prometheus/client_golang v1.6.0
 	github.com/prometheus/common v0.9.1
 	github.com/spf13/pflag v1.0.5
-	github.com/tsenart/vegeta v12.7.1-0.20190725001342-b5f4fca92137+incompatible
+	github.com/tsenart/vegeta/v12 v12.8.4
 	go.opencensus.io v0.22.4
 	go.uber.org/atomic v1.6.0
 	go.uber.org/automaxprocs v1.3.0
diff --git a/go.sum b/go.sum
index 4e380c86f..33f889c16 100644
--- a/go.sum
+++ b/go.sum
@@ -154,6 +154,7 @@ github.com/Shopify/sarama v1.23.1/go.mod h1:XLH1GYJnLVE0XCr6KdJGVJRTwY30moWNJ4sE
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
 github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
+github.com/alecthomas/jsonschema v0.0.0-20180308105923-f2c93856175a/go.mod h1:qpebaTNSsyUn5rPSJMsfqEtDw71TTggXM6stUDI16HA=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -213,6 +214,7 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8n
 github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
 github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
 github.com/bwmarrin/snowflake v0.0.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
+github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
 github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -271,8 +273,10 @@ github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8l
 github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
 github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-gk v0.0.0-20140819190930-201884a44051/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E=
 github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654 h1:XOPLOMn/zT4jIgxfxSsoXPxkrzz0FaCHwp33x5POJ+Q=
 github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E=
+github.com/dgryski/go-lttb v0.0.0-20180810165845-318fcdf10a77/go.mod h1:Va5MyIzkU0rAM92tn3hb3Anb7oz7KcnixF49+2wOMe4=
 github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
 github.com/djherbis/atime v1.0.0/go.mod h1:5W+KBIuTwVGcqjIfaTwt+KSYX1o6uep8dtevevQP/f8=
 github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
@@ -476,6 +480,15 @@ github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAO
 github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=
 github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
 github.com/gomodule/redigo v1.7.0/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=
+github.com/gonum/diff v0.0.0-20181124234638-500114f11e71/go.mod h1:22dM4PLscQl+Nzf64qNBurVJvfyvZELT0iRW2l/NN70=
+github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=
+github.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2/go.mod h1:pDgmNM6seYpwvPos3q+zxlXMsbve6mOIPucUnUOrI7Y=
+github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=
+github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=
+github.com/gonum/mathext v0.0.0-20181121095525-8a4bf007ea55/go.mod h1:fmo8aiSEWkJeiGXUJf+sPvuDgEFgqIoZSs843ePKrGg=
+github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=
+github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=
 github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 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=
@@ -619,6 +632,7 @@ github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
 github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/influxdata/influxdb v0.0.0-20161215172503-049f9b42e9a5/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
+github.com/influxdata/tdigest v0.0.0-20180711151920-a7d76c6f093a/go.mod h1:9GkyshztGufsdPQWjH+ifgnIr3xNUL5syI70g2dzU1o=
 github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9 h1:MHTrDWmQpHq/hkq+7cw9oYAt2PqUw52TZazRA0N7PGE=
 github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
@@ -733,6 +747,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0j
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
 github.com/mholt/archiver/v3 v3.3.0/go.mod h1:YnQtqsp+94Rwd0D/rk5cnLrxusUBUXg+08Ebtr1Mqao=
+github.com/miekg/dns v1.1.17/go.mod h1:WgzbA6oji13JREwiNsRDNfl7jYdPnmz+VEuLrA+/48M=
 github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
@@ -958,8 +973,11 @@ github.com/tektoncd/plumbing/pipelinerun-logs v0.0.0-20191206114338-712d544c2c21
 github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tsenart/go-tsz v0.0.0-20180814232043-cdeb9e1e981e/go.mod h1:SWZznP1z5Ki7hDT2ioqiFKEse8K9tU2OUvaRI0NeGQo=
 github.com/tsenart/vegeta v12.7.1-0.20190725001342-b5f4fca92137+incompatible h1:ErZrHhRveAoznVW80gbrxz+qxJNydpA2fcQxTPHkZbU=
 github.com/tsenart/vegeta v12.7.1-0.20190725001342-b5f4fca92137+incompatible/go.mod h1:Smz/ZWfhKRcyDDChZkG3CyTHdj87lHzio/HOCkbndXM=
+github.com/tsenart/vegeta/v12 v12.8.4 h1:UQ7tG7WkDorKj0wjx78Z4/vsMBP8RJQMGJqRVrkvngg=
+github.com/tsenart/vegeta/v12 v12.8.4/go.mod h1:ZiJtwLn/9M4fTPdMY7bdbIeyNeFVE8/AHbWFqCsUuho=
 github.com/ugorji/go v1.1.1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
@@ -1052,6 +1070,7 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/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=
@@ -1278,6 +1297,7 @@ golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDq
 golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
 golang.org/x/tools v0.0.0-20190807223507-b346f7fd45de/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -1619,6 +1639,8 @@ knative.dev/test-infra v0.0.0-20201013100530-45e0761df397/go.mod h1:PwM5WCaFn0Hl
 mvdan.cc/xurls/v2 v2.0.0/go.mod h1:2/webFPYOXN9jp/lzuj0zuAVlF+9g4KPFJANH1oJhRU=
 pack.ag/amqp v0.11.0/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
 pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
+pgregory.net/rapid v0.3.3 h1:jCjBsY4ln4Atz78QoBWxUEvAHaFyNDQg9+WU62aCn1U=
+pgregory.net/rapid v0.3.3/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/letsencrypt v0.0.3/go.mod h1:buyQKZ6IXrRnB7TdkHP0RyEybLx18HHyOSoTyoOLqNY=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
diff --git a/third_party/VENDOR-LICENSE/github.com/tsenart/vegeta/lib/LICENSE b/third_party/VENDOR-LICENSE/github.com/tsenart/vegeta/v12/lib/LICENSE
similarity index 96%
rename from third_party/VENDOR-LICENSE/github.com/tsenart/vegeta/lib/LICENSE
rename to third_party/VENDOR-LICENSE/github.com/tsenart/vegeta/v12/lib/LICENSE
index 441df0ab4..03ec4f5a8 100644
--- a/third_party/VENDOR-LICENSE/github.com/tsenart/vegeta/lib/LICENSE
+++ b/third_party/VENDOR-LICENSE/github.com/tsenart/vegeta/v12/lib/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2013-2016 Tomás Senart
+Copyright (c) 2013-2020 Tomás Senart
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in
diff --git a/vendor/github.com/mailru/easyjson/.gitignore b/vendor/github.com/mailru/easyjson/.gitignore
new file mode 100644
index 000000000..26156fb4b
--- /dev/null
+++ b/vendor/github.com/mailru/easyjson/.gitignore
@@ -0,0 +1,5 @@
+.root
+*_easyjson.go
+*.iml
+.idea
+*.swp
diff --git a/vendor/github.com/mailru/easyjson/.travis.yml b/vendor/github.com/mailru/easyjson/.travis.yml
new file mode 100644
index 000000000..0ececa071
--- /dev/null
+++ b/vendor/github.com/mailru/easyjson/.travis.yml
@@ -0,0 +1,12 @@
+language: go
+
+go:
+  - tip
+  - stable
+
+matrix:
+  allow_failures:
+    - go: tip
+
+install:
+  - go get golang.org/x/lint/golint
diff --git a/vendor/github.com/mailru/easyjson/Makefile b/vendor/github.com/mailru/easyjson/Makefile
new file mode 100644
index 000000000..66cc40243
--- /dev/null
+++ b/vendor/github.com/mailru/easyjson/Makefile
@@ -0,0 +1,54 @@
+all: test
+
+clean:
+	rm -rf bin
+	rm -rf tests/*_easyjson.go
+	rm -rf benchmark/*_easyjson.go
+
+build:
+	go build -i -o ./bin/easyjson ./easyjson
+
+generate: build
+	bin/easyjson -stubs \
+		./tests/snake.go \
+		./tests/data.go \
+		./tests/omitempty.go \
+		./tests/nothing.go \
+		./tests/named_type.go \
+		./tests/custom_map_key_type.go \
+		./tests/embedded_type.go \
+		./tests/reference_to_pointer.go \
+		./tests/html.go \
+
+	bin/easyjson -all ./tests/data.go
+	bin/easyjson -all ./tests/nothing.go
+	bin/easyjson -all ./tests/errors.go
+	bin/easyjson -all ./tests/html.go
+	bin/easyjson -snake_case ./tests/snake.go
+	bin/easyjson -omit_empty ./tests/omitempty.go
+	bin/easyjson -build_tags=use_easyjson ./benchmark/data.go
+	bin/easyjson ./tests/nested_easy.go
+	bin/easyjson ./tests/named_type.go
+	bin/easyjson ./tests/custom_map_key_type.go
+	bin/easyjson ./tests/embedded_type.go
+	bin/easyjson ./tests/reference_to_pointer.go
+	bin/easyjson ./tests/key_marshaler_map.go
+	bin/easyjson -disallow_unknown_fields ./tests/disallow_unknown.go
+
+test: generate
+	go test \
+		./tests \
+		./jlexer \
+		./gen \
+		./buffer
+	cd benchmark && go test -benchmem -tags use_easyjson -bench .
+	golint -set_exit_status ./tests/*_easyjson.go
+
+bench-other: generate
+	cd benchmark && make
+
+bench-python:
+	benchmark/ujson.sh
+
+
+.PHONY: clean generate test build
diff --git a/vendor/github.com/mailru/easyjson/README.md b/vendor/github.com/mailru/easyjson/README.md
new file mode 100644
index 000000000..3bdcf2d06
--- /dev/null
+++ b/vendor/github.com/mailru/easyjson/README.md
@@ -0,0 +1,336 @@
+# easyjson [![Build Status](https://travis-ci.org/mailru/easyjson.svg?branch=master)](https://travis-ci.org/mailru/easyjson) [![Go Report Card](https://goreportcard.com/badge/github.com/mailru/easyjson)](https://goreportcard.com/report/github.com/mailru/easyjson)
+
+Package easyjson provides a fast and easy way to marshal/unmarshal Go structs
+to/from JSON without the use of reflection. In performance tests, easyjson
+outperforms the standard `encoding/json` package by a factor of 4-5x, and other
+JSON encoding packages by a factor of 2-3x.
+
+easyjson aims to keep generated Go code simple enough so that it can be easily
+optimized or fixed. Another goal is to provide users with the ability to
+customize the generated code by providing options not available with the
+standard `encoding/json` package, such as generating "snake_case" names or
+enabling `omitempty` behavior by default.
+
+## Usage
+```sh
+# install
+go get -u github.com/mailru/easyjson/...
+
+# run
+easyjson -all <file>.go
+```
+
+The above will generate `<file>_easyjson.go` containing the appropriate marshaler and
+unmarshaler funcs for all structs contained in `<file>.go`.
+
+Please note that easyjson requires a full Go build environment and the `GOPATH`
+environment variable to be set. This is because easyjson code generation
+invokes `go run` on a temporary file (an approach to code generation borrowed
+from [ffjson](https://github.com/pquerna/ffjson)).
+
+## Options
+```txt
+Usage of easyjson:
+  -all
+    	generate marshaler/unmarshalers for all structs in a file
+  -build_tags string
+    	build tags to add to generated file
+  -leave_temps
+    	do not delete temporary files
+  -no_std_marshalers
+    	don't generate MarshalJSON/UnmarshalJSON funcs
+  -noformat
+    	do not run 'gofmt -w' on output file
+  -omit_empty
+    	omit empty fields by default
+  -output_filename string
+    	specify the filename of the output
+  -pkg
+    	process the whole package instead of just the given file
+  -snake_case
+    	use snake_case names instead of CamelCase by default
+  -lower_camel_case
+        use lowerCamelCase instead of CamelCase by default
+  -stubs
+    	only generate stubs for marshaler/unmarshaler funcs
+  -disallow_unknown_fields
+        return error if some unknown field in json appeared
+```
+
+Using `-all` will generate marshalers/unmarshalers for all Go structs in the
+file. If `-all` is not provided, then only those structs whose preceding
+comment starts with `easyjson:json` will have marshalers/unmarshalers
+generated. For example:
+
+```go
+//easyjson:json
+type A struct {}
+```
+
+Additional option notes:
+
+* `-snake_case` tells easyjson to generate snake\_case field names by default
+  (unless overridden by a field tag). The CamelCase to snake\_case conversion
+  algorithm should work in most cases (ie, HTTPVersion will be converted to
+  "http_version").
+
+* `-build_tags` will add the specified build tags to generated Go sources.
+
+## Generated Marshaler/Unmarshaler Funcs
+
+For Go struct types, easyjson generates the funcs `MarshalEasyJSON` /
+`UnmarshalEasyJSON` for marshaling/unmarshaling JSON. In turn, these satisify
+the `easyjson.Marshaler` and `easyjson.Unmarshaler` interfaces and when used in
+conjunction with `easyjson.Marshal` / `easyjson.Unmarshal` avoid unnecessary
+reflection / type assertions during marshaling/unmarshaling to/from JSON for Go
+structs.
+
+easyjson also generates `MarshalJSON` and `UnmarshalJSON` funcs for Go struct
+types compatible with the standard `json.Marshaler` and `json.Unmarshaler`
+interfaces. Please be aware that using the standard `json.Marshal` /
+`json.Unmarshal` for marshaling/unmarshaling will incur a significant
+performance penalty when compared to using `easyjson.Marshal` /
+`easyjson.Unmarshal`.
+
+Additionally, easyjson exposes utility funcs that use the `MarshalEasyJSON` and
+`UnmarshalEasyJSON` for marshaling/unmarshaling to and from standard readers
+and writers. For example, easyjson provides `easyjson.MarshalToHTTPResponseWriter`
+which marshals to the standard `http.ResponseWriter`. Please see the [GoDoc
+listing](https://godoc.org/github.com/mailru/easyjson) for the full listing of
+utility funcs that are available.
+
+## Controlling easyjson Marshaling and Unmarshaling Behavior
+
+Go types can provide their own `MarshalEasyJSON` and `UnmarshalEasyJSON` funcs
+that satisify the `easyjson.Marshaler` / `easyjson.Unmarshaler` interfaces.
+These will be used by `easyjson.Marshal` and `easyjson.Unmarshal` when defined
+for a Go type.
+
+Go types can also satisify the `easyjson.Optional` interface, which allows the
+type to define its own `omitempty` logic.
+
+## Type Wrappers
+
+easyjson provides additional type wrappers defined in the `easyjson/opt`
+package. These wrap the standard Go primitives and in turn satisify the
+easyjson interfaces.
+
+The `easyjson/opt` type wrappers are useful when needing to distinguish between
+a missing value and/or when needing to specifying a default value. Type
+wrappers allow easyjson to avoid additional pointers and heap allocations and
+can significantly increase performance when used properly.
+
+## Memory Pooling
+
+easyjson uses a buffer pool that allocates data in increasing chunks from 128
+to 32768 bytes. Chunks of 512 bytes and larger will be reused with the help of
+`sync.Pool`. The maximum size of a chunk is bounded to reduce redundant memory
+allocation and to allow larger reusable buffers.
+
+easyjson's custom allocation buffer pool is defined in the `easyjson/buffer`
+package, and the default behavior pool behavior can be modified (if necessary)
+through a call to `buffer.Init()` prior to any marshaling or unmarshaling.
+Please see the [GoDoc listing](https://godoc.org/github.com/mailru/easyjson/buffer)
+for more information.
+
+## Issues, Notes, and Limitations
+
+* easyjson is still early in its development. As such, there are likely to be
+  bugs and missing features when compared to `encoding/json`. In the case of a
+  missing feature or bug, please create a GitHub issue. Pull requests are
+  welcome!
+
+* Unlike `encoding/json`, object keys are case-sensitive. Case-insensitive
+  matching is not currently provided due to the significant performance hit
+  when doing case-insensitive key matching. In the future, case-insensitive
+  object key matching may be provided via an option to the generator.
+
+* easyjson makes use of `unsafe`, which simplifies the code and
+  provides significant performance benefits by allowing no-copy
+  conversion from `[]byte` to `string`. That said, `unsafe` is used
+  only when unmarshaling and parsing JSON, and any `unsafe` operations
+  / memory allocations done will be safely deallocated by
+  easyjson. Set the build tag `easyjson_nounsafe` to compile it
+  without `unsafe`.
+
+* easyjson is compatible with Google App Engine. The `appengine` build
+  tag (set by App Engine's environment) will automatically disable the
+  use of `unsafe`, which is not allowed in App Engine's Standard
+  Environment. Note that the use with App Engine is still experimental.
+
+* Floats are formatted using the default precision from Go's `strconv` package.
+  As such, easyjson will not correctly handle high precision floats when
+  marshaling/unmarshaling JSON. Note, however, that there are very few/limited
+  uses where this behavior is not sufficient for general use. That said, a
+  different package may be needed if precise marshaling/unmarshaling of high
+  precision floats to/from JSON is required.
+
+* While unmarshaling, the JSON parser does the minimal amount of work needed to
+  skip over unmatching parens, and as such full validation is not done for the
+  entire JSON value being unmarshaled/parsed.
+
+* Currently there is no true streaming support for encoding/decoding as
+  typically for many uses/protocols the final, marshaled length of the JSON
+  needs to be known prior to sending the data. Currently this is not possible
+  with easyjson's architecture.
+  
+* easyjson parser and codegen based on reflection, so it wont works on `package main` 
+  files, because they cant be imported by parser.
+
+## Benchmarks
+
+Most benchmarks were done using the example
+[13kB example JSON](https://dev.twitter.com/rest/reference/get/search/tweets)
+(9k after eliminating whitespace). This example is similar to real-world data,
+is well-structured, and contains a healthy variety of different types, making
+it ideal for JSON serialization benchmarks.
+
+Note:
+
+* For small request benchmarks, an 80 byte portion of the above example was
+  used.
+
+* For large request marshaling benchmarks, a struct containing 50 regular
+  samples was used, making a ~500kB output JSON.
+
+* Benchmarks are showing the results of easyjson's default behaviour,
+  which makes use of `unsafe`.
+
+Benchmarks are available in the repository and can be run by invoking `make`.
+
+### easyjson vs. encoding/json
+
+easyjson is roughly 5-6 times faster than the standard `encoding/json` for
+unmarshaling, and 3-4 times faster for non-concurrent marshaling. Concurrent
+marshaling is 6-7x faster if marshaling to a writer.
+
+### easyjson vs. ffjson
+
+easyjson uses the same approach for JSON marshaling as
+[ffjson](https://github.com/pquerna/ffjson), but takes a significantly
+different approach to lexing and parsing JSON during unmarshaling. This means
+easyjson is roughly 2-3x faster for unmarshaling and 1.5-2x faster for
+non-concurrent unmarshaling.
+
+As of this writing, `ffjson` seems to have issues when used concurrently:
+specifically, large request pooling hurts `ffjson`'s performance and causes
+scalability issues. These issues with `ffjson` can likely be fixed, but as of
+writing remain outstanding/known issues with `ffjson`.
+
+easyjson and `ffjson` have similar performance for small requests, however
+easyjson outperforms `ffjson` by roughly 2-5x times for large requests when
+used with a writer.
+
+### easyjson vs. go/codec
+
+[go/codec](https://github.com/ugorji/go) provides
+compile-time helpers for JSON generation. In this case, helpers do not work
+like marshalers as they are encoding-independent.
+
+easyjson is generally 2x faster than `go/codec` for non-concurrent benchmarks
+and about 3x faster for concurrent encoding (without marshaling to a writer).
+
+In an attempt to measure marshaling performance of `go/codec` (as opposed to
+allocations/memcpy/writer interface invocations), a benchmark was done with
+resetting length of a byte slice rather than resetting the whole slice to nil.
+However, the optimization in this exact form may not be applicable in practice,
+since the memory is not freed between marshaling operations.
+
+### easyjson vs 'ujson' python module
+
+[ujson](https://github.com/esnme/ultrajson) is using C code for parsing, so it
+is interesting to see how plain golang compares to that. It is imporant to note
+that the resulting object for python is slower to access, since the library
+parses JSON object into dictionaries.
+
+easyjson is slightly faster for unmarshaling and 2-3x faster than `ujson` for
+marshaling.
+
+### Benchmark Results
+
+`ffjson` results are from February 4th, 2016, using the latest `ffjson` and go1.6.
+`go/codec` results are from March 4th, 2016, using the latest `go/codec` and go1.6.
+
+#### Unmarshaling
+
+| lib      | json size | MB/s | allocs/op | B/op  |
+|:---------|:----------|-----:|----------:|------:|
+| standard | regular   | 22   | 218       | 10229 |
+| standard | small     | 9.7  | 14        | 720   |
+|          |           |      |           |       |
+| easyjson | regular   | 125  | 128       | 9794  |
+| easyjson | small     | 67   | 3         | 128   |
+|          |           |      |           |       |
+| ffjson   | regular   | 66   | 141       | 9985  |
+| ffjson   | small     | 17.6 | 10        | 488   |
+|          |           |      |           |       |
+| codec    | regular   | 55   | 434       | 19299 |
+| codec    | small     | 29   | 7         | 336   |
+|          |           |      |           |       |
+| ujson    | regular   | 103  | N/A       | N/A   |
+
+#### Marshaling, one goroutine.
+
+| lib       | json size | MB/s | allocs/op | B/op  |
+|:----------|:----------|-----:|----------:|------:|
+| standard  | regular   | 75   | 9         | 23256 |
+| standard  | small     | 32   | 3         | 328   |
+| standard  | large     | 80   | 17        | 1.2M  |
+|           |           |      |           |       |
+| easyjson  | regular   | 213  | 9         | 10260 |
+| easyjson* | regular   | 263  | 8         | 742   |
+| easyjson  | small     | 125  | 1         | 128   |
+| easyjson  | large     | 212  | 33        | 490k  |
+| easyjson* | large     | 262  | 25        | 2879  |
+|           |           |      |           |       |
+| ffjson    | regular   | 122  | 153       | 21340 |
+| ffjson**  | regular   | 146  | 152       | 4897  |
+| ffjson    | small     | 36   | 5         | 384   |
+| ffjson**  | small     | 64   | 4         | 128   |
+| ffjson    | large     | 134  | 7317      | 818k  |
+| ffjson**  | large     | 125  | 7320      | 827k  |
+|           |           |      |           |       |
+| codec     | regular   | 80   | 17        | 33601 |
+| codec***  | regular   | 108  | 9         | 1153  |
+| codec     | small     | 42   | 3         | 304   |
+| codec***  | small     | 56   | 1         | 48    |
+| codec     | large     | 73   | 483       | 2.5M  |
+| codec***  | large     | 103  | 451       | 66007 |
+|           |           |      |           |       |
+| ujson     | regular   | 92   | N/A       | N/A   |
+
+\* marshaling to a writer,
+\*\* using `ffjson.Pool()`,
+\*\*\* reusing output slice instead of resetting it to nil
+
+#### Marshaling, concurrent.
+
+| lib       | json size | MB/s | allocs/op | B/op  |
+|:----------|:----------|-----:|----------:|------:|
+| standard  | regular   | 252  | 9         | 23257 |
+| standard  | small     | 124  | 3         | 328   |
+| standard  | large     | 289  | 17        | 1.2M  |
+|           |           |      |           |       |
+| easyjson  | regular   | 792  | 9         | 10597 |
+| easyjson* | regular   | 1748 | 8         | 779   |
+| easyjson  | small     | 333  | 1         | 128   |
+| easyjson  | large     | 718  | 36        | 548k  |
+| easyjson* | large     | 2134 | 25        | 4957  |
+|           |           |      |           |       |
+| ffjson    | regular   | 301  | 153       | 21629 |
+| ffjson**  | regular   | 707  | 152       | 5148  |
+| ffjson    | small     | 62   | 5         | 384   |
+| ffjson**  | small     | 282  | 4         | 128   |
+| ffjson    | large     | 438  | 7330      | 1.0M  |
+| ffjson**  | large     | 131  | 7319      | 820k  |
+|           |           |      |           |       |
+| codec     | regular   | 183  | 17        | 33603 |
+| codec***  | regular   | 671  | 9         | 1157  |
+| codec     | small     | 147  | 3         | 304   |
+| codec***  | small     | 299  | 1         | 48    |
+| codec     | large     | 190  | 483       | 2.5M  |
+| codec***  | large     | 752  | 451       | 77574 |
+
+\* marshaling to a writer,
+\*\* using `ffjson.Pool()`,
+\*\*\* reusing output slice instead of resetting it to nil
diff --git a/vendor/github.com/mailru/easyjson/go.mod b/vendor/github.com/mailru/easyjson/go.mod
new file mode 100644
index 000000000..7bc4a6584
--- /dev/null
+++ b/vendor/github.com/mailru/easyjson/go.mod
@@ -0,0 +1,3 @@
+module github.com/mailru/easyjson
+
+go 1.12
diff --git a/vendor/github.com/mailru/easyjson/helpers.go b/vendor/github.com/mailru/easyjson/helpers.go
new file mode 100644
index 000000000..b86b87d22
--- /dev/null
+++ b/vendor/github.com/mailru/easyjson/helpers.go
@@ -0,0 +1,78 @@
+// Package easyjson contains marshaler/unmarshaler interfaces and helper functions.
+package easyjson
+
+import (
+	"io"
+	"io/ioutil"
+	"net/http"
+	"strconv"
+
+	"github.com/mailru/easyjson/jlexer"
+	"github.com/mailru/easyjson/jwriter"
+)
+
+// Marshaler is an easyjson-compatible marshaler interface.
+type Marshaler interface {
+	MarshalEasyJSON(w *jwriter.Writer)
+}
+
+// Marshaler is an easyjson-compatible unmarshaler interface.
+type Unmarshaler interface {
+	UnmarshalEasyJSON(w *jlexer.Lexer)
+}
+
+// Optional defines an undefined-test method for a type to integrate with 'omitempty' logic.
+type Optional interface {
+	IsDefined() bool
+}
+
+// Marshal returns data as a single byte slice. Method is suboptimal as the data is likely to be copied
+// from a chain of smaller chunks.
+func Marshal(v Marshaler) ([]byte, error) {
+	w := jwriter.Writer{}
+	v.MarshalEasyJSON(&w)
+	return w.BuildBytes()
+}
+
+// MarshalToWriter marshals the data to an io.Writer.
+func MarshalToWriter(v Marshaler, w io.Writer) (written int, err error) {
+	jw := jwriter.Writer{}
+	v.MarshalEasyJSON(&jw)
+	return jw.DumpTo(w)
+}
+
+// MarshalToHTTPResponseWriter sets Content-Length and Content-Type headers for the
+// http.ResponseWriter, and send the data to the writer. started will be equal to
+// false if an error occurred before any http.ResponseWriter methods were actually
+// invoked (in this case a 500 reply is possible).
+func MarshalToHTTPResponseWriter(v Marshaler, w http.ResponseWriter) (started bool, written int, err error) {
+	jw := jwriter.Writer{}
+	v.MarshalEasyJSON(&jw)
+	if jw.Error != nil {
+		return false, 0, jw.Error
+	}
+	w.Header().Set("Content-Type", "application/json")
+	w.Header().Set("Content-Length", strconv.Itoa(jw.Size()))
+
+	started = true
+	written, err = jw.DumpTo(w)
+	return
+}
+
+// Unmarshal decodes the JSON in data into the object.
+func Unmarshal(data []byte, v Unmarshaler) error {
+	l := jlexer.Lexer{Data: data}
+	v.UnmarshalEasyJSON(&l)
+	return l.Error()
+}
+
+// UnmarshalFromReader reads all the data in the reader and decodes as JSON into the object.
+func UnmarshalFromReader(r io.Reader, v Unmarshaler) error {
+	data, err := ioutil.ReadAll(r)
+	if err != nil {
+		return err
+	}
+	l := jlexer.Lexer{Data: data}
+	v.UnmarshalEasyJSON(&l)
+	return l.Error()
+}
diff --git a/vendor/github.com/mailru/easyjson/raw.go b/vendor/github.com/mailru/easyjson/raw.go
new file mode 100644
index 000000000..81bd002e1
--- /dev/null
+++ b/vendor/github.com/mailru/easyjson/raw.go
@@ -0,0 +1,45 @@
+package easyjson
+
+import (
+	"github.com/mailru/easyjson/jlexer"
+	"github.com/mailru/easyjson/jwriter"
+)
+
+// RawMessage is a raw piece of JSON (number, string, bool, object, array or
+// null) that is extracted without parsing and output as is during marshaling.
+type RawMessage []byte
+
+// MarshalEasyJSON does JSON marshaling using easyjson interface.
+func (v *RawMessage) MarshalEasyJSON(w *jwriter.Writer) {
+	if len(*v) == 0 {
+		w.RawString("null")
+	} else {
+		w.Raw(*v, nil)
+	}
+}
+
+// UnmarshalEasyJSON does JSON unmarshaling using easyjson interface.
+func (v *RawMessage) UnmarshalEasyJSON(l *jlexer.Lexer) {
+	*v = RawMessage(l.Raw())
+}
+
+// UnmarshalJSON implements encoding/json.Unmarshaler interface.
+func (v *RawMessage) UnmarshalJSON(data []byte) error {
+	*v = data
+	return nil
+}
+
+var nullBytes = []byte("null")
+
+// MarshalJSON implements encoding/json.Marshaler interface.
+func (v RawMessage) MarshalJSON() ([]byte, error) {
+	if len(v) == 0 {
+		return nullBytes, nil
+	}
+	return v, nil
+}
+
+// IsDefined is required for integration with omitempty easyjson logic.
+func (v *RawMessage) IsDefined() bool {
+	return len(*v) > 0
+}
diff --git a/vendor/github.com/tsenart/vegeta/lib/results_easyjson.go b/vendor/github.com/tsenart/vegeta/lib/results_easyjson.go
deleted file mode 100644
index b1594bdc3..000000000
--- a/vendor/github.com/tsenart/vegeta/lib/results_easyjson.go
+++ /dev/null
@@ -1,165 +0,0 @@
-// This file has been modified from the original generated code to make it work with
-// type alias jsonResult so that the methods aren't exposed in Result.
-
-package vegeta
-
-import (
-	"time"
-
-	"github.com/mailru/easyjson/jlexer"
-	"github.com/mailru/easyjson/jwriter"
-)
-
-type jsonResult Result
-
-func (r *jsonResult) decode(in *jlexer.Lexer) {
-	isTopLevel := in.IsStart()
-	if in.IsNull() {
-		if isTopLevel {
-			in.Consumed()
-		}
-		in.Skip()
-		return
-	}
-	in.Delim('{')
-	for !in.IsDelim('}') {
-		key := in.UnsafeString()
-		in.WantColon()
-		if in.IsNull() {
-			in.Skip()
-			in.WantComma()
-			continue
-		}
-		switch key {
-		case "attack":
-			r.Attack = string(in.String())
-		case "seq":
-			r.Seq = uint64(in.Uint64())
-		case "code":
-			r.Code = uint16(in.Uint16())
-		case "timestamp":
-			if data := in.Raw(); in.Ok() {
-				in.AddError((r.Timestamp).UnmarshalJSON(data))
-			}
-		case "latency":
-			r.Latency = time.Duration(in.Int64())
-		case "bytes_out":
-			r.BytesOut = uint64(in.Uint64())
-		case "bytes_in":
-			r.BytesIn = uint64(in.Uint64())
-		case "error":
-			r.Error = string(in.String())
-		case "body":
-			if in.IsNull() {
-				in.Skip()
-				r.Body = nil
-			} else {
-				r.Body = in.Bytes()
-			}
-		default:
-			in.SkipRecursive()
-		}
-		in.WantComma()
-	}
-	in.Delim('}')
-	if isTopLevel {
-		in.Consumed()
-	}
-}
-
-func (r jsonResult) encode(out *jwriter.Writer) {
-	out.RawByte('{')
-	first := true
-	_ = first
-	{
-		const prefix string = ",\"attack\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(r.Attack))
-	}
-	{
-		const prefix string = ",\"seq\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Uint64(uint64(r.Seq))
-	}
-	{
-		const prefix string = ",\"code\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Uint16(uint16(r.Code))
-	}
-	{
-		const prefix string = ",\"timestamp\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Raw((r.Timestamp).MarshalJSON())
-	}
-	{
-		const prefix string = ",\"latency\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Int64(int64(r.Latency))
-	}
-	{
-		const prefix string = ",\"bytes_out\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Uint64(uint64(r.BytesOut))
-	}
-	{
-		const prefix string = ",\"bytes_in\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Uint64(uint64(r.BytesIn))
-	}
-	{
-		const prefix string = ",\"error\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.String(string(r.Error))
-	}
-	{
-		const prefix string = ",\"body\":"
-		if first {
-			first = false
-			out.RawString(prefix[1:])
-		} else {
-			out.RawString(prefix)
-		}
-		out.Base64Bytes(r.Body)
-	}
-	out.RawByte('}')
-}
diff --git a/vendor/github.com/tsenart/vegeta/LICENSE b/vendor/github.com/tsenart/vegeta/v12/LICENSE
similarity index 96%
rename from vendor/github.com/tsenart/vegeta/LICENSE
rename to vendor/github.com/tsenart/vegeta/v12/LICENSE
index 441df0ab4..03ec4f5a8 100644
--- a/vendor/github.com/tsenart/vegeta/LICENSE
+++ b/vendor/github.com/tsenart/vegeta/v12/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2013-2016 Tomás Senart
+Copyright (c) 2013-2020 Tomás Senart
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in
diff --git a/vendor/github.com/tsenart/vegeta/lib/attack.go b/vendor/github.com/tsenart/vegeta/v12/lib/attack.go
similarity index 88%
rename from vendor/github.com/tsenart/vegeta/lib/attack.go
rename to vendor/github.com/tsenart/vegeta/v12/lib/attack.go
index 51c4957db..0294c628b 100644
--- a/vendor/github.com/tsenart/vegeta/lib/attack.go
+++ b/vendor/github.com/tsenart/vegeta/v12/lib/attack.go
@@ -10,6 +10,7 @@ import (
 	"net"
 	"net/http"
 	"net/url"
+	"strconv"
 	"sync"
 	"time"
 
@@ -28,6 +29,7 @@ type Attacker struct {
 	seqmu      sync.Mutex
 	seq        uint64
 	began      time.Time
+	chunked    bool
 }
 
 const (
@@ -40,6 +42,9 @@ const (
 	// DefaultConnections is the default amount of max open idle connections per
 	// target host.
 	DefaultConnections = 10000
+	// DefaultMaxConnections is the default amount of connections per target
+	// host.
+	DefaultMaxConnections = 0
 	// DefaultWorkers is the default initial number of workers used to carry an attack.
 	DefaultWorkers = 10
 	// DefaultMaxWorkers is the default maximum number of workers used to carry an attack.
@@ -81,6 +86,7 @@ func NewAttacker(opts ...func(*Attacker)) *Attacker {
 			Dial:                a.dialer.Dial,
 			TLSClientConfig:     DefaultTLSConfig,
 			MaxIdleConnsPerHost: DefaultConnections,
+			MaxConnsPerHost:     DefaultMaxConnections,
 		},
 	}
 
@@ -113,6 +119,21 @@ func Connections(n int) func(*Attacker) {
 	}
 }
 
+// MaxConnections returns a functional option which sets the number of maximum
+// connections per target host.
+func MaxConnections(n int) func(*Attacker) {
+	return func(a *Attacker) {
+		tr := a.client.Transport.(*http.Transport)
+		tr.MaxConnsPerHost = n
+	}
+}
+
+// ChunkedBody returns a functional option which makes the attacker send the
+// body of each request with the chunked transfer encoding.
+func ChunkedBody(b bool) func(*Attacker) {
+	return func(a *Attacker) { a.chunked = b }
+}
+
 // Redirects returns a functional option which sets the maximum
 // number of redirects an Attacker will follow.
 func Redirects(n int) func(*Attacker) {
@@ -229,6 +250,16 @@ func Client(c *http.Client) func(*Attacker) {
 	return func(a *Attacker) { a.client = *c }
 }
 
+// ProxyHeader returns a functional option that allows you to add your own
+// Proxy CONNECT headers
+func ProxyHeader(h http.Header) func(*Attacker) {
+	return func(a *Attacker) {
+		if tr, ok := a.client.Transport.(*http.Transport); ok {
+			tr.ProxyConnectHeader = h
+		}
+	}
+}
+
 // Attack reads its Targets from the passed Targeter and attacks them at
 // the rate specified by the Pacer. When the duration is zero the attack
 // runs until Stop is called. Results are sent to the returned channel as soon
@@ -336,11 +367,24 @@ func (a *Attacker) hit(tr Targeter, name string) *Result {
 		return &res
 	}
 
+	res.Method = tgt.Method
+	res.URL = tgt.URL
+
 	req, err := tgt.Request()
 	if err != nil {
 		return &res
 	}
 
+	if name != "" {
+		req.Header.Set("X-Vegeta-Attack", name)
+	}
+
+	req.Header.Set("X-Vegeta-Seq", strconv.FormatUint(res.Seq, 10))
+
+	if a.chunked {
+		req.TransferEncoding = append(req.TransferEncoding, "chunked")
+	}
+
 	r, err := a.client.Do(req)
 	if err != nil {
 		return &res
@@ -368,5 +412,7 @@ func (a *Attacker) hit(tr Targeter, name string) *Result {
 		res.Error = r.Status
 	}
 
+	res.Headers = r.Header
+
 	return &res
 }
diff --git a/vendor/github.com/tsenart/vegeta/v12/lib/attack_fuzz.go b/vendor/github.com/tsenart/vegeta/v12/lib/attack_fuzz.go
new file mode 100644
index 000000000..e14eef61f
--- /dev/null
+++ b/vendor/github.com/tsenart/vegeta/v12/lib/attack_fuzz.go
@@ -0,0 +1,154 @@
+// +build gofuzz
+
+package vegeta
+
+import (
+	"encoding/binary"
+	"fmt"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"os"
+	"time"
+)
+
+// FuzzAttackerTCP fuzzes binary responses to attacker.
+func FuzzAttackerTCP(fuzz []byte) int {
+	// Ignore empty fuzz
+	if len(fuzz) == 0 {
+		return -1
+	}
+
+	// Start server
+	directory, err := ioutil.TempDir("/tmp", "fuzz")
+	if err != nil {
+		panic(err.Error())
+	}
+	socket := fmt.Sprintf("%s/attacker.sock", directory)
+	listener, err := net.Listen("unix", socket)
+	if err != nil {
+		panic(err.Error())
+	}
+	go func() {
+		connection, err := listener.Accept()
+		if err != nil {
+			panic(err.Error())
+		}
+		_, err = connection.Write(fuzz)
+		if err != nil {
+			panic(err.Error())
+		}
+		err = connection.Close()
+		if err != nil {
+			panic(err.Error())
+		}
+	}()
+	defer listener.Close()
+	defer os.RemoveAll(directory)
+
+	// Setup targeter
+	targeter := Targeter(func(target *Target) error {
+		target.Method = "GET"
+		target.URL = "http://vegeta.test"
+		return nil
+	})
+
+	// Deliver a single hit
+	attacker := NewAttacker(
+		UnixSocket(socket),
+		Workers(1),
+		MaxWorkers(1),
+		Timeout(time.Second),
+		KeepAlive(false),
+	)
+	result := attacker.hit(targeter, "fuzz")
+	if result.Error != "" {
+		return 0
+	}
+	return 1
+}
+
+// FuzzAttackerHTTP fuzzes valid HTTP responses to attacker.
+func FuzzAttackerHTTP(fuzz []byte) int {
+	// Decode response
+	code, headers, body, ok := decodeFuzzResponse(fuzz)
+	if !ok {
+		return -1
+	}
+
+	// Start server
+	directory, err := ioutil.TempDir("/tmp", "fuzz")
+	if err != nil {
+		panic(err.Error())
+	}
+	socket := fmt.Sprintf("%s/attacker.sock", directory)
+	listener, err := net.Listen("unix", socket)
+	if err != nil {
+		panic(err.Error())
+	}
+	handler := func(response http.ResponseWriter, request *http.Request) {
+		for name, values := range headers {
+			for _, value := range values {
+				response.Header().Add(name, value)
+			}
+		}
+		response.WriteHeader(int(code))
+		_, err := response.Write(body)
+		if err != nil {
+			panic(err.Error())
+		}
+	}
+	server := http.Server{
+		Handler: http.HandlerFunc(handler),
+	}
+	defer server.Close()
+	defer listener.Close()
+	defer os.RemoveAll(directory)
+	go server.Serve(listener)
+
+	// Setup targeter
+	targeter := Targeter(func(target *Target) error {
+		target.Method = "GET"
+		target.URL = "http://vegeta.test"
+		return nil
+	})
+
+	// Deliver a single hit
+	attacker := NewAttacker(
+		UnixSocket(socket),
+		Workers(1),
+		MaxWorkers(1),
+		Timeout(time.Second),
+		KeepAlive(false),
+	)
+	result := attacker.hit(targeter, "fuzz")
+	if result.Error != "" {
+		return 0
+	}
+	return 1
+}
+
+func decodeFuzzResponse(fuzz []byte) (
+	code int,
+	headers map[string][]string,
+	body []byte,
+	ok bool,
+) {
+	if len(fuzz) < 2 {
+		return
+	}
+	headers = make(map[string][]string)
+	body = []byte{}
+	code = int(binary.LittleEndian.Uint16(fuzz[0:2]))
+	if len(fuzz) == 2 {
+		ok = true
+		return
+	}
+	fuzz, ok = decodeFuzzHeaders(fuzz[2:], headers)
+	if !ok {
+		return
+	}
+	body = fuzz
+	ok = true
+	return
+}
diff --git a/vendor/github.com/tsenart/vegeta/lib/histogram.go b/vendor/github.com/tsenart/vegeta/v12/lib/histogram.go
similarity index 91%
rename from vendor/github.com/tsenart/vegeta/lib/histogram.go
rename to vendor/github.com/tsenart/vegeta/v12/lib/histogram.go
index f8fd84462..acb546f19 100644
--- a/vendor/github.com/tsenart/vegeta/lib/histogram.go
+++ b/vendor/github.com/tsenart/vegeta/v12/lib/histogram.go
@@ -68,11 +68,15 @@ func (bs *Buckets) UnmarshalText(value []byte) error {
 	if len(value) < 2 || value[0] != '[' || value[len(value)-1] != ']' {
 		return fmt.Errorf("bad buckets: %s", value)
 	}
-	for _, v := range strings.Split(string(value[1:len(value)-1]), ",") {
+	for i, v := range strings.Split(string(value[1:len(value)-1]), ",") {
 		d, err := time.ParseDuration(strings.TrimSpace(v))
 		if err != nil {
 			return err
 		}
+		// add a default range of [0-Buckets[0]) if needed
+		if i == 0 && d > 0 {
+			*bs = append(*bs, 0)
+		}
 		*bs = append(*bs, d)
 	}
 	if len(*bs) == 0 {
diff --git a/vendor/github.com/tsenart/vegeta/lib/metrics.go b/vendor/github.com/tsenart/vegeta/v12/lib/metrics.go
similarity index 94%
rename from vendor/github.com/tsenart/vegeta/lib/metrics.go
rename to vendor/github.com/tsenart/vegeta/v12/lib/metrics.go
index 5b3cd626a..5d7dbf9b2 100644
--- a/vendor/github.com/tsenart/vegeta/lib/metrics.go
+++ b/vendor/github.com/tsenart/vegeta/v12/lib/metrics.go
@@ -89,6 +89,11 @@ func (m *Metrics) Add(r *Result) {
 // derived summary metrics which don't need to be run on every Add call.
 func (m *Metrics) Close() {
 	m.init()
+
+	if m.Requests == 0 {
+		return
+	}
+
 	m.Rate = float64(m.Requests)
 	m.Throughput = float64(m.success)
 	m.Duration = m.Latest.Sub(m.Earliest)
@@ -104,6 +109,7 @@ func (m *Metrics) Close() {
 	m.Success = float64(m.success) / float64(m.Requests)
 	m.Latencies.Mean = time.Duration(float64(m.Latencies.Total) / float64(m.Requests))
 	m.Latencies.P50 = m.Latencies.Quantile(0.50)
+	m.Latencies.P90 = m.Latencies.Quantile(0.90)
 	m.Latencies.P95 = m.Latencies.Quantile(0.95)
 	m.Latencies.P99 = m.Latencies.Quantile(0.99)
 }
@@ -130,12 +136,16 @@ type LatencyMetrics struct {
 	Mean time.Duration `json:"mean"`
 	// P50 is the 50th percentile request latency.
 	P50 time.Duration `json:"50th"`
+	// P90 is the 90th percentile request latency.
+	P90 time.Duration `json:"90th"`
 	// P95 is the 95th percentile request latency.
 	P95 time.Duration `json:"95th"`
 	// P99 is the 99th percentile request latency.
 	P99 time.Duration `json:"99th"`
 	// Max is the maximum observed request latency.
 	Max time.Duration `json:"max"`
+	// Min is the minimum observed request latency.
+	Min time.Duration `json:"min"`
 
 	estimator estimator
 }
@@ -146,6 +156,9 @@ func (l *LatencyMetrics) Add(latency time.Duration) {
 	if l.Total += latency; latency > l.Max {
 		l.Max = latency
 	}
+	if latency < l.Min || l.Min == 0 {
+		l.Min = latency
+	}
 	l.estimator.Add(float64(latency))
 }
 
diff --git a/vendor/github.com/tsenart/vegeta/lib/pacer.go b/vendor/github.com/tsenart/vegeta/v12/lib/pacer.go
similarity index 88%
rename from vendor/github.com/tsenart/vegeta/lib/pacer.go
rename to vendor/github.com/tsenart/vegeta/v12/lib/pacer.go
index 97c8310be..4c04d637f 100644
--- a/vendor/github.com/tsenart/vegeta/lib/pacer.go
+++ b/vendor/github.com/tsenart/vegeta/v12/lib/pacer.go
@@ -6,12 +6,17 @@ import (
 	"time"
 )
 
-// A Pacer defines the rate of hits during an Attack by
-// returning the duration an Attacker should wait until
-// hitting the next Target. If the second return value
-// is true, the attack will terminate.
+// A Pacer defines the rate of hits during an Attack.
 type Pacer interface {
+	// Pace returns the duration an Attacker should wait until
+	// hitting the next Target, 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 PacerFunc is a function adapter type that implements
@@ -65,6 +70,13 @@ func (cp ConstantPacer) Pace(elapsed time.Duration, hits uint64) (time.Duration,
 	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 attack rate this ConstantPacer represents, in
 // fractional hits per nanosecond.
 func (cp ConstantPacer) hitsPerNs() float64 {
@@ -184,6 +196,12 @@ func (sp SinePacer) Pace(elapsedTime time.Duration, elapsedHits uint64) (time.Du
 	return nextHitIn, false
 }
 
+// Rate returns a SinePacer's instantaneous hit rate (i.e. requests per second)
+// at the given elapsed duration of an attack.
+func (sp SinePacer) Rate(elapsed time.Duration) float64 {
+	return sp.hitsPerNs(elapsed) * 1e9
+}
+
 // ampHits returns AP/2𝛑, which is the number of hits added or subtracted
 // from the Mean due to the Amplitude over a quarter of the Period,
 // i.e. from 0 → 𝛑/2 radians
@@ -240,7 +258,7 @@ func (p LinearPacer) Pace(elapsed time.Duration, hits uint64) (time.Duration, bo
 		return 0, false
 	}
 
-	rate := p.rate(elapsed)
+	rate := p.Rate(elapsed)
 	interval := math.Round(1e9 / rate)
 
 	if n := uint64(interval); n != 0 && math.MaxInt64/n < hits {
@@ -254,6 +272,15 @@ func (p LinearPacer) Pace(elapsed time.Duration, hits uint64) (time.Duration, bo
 	return wait, false
 }
 
+// Rate returns a LinearPacer's instantaneous hit rate (i.e. requests per second)
+// at the given elapsed duration of an attack.
+func (p LinearPacer) Rate(elapsed time.Duration) float64 {
+	a := p.Slope
+	x := elapsed.Seconds()
+	b := p.StartAt.hitsPerNs() * 1e9
+	return a*x + b
+}
+
 // hits returns the number of hits that have been sent during an attack
 // lasting t nanoseconds. It returns a float so we can tell exactly how
 // much we've missed our target by when solving numerically in Pace.
@@ -268,12 +295,3 @@ func (p LinearPacer) hits(t time.Duration) float64 {
 
 	return (a*math.Pow(x, 2))/2 + b*x
 }
-
-// rate calculates the instantaneous rate of attack at
-// t nanoseconds after the attack began.
-func (p LinearPacer) rate(t time.Duration) float64 {
-	a := p.Slope
-	x := t.Seconds()
-	b := p.StartAt.hitsPerNs() * 1e9
-	return a*x + b
-}
diff --git a/vendor/github.com/tsenart/vegeta/lib/reporters.go b/vendor/github.com/tsenart/vegeta/v12/lib/reporters.go
similarity index 88%
rename from vendor/github.com/tsenart/vegeta/lib/reporters.go
rename to vendor/github.com/tsenart/vegeta/v12/lib/reporters.go
index e86a8fbdf..aa7e42513 100644
--- a/vendor/github.com/tsenart/vegeta/lib/reporters.go
+++ b/vendor/github.com/tsenart/vegeta/v12/lib/reporters.go
@@ -57,7 +57,7 @@ func NewHistogramReporter(h *Histogram) Reporter {
 func NewTextReporter(m *Metrics) Reporter {
 	const fmtstr = "Requests\t[total, rate, throughput]\t%d, %.2f, %.2f\n" +
 		"Duration\t[total, attack, wait]\t%s, %s, %s\n" +
-		"Latencies\t[mean, 50, 95, 99, max]\t%s, %s, %s, %s, %s\n" +
+		"Latencies\t[min, mean, 50, 90, 95, 99, max]\t%s, %s, %s, %s, %s, %s, %s\n" +
 		"Bytes In\t[total, mean]\t%d, %.2f\n" +
 		"Bytes Out\t[total, mean]\t%d, %.2f\n" +
 		"Success\t[ratio]\t%.2f%%\n" +
@@ -67,8 +67,16 @@ func NewTextReporter(m *Metrics) Reporter {
 		tw := tabwriter.NewWriter(w, 0, 8, 2, ' ', tabwriter.StripEscape)
 		if _, err = fmt.Fprintf(tw, fmtstr,
 			m.Requests, m.Rate, m.Throughput,
-			m.Duration+m.Wait, m.Duration, m.Wait,
-			m.Latencies.Mean, m.Latencies.P50, m.Latencies.P95, m.Latencies.P99, m.Latencies.Max,
+			round(m.Duration+m.Wait),
+			round(m.Duration),
+			round(m.Wait),
+			round(m.Latencies.Min),
+			round(m.Latencies.Mean),
+			round(m.Latencies.P50),
+			round(m.Latencies.P90),
+			round(m.Latencies.P95),
+			round(m.Latencies.P99),
+			round(m.Latencies.Max),
 			m.BytesIn.Total, m.BytesIn.Mean,
 			m.BytesOut.Total, m.BytesOut.Mean,
 			m.Success*100,
@@ -104,6 +112,25 @@ func NewTextReporter(m *Metrics) Reporter {
 	}
 }
 
+var durations = [...]time.Duration{
+	time.Hour,
+	time.Minute,
+	time.Second,
+	time.Millisecond,
+	time.Microsecond,
+	time.Nanosecond,
+}
+
+// round to the next most precise unit
+func round(d time.Duration) time.Duration {
+	for i, unit := range durations {
+		if d >= unit && i < len(durations)-1 {
+			return d.Round(durations[i+1])
+		}
+	}
+	return d
+}
+
 // NewJSONReporter returns a Reporter that writes out Metrics as JSON.
 func NewJSONReporter(m *Metrics) Reporter {
 	return func(w io.Writer) error {
diff --git a/vendor/github.com/tsenart/vegeta/lib/results.go b/vendor/github.com/tsenart/vegeta/v12/lib/results.go
similarity index 78%
rename from vendor/github.com/tsenart/vegeta/lib/results.go
rename to vendor/github.com/tsenart/vegeta/v12/lib/results.go
index 2754cb518..eacd42d01 100644
--- a/vendor/github.com/tsenart/vegeta/lib/results.go
+++ b/vendor/github.com/tsenart/vegeta/v12/lib/results.go
@@ -7,12 +7,15 @@ import (
 	"encoding/csv"
 	"encoding/gob"
 	"io"
+	"net/http"
+	"net/textproto"
 	"sort"
 	"strconv"
+	"strings"
 	"time"
 
 	"github.com/mailru/easyjson/jlexer"
-	jwriter "github.com/mailru/easyjson/jwriter"
+	"github.com/mailru/easyjson/jwriter"
 )
 
 func init() {
@@ -30,6 +33,9 @@ type Result struct {
 	BytesIn   uint64        `json:"bytes_in"`
 	Error     string        `json:"error"`
 	Body      []byte        `json:"body"`
+	Method    string        `json:"method"`
+	URL       string        `json:"url"`
+	Headers   http.Header   `json:"headers"`
 }
 
 // End returns the time at which a Result ended.
@@ -45,7 +51,32 @@ func (r Result) Equal(other Result) bool {
 		r.BytesIn == other.BytesIn &&
 		r.BytesOut == other.BytesOut &&
 		r.Error == other.Error &&
-		bytes.Equal(r.Body, other.Body)
+		bytes.Equal(r.Body, other.Body) &&
+		r.Method == other.Method &&
+		r.URL == other.URL &&
+		headerEqual(r.Headers, other.Headers)
+}
+
+func headerEqual(h1, h2 http.Header) bool {
+	if len(h1) != len(h2) {
+		return false
+	}
+	if h1 == nil || h2 == nil {
+		return h1 == nil && h2 == nil
+	}
+	for key, values1 := range h1 {
+		values2 := h2[key]
+		if len(values1) != len(values2) {
+			return false
+		}
+		for i := range values1 {
+			if values1[i] != values2[i] {
+				return false
+			}
+		}
+	}
+
+	return true
 }
 
 // Results is a slice of Result type elements.
@@ -150,8 +181,10 @@ func NewCSVEncoder(w io.Writer) Encoder {
 			base64.StdEncoding.EncodeToString(r.Body),
 			r.Attack,
 			strconv.FormatUint(r.Seq, 10),
+			r.Method,
+			r.URL,
+			base64.StdEncoding.EncodeToString(headerBytes(r.Headers)),
 		})
-
 		if err != nil {
 			return err
 		}
@@ -162,10 +195,19 @@ func NewCSVEncoder(w io.Writer) Encoder {
 	}
 }
 
+func headerBytes(h http.Header) []byte {
+	if h == nil {
+		return nil
+	}
+	var hdr bytes.Buffer
+	_ = h.Write(&hdr)
+	return append(hdr.Bytes(), '\r', '\n')
+}
+
 // NewCSVDecoder returns a Decoder that decodes CSV encoded Results.
-func NewCSVDecoder(rd io.Reader) Decoder {
-	dec := csv.NewReader(rd)
-	dec.FieldsPerRecord = 9
+func NewCSVDecoder(r io.Reader) Decoder {
+	dec := csv.NewReader(r)
+	dec.FieldsPerRecord = 12
 	dec.TrimLeadingSpace = true
 
 	return func(r *Result) error {
@@ -201,23 +243,42 @@ func NewCSVDecoder(rd io.Reader) Decoder {
 		}
 
 		r.Error = rec[5]
-		r.Body, err = base64.StdEncoding.DecodeString(rec[6])
+		if r.Body, err = base64.StdEncoding.DecodeString(rec[6]); err != nil {
+			return err
+		}
 
 		r.Attack = rec[7]
 		if r.Seq, err = strconv.ParseUint(rec[8], 10, 64); err != nil {
 			return err
 		}
 
+		r.Method = rec[9]
+		r.URL = rec[10]
+
+		if rec[11] != "" {
+			pr := textproto.NewReader(bufio.NewReader(
+				base64.NewDecoder(base64.StdEncoding, strings.NewReader(rec[11]))))
+			hdr, err := pr.ReadMIMEHeader()
+			if err != nil {
+				return err
+			}
+			r.Headers = http.Header(hdr)
+		}
+
 		return err
 	}
 }
 
+//go:generate easyjson -no_std_marshalers -output_filename results_easyjson.go results.go
+//easyjson:json
+type jsonResult Result
+
 // NewJSONEncoder returns an Encoder that dumps the given *Results as a JSON
 // object.
 func NewJSONEncoder(w io.Writer) Encoder {
 	var jw jwriter.Writer
 	return func(r *Result) error {
-		(*jsonResult)(r).encode(&jw)
+		(*jsonResult)(r).MarshalEasyJSON(&jw)
 		if jw.Error != nil {
 			return jw.Error
 		}
@@ -232,10 +293,10 @@ func NewJSONDecoder(r io.Reader) Decoder {
 	rd := bufio.NewReader(r)
 	return func(r *Result) (err error) {
 		var jl jlexer.Lexer
-		if jl.Data, err = rd.ReadSlice('\n'); err != nil {
+		if jl.Data, err = rd.ReadBytes('\n'); err != nil {
 			return err
 		}
-		(*jsonResult)(r).decode(&jl)
+		(*jsonResult)(r).UnmarshalEasyJSON(&jl)
 		return jl.Error()
 	}
 }
diff --git a/vendor/github.com/tsenart/vegeta/v12/lib/results_easyjson.go b/vendor/github.com/tsenart/vegeta/v12/lib/results_easyjson.go
new file mode 100644
index 000000000..3beb22f7d
--- /dev/null
+++ b/vendor/github.com/tsenart/vegeta/v12/lib/results_easyjson.go
@@ -0,0 +1,223 @@
+// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
+
+package vegeta
+
+import (
+	json "encoding/json"
+	easyjson "github.com/mailru/easyjson"
+	jlexer "github.com/mailru/easyjson/jlexer"
+	jwriter "github.com/mailru/easyjson/jwriter"
+	http "net/http"
+	time "time"
+)
+
+// suppress unused package warning
+var (
+	_ *json.RawMessage
+	_ *jlexer.Lexer
+	_ *jwriter.Writer
+	_ easyjson.Marshaler
+)
+
+func easyjsonBd1621b8DecodeGithubComTsenartVegetaV12Lib(in *jlexer.Lexer, out *jsonResult) {
+	isTopLevel := in.IsStart()
+	if in.IsNull() {
+		if isTopLevel {
+			in.Consumed()
+		}
+		in.Skip()
+		return
+	}
+	in.Delim('{')
+	for !in.IsDelim('}') {
+		key := in.UnsafeString()
+		in.WantColon()
+		if in.IsNull() {
+			in.Skip()
+			in.WantComma()
+			continue
+		}
+		switch key {
+		case "attack":
+			out.Attack = string(in.String())
+		case "seq":
+			out.Seq = uint64(in.Uint64())
+		case "code":
+			out.Code = uint16(in.Uint16())
+		case "timestamp":
+			if data := in.Raw(); in.Ok() {
+				in.AddError((out.Timestamp).UnmarshalJSON(data))
+			}
+		case "latency":
+			out.Latency = time.Duration(in.Int64())
+		case "bytes_out":
+			out.BytesOut = uint64(in.Uint64())
+		case "bytes_in":
+			out.BytesIn = uint64(in.Uint64())
+		case "error":
+			out.Error = string(in.String())
+		case "body":
+			if in.IsNull() {
+				in.Skip()
+				out.Body = nil
+			} else {
+				out.Body = in.Bytes()
+			}
+		case "method":
+			out.Method = string(in.String())
+		case "url":
+			out.URL = string(in.String())
+		case "headers":
+			if in.IsNull() {
+				in.Skip()
+			} else {
+				in.Delim('{')
+				if !in.IsDelim('}') {
+					out.Headers = make(http.Header)
+				} else {
+					out.Headers = nil
+				}
+				for !in.IsDelim('}') {
+					key := string(in.String())
+					in.WantColon()
+					var v2 []string
+					if in.IsNull() {
+						in.Skip()
+						v2 = nil
+					} else {
+						in.Delim('[')
+						if v2 == nil {
+							if !in.IsDelim(']') {
+								v2 = make([]string, 0, 4)
+							} else {
+								v2 = []string{}
+							}
+						} else {
+							v2 = (v2)[:0]
+						}
+						for !in.IsDelim(']') {
+							var v3 string
+							v3 = string(in.String())
+							v2 = append(v2, v3)
+							in.WantComma()
+						}
+						in.Delim(']')
+					}
+					(out.Headers)[key] = v2
+					in.WantComma()
+				}
+				in.Delim('}')
+			}
+		default:
+			in.SkipRecursive()
+		}
+		in.WantComma()
+	}
+	in.Delim('}')
+	if isTopLevel {
+		in.Consumed()
+	}
+}
+func easyjsonBd1621b8EncodeGithubComTsenartVegetaV12Lib(out *jwriter.Writer, in jsonResult) {
+	out.RawByte('{')
+	first := true
+	_ = first
+	{
+		const prefix string = ",\"attack\":"
+		out.RawString(prefix[1:])
+		out.String(string(in.Attack))
+	}
+	{
+		const prefix string = ",\"seq\":"
+		out.RawString(prefix)
+		out.Uint64(uint64(in.Seq))
+	}
+	{
+		const prefix string = ",\"code\":"
+		out.RawString(prefix)
+		out.Uint16(uint16(in.Code))
+	}
+	{
+		const prefix string = ",\"timestamp\":"
+		out.RawString(prefix)
+		out.Raw((in.Timestamp).MarshalJSON())
+	}
+	{
+		const prefix string = ",\"latency\":"
+		out.RawString(prefix)
+		out.Int64(int64(in.Latency))
+	}
+	{
+		const prefix string = ",\"bytes_out\":"
+		out.RawString(prefix)
+		out.Uint64(uint64(in.BytesOut))
+	}
+	{
+		const prefix string = ",\"bytes_in\":"
+		out.RawString(prefix)
+		out.Uint64(uint64(in.BytesIn))
+	}
+	{
+		const prefix string = ",\"error\":"
+		out.RawString(prefix)
+		out.String(string(in.Error))
+	}
+	{
+		const prefix string = ",\"body\":"
+		out.RawString(prefix)
+		out.Base64Bytes(in.Body)
+	}
+	{
+		const prefix string = ",\"method\":"
+		out.RawString(prefix)
+		out.String(string(in.Method))
+	}
+	{
+		const prefix string = ",\"url\":"
+		out.RawString(prefix)
+		out.String(string(in.URL))
+	}
+	{
+		const prefix string = ",\"headers\":"
+		out.RawString(prefix)
+		if in.Headers == nil && (out.Flags&jwriter.NilMapAsEmpty) == 0 {
+			out.RawString(`null`)
+		} else {
+			out.RawByte('{')
+			v6First := true
+			for v6Name, v6Value := range in.Headers {
+				if v6First {
+					v6First = false
+				} else {
+					out.RawByte(',')
+				}
+				out.String(string(v6Name))
+				out.RawByte(':')
+				if v6Value == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
+					out.RawString("null")
+				} else {
+					out.RawByte('[')
+					for v7, v8 := range v6Value {
+						if v7 > 0 {
+							out.RawByte(',')
+						}
+						out.String(string(v8))
+					}
+					out.RawByte(']')
+				}
+			}
+			out.RawByte('}')
+		}
+	}
+	out.RawByte('}')
+}
+
+// MarshalEasyJSON supports easyjson.Marshaler interface
+func (v jsonResult) MarshalEasyJSON(w *jwriter.Writer) {
+	easyjsonBd1621b8EncodeGithubComTsenartVegetaV12Lib(w, v)
+}
+
+// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
+func (v *jsonResult) UnmarshalEasyJSON(l *jlexer.Lexer) {
+	easyjsonBd1621b8DecodeGithubComTsenartVegetaV12Lib(l, v)
+}
diff --git a/vendor/github.com/tsenart/vegeta/v12/lib/results_fuzz.go b/vendor/github.com/tsenart/vegeta/v12/lib/results_fuzz.go
new file mode 100644
index 000000000..fced203da
--- /dev/null
+++ b/vendor/github.com/tsenart/vegeta/v12/lib/results_fuzz.go
@@ -0,0 +1,63 @@
+// +build gofuzz
+
+package vegeta
+
+import (
+	"bytes"
+	"io"
+)
+
+// FuzzResultsFormatDetection tests result list format detection.
+func FuzzResultsFormatDetection(fuzz []byte) int {
+	decoder := DecoderFor(bytes.NewReader(fuzz))
+	if decoder == nil {
+		return 0
+	}
+	ok := readAllResults(decoder)
+	if !ok {
+		return 0
+	}
+	return 1
+}
+
+// FuzzGobDecoder tests decoding a gob format result list.
+func FuzzGobDecoder(fuzz []byte) int {
+	decoder := NewDecoder(bytes.NewReader(fuzz))
+	ok := readAllResults(decoder)
+	if !ok {
+		return 0
+	}
+	return 1
+}
+
+// FuzzCSVDecoder tests decoding a CSV format result list.
+func FuzzCSVDecoder(fuzz []byte) int {
+	decoder := NewCSVDecoder(bytes.NewReader(fuzz))
+	ok := readAllResults(decoder)
+	if !ok {
+		return 0
+	}
+	return 1
+}
+
+// FuzzJSONDecoder tests decoding a JSON format result list.
+func FuzzJSONDecoder(fuzz []byte) int {
+	decoder := NewJSONDecoder(bytes.NewReader(fuzz))
+	ok := readAllResults(decoder)
+	if !ok {
+		return 0
+	}
+	return 1
+}
+
+func readAllResults(decoder Decoder) (ok bool) {
+	for {
+		result := &Result{}
+		err := decoder.Decode(result)
+		if err == io.EOF {
+			return true
+		} else if err != nil {
+			return false
+		}
+	}
+}
diff --git a/vendor/github.com/tsenart/vegeta/lib/target.schema.json b/vendor/github.com/tsenart/vegeta/v12/lib/target.schema.json
similarity index 100%
rename from vendor/github.com/tsenart/vegeta/lib/target.schema.json
rename to vendor/github.com/tsenart/vegeta/v12/lib/target.schema.json
diff --git a/vendor/github.com/tsenart/vegeta/lib/targets.go b/vendor/github.com/tsenart/vegeta/v12/lib/targets.go
similarity index 98%
rename from vendor/github.com/tsenart/vegeta/lib/targets.go
rename to vendor/github.com/tsenart/vegeta/v12/lib/targets.go
index 41415ffb5..2cb5dc2cf 100644
--- a/vendor/github.com/tsenart/vegeta/lib/targets.go
+++ b/vendor/github.com/tsenart/vegeta/v12/lib/targets.go
@@ -270,7 +270,7 @@ func NewHTTPTargeter(src io.Reader, body []byte, hdr http.Header) Targeter {
 			}
 			line = strings.TrimSpace(sc.Text())
 
-			if len(line) != 0 && line[0] != '#'{
+			if len(line) != 0 && line[0] != '#' {
 				break
 			}
 		}
@@ -300,6 +300,8 @@ func NewHTTPTargeter(src io.Reader, body []byte, hdr http.Header) Targeter {
 		for sc.Scan() {
 			if line = strings.TrimSpace(sc.Text()); line == "" {
 				break
+			} else if strings.HasPrefix(line, "#") {
+				continue
 			} else if strings.HasPrefix(line, "@") {
 				if tgt.Body, err = ioutil.ReadFile(line[1:]); err != nil {
 					return fmt.Errorf("bad body: %s", err)
@@ -327,7 +329,7 @@ func NewHTTPTargeter(src io.Reader, body []byte, hdr http.Header) Targeter {
 	}
 }
 
-var httpMethodChecker = regexp.MustCompile("^[A-Z]+\\s")
+var httpMethodChecker = regexp.MustCompile(`^[A-Z]+\s`)
 
 // A line starts with an http method when the first word is uppercase ascii
 // followed by a space.
diff --git a/vendor/github.com/tsenart/vegeta/lib/targets_easyjson.go b/vendor/github.com/tsenart/vegeta/v12/lib/targets_easyjson.go
similarity index 100%
rename from vendor/github.com/tsenart/vegeta/lib/targets_easyjson.go
rename to vendor/github.com/tsenart/vegeta/v12/lib/targets_easyjson.go
diff --git a/vendor/github.com/tsenart/vegeta/v12/lib/targets_fuzz.go b/vendor/github.com/tsenart/vegeta/v12/lib/targets_fuzz.go
new file mode 100644
index 000000000..87a925fe1
--- /dev/null
+++ b/vendor/github.com/tsenart/vegeta/v12/lib/targets_fuzz.go
@@ -0,0 +1,68 @@
+// +build gofuzz
+
+package vegeta
+
+import (
+	"bytes"
+	"net/http"
+)
+
+// FuzzHTTPTargeter tests decoding an HTTP encoded target list.
+func FuzzHTTPTargeter(fuzz []byte) int {
+	headers, body, fuzz, ok := decodeFuzzTargetDefaults(fuzz)
+	if !ok {
+		return -1
+	}
+	targeter := NewHTTPTargeter(
+		bytes.NewReader(fuzz),
+		body,
+		headers,
+	)
+	_, err := ReadAllTargets(targeter)
+	if err != nil {
+		return 0
+	}
+	return 1
+}
+
+// FuzzJSONTargeter tests decoding a JSON encoded target list.
+func FuzzJSONTargeter(fuzz []byte) int {
+	headers, body, fuzz, ok := decodeFuzzTargetDefaults(fuzz)
+	if !ok {
+		return -1
+	}
+	targeter := NewJSONTargeter(
+		bytes.NewReader(fuzz),
+		body,
+		headers,
+	)
+	_, err := ReadAllTargets(targeter)
+	if err != nil {
+		return 0
+	}
+	return 1
+}
+
+func decodeFuzzTargetDefaults(fuzz []byte) (
+	headers http.Header,
+	body []byte,
+	rest []byte,
+	ok bool,
+) {
+	if len(fuzz) < 2 {
+		return
+	}
+	headers = make(map[string][]string)
+	body = []byte{}
+	rest = []byte{}
+	rest, ok = decodeFuzzHeaders(fuzz, headers)
+	if !ok {
+		return
+	}
+	if len(rest) == 0 {
+		ok = true
+		return
+	}
+	body, rest, ok = extractFuzzByteString(rest)
+	return
+}
diff --git a/vendor/github.com/tsenart/vegeta/v12/lib/util_fuzz.go b/vendor/github.com/tsenart/vegeta/v12/lib/util_fuzz.go
new file mode 100644
index 000000000..60c758fea
--- /dev/null
+++ b/vendor/github.com/tsenart/vegeta/v12/lib/util_fuzz.go
@@ -0,0 +1,120 @@
+// +build gofuzz
+
+package vegeta
+
+func decodeFuzzHeaders(fuzz []byte, headers map[string][]string) (
+	rest []byte,
+	ok bool,
+) {
+	rest = fuzz
+	for {
+		if len(rest) == 0 {
+			// Consumed all fuzz
+			ok = true
+			return
+		}
+		if fuzz[0] == 0 {
+			// Headers terminated
+			if len(rest) == 1 {
+				rest = []byte{}
+			} else {
+				rest = rest[1:]
+			}
+			ok = true
+			return
+		}
+		if len(fuzz) == 1 {
+			// Invalid headers encoding
+			return
+		}
+		rest, ok = decodeFuzzHeader(rest[1:], headers)
+		if !ok {
+			return
+		}
+	}
+}
+
+func decodeFuzzHeader(fuzz []byte, headers map[string][]string) (
+	rest []byte,
+	ok bool,
+) {
+	if len(fuzz) == 0 {
+		ok = true
+		return
+	}
+	name, rest, ok := extractFuzzString(fuzz)
+	if !ok {
+		return
+	}
+	value, rest, ok := extractFuzzString(rest)
+	if !ok {
+		return
+	}
+	if header, ok := headers[name]; ok {
+		headers[name] = append(header, value)
+	} else {
+		headers[name] = []string{value}
+	}
+	ok = true
+	return
+}
+
+func extractFuzzString(fuzz []byte) (
+	value string,
+	rest []byte,
+	ok bool,
+) {
+	if len(fuzz) < 2 {
+		// Invalid string encoding
+		return
+	}
+	length := int(fuzz[0])
+	if length == 0 {
+		// Invalid length
+		return
+	}
+	if len(fuzz) < (length + 1) {
+		// Insufficient fuzz
+		return
+	}
+	value = string(fuzz[1 : length+1])
+	if len(fuzz) == (length + 1) {
+		// Consumed all fuzz
+		rest = []byte{}
+	} else {
+		// More fuzz
+		rest = fuzz[length+1:]
+	}
+	ok = true
+	return
+}
+
+func extractFuzzByteString(fuzz []byte) (
+	value []byte,
+	rest []byte,
+	ok bool,
+) {
+	if len(fuzz) < 2 {
+		// Invalid byte string encoding
+		return
+	}
+	length := int(fuzz[0])
+	if length == 0 {
+		// Invalid length
+		return
+	}
+	if len(fuzz) < (length + 1) {
+		// Insufficient fuzz
+		return
+	}
+	value = fuzz[1 : length+1]
+	if len(fuzz) == (length + 1) {
+		// Consumed all fuzz
+		rest = []byte{}
+	} else {
+		// More fuzz
+		rest = fuzz[length+1:]
+	}
+	ok = true
+	return
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 48d8cc398..1504f6ba8 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -206,6 +206,7 @@ github.com/kelseyhightower/envconfig
 # github.com/konsorten/go-windows-terminal-sequences v1.0.3
 github.com/konsorten/go-windows-terminal-sequences
 # github.com/mailru/easyjson v0.7.1-0.20191009090205-6c0755d89d1e
+github.com/mailru/easyjson
 github.com/mailru/easyjson/buffer
 github.com/mailru/easyjson/jlexer
 github.com/mailru/easyjson/jwriter
@@ -260,9 +261,9 @@ github.com/sirupsen/logrus
 # github.com/spf13/pflag v1.0.5
 ## explicit
 github.com/spf13/pflag
-# github.com/tsenart/vegeta v12.7.1-0.20190725001342-b5f4fca92137+incompatible
+# github.com/tsenart/vegeta/v12 v12.8.4
 ## explicit
-github.com/tsenart/vegeta/lib
+github.com/tsenart/vegeta/v12/lib
 # go.opencensus.io v0.22.4
 ## explicit
 go.opencensus.io


Test patch:
diff --git a/configmap/testing/configmap.go b/configmap/testing/configmap.go
index 11021bd94..e434b2422 100644
--- a/configmap/testing/configmap.go
+++ b/configmap/testing/configmap.go
@@ -88,7 +88,7 @@ func ConfigMapsFromTestFile(t *testing.T, name string, allowed ...string) (*core
 	if hasExampleBody && hasExampleChecksumAnnotation {
 		wantChecksum := configmap.Checksum(exampleBody)
 		if gotChecksum != wantChecksum {
-			t.Errorf("example checksum annotation = %s, want %s", gotChecksum, wantChecksum)
+			t.Errorf("example checksum annotation = %s, want %s (you may need to re-run ./hack/update-codegen.sh)", gotChecksum, wantChecksum)
 		}
 	}
 
diff --git a/test/vegeta/pacers/combined_pacer.go b/test/vegeta/pacers/combined_pacer.go
index 7df4e284c..5b0aa3ed4 100644
--- a/test/vegeta/pacers/combined_pacer.go
+++ b/test/vegeta/pacers/combined_pacer.go
@@ -22,7 +22,7 @@ import (
 	"strings"
 	"time"
 
-	vegeta "github.com/tsenart/vegeta/lib"
+	vegeta "github.com/tsenart/vegeta/v12/lib"
 )
 
 // combinedPacer is a Pacer that combines multiple Pacers and runs them sequentially when being used for attack.
@@ -103,6 +103,12 @@ func (cp *combinedPacer) Pace(elapsedTime time.Duration, elapsedHits uint64) (ti
 	return curPacer.Pace(curElapsedTime, curElapsedHits)
 }
 
+func (cp *combinedPacer) Rate(elapsedTime time.Duration) float64 {
+	curPacer := cp.pacers[cp.curPacerIndex]
+	curElapsedTime := time.Duration(uint64(elapsedTime) - cp.prevElapsedTime)
+	return curPacer.Rate(curElapsedTime)
+}
+
 // pacerIndex returns the index of pacer that pacerTimeOffset falls into
 func (cp *combinedPacer) pacerIndex(pacerTimeOffset uint64) uint {
 	i, j := 0, len(cp.stepDurations)
diff --git a/test/vegeta/pacers/combined_pacer_test.go b/test/vegeta/pacers/combined_pacer_test.go
index 48981a938..4fd946e56 100644
--- a/test/vegeta/pacers/combined_pacer_test.go
+++ b/test/vegeta/pacers/combined_pacer_test.go
@@ -20,7 +20,7 @@ import (
 	"testing"
 	"time"
 
-	vegeta "github.com/tsenart/vegeta/lib"
+	vegeta "github.com/tsenart/vegeta/v12/lib"
 )
 
 func TestCombinedPacer(t *testing.T) {
diff --git a/test/vegeta/pacers/steady_up_pacer.go b/test/vegeta/pacers/steady_up_pacer.go
index 18584cc2f..9c8f01413 100644
--- a/test/vegeta/pacers/steady_up_pacer.go
+++ b/test/vegeta/pacers/steady_up_pacer.go
@@ -22,7 +22,7 @@ import (
 	"math"
 	"time"
 
-	vegeta "github.com/tsenart/vegeta/lib"
+	vegeta "github.com/tsenart/vegeta/v12/lib"
 )
 
 // steadyUpPacer is a Pacer that describes attack request rates that increases in the beginning then becomes steady.
@@ -107,6 +107,12 @@ func (sup *steadyUpPacer) Pace(elapsedTime time.Duration, elapsedHits uint64) (t
 	return nextHitIn, false
 }
 
+// Rate returns a Pacer's instantaneous hit rate (per seconds) at the given elapsed
+// duration of an attack.
+func (sup *steadyUpPacer) Rate(elapsedTime time.Duration) float64 {
+	return sup.hitsPerNs(elapsedTime) * 1e9
+}
+
 // hits returns the number of expected hits for this pacer during the given time.
 func (sup *steadyUpPacer) hits(t time.Duration) float64 {
 	// If t is smaller than the upDuration, calculate the hits as a trapezoid.
diff --git a/test/vegeta/pacers/steady_up_pacer_test.go b/test/vegeta/pacers/steady_up_pacer_test.go
index dafb8ae9e..12bef48c2 100644
--- a/test/vegeta/pacers/steady_up_pacer_test.go
+++ b/test/vegeta/pacers/steady_up_pacer_test.go
@@ -20,7 +20,7 @@ import (
 	"testing"
 	"time"
 
-	vegeta "github.com/tsenart/vegeta/lib"
+	vegeta "github.com/tsenart/vegeta/v12/lib"
 )
 
 func TestSteadyUpPacer(t *testing.T) {