Repository: knative/pkg
Pull request #1804 (2020-10-13 17:51:31)

drop the default log url template
this had references to the monitoring bundle which is being removed here: https://github.com/knative/serving/pull/9807

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

This pull-request has been approved by: *<a href="https://github.com/knative/pkg/pull/1804#" title="Author self-approved">dprotaso</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:

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

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":[]} -->
knative-prow-robot: @dprotaso: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-pkg-unit-tests | 55790c68a246b6c26927ec5f9257f93d0c115d8a | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_pkg/1804/pull-knative-pkg-unit-tests/1316074427221282816) | `/test pull-knative-pkg-unit-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_pkg/1804). [Your PR dashboard](https://gubernator.knative.dev/pr/dprotaso).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
dprotaso: interesting logstream flaked 

```go
--- FAIL: TestNamespaceStream (1.00s)
    stream_test.go:87: timed out: log message wasn't received
```
dprotaso: /retest
codecov[bot]: # [Codecov](https://codecov.io/gh/knative/pkg/pull/1804?src=pr&el=h1) Report
> Merging [#1804](https://codecov.io/gh/knative/pkg/pull/1804?src=pr&el=desc) into [master](https://codecov.io/gh/knative/pkg/commit/602db5ee124da2e31baf3eb65c379f4b5893d0ff?el=desc) will **decrease** coverage by `0.02%`.
> The diff coverage is `n/a`.

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

```diff
@@            Coverage Diff             @@
##           master    #1804      +/-   ##
==========================================
- Coverage   68.00%   67.98%   -0.03%     
==========================================
  Files         221      221              
  Lines        9413     9413              
==========================================
- Hits         6401     6399       -2     
- Misses       2691     2692       +1     
- Partials      321      322       +1     
```


| [Impacted Files](https://codecov.io/gh/knative/pkg/pull/1804?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [metrics/config\_observability.go](https://codecov.io/gh/knative/pkg/pull/1804/diff?src=pr&el=tree#diff-bWV0cmljcy9jb25maWdfb2JzZXJ2YWJpbGl0eS5nbw==) | `92.00% <ø> (ø)` | |
| [test/gcs/mock/mock.go](https://codecov.io/gh/knative/pkg/pull/1804/diff?src=pr&el=tree#diff-dGVzdC9nY3MvbW9jay9tb2NrLmdv) | `90.32% <0.00%> (-1.08%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/knative/pkg/pull/1804?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/1804?src=pr&el=footer). Last update [602db5e...55790c6](https://codecov.io/gh/knative/pkg/pull/1804?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

vagababov: This will break stuff.
We use it in the serving.
dprotaso: https://github.com/knative/serving/pull/9807
vagababov: I think the default _is not_ an empty string anymore. Those were the changes we did recently.
Since we added a new boolean toggle so default _always_ has a value now.
dprotaso: > I think the default _is not_ an empty string anymore. Those were the changes we did recently.
> Since we added a new boolean toggle so default _always_ has a value now.

I'm confused - the logging template is a string value
vagababov: Yes it always has a value now.

Initial implementation was:
```
if template == "" { no logging }
else {use the template}
```

And the default value was empty.

Current is
```
if !enable_logging {no logging}
else {use the template}
```

And template is *always* non empty now (since the toggle is false by default).
Test here: https://github.com/knative/serving/blob/master/pkg/metrics/config_test.go verify that.
Also it has to match the example set in config/core/configmaps/observability.yaml.
dprotaso: it looks like the change your made was for the request log template

I'm changing the `DefaultLogURLTemplate`
vagababov: Too many templates! :-)

I think it still has to be non-empty so that we can have a non-empty example.
dprotaso: > I think it still has to be non-empty so that we can have a non-empty example.

I'm going to drop that bit of logic in serving 
vagababov: Which part? Checking in UT that the example value is not empty?
vagababov: Not really. We actually had an issue about that. 
Why do you want to drop the default?
dprotaso: > Why do you want to drop the default?

With the monitoring bundle gone an empty default make sense - but we _would_ want an example that highlights the revision UID substitution 
dprotaso: downstream serving fix is here: https://github.com/knative/serving/pull/9807
vagababov: The problem with example not matching the default, is that just copy-pasting example as actual config is going to break stuff. Which might or might not be expected :)

Anyway, I am not going to fight this a lot :)

/cc @mattmoor 
For more insights :)

Patch:
diff --git a/.github/workflows/knative-downstream.yaml b/.github/workflows/knative-downstream.yaml
index 9dbcb36ca..cf85852dd 100644
--- a/.github/workflows/knative-downstream.yaml
+++ b/.github/workflows/knative-downstream.yaml
@@ -20,211 +20,80 @@ on:
 
 jobs:
 
-  downstream-serving:
-    name: Serving
+  downstream-knative:
+    name: Unit Test
     strategy:
+      fail-fast: false # Keep running if one leg fails.
       matrix:
-        go-version: [1.14.x]
-        platform: [ubuntu-latest]
-
-    runs-on: ${{ matrix.platform }}
-
-    env:
-      GOPATH: ${{ github.workspace }}
-
-    steps:
-
-    - name: Set up Go ${{ matrix.go-version }}
-      uses: actions/setup-go@v2
-      with:
-        go-version: ${{ matrix.go-version }}
-      id: go
-
-    - name: Install Dependencies
-      run: |
-        go get github.com/google/go-licenses
-
-    - name: Checkout Upstream
-      uses: actions/checkout@v2
-      with:
-        path: ./src/knative.dev/${{ github.event.repository.name }}
-
-    - name: Checkout Downstream
-      uses: actions/checkout@v2
-      with:
-        repository: knative/serving
-        path: ./src/knative.dev/serving
-
-    - name: Test Downstream
-      uses: knative-sandbox/downstream-test-go@v1.0.1
-      with:
-        upstream-module: knative.dev/${{ github.event.repository.name }}
-        downstream-module: knative.dev/serving
-
-  downstream-eventing:
-    name: Eventing
-    strategy:
-      matrix:
-        go-version: [1.14.x]
-        platform: [ubuntu-latest]
-
-    runs-on: ${{ matrix.platform }}
-
-    env:
-      GOPATH: ${{ github.workspace }}
-
-    steps:
-
-    - name: Set up Go ${{ matrix.go-version }}
-      uses: actions/setup-go@v2
-      with:
-        go-version: ${{ matrix.go-version }}
-      id: go
-
-    - name: Install Dependencies
-      run: |
-        go get github.com/google/go-licenses
-
-    - name: Checkout Upstream
-      uses: actions/checkout@v2
-      with:
-        path: ./src/knative.dev/${{ github.event.repository.name }}
-
-    - name: Checkout Downstream
-      uses: actions/checkout@v2
-      with:
-        repository: knative/eventing
-        path: ./src/knative.dev/eventing
-
-    - name: Test Downstream
-      uses: knative-sandbox/downstream-test-go@v1.0.1
-      with:
-        upstream-module: knative.dev/${{ github.event.repository.name }}
-        downstream-module: knative.dev/eventing
-
-  downstream-eventing-contrib:
-    name: Eventing Contrib
-    strategy:
-      matrix:
-        go-version: [1.14.x]
-        platform: [ubuntu-latest]
-
-    runs-on: ${{ matrix.platform }}
-
-    env:
-      GOPATH: ${{ github.workspace }}
-
-    steps:
-
-    - name: Set up Go ${{ matrix.go-version }}
-      uses: actions/setup-go@v2
-      with:
-        go-version: ${{ matrix.go-version }}
-      id: go
-
-    - name: Install Dependencies
-      run: |
-        go get github.com/google/go-licenses
-
-    - name: Checkout Upstream
-      uses: actions/checkout@v2
-      with:
-        path: ./src/knative.dev/${{ github.event.repository.name }}
-
-    - name: Checkout Downstream
-      uses: actions/checkout@v2
-      with:
-        repository: knative/eventing-contrib
-        path: ./src/knative.dev/eventing-contrib
-
-    - name: Test Downstream
-      uses: knative-sandbox/downstream-test-go@v1.0.1
-      with:
-        upstream-module: knative.dev/${{ github.event.repository.name }}
-        downstream-module: knative.dev/eventing-contrib
-
-
-  downstream-sample-controller:
-    name: Sample Controller
-    strategy:
-      matrix:
-        go-version: [1.14.x]
-        platform: [ubuntu-latest]
-
-    runs-on: ${{ matrix.platform }}
-
-    env:
-      GOPATH: ${{ github.workspace }}
-
-    steps:
-
-    - name: Set up Go ${{ matrix.go-version }}
-      uses: actions/setup-go@v2
-      with:
-        go-version: ${{ matrix.go-version }}
-      id: go
-
-    - name: Install Dependencies
-      run: |
-        go get github.com/google/go-licenses
-
-    - name: Checkout Upstream
-      uses: actions/checkout@v2
-      with:
-        path: ./src/knative.dev/${{ github.event.repository.name }}
-
-    - name: Checkout Downstream
-      uses: actions/checkout@v2
-      with:
-        repository: knative-sandbox/sample-controller
-        path: ./src/knative.dev/sample-controller
-
-    - name: Test Downstream
-      uses: knative-sandbox/downstream-test-go@v1.0.1
-      with:
-        upstream-module: knative.dev/${{ github.event.repository.name }}
-        downstream-module: knative.dev/sample-controller
-
-
-  downstream-sample-source:
-    name: Sample Source
-    strategy:
-      matrix:
-        go-version: [1.14.x]
-        platform: [ubuntu-latest]
-
-    runs-on: ${{ matrix.platform }}
-
+        include:
+          - repo: serving
+            org: knative
+          - repo: networking
+            org: knative
+          - repo: eventing
+            org: knative
+          - repo: operator
+            org: knative
+          - repo: client
+            org: knative
+          - repo: discovery
+            org: knative-sandbox
+          - repo: eventing-autoscaler-keda
+            org: knative-sandbox
+          - repo: eventing-awssqs
+            org: knative-sandbox
+          - repo: eventing-camel
+            org: knative-sandbox
+          - repo: eventing-ceph
+            org: knative-sandbox
+          - repo: eventing-couchdb
+            org: knative-sandbox
+          - repo: eventing-github
+            org: knative-sandbox
+          - repo: eventing-gitlab
+            org: knative-sandbox
+          - repo: eventing-kafka
+            org: knative-sandbox
+          - repo: eventing-kafka-broker
+            org: knative-sandbox
+          - repo: eventing-natss
+            org: knative-sandbox
+          - repo: eventing-prometheus
+            org: knative-sandbox
+          - repo: eventing-rabbitmq
+            org: knative-sandbox
+          - repo: net-certmanager
+            org: knative-sandbox
+          - repo: net-contour
+            org: knative-sandbox
+          - repo: net-http01
+            org: knative-sandbox
+          - repo: net-istio
+            org: knative-sandbox
+          - repo: net-kourier
+            org: knative-sandbox
+    runs-on: ubuntu-latest
     env:
       GOPATH: ${{ github.workspace }}
-
     steps:
-
-    - name: Set up Go ${{ matrix.go-version }}
+    - name: Set up Go 1.15.x
       uses: actions/setup-go@v2
       with:
-        go-version: ${{ matrix.go-version }}
-      id: go
-
+        go-version: 1.15.x
     - name: Install Dependencies
       run: |
         go get github.com/google/go-licenses
-
     - name: Checkout Upstream
       uses: actions/checkout@v2
       with:
         path: ./src/knative.dev/${{ github.event.repository.name }}
-
     - name: Checkout Downstream
       uses: actions/checkout@v2
       with:
-        repository: knative-sandbox/sample-source
-        path: ./src/knative.dev/sample-source
-
+        repository: ${{ matrix.org }}/${{ matrix.repo }}
+        path: ./src/knative.dev/${{ matrix.repo }}
     - name: Test Downstream
       uses: knative-sandbox/downstream-test-go@v1.0.1
       with:
         upstream-module: knative.dev/${{ github.event.repository.name }}
-        downstream-module: knative.dev/sample-source
-
-
+        downstream-module: knative.dev/${{ matrix.repo }}
diff --git a/configmap/store.go b/configmap/store.go
index ee643311b..8b94ab74b 100644
--- a/configmap/store.go
+++ b/configmap/store.go
@@ -31,7 +31,7 @@ type Logger interface {
 	Errorf(string, ...interface{})
 }
 
-// Constructors is a map for specifying config names to
+// Constructors is a map for specifying configmap names to
 // their function constructors
 //
 // The values of this map must be functions with the definition
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/injection/sharedmain/main.go b/injection/sharedmain/main.go
index 44343ecc9..1f104d88d 100644
--- a/injection/sharedmain/main.go
+++ b/injection/sharedmain/main.go
@@ -253,7 +253,7 @@ func flush(logger *zap.SugaredLogger) {
 
 // ParseAndGetConfigOrDie parses the rest config flags and creates a client or
 // dies by calling log.Fatalf.
-// Deprecated: use injeciton.ParseAndGetRESTConfigOrDie
+// Deprecated: use injection.ParseAndGetRESTConfigOrDie
 func ParseAndGetConfigOrDie() *rest.Config {
 	return injection.ParseAndGetRESTConfigOrDie()
 }
diff --git a/metrics/config_observability.go b/metrics/config_observability.go
index 177b35cb1..ae622c928 100644
--- a/metrics/config_observability.go
+++ b/metrics/config_observability.go
@@ -27,7 +27,8 @@ import (
 
 const (
 	// The following is used to set the default log url template
-	DefaultLogURLTemplate = "http://localhost:8001/api/v1/namespaces/knative-monitoring/services/kibana-logging/proxy/app/kibana#/discover?_a=(query:(match:(kubernetes.labels.knative-dev%2FrevisionUID:(query:'${REVISION_UID}',type:phrase))))"
+	DefaultLogURLTemplate = ""
+
 	// DefaultRequestLogTemplate is the default format for emitting request logs.
 	DefaultRequestLogTemplate = `{"httpRequest": {"requestMethod": "{{.Request.Method}}", "requestUrl": "{{js .Request.RequestURI}}", "requestSize": "{{.Request.ContentLength}}", "status": {{.Response.Code}}, "responseSize": "{{.Response.Size}}", "userAgent": "{{js .Request.UserAgent}}", "remoteIp": "{{js .Request.RemoteAddr}}", "serverIp": "{{.Revision.PodIP}}", "referer": "{{js .Request.Referer}}", "latency": "{{.Response.Latency}}s", "protocol": "{{.Request.Proto}}"}, "traceId": "{{index .Request.Header "X-B3-Traceid"}}"}`
 
@@ -42,6 +43,9 @@ const (
 
 	// EnableReqLogKey is the CM key to enable request log.
 	EnableReqLogKey = "logging.enable-request-log"
+
+	// EnableProbeReqLogKey is the CM key to enable request logs for probe requests.
+	EnableProbeReqLogKey = "logging.enable-probe-request-log"
 )
 
 // ObservabilityConfig contains the configuration defined in the observability ConfigMap.
@@ -94,7 +98,7 @@ func NewObservabilityConfigFromConfigMap(configMap *corev1.ConfigMap) (*Observab
 		cm.AsString("logging.revision-url-template", &oc.LoggingURLTemplate),
 		cm.AsString(ReqLogTemplateKey, &oc.RequestLogTemplate),
 		cm.AsBool(EnableReqLogKey, &oc.EnableRequestLog),
-		cm.AsBool("logging.enable-probe-request-log", &oc.EnableProbeRequestLog),
+		cm.AsBool(EnableProbeReqLogKey, &oc.EnableProbeRequestLog),
 		cm.AsString("metrics.request-metrics-backend-destination", &oc.RequestMetricsBackend),
 		cm.AsBool("profiling.enable", &oc.EnableProfiling),
 		cm.AsString("metrics.opencensus-address", &oc.MetricsCollectorAddress),
diff --git a/metrics/stackdriver_exporter.go b/metrics/stackdriver_exporter.go
index e3bebe187..101d96bb2 100644
--- a/metrics/stackdriver_exporter.go
+++ b/metrics/stackdriver_exporter.go
@@ -69,7 +69,7 @@ var (
 	newStackdriverExporterFunc func(sd.Options) (view.Exporter, error)
 
 	// kubeclient is the in-cluster Kubernetes kubeclient, which is lazy-initialized on first use.
-	kubeclient *kubernetes.Clientset
+	kubeclient kubernetes.Interface
 	// initClientOnce is the lazy initializer for kubeclient.
 	initClientOnce sync.Once
 	// kubeclientInitErr capture an error during initClientOnce
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/tracing/config/tracing.go b/tracing/config/tracing.go
index 84128b440..2a9305faf 100644
--- a/tracing/config/tracing.go
+++ b/tracing/config/tracing.go
@@ -67,7 +67,8 @@ func (cfg *Config) Equals(other *Config) bool {
 	return reflect.DeepEqual(other, cfg)
 }
 
-func defaultConfig() *Config {
+// NoopConfig returns a new noop config
+func NoopConfig() *Config {
 	return &Config{
 		Backend:    None,
 		Debug:      false,
@@ -77,7 +78,7 @@ func defaultConfig() *Config {
 
 // NewTracingConfigFromMap returns a Config given a map corresponding to a ConfigMap
 func NewTracingConfigFromMap(cfgMap map[string]string) (*Config, error) {
-	tc := defaultConfig()
+	tc := NoopConfig()
 
 	if backend, ok := cfgMap[backendKey]; ok {
 		switch bt := BackendType(backend); bt {
@@ -127,6 +128,9 @@ func NewTracingConfigFromMap(cfgMap map[string]string) (*Config, error) {
 
 // NewTracingConfigFromConfigMap returns a Config for the given configmap
 func NewTracingConfigFromConfigMap(config *corev1.ConfigMap) (*Config, error) {
+	if config == nil {
+		return NewTracingConfigFromMap(nil)
+	}
 	return NewTracingConfigFromMap(config.Data)
 }
 
@@ -135,17 +139,17 @@ func NewTracingConfigFromConfigMap(config *corev1.ConfigMap) (*Config, error) {
 func JsonToTracingConfig(jsonCfg string) (*Config, error) { //nolint:stylecheck for backcompat.
 
 	if jsonCfg == "" {
-		return defaultConfig(), errors.New("empty json tracing config")
+		return NoopConfig(), errors.New("empty json tracing config")
 	}
 
 	var configMap map[string]string
 	if err := json.Unmarshal([]byte(jsonCfg), &configMap); err != nil {
-		return defaultConfig(), err
+		return NoopConfig(), err
 	}
 
 	cfg, err := NewTracingConfigFromMap(configMap)
 	if err != nil {
-		return defaultConfig(), nil
+		return NoopConfig(), nil
 	}
 	return cfg, nil
 }
diff --git a/tracing/setup.go b/tracing/setup.go
new file mode 100644
index 000000000..503e424e3
--- /dev/null
+++ b/tracing/setup.go
@@ -0,0 +1,68 @@
+/*
+Copyright 2020 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tracing
+
+import (
+	"fmt"
+
+	"go.uber.org/zap"
+
+	"knative.dev/pkg/configmap"
+	"knative.dev/pkg/tracing/config"
+)
+
+// SetupStaticPublishing sets up trace publishing for the process. Note that other
+// pieces still need to generate the traces, this just ensures that if generated, they are collected
+// appropriately. This is normally done by using tracing.HTTPSpanMiddleware as a middleware HTTP
+// handler. The configuration will not be dynamically updated.
+func SetupStaticPublishing(logger *zap.SugaredLogger, serviceName string, cfg *config.Config) error {
+	oct := NewOpenCensusTracer(WithExporter(serviceName, logger))
+	if err := oct.ApplyConfig(cfg); err != nil {
+		return fmt.Errorf("unable to set OpenCensusTracing config: %w", err)
+	}
+	return nil
+}
+
+// SetupDynamicPublishing sets up trace publishing for the process, by watching a
+// ConfigMap for the configuration. Note that other pieces still need to generate the traces, this
+// just ensures that if generated, they are collected appropriately. This is normally done by using
+// tracing.HTTPSpanMiddleware as a middleware HTTP handler. The configuration will be dynamically
+// updated when the ConfigMap is updated.
+func SetupDynamicPublishing(logger *zap.SugaredLogger, configMapWatcher *configmap.InformedWatcher, serviceName, tracingConfigName string) error {
+	oct := NewOpenCensusTracer(WithExporter(serviceName, logger))
+
+	tracerUpdater := func(name string, value interface{}) {
+		if name == tracingConfigName {
+			cfg := value.(*config.Config)
+			logger.Debugw("Updating tracing config", zap.Any("cfg", cfg))
+			if err := oct.ApplyConfig(cfg); err != nil {
+				logger.Errorw("Unable to apply open census tracer config", zap.Error(err))
+			}
+		}
+	}
+
+	// Set up our config store.
+	configStore := configmap.NewUntypedStore(
+		"config-tracing-store",
+		logger,
+		configmap.Constructors{
+			"config-tracing": config.NewTracingConfigFromConfigMap,
+		},
+		tracerUpdater)
+	configStore.WatchConfigs(configMapWatcher)
+	return nil
+}
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/metrics/config_observability_test.go b/metrics/config_observability_test.go
index 075db67b9..64dac6891 100644
--- a/metrics/config_observability_test.go
+++ b/metrics/config_observability_test.go
@@ -44,7 +44,7 @@ func TestObservabilityConfiguration(t *testing.T) {
 			RequestMetricsBackend:  "stackdriver",
 		},
 		data: map[string]string{
-			"logging.enable-probe-request-log":            "true",
+			EnableProbeReqLogKey:                          "true",
 			"logging.enable-var-log-collection":           "true",
 			ReqLogTemplateKey:                             `{"requestMethod": "{{.Request.Method}}"}`,
 			"logging.revision-url-template":               "https://logging.io",
@@ -64,7 +64,7 @@ func TestObservabilityConfiguration(t *testing.T) {
 	}, {
 		name: "observability configuration with request log set and template default",
 		data: map[string]string{
-			"logging.enable-probe-request-log":            "true",
+			EnableProbeReqLogKey:                          "true",
 			EnableReqLogKey:                               "true",
 			"logging.enable-var-log-collection":           "true",
 			"logging.revision-url-template":               "https://logging.io",
@@ -90,7 +90,7 @@ func TestObservabilityConfiguration(t *testing.T) {
 			RequestMetricsBackend:  "stackdriver",
 		},
 		data: map[string]string{
-			"logging.enable-probe-request-log":            "true",
+			EnableProbeReqLogKey:                          "true",
 			EnableReqLogKey:                               "false",
 			"logging.enable-var-log-collection":           "true",
 			ReqLogTemplateKey:                             "",
@@ -102,7 +102,7 @@ func TestObservabilityConfiguration(t *testing.T) {
 		name:    "observability configuration with request log set and template not set",
 		wantErr: true,
 		data: map[string]string{
-			"logging.enable-probe-request-log":            "true",
+			EnableProbeReqLogKey:                          "true",
 			EnableReqLogKey:                               "true",
 			"logging.enable-var-log-collection":           "true",
 			ReqLogTemplateKey:                             "",
@@ -121,7 +121,7 @@ func TestObservabilityConfiguration(t *testing.T) {
 			RequestMetricsBackend:  "stackdriver",
 		},
 		data: map[string]string{
-			"logging.enable-probe-request-log":            "true",
+			EnableProbeReqLogKey:                          "true",
 			"logging.enable-var-log-collection":           "true",
 			ReqLogTemplateKey:                             `{"requestMethod": "{{.Request.Method}}"}`,
 			"logging.revision-url-template":               "https://logging.io",
diff --git a/test/clients.go b/test/clients.go
index bd1e58a6b..dd5c6e301 100644
--- a/test/clients.go
+++ b/test/clients.go
@@ -34,19 +34,21 @@ import (
 )
 
 // KubeClient holds instances of interfaces for making requests to kubernetes client.
+// Deprecated: use a kubeclient and the helper methods in test.
 type KubeClient struct {
-	Kube *kubernetes.Clientset
+	kubernetes.Interface
 }
 
 // NewSpoofingClient returns a spoofing client to make requests
 func NewSpoofingClient(ctx context.Context, client *KubeClient, logf logging.FormatLogger,
 	domain string, resolvable bool, opts ...spoof.TransportOption) (*spoof.SpoofingClient, error) {
-	return spoof.New(ctx, client.Kube, logf, domain, resolvable, Flags.IngressEndpoint,
+	return spoof.New(ctx, client, logf, domain, resolvable, Flags.IngressEndpoint,
 		Flags.SpoofRequestInterval, Flags.SpoofRequestTimeout, opts...)
 }
 
 // NewKubeClient instantiates and returns several clientsets required for making request to the
 // kube client specified by the combination of clusterName and configPath. Clients can make requests within namespace.
+// Deprecated: use a kubeclient and the helper methods in test.
 func NewKubeClient(configPath string, clusterName string) (*KubeClient, error) {
 	cfg, err := BuildClientConfig(configPath, clusterName)
 	if err != nil {
@@ -57,7 +59,7 @@ func NewKubeClient(configPath string, clusterName string) (*KubeClient, error) {
 	if err != nil {
 		return nil, err
 	}
-	return &KubeClient{Kube: k}, nil
+	return &KubeClient{Interface: k}, nil
 }
 
 // BuildClientConfig builds the client config specified by the config path and the cluster name
@@ -73,8 +75,14 @@ func BuildClientConfig(kubeConfigPath string, clusterName string) (*rest.Config,
 }
 
 // UpdateConfigMap updates the config map for specified @name with values
+// Deprecated: use UpdateConfigMap
 func (client *KubeClient) UpdateConfigMap(ctx context.Context, name string, configName string, values map[string]string) error {
-	configMap, err := client.GetConfigMap(name).Get(ctx, configName, metav1.GetOptions{})
+	return UpdateConfigMap(ctx, client, name, configName, values)
+}
+
+// UpdateConfigMap updates the config map for specified @name with values
+func UpdateConfigMap(ctx context.Context, client kubernetes.Interface, name string, configName string, values map[string]string) error {
+	configMap, err := client.CoreV1().ConfigMaps(name).Get(ctx, configName, metav1.GetOptions{})
 	if err != nil {
 		return err
 	}
@@ -83,24 +91,37 @@ func (client *KubeClient) UpdateConfigMap(ctx context.Context, name string, conf
 		configMap.Data[key] = value
 	}
 
-	_, err = client.GetConfigMap(name).Update(ctx, configMap, metav1.UpdateOptions{})
+	_, err = client.CoreV1().ConfigMaps(name).Update(ctx, configMap, metav1.UpdateOptions{})
 	return err
 }
 
 // GetConfigMap gets the knative serving config map.
+// Deprecated: use kubeclient.CoreV1().ConfigMaps(name)
 func (client *KubeClient) GetConfigMap(name string) k8styped.ConfigMapInterface {
-	return client.Kube.CoreV1().ConfigMaps(name)
+	return client.CoreV1().ConfigMaps(name)
 }
 
 // CreatePod will create a Pod
+// Deprecated: use CreatePod
 func (client *KubeClient) CreatePod(ctx context.Context, pod *corev1.Pod) (*corev1.Pod, error) {
-	pods := client.Kube.CoreV1().Pods(pod.GetNamespace())
+	return CreatePod(ctx, client, pod)
+}
+
+// CreatePod will create a Pod
+func CreatePod(ctx context.Context, client kubernetes.Interface, pod *corev1.Pod) (*corev1.Pod, error) {
+	pods := client.CoreV1().Pods(pod.GetNamespace())
 	return pods.Create(ctx, pod, metav1.CreateOptions{})
 }
 
 // PodLogs returns Pod logs for given Pod and Container in the namespace
+// Deprecated: use PodLogs
 func (client *KubeClient) PodLogs(ctx context.Context, podName, containerName, namespace string) ([]byte, error) {
-	pods := client.Kube.CoreV1().Pods(namespace)
+	return PodLogs(ctx, client, podName, containerName, namespace)
+}
+
+// PodLogs returns Pod logs for given Pod and Container in the namespace
+func PodLogs(ctx context.Context, client kubernetes.Interface, podName, containerName, namespace string) ([]byte, error) {
+	pods := client.CoreV1().Pods(namespace)
 	podList, err := pods.List(ctx, metav1.ListOptions{})
 	if err != nil {
 		return nil, err
diff --git a/test/ha/ha.go b/test/ha/ha.go
index 0b51a5c8b..4007085ef 100644
--- a/test/ha/ha.go
+++ b/test/ha/ha.go
@@ -51,7 +51,7 @@ func extractDeployment(pod string) string {
 // GetLeaders collects all of the leader pods from the specified deployment.
 // GetLeaders will return duplicate pods by design.
 func GetLeaders(ctx context.Context, t *testing.T, client *test.KubeClient, deploymentName, namespace string) ([]string, error) {
-	leases, err := client.Kube.CoordinationV1().Leases(namespace).List(ctx, metav1.ListOptions{})
+	leases, err := client.CoordinationV1().Leases(namespace).List(ctx, metav1.ListOptions{})
 	if err != nil {
 		return nil, fmt.Errorf("error getting leases for deployment %q: %w", deploymentName, err)
 	}
@@ -104,7 +104,7 @@ func WaitForNewLeader(ctx context.Context, client *test.KubeClient, lease, names
 	defer span.End()
 	var leader string
 	err := wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
-		lease, err := client.Kube.CoordinationV1().Leases(namespace).Get(ctx, lease, metav1.GetOptions{})
+		lease, err := client.CoordinationV1().Leases(namespace).Get(ctx, lease, metav1.GetOptions{})
 		if err != nil {
 			return false, fmt.Errorf("error getting lease %s: %w", lease, err)
 		}
diff --git a/test/ingress/ingress.go b/test/ingress/ingress.go
index 6b5094151..fc9f9a19c 100644
--- a/test/ingress/ingress.go
+++ b/test/ingress/ingress.go
@@ -38,7 +38,7 @@ const (
 //    should connect.  This is used when the resolution to address goes through some
 //    sort of port-mapping, e.g. Kubernetes node ports.
 // err - an error when address/portMap cannot be established.
-func GetIngressEndpoint(ctx context.Context, kubeClientset *kubernetes.Clientset, endpointOverride string) (address string, portMap func(string) string, err error) {
+func GetIngressEndpoint(ctx context.Context, kubeClientset kubernetes.Interface, endpointOverride string) (address string, portMap func(string) string, err error) {
 	ingressName := istioIngressName
 	if gatewayOverride := os.Getenv("GATEWAY_OVERRIDE"); gatewayOverride != "" {
 		ingressName = gatewayOverride
diff --git a/test/kube_checks.go b/test/kube_checks.go
index cf804a9b1..d6bcd648a 100644
--- a/test/kube_checks.go
+++ b/test/kube_checks.go
@@ -25,6 +25,8 @@ import (
 	"strings"
 	"time"
 
+	"k8s.io/client-go/kubernetes"
+
 	"github.com/google/go-cmp/cmp"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
@@ -45,8 +47,8 @@ const (
 // from client every interval until inState returns `true` indicating it
 // is done, returns an error or timeout. desc will be used to name the metric
 // that is emitted to track how long it took for name to get into the state checked by inState.
-func WaitForDeploymentState(ctx context.Context, client *KubeClient, name string, inState func(d *appsv1.Deployment) (bool, error), desc string, namespace string, timeout time.Duration) error {
-	d := client.Kube.AppsV1().Deployments(namespace)
+func WaitForDeploymentState(ctx context.Context, client kubernetes.Interface, name string, inState func(d *appsv1.Deployment) (bool, error), desc string, namespace string, timeout time.Duration) error {
+	d := client.AppsV1().Deployments(namespace)
 	span := logging.GetEmitableSpan(ctx, fmt.Sprintf("WaitForDeploymentState/%s/%s", name, desc))
 	defer span.End()
 
@@ -63,8 +65,8 @@ func WaitForDeploymentState(ctx context.Context, client *KubeClient, name string
 // from client every interval until inState returns `true` indicating it
 // is done, returns an error or timeout. desc will be used to name the metric
 // that is emitted to track how long it took to get into the state checked by inState.
-func WaitForPodListState(ctx context.Context, client *KubeClient, inState func(p *corev1.PodList) (bool, error), desc string, namespace string) error {
-	p := client.Kube.CoreV1().Pods(namespace)
+func WaitForPodListState(ctx context.Context, client kubernetes.Interface, inState func(p *corev1.PodList) (bool, error), desc string, namespace string) error {
+	p := client.CoreV1().Pods(namespace)
 	span := logging.GetEmitableSpan(ctx, "WaitForPodListState/"+desc)
 	defer span.End()
 
@@ -81,8 +83,8 @@ func WaitForPodListState(ctx context.Context, client *KubeClient, inState func(p
 // from client every interval until inState returns `true` indicating it
 // is done, returns an error or timeout. desc will be used to name the metric
 // that is emitted to track how long it took to get into the state checked by inState.
-func WaitForPodState(ctx context.Context, client *KubeClient, inState func(p *corev1.Pod) (bool, error), name string, namespace string) error {
-	p := client.Kube.CoreV1().Pods(namespace)
+func WaitForPodState(ctx context.Context, client kubernetes.Interface, inState func(p *corev1.Pod) (bool, error), name string, namespace string) error {
+	p := client.CoreV1().Pods(namespace)
 	span := logging.GetEmitableSpan(ctx, "WaitForPodState/"+name)
 	defer span.End()
 
@@ -96,7 +98,7 @@ func WaitForPodState(ctx context.Context, client *KubeClient, inState func(p *co
 }
 
 // WaitForPodDeleted waits for the given pod to disappear from the given namespace.
-func WaitForPodDeleted(ctx context.Context, client *KubeClient, name, namespace string) error {
+func WaitForPodDeleted(ctx context.Context, client kubernetes.Interface, name, namespace string) error {
 	if err := WaitForPodState(ctx, client, func(p *corev1.Pod) (bool, error) {
 		// Always return false. We're oly interested in the error which indicates pod deletion or timeout.
 		return false, nil
@@ -110,8 +112,8 @@ func WaitForPodDeleted(ctx context.Context, client *KubeClient, name, namespace
 
 // WaitForServiceHasAtLeastOneEndpoint polls the status of the specified Service
 // from client every interval until number of service endpoints = numOfEndpoints
-func WaitForServiceEndpoints(ctx context.Context, client *KubeClient, svcName string, svcNamespace string, numOfEndpoints int) error {
-	endpointsService := client.Kube.CoreV1().Endpoints(svcNamespace)
+func WaitForServiceEndpoints(ctx context.Context, client kubernetes.Interface, svcName string, svcNamespace string, numOfEndpoints int) error {
+	endpointsService := client.CoreV1().Endpoints(svcNamespace)
 	span := logging.GetEmitableSpan(ctx, "WaitForServiceHasAtLeastOneEndpoint/"+svcName)
 	defer span.End()
 
@@ -137,8 +139,8 @@ func countEndpointsNum(e *corev1.Endpoints) int {
 }
 
 // GetEndpointAddresses returns addresses of endpoints for the given service.
-func GetEndpointAddresses(ctx context.Context, client *KubeClient, svcName, svcNamespace string) ([]string, error) {
-	endpoints, err := client.Kube.CoreV1().Endpoints(svcNamespace).Get(ctx, svcName, metav1.GetOptions{})
+func GetEndpointAddresses(ctx context.Context, client kubernetes.Interface, svcName, svcNamespace string) ([]string, error) {
+	endpoints, err := client.CoreV1().Endpoints(svcNamespace).Get(ctx, svcName, metav1.GetOptions{})
 	if err != nil || countEndpointsNum(endpoints) == 0 {
 		return nil, fmt.Errorf("no endpoints or error: %w", err)
 	}
@@ -152,7 +154,7 @@ func GetEndpointAddresses(ctx context.Context, client *KubeClient, svcName, svcN
 }
 
 // WaitForChangedEndpoints waits until the endpoints for the given service differ from origEndpoints.
-func WaitForChangedEndpoints(ctx context.Context, client *KubeClient, svcName, svcNamespace string, origEndpoints []string) error {
+func WaitForChangedEndpoints(ctx context.Context, client kubernetes.Interface, svcName, svcNamespace string, origEndpoints []string) error {
 	return wait.PollImmediate(1*time.Second, 2*time.Minute, func() (bool, error) {
 		newEndpoints, err := GetEndpointAddresses(ctx, client, svcName, svcNamespace)
 		return !cmp.Equal(origEndpoints, newEndpoints), err
@@ -160,8 +162,8 @@ func WaitForChangedEndpoints(ctx context.Context, client *KubeClient, svcName, s
 }
 
 // GetConfigMap gets the configmaps for a given namespace
-func GetConfigMap(client *KubeClient, namespace string) k8styped.ConfigMapInterface {
-	return client.Kube.CoreV1().ConfigMaps(namespace)
+func GetConfigMap(client kubernetes.Interface, namespace string) k8styped.ConfigMapInterface {
+	return client.CoreV1().ConfigMaps(namespace)
 }
 
 // DeploymentScaledToZeroFunc returns a func that evaluates if a deployment has scaled to 0 pods
@@ -184,13 +186,13 @@ func WaitForLogContent(ctx context.Context, client *KubeClient, podName, contain
 }
 
 // WaitForAllPodsRunning waits for all the pods to be in running state
-func WaitForAllPodsRunning(ctx context.Context, client *KubeClient, namespace string) error {
+func WaitForAllPodsRunning(ctx context.Context, client kubernetes.Interface, namespace string) error {
 	return WaitForPodListState(ctx, client, podsRunning, "PodsAreRunning", namespace)
 }
 
 // WaitForPodRunning waits for the given pod to be in running state
-func WaitForPodRunning(ctx context.Context, client *KubeClient, name string, namespace string) error {
-	p := client.Kube.CoreV1().Pods(namespace)
+func WaitForPodRunning(ctx context.Context, client kubernetes.Interface, name string, namespace string) error {
+	p := client.CoreV1().Pods(namespace)
 	return wait.PollImmediate(interval, podTimeout, func() (bool, error) {
 		p, err := p.Get(ctx, name, metav1.GetOptions{})
 		if err != nil {
@@ -217,7 +219,7 @@ func podRunning(pod *corev1.Pod) bool {
 }
 
 // WaitForDeploymentScale waits until the given deployment has the expected scale.
-func WaitForDeploymentScale(ctx context.Context, client *KubeClient, name, namespace string, scale int) error {
+func WaitForDeploymentScale(ctx context.Context, client kubernetes.Interface, name, namespace string, scale int) error {
 	return WaitForDeploymentState(
 		ctx,
 		client,
diff --git a/test/logstream/interface.go b/test/logstream/interface.go
index 1c00cdfb5..88418fb7a 100644
--- a/test/logstream/interface.go
+++ b/test/logstream/interface.go
@@ -44,7 +44,7 @@ func Start(t test.TLegacy) Canceler {
 				return
 			}
 
-			stream = &shim{logstreamv2.FromNamespace(context.TODO(), kc.Kube, ns)}
+			stream = &shim{logstreamv2.FromNamespace(context.TODO(), kc, ns)}
 
 		} else {
 			// Otherwise set up a null stream.
diff --git a/test/logstream/v2/stream_test.go b/test/logstream/v2/stream_test.go
index 10d78fad0..515d70cbe 100644
--- a/test/logstream/v2/stream_test.go
+++ b/test/logstream/v2/stream_test.go
@@ -22,7 +22,6 @@ import (
 	"fmt"
 	"io/ioutil"
 	"net/http"
-	"sync"
 	"testing"
 	"time"
 
@@ -60,6 +59,11 @@ var readyStatus = corev1.PodStatus{
 func TestNamespaceStream(t *testing.T) {
 	f := newK8sFake(fake.NewSimpleClientset())
 
+	// We populate this buffer first to avoid races and the need to synchronize
+	if _, err := f.logBuffer.WriteString("test\n"); err != nil {
+		t.Fatal("WriteString()=", err)
+	}
+
 	logFuncInvoked := make(chan string)
 	logFunc := func(format string, args ...interface{}) {
 		close(logFuncInvoked)
@@ -78,10 +82,6 @@ func TestNamespaceStream(t *testing.T) {
 		t.Fatal("UpdatePod()=", err)
 	}
 
-	if _, err := f.logBuffer.WriteString("test\n"); err != nil {
-		t.Fatal("WriteString()=", err)
-	}
-
 	select {
 	case <-time.After(time.Second):
 		t.Error("timed out: log message wasn't received")
@@ -93,7 +93,7 @@ func newK8sFake(c *fake.Clientset) *fakeclient {
 	return &fakeclient{
 		Clientset:  c,
 		FakeCoreV1: &fakecorev1.FakeCoreV1{Fake: &c.Fake},
-		logBuffer:  new(buffer),
+		logBuffer:  new(bytes.Buffer),
 	}
 }
 
@@ -101,7 +101,7 @@ type fakeclient struct {
 	*fake.Clientset
 	*fakecorev1.FakeCoreV1
 
-	logBuffer *buffer
+	logBuffer *bytes.Buffer
 }
 
 type fakePods struct {
@@ -135,21 +135,3 @@ func (f *fakePods) GetLogs(name string, opts *corev1.PodLogOptions) *restclient.
 	}
 	return fakeClient.Request()
 }
-
-// use a synchronous buffer
-type buffer struct {
-	bytes.Buffer
-	sync.Mutex
-}
-
-func (b *buffer) WriteString(s string) (n int, err error) {
-	b.Lock()
-	defer b.Unlock()
-	return b.Buffer.WriteString(s)
-}
-
-func (b *buffer) Read(p []byte) (n int, err error) {
-	b.Lock()
-	defer b.Unlock()
-	return b.Buffer.Read(p)
-}
diff --git a/test/monitoring/doc.go b/test/monitoring/doc.go
index b15d7c726..662f3c7e4 100644
--- a/test/monitoring/doc.go
+++ b/test/monitoring/doc.go
@@ -21,7 +21,7 @@ This package exposes following methods:
 
 	CheckPortAvailability(port int) error
 		Checks if the given port is available
-	GetPods(kubeClientset *kubernetes.Clientset, app string) (*v1.PodList, error)
+	GetPods(kubeClientset kubernetes.Interface, app string) (*v1.PodList, error)
 		Gets the list of pods that satisfy the lable selector app=<app>
 	Cleanup(pid int) error
 		Kill the current port forwarding process running in the background
diff --git a/test/monitoring/monitoring.go b/test/monitoring/monitoring.go
index 1a6bbc19e..8b8e33dbb 100644
--- a/test/monitoring/monitoring.go
+++ b/test/monitoring/monitoring.go
@@ -43,7 +43,7 @@ func CheckPortAvailability(port int) error {
 
 // GetPods retrieves the current existing podlist for the app in monitoring namespace
 // This uses app=<app> as labelselector for selecting pods
-func GetPods(ctx context.Context, kubeClientset *kubernetes.Clientset, app, namespace string) (*v1.PodList, error) {
+func GetPods(ctx context.Context, kubeClientset kubernetes.Interface, app, namespace string) (*v1.PodList, error) {
 	pods, err := kubeClientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: "app=" + app})
 	if err == nil && len(pods.Items) == 0 {
 		err = fmt.Errorf("pod %s not found on the cluster. Ensure monitoring is switched on for your Knative Setup", app)
diff --git a/test/prometheus/prometheus.go b/test/prometheus/prometheus.go
index 6d7dcaa97..ff7b10136 100644
--- a/test/prometheus/prometheus.go
+++ b/test/prometheus/prometheus.go
@@ -51,7 +51,7 @@ type PromProxy struct {
 }
 
 // Setup performs a port forwarding for app prometheus-test in given namespace
-func (p *PromProxy) Setup(ctx context.Context, kubeClientset *kubernetes.Clientset, logf logging.FormatLogger) {
+func (p *PromProxy) Setup(ctx context.Context, kubeClientset kubernetes.Interface, logf logging.FormatLogger) {
 	setupOnce.Do(func() {
 		if err := monitoring.CheckPortAvailability(prometheusPort); err != nil {
 			logf("Prometheus port not available: %v", err)
diff --git a/test/spoof/spoof.go b/test/spoof/spoof.go
index 06194090f..47d522564 100644
--- a/test/spoof/spoof.go
+++ b/test/spoof/spoof.go
@@ -86,7 +86,7 @@ type TransportOption func(transport *http.Transport) *http.Transport
 // If that's a problem, see test/request.go#WaitForEndpointState for oneshot spoofing.
 func New(
 	ctx context.Context,
-	kubeClientset *kubernetes.Clientset,
+	kubeClientset kubernetes.Interface,
 	logf logging.FormatLogger,
 	domain string,
 	resolvable bool,
@@ -133,7 +133,7 @@ func New(
 
 // ResolveEndpoint resolves the endpoint address considering whether the domain is resolvable and taking into
 // account whether the user overrode the endpoint address externally
-func ResolveEndpoint(ctx context.Context, kubeClientset *kubernetes.Clientset, domain string, resolvable bool, endpointOverride string) (string, func(string) string, error) {
+func ResolveEndpoint(ctx context.Context, kubeClientset kubernetes.Interface, domain string, resolvable bool, endpointOverride string) (string, func(string) string, error) {
 	id := func(in string) string { return in }
 	// If the domain is resolvable, it can be used directly
 	if resolvable {
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) {
diff --git a/test/zipkin/doc.go b/test/zipkin/doc.go
index 0ce3c54fd..c4464f459 100644
--- a/test/zipkin/doc.go
+++ b/test/zipkin/doc.go
@@ -21,7 +21,7 @@ i.e HTTP request that have HTTP status between 500 to 600.
 
 This package exposes following methods:
 
-	SetupZipkinTracing(*kubernetes.Clientset) error
+	SetupZipkinTracing(kubernetes.Interface) error
 		SetupZipkinTracing sets up zipkin tracing by setting up port-forwarding from
 		localhost to zipkin pod on the cluster. On successful setup this method sets
 		an internal flag zipkinTracingEnabled to true.
@@ -33,7 +33,7 @@ This package exposes following methods:
 
 A general flow for a Test Suite to use Zipkin Tracing support is as follows:
 
-		1. Call SetupZipkinTracing(*kubernetes.Clientset) in TestMain.
+		1. Call SetupZipkinTracing(kubernetes.Interface) in TestMain.
 		2. Use SpoofingClient to make HTTP requests.
 		3. Call CleanupZipkinTracingSetup on cleanup after tests are executed.
 
diff --git a/test/zipkin/util.go b/test/zipkin/util.go
index 9ac2f4249..9a270fe9f 100644
--- a/test/zipkin/util.go
+++ b/test/zipkin/util.go
@@ -72,7 +72,7 @@ var (
 
 // SetupZipkinTracingFromConfigTracing setups zipkin tracing like SetupZipkinTracing but retrieving the zipkin configuration
 // from config-tracing config map
-func SetupZipkinTracingFromConfigTracing(ctx context.Context, kubeClientset *kubernetes.Clientset, logf logging.FormatLogger, configMapNamespace string) error {
+func SetupZipkinTracingFromConfigTracing(ctx context.Context, kubeClientset kubernetes.Interface, logf logging.FormatLogger, configMapNamespace string) error {
 	cm, err := kubeClientset.CoreV1().ConfigMaps(configMapNamespace).Get(ctx, "config-tracing", metav1.GetOptions{})
 	if err != nil {
 		return fmt.Errorf("error while retrieving config-tracing config map: %w", err)
@@ -103,7 +103,7 @@ func SetupZipkinTracingFromConfigTracing(ctx context.Context, kubeClientset *kub
 }
 
 // SetupZipkinTracingFromConfigTracingOrFail is same as SetupZipkinTracingFromConfigTracing, but fails the test if an error happens
-func SetupZipkinTracingFromConfigTracingOrFail(ctx context.Context, t testing.TB, kubeClientset *kubernetes.Clientset, configMapNamespace string) {
+func SetupZipkinTracingFromConfigTracingOrFail(ctx context.Context, t testing.TB, kubeClientset kubernetes.Interface, configMapNamespace string) {
 	if err := SetupZipkinTracingFromConfigTracing(ctx, kubeClientset, t.Logf, configMapNamespace); err != nil {
 		t.Fatal("Error while setup Zipkin tracing:", err)
 	}
@@ -114,7 +114,7 @@ func SetupZipkinTracingFromConfigTracingOrFail(ctx context.Context, t testing.TB
 //    (pid of the process doing Port-Forward is stored in a global variable).
 // 2. Enable AlwaysSample config for tracing for the SpoofingClient.
 // The zipkin deployment must have the label app=zipkin
-func SetupZipkinTracing(ctx context.Context, kubeClientset *kubernetes.Clientset, logf logging.FormatLogger, zipkinRemotePort int, zipkinNamespace string) (err error) {
+func SetupZipkinTracing(ctx context.Context, kubeClientset kubernetes.Interface, logf logging.FormatLogger, zipkinRemotePort int, zipkinNamespace string) (err error) {
 	setupOnce.Do(func() {
 		if e := monitoring.CheckPortAvailability(zipkinRemotePort); e != nil {
 			err = fmt.Errorf("zipkin port not available on the machine: %w", err)
@@ -143,7 +143,7 @@ func SetupZipkinTracing(ctx context.Context, kubeClientset *kubernetes.Clientset
 }
 
 // SetupZipkinTracingOrFail is same as SetupZipkinTracing, but fails the test if an error happens
-func SetupZipkinTracingOrFail(ctx context.Context, t testing.TB, kubeClientset *kubernetes.Clientset, zipkinRemotePort int, zipkinNamespace string) {
+func SetupZipkinTracingOrFail(ctx context.Context, t testing.TB, kubeClientset kubernetes.Interface, zipkinRemotePort int, zipkinNamespace string) {
 	if err := SetupZipkinTracing(ctx, kubeClientset, t.Logf, zipkinRemotePort, zipkinNamespace); err != nil {
 		t.Fatal("Error while setup zipkin tracing:", err)
 	}
diff --git a/tracing/config/tracing_test.go b/tracing/config/tracing_test.go
index f04fcc251..8251859b1 100644
--- a/tracing/config/tracing_test.go
+++ b/tracing/config/tracing_test.go
@@ -58,7 +58,7 @@ func TestNewConfigSuccess(t *testing.T) {
 	}{{
 		name:   "Empty map",
 		input:  map[string]string{},
-		output: defaultConfig(),
+		output: NoopConfig(),
 	}, {
 		name: "Everything enabled (legacy)",
 		input: map[string]string{
@@ -145,7 +145,7 @@ func TestNewConfigJson(t *testing.T) {
 	}{{
 		name:   "Empty map",
 		input:  map[string]string{},
-		output: defaultConfig(),
+		output: NoopConfig(),
 	}, {
 		name: "Everything enabled (legacy)",
 		input: map[string]string{