Repository: knative/pkg
Pull request #1771 (2020-10-05 13:33:55)

[Automated] Upgrade to latest dependencies
Produced via:
```shell
./hack/update-deps.sh --upgrade
./hack/update-codegen.sh
```
/cc n3wscott vagababov
/assign n3wscott vagababov

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

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

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

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

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

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

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

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

```diff
@@            Coverage Diff             @@
##           master    #1771      +/-   ##
==========================================
+ Coverage   68.51%   68.55%   +0.04%     
==========================================
  Files         215      215              
  Lines        9216     9216              
==========================================
+ Hits         6314     6318       +4     
+ Misses       2585     2583       -2     
+ Partials      317      315       -2     
```


| [Impacted Files](https://codecov.io/gh/knative/pkg/pull/1771?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [test/gcs/mock/mock.go](https://codecov.io/gh/knative/pkg/pull/1771/diff?src=pr&el=tree#diff-dGVzdC9nY3MvbW9jay9tb2NrLmdv) | `92.47% <0.00%> (+2.15%)` | :arrow_up: |

------

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


Patch:
diff --git a/.codecov.yaml b/.codecov.yaml
index 293b2a951..da447d16a 100644
--- a/.codecov.yaml
+++ b/.codecov.yaml
@@ -4,7 +4,9 @@ coverage:
   status:
     project:
       default:
-        target: 80
+        # https://github.com/knative/pkg/issues/1785
+        # Real goal is 80, but prow sees this as a blocker if not met for merging.
+        target: 68
         threshold: 1%
     patch:
       # Disable the coverage threshold of the patch, so that PRs are
diff --git a/.github/workflows/knative-boilerplate.yaml b/.github/workflows/knative-boilerplate.yaml
index f79d4401b..b45afcca5 100644
--- a/.github/workflows/knative-boilerplate.yaml
+++ b/.github/workflows/knative-boilerplate.yaml
@@ -64,7 +64,7 @@ jobs:
           go get github.com/mattmoor/boilerplate-check/cmd/boilerplate-check
           echo '::endgroup::'
 
-          echo "::add-path::${TEMP_PATH}"
+          echo "${TEMP_PATH}" >> $GITHUB_PATH
 
       - id: boilerplate_txt
         uses: andstor/file-existence-action@v1
diff --git a/.github/workflows/knative-security.yaml b/.github/workflows/knative-security.yaml
new file mode 100644
index 000000000..a5f7703d2
--- /dev/null
+++ b/.github/workflows/knative-security.yaml
@@ -0,0 +1,52 @@
+# 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.
+
+# This file is automagically synced here from github.com/knative-sandbox/.github
+# repo by knobots: https://github.com/mattmoor/knobots and will be overwritten.
+
+name: 'Security'
+
+on:
+  pull_request:
+    branches: [ 'master', 'release-*' ]
+
+jobs:
+  analyze:
+    name: Analyze
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v2
+      with:
+        # We must fetch at least the immediate parents so that if this is
+        # a pull request then we can checkout the head.
+        fetch-depth: 2
+
+    # If this run was triggered by a pull request event, then checkout
+    # the head of the pull request instead of the merge commit.
+    - run: git checkout HEAD^2
+      if: ${{ github.event_name == 'pull_request' }}
+
+    # Initializes the CodeQL tools for scanning.
+    - name: Initialize CodeQL
+      uses: github/codeql-action/init@v1
+      with:
+        languages: go
+
+    - name: Autobuild
+      uses: github/codeql-action/autobuild@v1
+
+    - name: Perform CodeQL Analysis
+      uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/knative-style.yaml b/.github/workflows/knative-style.yaml
index be0bd9ddd..a2603b1e4 100644
--- a/.github/workflows/knative-style.yaml
+++ b/.github/workflows/knative-style.yaml
@@ -109,7 +109,7 @@ jobs:
           curl -sfL https://raw.githubusercontent.com/get-woke/woke/main/install.sh | sh -s -- -b "${TEMP_PATH}" "${WOKE_VERSION}" 2>&1
           echo '::endgroup::'
 
-          echo "::add-path::${TEMP_PATH}"
+          echo "${TEMP_PATH}" >> $GITHUB_PATH
 
       - id: golangci_configuration
         uses: andstor/file-existence-action@v1
@@ -179,7 +179,12 @@ jobs:
           echo '::group:: Flagging missing EOF newlines with reviewdog 🐶 ...'
           # Don't fail because of misspell
           set +o pipefail
-          for x in $(find . -type f -not -path './vendor/*' -not -path './third_party/*' -not -path './.git/*'); do
+          # Lint exclude rule:
+          #  - nothing in vendor/
+          #  - nothing in third_party
+          #  - nothing in .git/
+          #  - no *.ai (Adobe Illustrator) files.
+          for x in $(find . -type f -not -name '*.ai' -not -path './vendor/*' -not -path './third_party/*' -not -path './.git/*'); do
             # Based on https://stackoverflow.com/questions/34943632/linux-check-if-there-is-an-empty-line-at-the-end-of-a-file
             if [[ -f $x && ! ( -s "$x" && -z "$(tail -c 1 $x)" ) ]]; then
               # We add 1 to `wc -l` here because of this limitation (from the man page):
@@ -219,7 +224,19 @@ jobs:
               file=$(echo $ent | cut -d':' -f 1);
               line=$(echo $ent | cut -d':' -f 2);
               ch=$(echo $ent | cut -d':' -f3-);
-              err=$(echo $ch | sed -E 's/([^.]+\.)(Fatal|Error|Warn|Info|Debug|Log|Sprint|Print|Fprint)f([^\%]+)(%[^wq]",)([^,]+)/\1\2\3",\5/');
+              err="Unknown printer tool, please file an issue in knative-sandbox/.github and assign to @vagababov: $ch"
+              if echo $ch | grep --quiet -E "^t.(Errorf|Fatalf|Logf)" ; then
+                err=$(echo $ch | sed  -E 's/([^.fm]+t\.)(Fatal|Error|Log)f([^\%]+)( %[^Tq]",)([^,]+)/\1\2\3",\5/')
+                # Not a test. Here we deal with various loggers and fmt helpers.
+              elif echo $ch | grep --quiet "log" ; then
+                # Capture (x)?log(er)?.
+                err=$(echo $ch | sed -E 's/(.*log.*\.)(Print|Fatal|Error|Info|Warn)f([^\%]+)(%[^Tq]",)([^,]+)/\1\2\3",\5/')
+              elif echo $ch | grep --quiet -E "fmt\.Sprintf" ; then
+                # Always space after sprintf
+                err=$(echo $ch | sed -E 's/(fmt\.)(Sprint)f([^%]+) (%s",)([^,]+)/\1\2\3 ",\5/')
+              elif echo $ch | grep --quiet -E "fmt\." ; then  # all other fmt. printers.
+                err=$(echo $ch | sed -E 's/(fmt\.)(Print|Fprint)f([^%]+) (%[^sTxq]",)([^,]+)/\1\2\3",\5/')
+              fi
               echo "$file:$line: Please consider avoiding tail format like this:%0A$err"
           done |
           reviewdog -efm="%f:%l: %m" \
diff --git a/.golangci.yaml b/.golangci.yaml
index 93c24b0f4..5f4977dd2 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -2,12 +2,14 @@ run:
   timeout: 5m
 
   skip-dirs:
+    - client
     - injection/client
-    - test/mako
+    - third_party
 
 linters:
   enable:
     - asciicheck
+    - golint
     - gosec
     - prealloc
     - stylecheck
diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES
index 08d75e775..5eed75f37 100644
--- a/OWNERS_ALIASES
+++ b/OWNERS_ALIASES
@@ -8,6 +8,7 @@ aliases:
   - tcnghia
   - vagababov
   - vaikas
+  - n3wscott
 
   apis-approvers:
   - mattmoor
diff --git a/README.md b/README.md
index b0098ca9c..7304a49c8 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
 
 [![GoDoc](https://godoc.org/github.com/knative/pkg?status.svg)](https://godoc.org/github.com/knative/pkg)
 [![Go Report Card](https://goreportcard.com/badge/knative/pkg)](https://goreportcard.com/report/knative/pkg)
+[![codecov](https://codecov.io/gh/knative/pkg/branch/master/graph/badge.svg)](https://codecov.io/gh/knative/pkg)
 
 Knative `pkg` provides a place for sharing common Knative packages across the
 Knative repos.
diff --git a/RELEASE-LEADS.md b/RELEASE-LEADS.md
index 569273626..b9479305a 100644
--- a/RELEASE-LEADS.md
+++ b/RELEASE-LEADS.md
@@ -260,11 +260,13 @@ to be pinned in all repositories that depend on them.
 
 For **serving** that is:
 
+- [knative/client](https://github.com/knative/client)
 - [knative-sandbox/net-kourier](https://github.com/knative-sandbox/net-kourier)
 - [knative/eventing-contrib](https://github.com/knative/eventing-contrib)
 
 For **eventing** that is:
 
+- [knative/client](https://github.com/knative/client)
 - [knative/eventing-contrib](https://github.com/knative/eventing-contrib)
 - [knative-sandbox/eventing-kafka-broker](https://github.com/knative-sandbox/eventing-kafka-broker)
 
@@ -275,7 +277,8 @@ branch of each repository respectively.
 ### Cut `release-x.y` branches of all remaining repositories
 
 After the pin PRs are merged, cut the `release-x.y` branch in each of the
-remaining repositories (except `operator`):
+remaining repositories (except `operator` and `client` as they are cut
+separately by the respective working group):
 
 - [knative-sandbox/net-kourier](https://github.com/knative-sandbox/net-kourier)
 - [knative/eventing-contrib](https://github.com/knative/eventing-contrib)
diff --git a/apiextensions/storageversion/cmd/migrate/main.go b/apiextensions/storageversion/cmd/migrate/main.go
index fd44df6f0..acbe5a43f 100644
--- a/apiextensions/storageversion/cmd/migrate/main.go
+++ b/apiextensions/storageversion/cmd/migrate/main.go
@@ -76,7 +76,7 @@ func setupLogger() *zap.SugaredLogger {
 
 	config, err := logging.NewConfigFromMap(nil)
 	if err != nil {
-		log.Fatalf("Failed to create logging config: %s", err)
+		log.Fatal("Failed to create logging config: ", err)
 	}
 
 	logger, _ := logging.NewLoggerFromConfig(config, component)
diff --git a/apis/duck/v1/binding_types.go b/apis/duck/v1/binding_types.go
new file mode 100644
index 000000000..71b224cf5
--- /dev/null
+++ b/apis/duck/v1/binding_types.go
@@ -0,0 +1,92 @@
+/*
+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 v1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+
+	"knative.dev/pkg/apis"
+	"knative.dev/pkg/apis/duck"
+	"knative.dev/pkg/tracker"
+)
+
+// +genduck
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// Binding is a duck type that specifies the partial schema to which all
+// Binding implementations should adhere.
+type Binding struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec BindingSpec `json:"spec"`
+}
+
+// Verify that Binding implements the appropriate interfaces.
+var (
+	_ duck.Implementable = (*Binding)(nil)
+	_ duck.Populatable   = (*Binding)(nil)
+	_ apis.Listable      = (*Binding)(nil)
+)
+
+// BindingSpec specifies the spec portion of the Binding partial-schema.
+type BindingSpec struct {
+	// Subject references the resource(s) whose "runtime contract" should be
+	// augmented by Binding implementations.
+	Subject tracker.Reference `json:"subject"`
+}
+
+// GetFullType implements duck.Implementable
+func (*Binding) GetFullType() duck.Populatable {
+	return &Binding{}
+}
+
+// Populate implements duck.Populatable
+func (t *Binding) Populate() {
+	t.Spec = BindingSpec{
+		Subject: tracker.Reference{
+			APIVersion: "apps/v1",
+			Kind:       "Deployment",
+			Namespace:  "default",
+			// Name and Selector are mutually exclusive,
+			// but we fill them both in for this test.
+			Name: "bazinga",
+			Selector: &metav1.LabelSelector{
+				MatchLabels: map[string]string{
+					"foo": "bar",
+					"baz": "blah",
+				},
+			},
+		},
+	}
+}
+
+// GetListType implements apis.Listable
+func (*Binding) GetListType() runtime.Object {
+	return &BindingList{}
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// BindingList is a list of Binding resources
+type BindingList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata"`
+
+	Items []Binding `json:"items"`
+}
diff --git a/apis/duck/v1/fuzzer.go b/apis/duck/v1/fuzzer.go
new file mode 100644
index 000000000..4ef66e589
--- /dev/null
+++ b/apis/duck/v1/fuzzer.go
@@ -0,0 +1,44 @@
+/*
+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 v1
+
+import (
+	fuzz "github.com/google/gofuzz"
+	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
+	"k8s.io/apimachinery/pkg/runtime/serializer"
+	"knative.dev/pkg/apis"
+	pkgfuzzer "knative.dev/pkg/apis/testing/fuzzer"
+)
+
+var testConditions = apis.Conditions{{Type: apis.ConditionReady}, {Type: apis.ConditionSucceeded}}
+
+// FuzzerFuncs includes fuzzing funcs for knative.dev/duck v1 types
+// In particular it makes sure that Delivery has only valid BackoffPolicyType in it.
+//
+// For other examples see
+// https://github.com/kubernetes/apimachinery/blob/master/pkg/apis/meta/fuzzer/fuzzer.go
+var FuzzerFuncs = fuzzer.MergeFuzzerFuncs(
+	func(codecs serializer.CodecFactory) []interface{} {
+		return []interface{}{
+			func(status *Status, c fuzz.Continue) {
+				c.FuzzNoCustom(status) // fuzz the Status
+				status.SetConditions(testConditions)
+				pkgfuzzer.FuzzConditions(status, c)
+			},
+		}
+	},
+)
diff --git a/apis/duck/v1/register.go b/apis/duck/v1/register.go
index 69bb11578..67418b3e9 100644
--- a/apis/duck/v1/register.go
+++ b/apis/duck/v1/register.go
@@ -54,6 +54,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
 		(&Source{}).GetListType(),
 		&WithPod{},
 		(&WithPod{}).GetListType(),
+		&Binding{},
+		(&Binding{}).GetListType(),
 	)
 	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
 	return nil
diff --git a/apis/duck/v1/zz_generated.deepcopy.go b/apis/duck/v1/zz_generated.deepcopy.go
index 190d773e7..f4da66047 100644
--- a/apis/duck/v1/zz_generated.deepcopy.go
+++ b/apis/duck/v1/zz_generated.deepcopy.go
@@ -127,6 +127,83 @@ func (in *AddressableTypeList) DeepCopyObject() runtime.Object {
 	return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Binding) DeepCopyInto(out *Binding) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Binding.
+func (in *Binding) DeepCopy() *Binding {
+	if in == nil {
+		return nil
+	}
+	out := new(Binding)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Binding) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BindingList) DeepCopyInto(out *BindingList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]Binding, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BindingList.
+func (in *BindingList) DeepCopy() *BindingList {
+	if in == nil {
+		return nil
+	}
+	out := new(BindingList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *BindingList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BindingSpec) DeepCopyInto(out *BindingSpec) {
+	*out = *in
+	in.Subject.DeepCopyInto(&out.Subject)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BindingSpec.
+func (in *BindingSpec) DeepCopy() *BindingSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(BindingSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *CloudEventAttributes) DeepCopyInto(out *CloudEventAttributes) {
 	*out = *in
diff --git a/apis/duck/v1beta1/fuzzer.go b/apis/duck/v1beta1/fuzzer.go
new file mode 100644
index 000000000..efc8be973
--- /dev/null
+++ b/apis/duck/v1beta1/fuzzer.go
@@ -0,0 +1,43 @@
+/*
+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 v1beta1
+
+import (
+	fuzz "github.com/google/gofuzz"
+	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
+	"k8s.io/apimachinery/pkg/runtime/serializer"
+	"knative.dev/pkg/apis"
+	pkgfuzzer "knative.dev/pkg/apis/testing/fuzzer"
+)
+
+var testConditions = apis.Conditions{{Type: apis.ConditionReady}, {Type: apis.ConditionSucceeded}}
+
+// FuzzerFuncs includes fuzzing funcs for knative.dev/duck v1beta1 Status types
+//
+// For other examples see
+// https://github.com/kubernetes/apimachinery/blob/master/pkg/apis/meta/fuzzer/fuzzer.go
+var FuzzerFuncs = fuzzer.MergeFuzzerFuncs(
+	func(codecs serializer.CodecFactory) []interface{} {
+		return []interface{}{
+			func(status *Status, c fuzz.Continue) {
+				c.FuzzNoCustom(status) // fuzz the Status
+				status.SetConditions(testConditions)
+				pkgfuzzer.FuzzConditions(status, c)
+			},
+		}
+	},
+)
diff --git a/apis/field_error.go b/apis/field_error.go
index 3997b5944..e807ec781 100644
--- a/apis/field_error.go
+++ b/apis/field_error.go
@@ -327,7 +327,7 @@ func ErrInvalidArrayValue(value interface{}, field string, index int) *FieldErro
 // invalid value.
 func ErrInvalidValue(value interface{}, fieldPath string) *FieldError {
 	return &FieldError{
-		Message: fmt.Sprintf("invalid value: %v", value),
+		Message: fmt.Sprint("invalid value: ", value),
 		Paths:   []string{fieldPath},
 	}
 }
diff --git a/client/injection/ducks/duck/v1/binding/binding.go b/client/injection/ducks/duck/v1/binding/binding.go
new file mode 100644
index 000000000..6335b1179
--- /dev/null
+++ b/client/injection/ducks/duck/v1/binding/binding.go
@@ -0,0 +1,60 @@
+/*
+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.
+*/
+
+// Code generated by injection-gen. DO NOT EDIT.
+
+package binding
+
+import (
+	context "context"
+
+	duck "knative.dev/pkg/apis/duck"
+	v1 "knative.dev/pkg/apis/duck/v1"
+	controller "knative.dev/pkg/controller"
+	injection "knative.dev/pkg/injection"
+	dynamicclient "knative.dev/pkg/injection/clients/dynamicclient"
+	logging "knative.dev/pkg/logging"
+)
+
+func init() {
+	injection.Default.RegisterDuck(WithDuck)
+}
+
+// Key is used for associating the Informer inside the context.Context.
+type Key struct{}
+
+func WithDuck(ctx context.Context) context.Context {
+	dc := dynamicclient.Get(ctx)
+	dif := &duck.CachedInformerFactory{
+		Delegate: &duck.TypedInformerFactory{
+			Client:       dc,
+			Type:         (&v1.Binding{}).GetFullType(),
+			ResyncPeriod: controller.GetResyncPeriod(ctx),
+			StopChannel:  ctx.Done(),
+		},
+	}
+	return context.WithValue(ctx, Key{}, dif)
+}
+
+// Get extracts the typed informer from the context.
+func Get(ctx context.Context) duck.InformerFactory {
+	untyped := ctx.Value(Key{})
+	if untyped == nil {
+		logging.FromContext(ctx).Panic(
+			"Unable to fetch knative.dev/pkg/apis/duck.InformerFactory from context.")
+	}
+	return untyped.(duck.InformerFactory)
+}
diff --git a/client/injection/ducks/duck/v1/binding/fake/fake.go b/client/injection/ducks/duck/v1/binding/fake/fake.go
new file mode 100644
index 000000000..db0e77311
--- /dev/null
+++ b/client/injection/ducks/duck/v1/binding/fake/fake.go
@@ -0,0 +1,30 @@
+/*
+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.
+*/
+
+// Code generated by injection-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	binding "knative.dev/pkg/client/injection/ducks/duck/v1/binding"
+	injection "knative.dev/pkg/injection"
+)
+
+var Get = binding.Get
+
+func init() {
+	injection.Fake.RegisterDuck(binding.WithDuck)
+}
diff --git a/codegen/cmd/injection-gen/generators/packages.go b/codegen/cmd/injection-gen/generators/packages.go
index 9345cdea0..d1fa91e09 100644
--- a/codegen/cmd/injection-gen/generators/packages.go
+++ b/codegen/cmd/injection-gen/generators/packages.go
@@ -36,7 +36,7 @@ import (
 func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
 	boilerplate, err := arguments.LoadGoBoilerplate()
 	if err != nil {
-		klog.Fatalf("Failed loading boilerplate: %v", err)
+		klog.Fatal("Failed loading boilerplate: ", err)
 	}
 
 	customArgs, ok := arguments.CustomArgs.(*informergenargs.CustomArgs)
diff --git a/codegen/cmd/injection-gen/main.go b/codegen/cmd/injection-gen/main.go
index 26410cc99..30f20a0dd 100644
--- a/codegen/cmd/injection-gen/main.go
+++ b/codegen/cmd/injection-gen/main.go
@@ -44,7 +44,7 @@ func main() {
 	pflag.Parse()
 
 	if err := generatorargs.Validate(genericArgs); err != nil {
-		klog.Fatalf("Error: %v", err)
+		klog.Fatal("Error: ", err)
 	}
 
 	// Run it.
@@ -53,7 +53,7 @@ func main() {
 		generators.DefaultNameSystem(),
 		generators.Packages,
 	); err != nil {
-		klog.Fatalf("Error: %v", err)
+		klog.Fatal("Error: ", err)
 	}
 	klog.V(2).Info("Completed successfully.")
 }
diff --git a/controller/controller.go b/controller/controller.go
index ee35f37bf..cee68931d 100644
--- a/controller/controller.go
+++ b/controller/controller.go
@@ -207,7 +207,7 @@ type Impl struct {
 
 // ControllerOptions encapsulates options for creating a new controller,
 // including throttling and stats behavior.
-type ControllerOptions struct {
+type ControllerOptions struct { //nolint for backcompat.
 	WorkQueueName string
 	Logger        *zap.SugaredLogger
 	Reporter      StatsReporter
diff --git a/go.mod b/go.mod
index 1812627e1..9503b4554 100644
--- a/go.mod
+++ b/go.mod
@@ -55,7 +55,7 @@ require (
 	k8s.io/code-generator v0.18.8
 	k8s.io/gengo v0.0.0-20200205140755-e0e292d8aa12
 	k8s.io/klog v1.0.0
-	knative.dev/test-infra v0.0.0-20201002164834-8c07ff018549
+	knative.dev/test-infra v0.0.0-20201005021934-df2084cec789
 	sigs.k8s.io/boskos v0.0.0-20200729174948-794df80db9c9
 )
 
diff --git a/go.sum b/go.sum
index 725953d8d..ed1b3fc9c 100644
--- a/go.sum
+++ b/go.sum
@@ -1846,8 +1846,8 @@ knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55/go.mod h1:WqF1Azka+FxP
 knative.dev/test-infra v0.0.0-20200513011557-d03429a76034/go.mod h1:aMif0KXL4g19YCYwsy4Ocjjz5xgPlseYV+B95Oo4JGE=
 knative.dev/test-infra v0.0.0-20200519015156-82551620b0a9/go.mod h1:A5b2OAXTOeHT3hHhVQm3dmtbuWvIDP7qzgtqxA3/2pE=
 knative.dev/test-infra v0.0.0-20200707183444-aed09e56ddc7/go.mod h1:RjYAhXnZqeHw9+B0zsbqSPlae0lCvjekO/nw5ZMpLCs=
-knative.dev/test-infra v0.0.0-20201002164834-8c07ff018549 h1:8UObRJ0lnWvU9FuD8ubmkwJo14+aZtboq1ncBWbs2uI=
-knative.dev/test-infra v0.0.0-20201002164834-8c07ff018549/go.mod h1:Pmg2c7Z7q7BGFUV/GOpU5BlrD3ePJft4MPqx8AYBplc=
+knative.dev/test-infra v0.0.0-20201005021934-df2084cec789 h1:/MuFtTXaQXe8romYte5sTLtfCnVh9jYy1BWlWrvLpv4=
+knative.dev/test-infra v0.0.0-20201005021934-df2084cec789/go.mod h1:Pmg2c7Z7q7BGFUV/GOpU5BlrD3ePJft4MPqx8AYBplc=
 mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
 mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
 mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
diff --git a/hash/hash.go b/hash/hash.go
index b6b642122..1c4c5352f 100644
--- a/hash/hash.go
+++ b/hash/hash.go
@@ -153,7 +153,7 @@ func ChooseSubset(from sets.String, n int, target string) sets.String {
 		}
 		// Already matched this one. Continue to the next index.
 		for selection.Has(root) {
-			root += 1
+			root++
 			if root == hpl {
 				root = 0
 			}
diff --git a/injection/config.go b/injection/config.go
new file mode 100644
index 000000000..bae2b2e25
--- /dev/null
+++ b/injection/config.go
@@ -0,0 +1,99 @@
+/*
+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 injection
+
+import (
+	"errors"
+	"flag"
+	"log"
+	"math"
+	"os"
+	"os/user"
+	"path/filepath"
+
+	"k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/clientcmd"
+	"k8s.io/klog"
+)
+
+// ParseAndGetRESTConfigOrDie parses the rest config flags and creates a client or
+// dies by calling log.Fatalf.
+func ParseAndGetRESTConfigOrDie() *rest.Config {
+	var (
+		serverURL = flag.String("server", "",
+			"The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
+		kubeconfig = flag.String("kubeconfig", "",
+			"Path to a kubeconfig. Only required if out-of-cluster.")
+
+		burst = flag.Int("kube-api-burst", 0, "Maximum burst for throttle.")
+		qps   = flag.Float64("kube-api-qps", 0, "Maximum QPS to the server from the client.")
+	)
+	klog.InitFlags(flag.CommandLine)
+	flag.Parse()
+
+	cfg, err := GetRESTConfig(*serverURL, *kubeconfig)
+	if err != nil {
+		log.Fatal("Error building kubeconfig: ", err)
+	}
+
+	if *burst < 0 {
+		log.Fatal("Invalid burst value", *burst)
+	}
+	if *qps < 0 || *qps > math.MaxFloat32 {
+		log.Fatal("Invalid QPS value", *qps)
+	}
+
+	cfg.Burst = *burst
+	cfg.QPS = float32(*qps)
+
+	return cfg
+}
+
+// GetRESTConfig returns a rest.Config to be used for kubernetes client creation.
+// It does so in the following order:
+//   1. Use the passed kubeconfig/serverURL.
+//   2. Fallback to the KUBECONFIG environment variable.
+//   3. Fallback to in-cluster config.
+//   4. Fallback to the ~/.kube/config.
+func GetRESTConfig(serverURL, kubeconfig string) (*rest.Config, error) {
+	if kubeconfig == "" {
+		kubeconfig = os.Getenv("KUBECONFIG")
+	}
+
+	// If we have an explicit indication of where the kubernetes config lives, read that.
+	if kubeconfig != "" {
+		c, err := clientcmd.BuildConfigFromFlags(serverURL, kubeconfig)
+		if err != nil {
+			return nil, err
+		}
+		return c, nil
+	}
+
+	// If not, try the in-cluster config.
+	if c, err := rest.InClusterConfig(); err == nil {
+		return c, nil
+	}
+
+	// If no in-cluster config, try the default location in the user's home directory.
+	if usr, err := user.Current(); err == nil {
+		if c, err := clientcmd.BuildConfigFromFlags("", filepath.Join(usr.HomeDir, ".kube", "config")); err == nil {
+			return c, nil
+		}
+	}
+
+	return nil, errors.New("could not create a valid kubeconfig")
+}
diff --git a/injection/injection.go b/injection/injection.go
new file mode 100644
index 000000000..d8cefc9fb
--- /dev/null
+++ b/injection/injection.go
@@ -0,0 +1,66 @@
+/*
+Copyright 2019 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 injection
+
+import (
+	"context"
+
+	"go.uber.org/zap"
+	"k8s.io/client-go/rest"
+
+	"knative.dev/pkg/controller"
+	"knative.dev/pkg/logging"
+	"knative.dev/pkg/signals"
+)
+
+// EnableInjectionOrDie enables Knative Client Injection, and provides a
+// callback to start the informers. Both Context and Config are optional.
+// Returns context with rest config set and a callback to start the informers
+// after watches have been set.
+//
+// Typical integration:
+// ```go
+//   ctx, startInformers := injection.EnableInjectionOrDie(signals.NewContext(), nil)
+//   ... start watches with informers, if required ...
+//   startInformers()
+// ```
+func EnableInjectionOrDie(ctx context.Context, cfg *rest.Config) (context.Context, func()) {
+	if ctx == nil {
+		ctx = signals.NewContext()
+	}
+	if cfg == nil {
+		cfg = ParseAndGetRESTConfigOrDie()
+	}
+
+	// Respect user provided settings, but if omitted customize the default behavior.
+	if cfg.QPS == 0 {
+		cfg.QPS = rest.DefaultQPS
+	}
+	if cfg.Burst == 0 {
+		cfg.Burst = rest.DefaultBurst
+	}
+	ctx = WithConfig(ctx, cfg)
+
+	ctx, informers := Default.SetupInformers(ctx, cfg)
+
+	return ctx, func() {
+		logging.FromContext(ctx).Info("Starting informers...")
+		if err := controller.StartInformers(ctx.Done(), informers...); err != nil {
+			logging.FromContext(ctx).Fatalw("Failed to start informers", zap.Error(err))
+		}
+	}
+}
diff --git a/injection/sharedmain/main.go b/injection/sharedmain/main.go
index 0fceb6983..c9dd034de 100644
--- a/injection/sharedmain/main.go
+++ b/injection/sharedmain/main.go
@@ -18,16 +18,15 @@ package sharedmain
 
 import (
 	"context"
-	"errors"
 	"flag"
 	"log"
 	"net/http"
 	"os"
-	"os/user"
-	"path/filepath"
 	"time"
 
 	"go.opencensus.io/stats/view"
+	_ "go.uber.org/automaxprocs" // automatically set GOMAXPROCS based on cgroups
+	"go.uber.org/zap"
 	"golang.org/x/sync/errgroup"
 	corev1 "k8s.io/api/core/v1"
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -35,11 +34,6 @@ import (
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/util/wait"
 	"k8s.io/client-go/rest"
-	"k8s.io/client-go/tools/clientcmd"
-	"k8s.io/klog"
-
-	_ "go.uber.org/automaxprocs" // automatically set GOMAXPROCS based on cgroups
-	"go.uber.org/zap"
 
 	kubeclient "knative.dev/pkg/client/injection/kube/client"
 	"knative.dev/pkg/configmap"
@@ -63,37 +57,9 @@ import (
 //   2. Fallback to the KUBECONFIG environment variable.
 //   3. Fallback to in-cluster config.
 //   4. Fallback to the ~/.kube/config.
+// Deprecated: use injection.GetRESTConfig
 func GetConfig(serverURL, kubeconfig string) (*rest.Config, error) {
-	if kubeconfig == "" {
-		kubeconfig = os.Getenv("KUBECONFIG")
-	}
-
-	// We produce configs a bunch of ways, this gives us a single place
-	// to "decorate" them with common useful things (e.g. for debugging)
-	decorate := func(cfg *rest.Config) *rest.Config {
-		return cfg
-	}
-
-	// If we have an explicit indication of where the kubernetes config lives, read that.
-	if kubeconfig != "" {
-		c, err := clientcmd.BuildConfigFromFlags(serverURL, kubeconfig)
-		if err != nil {
-			return nil, err
-		}
-		return decorate(c), nil
-	}
-	// If not, try the in-cluster config.
-	if c, err := rest.InClusterConfig(); err == nil {
-		return decorate(c), nil
-	}
-	// If no in-cluster config, try the default location in the user's home directory.
-	if usr, err := user.Current(); err == nil {
-		if c, err := clientcmd.BuildConfigFromFlags("", filepath.Join(usr.HomeDir, ".kube", "config")); err == nil {
-			return decorate(c), nil
-		}
-	}
-
-	return nil, errors.New("could not create a valid kubeconfig")
+	return injection.GetRESTConfig(serverURL, kubeconfig)
 }
 
 // GetLoggingConfig gets the logging config from either the file system if present
@@ -129,40 +95,16 @@ func GetLeaderElectionConfig(ctx context.Context) (*leaderelection.Config, error
 
 // EnableInjectionOrDie enables Knative Injection and starts the informers.
 // Both Context and Config are optional.
+// Deprecated: use injection.EnableInjectionOrDie
 func EnableInjectionOrDie(ctx context.Context, cfg *rest.Config) context.Context {
-	if ctx == nil {
-		ctx = signals.NewContext()
-	}
-	if cfg == nil {
-		cfg = ParseAndGetConfigOrDie()
-	}
-
-	// Respect user provided settings, but if omitted customize the default behavior.
-	if cfg.QPS == 0 {
-		cfg.QPS = rest.DefaultQPS
-	}
-	if cfg.Burst == 0 {
-		cfg.Burst = rest.DefaultBurst
-	}
-	ctx = injection.WithConfig(ctx, cfg)
-
-	ctx, informers := injection.Default.SetupInformers(ctx, cfg)
-
-	// Start the injection clients and informers.
-	logging.FromContext(ctx).Info("Starting informers...")
-	go func(ctx context.Context) {
-		if err := controller.StartInformers(ctx.Done(), informers...); err != nil {
-			logging.FromContext(ctx).Fatalw("Failed to start informers", zap.Error(err))
-		}
-		<-ctx.Done()
-	}(ctx)
-
+	ctx, startInformers := injection.EnableInjectionOrDie(ctx, cfg)
+	go startInformers()
 	return ctx
 }
 
 // Main runs the generic main flow with a new context.
-// If any of the contructed controllers are AdmissionControllers or Conversion webhooks,
-// then a webhook is started to serve them.
+// If any of the constructed controllers are AdmissionControllers or Conversion
+// webhooks, then a webhook is started to serve them.
 func Main(component string, ctors ...injection.ControllerConstructor) {
 	// Set up signals so we handle the first shutdown signal gracefully.
 	MainWithContext(signals.NewContext(), component, ctors...)
@@ -185,7 +127,7 @@ func MainWithContext(ctx context.Context, component string, ctors ...injection.C
 			"issue upstream!")
 
 	// HACK: This parses flags, so the above should be set once this runs.
-	cfg := ParseAndGetConfigOrDie()
+	cfg := injection.ParseAndGetRESTConfigOrDie()
 
 	if *disableHighAvailability {
 		ctx = WithHADisabled(ctx)
@@ -225,16 +167,7 @@ func MainWithConfig(ctx context.Context, component string, cfg *rest.Config, cto
 		cfg.Burst = len(ctors) * rest.DefaultBurst
 	}
 
-	// Respect user provided settings, but if omitted customize the default behavior.
-	if cfg.QPS == 0 {
-		cfg.QPS = rest.DefaultQPS
-	}
-	if cfg.Burst == 0 {
-		cfg.Burst = rest.DefaultBurst
-	}
-	ctx = injection.WithConfig(ctx, cfg)
-
-	ctx, informers := injection.Default.SetupInformers(ctx, cfg)
+	ctx, startInformers := injection.EnableInjectionOrDie(ctx, cfg)
 
 	logger, atomicLevel := SetupLoggerOrDie(ctx, component)
 	defer flush(logger)
@@ -287,11 +220,10 @@ func MainWithConfig(ctx context.Context, component string, cfg *rest.Config, cto
 			return wh.Run(ctx.Done())
 		})
 	}
+
 	// Start the injection clients and informers.
-	logging.FromContext(ctx).Info("Starting informers...")
-	if err := controller.StartInformers(ctx.Done(), informers...); err != nil {
-		logging.FromContext(ctx).Fatalw("Failed to start informers", zap.Error(err))
-	}
+	startInformers()
+
 	// Wait for webhook informers to sync.
 	if wh != nil {
 		wh.InformersHaveSynced()
@@ -317,22 +249,9 @@ 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
 func ParseAndGetConfigOrDie() *rest.Config {
-	var (
-		serverURL = flag.String("server", "",
-			"The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.")
-		kubeconfig = flag.String("kubeconfig", "",
-			"Path to a kubeconfig. Only required if out-of-cluster.")
-	)
-	klog.InitFlags(flag.CommandLine)
-	flag.Parse()
-
-	cfg, err := GetConfig(*serverURL, *kubeconfig)
-	if err != nil {
-		log.Fatalf("Error building kubeconfig: %v", err)
-	}
-
-	return cfg
+	return injection.ParseAndGetRESTConfigOrDie()
 }
 
 // MemStatsOrDie sets up reporting on Go memory usage every 30 seconds or dies
@@ -342,7 +261,7 @@ func MemStatsOrDie(ctx context.Context) {
 	msp.Start(ctx, 30*time.Second)
 
 	if err := view.Register(msp.DefaultViews()...); err != nil {
-		log.Fatalf("Error exporting go memstats view: %v", err)
+		log.Fatal("Error exporting go memstats view: ", err)
 	}
 }
 
@@ -351,7 +270,7 @@ func MemStatsOrDie(ctx context.Context) {
 func SetupLoggerOrDie(ctx context.Context, component string) (*zap.SugaredLogger, zap.AtomicLevel) {
 	loggingConfig, err := GetLoggingConfig(ctx)
 	if err != nil {
-		log.Fatalf("Error reading/parsing logging configuration: %v", err)
+		log.Fatal("Error reading/parsing logging configuration: ", err)
 	}
 	l, level := logging.NewLoggerFromConfig(loggingConfig, component)
 
diff --git a/leaderelection/chaosduck/main.go b/leaderelection/chaosduck/main.go
index fb3e5eb9b..2ad685fe3 100644
--- a/leaderelection/chaosduck/main.go
+++ b/leaderelection/chaosduck/main.go
@@ -27,13 +27,14 @@ import (
 	"strings"
 	"time"
 
+	"knative.dev/pkg/injection"
+
 	"golang.org/x/sync/errgroup"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/apimachinery/pkg/util/wait"
 	"k8s.io/client-go/kubernetes"
 	kubeclient "knative.dev/pkg/client/injection/kube/client"
-	"knative.dev/pkg/injection/sharedmain"
 	"knative.dev/pkg/kflag"
 	"knative.dev/pkg/signals"
 	"knative.dev/pkg/system"
@@ -116,8 +117,7 @@ func quack(ctx context.Context, kc kubernetes.Interface, component string, leade
 }
 
 func main() {
-	ctx := signals.NewContext()
-	ctx = sharedmain.EnableInjectionOrDie(ctx, nil)
+	ctx, _ := injection.EnableInjectionOrDie(signals.NewContext(), nil)
 
 	kc := kubeclient.Get(ctx)
 
@@ -126,7 +126,7 @@ func main() {
 	wait.JitterUntilWithContext(ctx, func(ctx context.Context) {
 		components, err := buildComponents(ctx, kc)
 		if err != nil {
-			log.Printf("Error building components: %v", err)
+			log.Print("Error building components: ", err)
 		}
 		log.Printf("Got components: %#v", components)
 
@@ -141,7 +141,7 @@ func main() {
 			})
 		}
 		if err := eg.Wait(); err != nil {
-			log.Printf("Ended iteration with err: %v", err)
+			log.Print("Ended iteration with err: ", err)
 		}
 	}, tributePeriod, tributeFactor, true /* sliding: do not include the runtime of the above in the interval */)
 }
diff --git a/logging/config.go b/logging/config.go
index 65409032a..d8c4b609b 100644
--- a/logging/config.go
+++ b/logging/config.go
@@ -240,8 +240,7 @@ func ConfigMapName() string {
 
 // JsonToLoggingConfig converts a json string of a Config.
 // Returns a non-nil Config always.
-// nolint:stylecheck // No rename due to backwards incompatibility.
-func JsonToLoggingConfig(jsonCfg string) (*Config, error) {
+func JsonToLoggingConfig(jsonCfg string) (*Config, error) { //nolint No rename due to backwards incompatibility.
 	if jsonCfg == "" {
 		return nil, errEmptyJSONLogginString
 	}
@@ -260,8 +259,7 @@ func JsonToLoggingConfig(jsonCfg string) (*Config, error) {
 }
 
 // LoggingConfigToJson converts a Config to a json string.
-// nolint:stylecheck // No rename due to backwards incompatibility.
-func LoggingConfigToJson(cfg *Config) (string, error) {
+func LoggingConfigToJson(cfg *Config) (string, error) { //nolint No rename due to backwards incompatibility.
 	if cfg == nil || cfg.LoggingConfig == "" {
 		return "", nil
 	}
diff --git a/metrics/config.go b/metrics/config.go
index bb55d410b..8eb829775 100644
--- a/metrics/config.go
+++ b/metrics/config.go
@@ -346,8 +346,7 @@ func prometheusPort() (int, error) {
 
 // JsonToMetricsOptions converts a json string of a
 // ExporterOptions. Returns a non-nil ExporterOptions always.
-// nolint:stylecheck // No rename due to backwards incompatibility.
-func JsonToMetricsOptions(jsonOpts string) (*ExporterOptions, error) {
+func JsonToMetricsOptions(jsonOpts string) (*ExporterOptions, error) { // nolint No rename due to backwards incompatibility.
 	var opts ExporterOptions
 	if jsonOpts == "" {
 		return nil, errors.New("json options string is empty")
@@ -361,8 +360,7 @@ func JsonToMetricsOptions(jsonOpts string) (*ExporterOptions, error) {
 }
 
 // MetricsOptionsToJson converts a ExporterOptions to a json string.
-// nolint:stylecheck // No rename due to backwards incompatibility.
-func MetricsOptionsToJson(opts *ExporterOptions) (string, error) {
+func MetricsOptionsToJson(opts *ExporterOptions) (string, error) { // nolint No rename due to backwards incompatibility.
 	if opts == nil {
 		return "", nil
 	}
diff --git a/metrics/prometheus_exporter.go b/metrics/prometheus_exporter.go
index d60fe8f07..7f49302cc 100644
--- a/metrics/prometheus_exporter.go
+++ b/metrics/prometheus_exporter.go
@@ -82,7 +82,7 @@ func startNewPromSrv(e *prom.Exporter, port int) *http.Server {
 		curPromSrv.Close()
 	}
 	curPromSrv = &http.Server{
-		Addr:    fmt.Sprintf(":%v", port),
+		Addr:    fmt.Sprint(":", port),
 		Handler: sm,
 	}
 	return curPromSrv
diff --git a/metrics/resource_view.go b/metrics/resource_view.go
index 1e89787f6..57e54a6f0 100644
--- a/metrics/resource_view.go
+++ b/metrics/resource_view.go
@@ -29,6 +29,7 @@ import (
 	"go.opencensus.io/stats"
 	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
+	"k8s.io/apimachinery/pkg/util/clock"
 )
 
 type storedViews struct {
@@ -40,6 +41,7 @@ type meterExporter struct {
 	m view.Meter    // NOTE: DO NOT RETURN THIS DIRECTLY; the view.Meter will not work for the empty Resource
 	o stats.Options // Cache the option to reduce allocations
 	e view.Exporter
+	t time.Time // Time when last access occurred
 }
 
 // ResourceExporterFactory provides a hook for producing separate view.Exporters
@@ -48,17 +50,70 @@ type meterExporter struct {
 // Tags are.
 type ResourceExporterFactory func(*resource.Resource) (view.Exporter, error)
 type meters struct {
-	meters  map[string]*meterExporter
-	factory ResourceExporterFactory
-	lock    sync.Mutex
+	meters         map[string]*meterExporter
+	factory        ResourceExporterFactory
+	lock           sync.Mutex
+	clock          clock.Clock
+	ticker         clock.Ticker
+	tickerStopChan chan struct{}
 }
 
 // Lock regime: lock allMeters before resourceViews. The critical path is in
 // optionForResource, which must lock allMeters, but only needs to lock
 // resourceViews if a new meter needs to be created.
-var resourceViews = storedViews{}
-var allMeters = meters{
-	meters: map[string]*meterExporter{"": &defaultMeter},
+var (
+	resourceViews = storedViews{}
+	allMeters     = meters{
+		meters: map[string]*meterExporter{"": &defaultMeter},
+		clock:  clock.Clock(clock.RealClock{}),
+	}
+
+	cleanupOnce                = new(sync.Once)
+	meterExporterScanFrequency = 1 * time.Minute
+	maxMeterExporterAge        = 10 * time.Minute
+)
+
+// cleanup looks through allMeter's meterExporter and prunes any that were last visited
+// more than maxMeterExporterAge ago. This will clean up any exporters for resources
+// that no longer exist on the system (a replaced revision, e.g.).  All meterExporter
+// lookups (and creations) run meterExporterForResource, which updates the timestamp
+// on each access.
+func cleanup() {
+	expiryCutoff := allMeters.clock.Now().Add(-1 * maxMeterExporterAge)
+	allMeters.lock.Lock()
+	defer allMeters.lock.Unlock()
+	for key, meter := range allMeters.meters {
+		if key != "" && meter.t.Before(expiryCutoff) {
+			flushGivenExporter(meter.e)
+			delete(allMeters.meters, key)
+		}
+	}
+}
+
+// startCleanup is configured to only run once in production.  For testing,
+// calling startCleanup again will terminate old cleanup threads,
+// and restart anew.
+func startCleanup() {
+	if allMeters.tickerStopChan != nil {
+		close(allMeters.tickerStopChan)
+	}
+
+	curClock := allMeters.clock
+	newTicker := curClock.NewTicker(meterExporterScanFrequency)
+	newStopChan := make(chan struct{})
+	allMeters.tickerStopChan = newStopChan
+	allMeters.ticker = newTicker
+	go func() {
+		defer newTicker.Stop()
+		for {
+			select {
+			case <-newTicker.C():
+				cleanup()
+			case <-newStopChan:
+				return
+			}
+		}
+	}()
 }
 
 // RegisterResourceView is similar to view.Register(), except that it will
@@ -174,7 +229,9 @@ func flushResourceExporters() {
 }
 
 // ClearMetersForTest clears the internal set of metrics being exported,
-// including cleaning up background threads.
+// including cleaning up background threads, and restarts cleanup thread.
+//
+// If a replacement clock is desired, it should be in allMeters.clock before calling.
 func ClearMetersForTest() {
 	allMeters.lock.Lock()
 	defer allMeters.lock.Unlock()
@@ -186,6 +243,7 @@ func ClearMetersForTest() {
 		meter.m.Stop()
 		delete(allMeters.meters, k)
 	}
+	startCleanup()
 }
 
 func meterExporterForResource(r *resource.Resource) *meterExporter {
@@ -195,6 +253,7 @@ func meterExporterForResource(r *resource.Resource) *meterExporter {
 		mE = &meterExporter{}
 		allMeters.meters[key] = mE
 	}
+	mE.t = allMeters.clock.Now()
 	if mE.o != nil {
 		return mE
 	}
@@ -219,6 +278,9 @@ func meterExporterForResource(r *resource.Resource) *meterExporter {
 // optionForResource finds or creates a stats exporter for the resource, and
 // returns a stats.Option indicating which meter to record to.
 func optionForResource(r *resource.Resource) (stats.Options, error) {
+	// Start the allMeters cleanup thread, if not already started
+	cleanupOnce.Do(startCleanup)
+
 	allMeters.lock.Lock()
 	defer allMeters.lock.Unlock()
 
@@ -313,6 +375,7 @@ var _ view.Meter = (*defaultMeterImpl)(nil)
 var defaultMeter = meterExporter{
 	m: &defaultMeterImpl{},
 	o: stats.WithRecorder(nil),
+	t: time.Now(), // time.Now() here is ok - defaultMeter never expires, so this won't be checked
 }
 
 func (*defaultMeterImpl) Record(*tag.Map, interface{}, map[string]interface{}) {
diff --git a/metrics/stackdriver_exporter.go b/metrics/stackdriver_exporter.go
index ec9dba8c2..e3bebe187 100644
--- a/metrics/stackdriver_exporter.go
+++ b/metrics/stackdriver_exporter.go
@@ -89,7 +89,7 @@ var (
 	// which should be promoted to Stackdriver Resource labels via opencensus resources.
 	metricToResourceLabels = map[string]*resourceTemplate{}
 
-	// A variable for testing to reduce the size (number of metrics) buffered before
+	// TestOverrideBundleCount is a variable for testing to reduce the size (number of metrics) buffered before
 	// Stackdriver will send a bundled metric report. Only applies if non-zero.
 	TestOverrideBundleCount = 0
 )
diff --git a/profiling/server.go b/profiling/server.go
index 0dc1eebc0..9ac2c5f05 100644
--- a/profiling/server.go
+++ b/profiling/server.go
@@ -94,7 +94,7 @@ func (h *Handler) UpdateFromConfigMap(configMap *corev1.ConfigMap) {
 	}
 
 	if h.enabled.Swap(enabled) != enabled {
-		h.log.Infof("Profiling enabled: %t", enabled)
+		h.log.Info("Profiling enabled: ", enabled)
 	}
 }
 
diff --git a/reconciler/events.go b/reconciler/events.go
index 6c635643b..262b26222 100644
--- a/reconciler/events.go
+++ b/reconciler/events.go
@@ -62,7 +62,7 @@ func NewEvent(eventtype, reason, messageFmt string, args ...interface{}) Event {
 
 // ReconcilerEvent wraps the fields required for recorders to create a
 // kubernetes recorder Event.
-type ReconcilerEvent struct {
+type ReconcilerEvent struct { //nolint for backcompat.
 	EventType string
 	Reason    string
 	Format    string
diff --git a/tracing/config/tracing.go b/tracing/config/tracing.go
index baa3377d1..84128b440 100644
--- a/tracing/config/tracing.go
+++ b/tracing/config/tracing.go
@@ -132,8 +132,8 @@ func NewTracingConfigFromConfigMap(config *corev1.ConfigMap) (*Config, error) {
 
 // JsonToTracingConfig converts a json string of a Config.
 // Returns a non-nil Config always and an eventual error.
-// nolint:stylecheck // No rename due to backwards incompatibility.
-func JsonToTracingConfig(jsonCfg string) (*Config, error) {
+func JsonToTracingConfig(jsonCfg string) (*Config, error) { //nolint:stylecheck for backcompat.
+
 	if jsonCfg == "" {
 		return defaultConfig(), errors.New("empty json tracing config")
 	}
@@ -151,8 +151,7 @@ func JsonToTracingConfig(jsonCfg string) (*Config, error) {
 }
 
 // TracingConfigToJson converts a Config to a json string.
-// nolint:stylecheck // No rename due to backwards incompatibility.
-func TracingConfigToJson(cfg *Config) (string, error) {
+func TracingConfigToJson(cfg *Config) (string, error) { //nolint:stylecheck for backcompat.
 	if cfg == nil {
 		return "", nil
 	}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 01e6688fc..4f0c80f28 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -972,7 +972,7 @@ k8s.io/utils/buffer
 k8s.io/utils/integer
 k8s.io/utils/pointer
 k8s.io/utils/trace
-# knative.dev/test-infra v0.0.0-20201002164834-8c07ff018549
+# knative.dev/test-infra v0.0.0-20201005021934-df2084cec789
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/boskos v0.0.0-20200729174948-794df80db9c9
diff --git a/webhook/resourcesemantics/conversion/internal/types.go b/webhook/resourcesemantics/conversion/internal/types.go
index 52f67006a..7f650c885 100644
--- a/webhook/resourcesemantics/conversion/internal/types.go
+++ b/webhook/resourcesemantics/conversion/internal/types.go
@@ -143,7 +143,7 @@ func NewV2(prop string) *V2Resource {
 			APIVersion: Group + "/v2",
 		},
 		Spec: Spec{
-			Property: fmt.Sprintf("prefix/%s", prop),
+			Property: "prefix/" + prop,
 		},
 	}
 }
@@ -157,7 +157,7 @@ func NewV3(prop string) *V3Resource {
 			APIVersion: Group + "/v3",
 		},
 		Spec: SpecWithDefault{
-			Property: fmt.Sprintf("%s/suffix", prop),
+			Property: prop + "/suffix",
 		},
 	}
 	v3.SetDefaults(context.Background())
diff --git a/webhook/webhook.go b/webhook/webhook.go
index aff5cf8b4..55726c5f5 100644
--- a/webhook/webhook.go
+++ b/webhook/webhook.go
@@ -181,7 +181,7 @@ func (wh *Webhook) Run(stop <-chan struct{}) error {
 
 	server := &http.Server{
 		Handler: drainer,
-		Addr:    fmt.Sprintf(":%d", wh.Options.Port),
+		Addr:    fmt.Sprint(":", wh.Options.Port),
 		TLSConfig: &tls.Config{
 			MinVersion: tls.VersionTLS12,
 			GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) {


Test patch:
diff --git a/.github/workflows/knative-go-test.yaml b/.github/workflows/knative-go-test.yaml
index bfe6c6ad2..8810ba7db 100644
--- a/.github/workflows/knative-go-test.yaml
+++ b/.github/workflows/knative-go-test.yaml
@@ -55,7 +55,7 @@ jobs:
 
       - if: steps.codecov-enabled.outputs.files_exists == 'true'
         name: Produce Go Coverage
-        run: echo '::set-env name=COVER_OPTS::-coverprofile=coverage.txt -covermode=atomic'
+        run: echo 'COVER_OPTS=-coverprofile=coverage.txt -covermode=atomic' >> $GITHUB_ENV
 
       - name: Test
         run: go test -race $COVER_OPTS ./...
diff --git a/apis/duck/v1/roundtrip_test.go b/apis/duck/v1/roundtrip_test.go
new file mode 100644
index 000000000..25bca151b
--- /dev/null
+++ b/apis/duck/v1/roundtrip_test.go
@@ -0,0 +1,38 @@
+/*
+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 v1
+
+import (
+	"testing"
+
+	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
+	"k8s.io/apimachinery/pkg/runtime"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	pkgfuzzer "knative.dev/pkg/apis/testing/fuzzer"
+	"knative.dev/pkg/apis/testing/roundtrip"
+)
+
+func TestEventingRoundTripTypesToJSON(t *testing.T) {
+	scheme := runtime.NewScheme()
+	utilruntime.Must(AddToScheme(scheme))
+
+	fuzzerFuncs := fuzzer.MergeFuzzerFuncs(
+		pkgfuzzer.Funcs,
+		FuzzerFuncs,
+	)
+	roundtrip.ExternalTypesViaJSON(t, scheme, fuzzerFuncs)
+}
diff --git a/apis/duck/v1alpha1/roundtrip_test.go b/apis/duck/v1alpha1/roundtrip_test.go
new file mode 100644
index 000000000..c40197e50
--- /dev/null
+++ b/apis/duck/v1alpha1/roundtrip_test.go
@@ -0,0 +1,37 @@
+/*
+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 v1alpha1
+
+import (
+	"testing"
+
+	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
+	"k8s.io/apimachinery/pkg/runtime"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	pkgfuzzer "knative.dev/pkg/apis/testing/fuzzer"
+	"knative.dev/pkg/apis/testing/roundtrip"
+)
+
+func TestEventingRoundTripTypesToJSON(t *testing.T) {
+	scheme := runtime.NewScheme()
+	utilruntime.Must(AddToScheme(scheme))
+
+	fuzzerFuncs := fuzzer.MergeFuzzerFuncs(
+		pkgfuzzer.Funcs,
+	)
+	roundtrip.ExternalTypesViaJSON(t, scheme, fuzzerFuncs)
+}
diff --git a/apis/duck/v1beta1/roundtrip_test.go b/apis/duck/v1beta1/roundtrip_test.go
new file mode 100644
index 000000000..f727740ae
--- /dev/null
+++ b/apis/duck/v1beta1/roundtrip_test.go
@@ -0,0 +1,38 @@
+/*
+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 v1beta1
+
+import (
+	"testing"
+
+	"k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
+	"k8s.io/apimachinery/pkg/runtime"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	pkgfuzzer "knative.dev/pkg/apis/testing/fuzzer"
+	"knative.dev/pkg/apis/testing/roundtrip"
+)
+
+func TestEventingRoundTripTypesToJSON(t *testing.T) {
+	scheme := runtime.NewScheme()
+	utilruntime.Must(AddToScheme(scheme))
+
+	fuzzerFuncs := fuzzer.MergeFuzzerFuncs(
+		pkgfuzzer.Funcs,
+		FuzzerFuncs,
+	)
+	roundtrip.ExternalTypesViaJSON(t, scheme, fuzzerFuncs)
+}
diff --git a/apis/field_error_test.go b/apis/field_error_test.go
index 7adc9202b..2f1a78a38 100644
--- a/apis/field_error_test.go
+++ b/apis/field_error_test.go
@@ -17,7 +17,6 @@ limitations under the License.
 package apis
 
 import (
-	"fmt"
 	"strconv"
 	"strings"
 	"testing"
@@ -241,7 +240,7 @@ Second: X, Y, Z`,
 
 			for _, p := range []string{"3", "2", "1"} {
 				e := fe.ViaField(p)
-				e.Details = fmt.Sprintf("here at %s", p)
+				e.Details = "here at " + p
 				for i := 0; i < 3; i++ {
 					fe = fe.Also(e)
 				}
diff --git a/kvstore/kvstore_cm_test.go b/kvstore/kvstore_cm_test.go
index 4225e1ae0..abcb5cae5 100644
--- a/kvstore/kvstore_cm_test.go
+++ b/kvstore/kvstore_cm_test.go
@@ -46,7 +46,7 @@ type testClient struct {
 	clientset v1.CoreV1Interface
 }
 
-func NewTestClient(objects ...runtime.Object) *testClient {
+func newTestClient(objects ...runtime.Object) *testClient {
 	tc := testClient{}
 	cs := fake.NewSimpleClientset(objects...)
 	cs.PrependReactor("create", "*", func(action clientgotesting.Action) (handled bool, ret runtime.Object, err error) {
@@ -64,7 +64,7 @@ func NewTestClient(objects ...runtime.Object) *testClient {
 }
 
 func TestInitCreates(t *testing.T) {
-	tc := NewTestClient()
+	tc := newTestClient()
 	cs := NewConfigMapKVStore(context.Background(), name, namespace, tc.clientset)
 	err := cs.Init(context.Background())
 	if err != nil {
@@ -82,14 +82,14 @@ func TestInitCreates(t *testing.T) {
 }
 
 func TestLoadNonexisting(t *testing.T) {
-	tc := NewTestClient()
+	tc := newTestClient()
 	if NewConfigMapKVStore(context.Background(), name, namespace, tc.clientset).Load(context.Background()) == nil {
 		t.Error("non-existent store load didn't fail")
 	}
 }
 
 func TestInitLoads(t *testing.T) {
-	tc := NewTestClient([]runtime.Object{configMap(map[string]string{"foo": marshal(t, "bar")})}...)
+	tc := newTestClient([]runtime.Object{configMap(map[string]string{"foo": marshal(t, "bar")})}...)
 	cs := NewConfigMapKVStore(context.Background(), name, namespace, tc.clientset)
 	err := cs.Init(context.Background())
 	if err != nil {
@@ -115,7 +115,7 @@ func TestInitLoads(t *testing.T) {
 }
 
 func TestLoadSaveUpdate(t *testing.T) {
-	tc := NewTestClient([]runtime.Object{configMap(map[string]string{"foo": marshal(t, "bar")})}...)
+	tc := newTestClient([]runtime.Object{configMap(map[string]string{"foo": marshal(t, "bar")})}...)
 	cs := NewConfigMapKVStore(context.Background(), name, namespace, tc.clientset)
 	err := cs.Init(context.Background())
 	if err != nil {
@@ -145,7 +145,7 @@ func TestLoadSaveUpdateComplex(t *testing.T) {
 		Stuff:              []string{"first", "second", "third"},
 	}
 
-	tc := NewTestClient([]runtime.Object{configMap(map[string]string{"foo": marshal(t, &ts)})}...)
+	tc := newTestClient([]runtime.Object{configMap(map[string]string{"foo": marshal(t, &ts)})}...)
 	cs := NewConfigMapKVStore(context.Background(), name, namespace, tc.clientset)
 	err := cs.Init(context.Background())
 	if err != nil {
diff --git a/metrics/resource_view_test.go b/metrics/resource_view_test.go
index 3c5a8e241..e527672b8 100644
--- a/metrics/resource_view_test.go
+++ b/metrics/resource_view_test.go
@@ -37,6 +37,7 @@ import (
 	"go.opencensus.io/stats"
 	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
+	"k8s.io/apimachinery/pkg/util/clock"
 
 	emptypb "github.com/golang/protobuf/ptypes/empty"
 	"github.com/google/go-cmp/cmp"
@@ -144,6 +145,70 @@ func TestSetFactory(t *testing.T) {
 	}
 }
 
+func TestAllMetersExpiration(t *testing.T) {
+	allMeters.clock = clock.Clock(clock.NewFakeClock(time.Now()))
+	var fakeClock *clock.FakeClock = allMeters.clock.(*clock.FakeClock)
+	ClearMetersForTest() // t+0m
+
+	// Add resource123
+	resource123 := r
+	resource123.Labels["id"] = "123"
+	_, err := optionForResource(&resource123)
+	if err != nil {
+		t.Error("Should succeed getting option, instead got error ", err)
+	}
+	// (123=0m, 456=Inf)
+
+	// Bump time to make resource123's expiry offset from resource456
+	fakeClock.Step(90 * time.Second) // t+1.5m
+	// (123=0m, 456=Inf)
+
+	// Add 456
+	resource456 := r
+	resource456.Labels["id"] = "456"
+	_, err = optionForResource(&resource456)
+	if err != nil {
+		t.Error("Should succeed getting option, instead got error ", err)
+	}
+	allMeters.lock.Lock()
+	if len(allMeters.meters) != 3 {
+		t.Errorf("len(allMeters)=%d, want: 3", len(allMeters.meters))
+	}
+	allMeters.lock.Unlock()
+	// (123=1.5m, 456=0m)
+
+	// Warm up the older entry
+	fakeClock.Step(90 * time.Second) //t+3m
+	// (123=4.5m, 456=3m)
+
+	// Refresh the first entry
+	_, err = optionForResource(&resource123)
+	if err != nil {
+		t.Error("Should succeed getting option, instead got error ", err)
+	}
+	// (123=0, 456=1.5m)
+
+	// Expire the second entry
+	fakeClock.Step(9 * time.Minute) // t+12m
+	time.Sleep(1 * time.Second)     // Wait a second on the wallclock, so that the cleanup thread has time to finish a loop
+	allMeters.lock.Lock()
+	if len(allMeters.meters) != 2 {
+		t.Errorf("len(allMeters)=%d, want: 2", len(allMeters.meters))
+	}
+	allMeters.lock.Unlock()
+	// (123=9m, 456=10.5m)
+	// non-expiring defaultMeter was just tested
+
+	// Add resource789
+	resource789 := r
+	resource789.Labels["id"] = "789"
+	_, err = optionForResource(&resource789)
+	if err != nil {
+		t.Error("Should succeed getting option, instead got error ", err)
+	}
+	// (123=9m, 456=evicted, 789=0m)
+}
+
 func TestResourceAsString(t *testing.T) {
 	r1 := &resource.Resource{Type: "foobar", Labels: map[string]string{"k1": "v1", "k3": "v3", "k2": "v2"}}
 	r2 := &resource.Resource{Type: "foobar", Labels: map[string]string{"k2": "v2", "k3": "v3", "k1": "v1"}}
diff --git a/reconciler/retry_test.go b/reconciler/retry_test.go
index b10f41284..fb9f4d57d 100644
--- a/reconciler/retry_test.go
+++ b/reconciler/retry_test.go
@@ -81,7 +81,7 @@ func TestRetryUpdateConflicts(t *testing.T) {
 
 func TestRetryTestErrors(t *testing.T) {
 	errAny := errors.New("foo")
-	errGKE := errors.New(`Operation cannot be fulfilled on resourcequotas "gke-resource-quotas": StorageError: invalid object, Code: 4, Key: /registry/resourcequotas/serving-tests/gke-resource-quotas, ResourceVersion: 0, AdditionalErrorMsg: Precondition failed: UID in precondition: 7aaedbdf-caa8-41e7-94cb-f8c053038e86, UID in object meta:`)
+	errGKE := errors.New(`operation cannot be fulfilled on resourcequotas "gke-resource-quotas": StorageError: invalid object, Code: 4, Key: /registry/resourcequotas/serving-tests/gke-resource-quotas, ResourceVersion: 0, AdditionalErrorMsg: Precondition failed: UID in precondition: 7aaedbdf-caa8-41e7-94cb-f8c053038e86, UID in object meta`)
 	errEtcd := errors.New("etcdserver: request timed out")
 	errConflict := apierrs.NewConflict(v1.Resource("foo"), "bar", errAny)
 
diff --git a/test/gcs/gcs.go b/test/gcs/gcs.go
index 13ae206fa..f1b9a695a 100644
--- a/test/gcs/gcs.go
+++ b/test/gcs/gcs.go
@@ -31,6 +31,7 @@ import (
 	"google.golang.org/api/option"
 )
 
+//nolint — there's also Client so they collide.
 type GCSClient struct {
 	*storage.Client
 }
diff --git a/test/gcs/mock/errors.go b/test/gcs/mock/errors.go
index f8c58e434..fefda08c8 100644
--- a/test/gcs/mock/errors.go
+++ b/test/gcs/mock/errors.go
@@ -28,7 +28,7 @@ func (e *notEmptyBucketError) Error() string {
 	return fmt.Sprintf("bucket %q not empty, use force=true", e.bkt)
 }
 
-func NewNotEmptyBucketError(bkt string) *notEmptyBucketError {
+func NewNotEmptyBucketError(bkt string) error {
 	return &notEmptyBucketError{bkt}
 }
 
@@ -36,7 +36,7 @@ type noBucketError struct {
 	bkt string
 }
 
-func NewNoBucketError(bkt string) *noBucketError {
+func NewNoBucketError(bkt string) error {
 	return &noBucketError{bkt}
 }
 
@@ -48,7 +48,7 @@ type bucketExistError struct {
 	bkt string
 }
 
-func NewBucketExistError(bkt string) *bucketExistError {
+func NewBucketExistError(bkt string) error {
 	return &bucketExistError{bkt}
 }
 
@@ -62,7 +62,7 @@ type noObjectError struct {
 	path string
 }
 
-func NewNoObjectError(bkt, obj, path string) *noObjectError {
+func NewNoObjectError(bkt, obj, path string) error {
 	return &noObjectError{
 		bkt:  bkt,
 		obj:  obj,
diff --git a/test/gcs/mock/mock.go b/test/gcs/mock/mock.go
index 801b0f1e3..4af5aae66 100644
--- a/test/gcs/mock/mock.go
+++ b/test/gcs/mock/mock.go
@@ -65,13 +65,12 @@ type clientMocker struct {
 	revIndex map[bucket]project
 }
 
-func NewClientMocker() *clientMocker {
-	c := &clientMocker{
+func newClientMocker() *clientMocker {
+	return &clientMocker{
 		gcp:      make(map[project]*buckets),
 		err:      make(map[Method]*ReturnError),
 		revIndex: make(map[bucket]project),
 	}
-	return c
 }
 
 // SetError sets the number of calls of an interface function before an error is returned.
diff --git a/test/gcs/mock/mock_example_test.go b/test/gcs/mock/mock_example_test.go
index 0ddbcdaed..ef7ff43a5 100644
--- a/test/gcs/mock/mock_example_test.go
+++ b/test/gcs/mock/mock_example_test.go
@@ -57,7 +57,7 @@ func topFunction(c gcs.Client) error {
 }
 
 func Example_topFunction() {
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 
 	// Call to ReadObject, first call should return error, but returns nil
 	// because it is overridden.
diff --git a/test/gcs/mock/mock_test.go b/test/gcs/mock/mock_test.go
index 1c4570c65..c294c5f48 100644
--- a/test/gcs/mock/mock_test.go
+++ b/test/gcs/mock/mock_test.go
@@ -91,7 +91,7 @@ func TestSetError(t *testing.T) {
 
 	for _, tt := range testCases {
 		t.Run(tt.testname, func(t *testing.T) {
-			mockClient := NewClientMocker()
+			mockClient := newClientMocker()
 			mockClient.SetError(tt.m)
 
 			for k, v := range tt.m {
@@ -277,7 +277,7 @@ func TestClearError(t *testing.T) {
 
 	for _, tt := range testCases {
 		t.Run(tt.testname, func(t *testing.T) {
-			mockClient := NewClientMocker()
+			mockClient := newClientMocker()
 			mockClient.SetError(tt.m)
 			mockClient.ClearError()
 
@@ -338,7 +338,7 @@ func TestClearError(t *testing.T) {
 
 func TestNewStorageBucket(t *testing.T) {
 	ctx := context.Background()
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 	bktName1 := "test-bucket1"
 	project1 := "test-project1"
 	bktName2 := "test-bucket2"
@@ -406,7 +406,7 @@ func TestNewStorageBucket(t *testing.T) {
 
 func TestDeleteStorageBucket(t *testing.T) {
 	ctx := context.Background()
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 	bktName1 := "test-bucket1"
 	project1 := "test-project1"
 
@@ -464,7 +464,7 @@ func TestDeleteStorageBucket(t *testing.T) {
 
 func TestExists(t *testing.T) {
 	ctx := context.Background()
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 	bktName1 := "test-bucket1"
 	project1 := "test-project1"
 	object1 := "object1"
@@ -522,7 +522,7 @@ func TestExists(t *testing.T) {
 
 func TestListChildrenFiles(t *testing.T) {
 	ctx := context.Background()
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 	bktName1 := "test-bucket1"
 	project1 := "test-project1"
 	dir1 := "dir"
@@ -586,7 +586,7 @@ func TestListChildrenFiles(t *testing.T) {
 
 func TestListDirectChildren(t *testing.T) {
 	ctx := context.Background()
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 	bktName1 := "test-bucket1"
 	project1 := "test-project1"
 	dir1 := "dir"
@@ -652,7 +652,7 @@ func TestListDirectChildren(t *testing.T) {
 
 func TestAttrObject(t *testing.T) {
 	ctx := context.Background()
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 	bktName1 := "test-bucket1"
 	project1 := "test-project1"
 	object1 := "dir/object1"
@@ -713,7 +713,7 @@ func TestAttrObject(t *testing.T) {
 
 func TestCopyObject(t *testing.T) {
 	ctx := context.Background()
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 	bktName1 := "test-bucket1"
 	bktName2 := "test-bucket2"
 	project1 := "test-project1"
@@ -805,7 +805,7 @@ func TestCopyObject(t *testing.T) {
 
 func TestReadObject(t *testing.T) {
 	ctx := context.Background()
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 	bktName1 := "test-bucket1"
 	project1 := "test-project1"
 	object1 := "object1"
@@ -862,7 +862,7 @@ func TestReadObject(t *testing.T) {
 
 func TestWriteObject(t *testing.T) {
 	ctx := context.Background()
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 	bktName1 := "test-bucket1"
 	project1 := "test-project1"
 	badBkt := "non-existent-bucket"
@@ -924,7 +924,7 @@ func TestWriteObject(t *testing.T) {
 
 func TestDeleteObject(t *testing.T) {
 	ctx := context.Background()
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 	bktName1 := "test-bucket1"
 	project1 := "test-project1"
 	object1 := "dir/object1"
@@ -974,7 +974,7 @@ func TestDeleteObject(t *testing.T) {
 
 func TestDownload(t *testing.T) {
 	ctx := context.Background()
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 	bktName1 := "test-bucket1"
 	project1 := "test-project1"
 	object1 := "dir/object1"
@@ -1036,7 +1036,7 @@ func TestDownload(t *testing.T) {
 
 func TestUpload(t *testing.T) {
 	ctx := context.Background()
-	mockClient := NewClientMocker()
+	mockClient := newClientMocker()
 	bktName1 := "test-bucket1"
 	project1 := "test-project1"
 	object1 := "dir/object1"
diff --git a/test/helpers/dryrun.go b/test/helpers/dryrun.go
index 68f799444..c19881556 100644
--- a/test/helpers/dryrun.go
+++ b/test/helpers/dryrun.go
@@ -23,7 +23,7 @@ import (
 // Run can run functions that needs dryrun support.
 func Run(message string, call func() error, dryrun bool) error {
 	if dryrun {
-		log.Printf("[dry run] %s", message)
+		log.Print("[dry run] ", message)
 		return nil
 	}
 	log.Print(message)
diff --git a/test/helpers/name.go b/test/helpers/name.go
index 9cb0d529f..ef5ac4dbd 100644
--- a/test/helpers/name.go
+++ b/test/helpers/name.go
@@ -71,8 +71,8 @@ func RandomString() string {
 }
 
 // For the same prefix more specific should come first.
-// Note: we expect GRPC.
-var knownNames = []string{"HTTPS", "HTTP2", "HTTP", "GRPC", "TLS", "WS", "H2C"}
+// Note: we expect GRPC vs gRPC.
+var knownNames = []string{"GRPC", "H2C", "HTTPS", "HTTP2", "HTTP", "REST", "TLS", "WS"}
 
 // MakeK8sNamePrefix converts each chunk of non-alphanumeric character into a single dash
 // and also convert camelcase tokens into dash-delimited lowercase tokens.
diff --git a/test/interactive/docker.go b/test/interactive/docker.go
index 08bbbc9c9..679ae3ada 100644
--- a/test/interactive/docker.go
+++ b/test/interactive/docker.go
@@ -102,7 +102,7 @@ func (d *Docker) AddRWOverlay(externalDirectory, internalDirectory string) func(
 	// Print command to run so user knows why it is asking for sudo password (if it does)
 	log.Println(mount)
 	if err = mount.Run(); err != nil {
-		log.Fatalf("Unable to create overlay mount, so giving up: %v", err)
+		log.Fatal("Unable to create overlay mount, so giving up: ", err)
 	}
 	d.AddMount("bind", overlayDir, internalDirectory)
 	return func() {
diff --git a/test/kube_checks.go b/test/kube_checks.go
index a406f211a..cf804a9b1 100644
--- a/test/kube_checks.go
+++ b/test/kube_checks.go
@@ -65,7 +65,7 @@ func WaitForDeploymentState(ctx context.Context, client *KubeClient, name string
 // 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)
-	span := logging.GetEmitableSpan(ctx, fmt.Sprintf("WaitForPodListState/%s", desc))
+	span := logging.GetEmitableSpan(ctx, "WaitForPodListState/"+desc)
 	defer span.End()
 
 	return wait.PollImmediate(interval, podTimeout, func() (bool, error) {
diff --git a/test/logging/error.go b/test/logging/error.go
index e34916075..774a60612 100644
--- a/test/logging/error.go
+++ b/test/logging/error.go
@@ -77,7 +77,7 @@ func (e *structuredError) EnableValuePrinting() {
 
 // Create a StructuredError. Gives a little better logging when given to a TLogger.
 // This may prove to not be useful if users use the logger's WithValues() better.
-func Error(msg string, keysAndValues ...interface{}) *structuredError {
+func Error(msg string, keysAndValues ...interface{}) error {
 	return &structuredError{msg, keysAndValues, true}
 }
 
diff --git a/test/logging/tlogger.go b/test/logging/tlogger.go
index 6f58f3014..42891bc21 100644
--- a/test/logging/tlogger.go
+++ b/test/logging/tlogger.go
@@ -137,7 +137,7 @@ func validateKeysAndValues(keysAndValues ...interface{}) bool {
 		_, isField := keysAndValues[i].(zapcore.Field)
 		_, isString := keysAndValues[i].(string)
 		if isField {
-			i += 1
+			i++
 		} else if isString {
 			if i == length-1 {
 				return false
@@ -160,8 +160,7 @@ func (o *TLogger) interfacesToFields(things ...interface{}) []interface{} {
 	return fields
 }
 
-// nolint:stylecheck // Returning the error first is okay and expected here.
-func (o *TLogger) errorWithRuntimeCheck(stringThenKeysAndValues ...interface{}) (error, string, []interface{}) {
+func (o *TLogger) errorWithRuntimeCheck(stringThenKeysAndValues ...interface{}) (error, string, []interface{}) { //nolint Returning the error first is okay and expected here.
 	if len(stringThenKeysAndValues) == 0 {
 		return nil, "", nil
 	}
diff --git a/test/mako/analyzer.go b/test/mako/analyzer.go
index d581543b7..b945aa4e5 100644
--- a/test/mako/analyzer.go
+++ b/test/mako/analyzer.go
@@ -17,9 +17,9 @@ limitations under the License.
 package mako
 
 import (
-	"github.com/golang/protobuf/proto"
 	tpb "github.com/google/mako/clients/proto/analyzers/threshold_analyzer_go_proto"
 	mpb "github.com/google/mako/spec/proto/mako_go_proto"
+	"google.golang.org/protobuf/proto"
 )
 
 // NewCrossRunConfig returns a config that can be used in ThresholdAnalyzer.
diff --git a/test/mako/config/benchmark.go b/test/mako/config/benchmark.go
index 4a1f2e0bc..8f22a865c 100644
--- a/test/mako/config/benchmark.go
+++ b/test/mako/config/benchmark.go
@@ -23,7 +23,7 @@ import (
 	"os"
 	"path/filepath"
 
-	"github.com/golang/protobuf/proto"
+	"github.com/golang/protobuf/proto" //nolint apis incompatible with the new version
 	mpb "github.com/google/mako/spec/proto/mako_go_proto"
 )
 
@@ -34,7 +34,7 @@ const configMako = "/etc/config-mako"
 func MustGetBenchmark() *mpb.BenchmarkInfo {
 	bench, err := getBenchmark()
 	if err != nil {
-		log.Fatalf("unable to determine benchmark info: %v", err)
+		log.Fatal("unable to determine benchmark info: ", err)
 	}
 	return bench
 }
diff --git a/test/mako/config/environment.go b/test/mako/config/environment.go
index 62e72c243..464d6030a 100644
--- a/test/mako/config/environment.go
+++ b/test/mako/config/environment.go
@@ -51,7 +51,7 @@ func GetRepository() string {
 func MustGetTags() []string {
 	cfg, err := loadConfig()
 	if err != nil {
-		log.Fatalf("unable to load config from the configmap: %v", err)
+		log.Fatal("unable to load config from the configmap: ", err)
 	}
 	return cfg.AdditionalTags
 }
diff --git a/test/mako/sidecar.go b/test/mako/sidecar.go
index 92377143a..cf801977d 100644
--- a/test/mako/sidecar.go
+++ b/test/mako/sidecar.go
@@ -123,17 +123,17 @@ func SetupHelper(ctx context.Context, benchmarkKey *string, benchmarkName *strin
 
 	// Decorate GCP metadata as tags (when we're running on GCP).
 	if projectID, err := metadata.ProjectID(); err != nil {
-		log.Printf("GCP project ID is not available: %v", err)
+		log.Print("GCP project ID is not available: ", err)
 	} else {
 		tags = append(tags, "project-id="+EscapeTag(projectID))
 	}
 	if zone, err := metadata.Zone(); err != nil {
-		log.Printf("GCP zone is not available: %v", err)
+		log.Print("GCP zone is not available: ", err)
 	} else {
 		tags = append(tags, "zone="+EscapeTag(zone))
 	}
 	if machineType, err := metadata.Get("instance/machine-type"); err != nil {
-		log.Printf("GCP machine type is not available: %v", err)
+		log.Print("GCP machine type is not available: ", err)
 	} else if parts := strings.Split(machineType, "/"); len(parts) != 4 {
 		tags = append(tags, "instanceType="+EscapeTag(parts[3]))
 	}
diff --git a/test/mako/stub-sidecar/main.go b/test/mako/stub-sidecar/main.go
index 540a208d6..8f7fe4101 100644
--- a/test/mako/stub-sidecar/main.go
+++ b/test/mako/stub-sidecar/main.go
@@ -24,7 +24,7 @@ import (
 	"net/http"
 	"strings"
 
-	"github.com/golang/protobuf/jsonpb"
+	"github.com/golang/protobuf/jsonpb" //nolint: the newer package has differnet interface.
 	mako "github.com/google/mako/spec/proto/mako_go_proto"
 
 	"log"
diff --git a/testutils/clustermanager/e2e-tests/boskos/fake/fake.go b/testutils/clustermanager/e2e-tests/boskos/fake/fake.go
index bd434b34d..7f28ce7df 100644
--- a/testutils/clustermanager/e2e-tests/boskos/fake/fake.go
+++ b/testutils/clustermanager/e2e-tests/boskos/fake/fake.go
@@ -23,28 +23,26 @@ import (
 	boskoscommon "sigs.k8s.io/boskos/common"
 )
 
-const (
-	fakeOwner = "fake-owner"
-)
+const fakeOwner = "fake-owner"
 
-// FakeBoskosClient implements boskos.Operation
-type FakeBoskosClient struct {
+// BoskosClient implements boskos.Operation
+type BoskosClient struct {
 	resources []*boskoscommon.Resource
 }
 
-func (c *FakeBoskosClient) getOwner(host *string) string {
+func (c *BoskosClient) getOwner(host *string) string {
 	if host == nil {
 		return fakeOwner
 	}
 	return *host
 }
 
-func (c *FakeBoskosClient) GetResources() []*boskoscommon.Resource {
+func (c *BoskosClient) GetResources() []*boskoscommon.Resource {
 	return c.resources
 }
 
 // AcquireGKEProject fakes to be no op
-func (c *FakeBoskosClient) AcquireGKEProject(resType string) (*boskoscommon.Resource, error) {
+func (c *BoskosClient) AcquireGKEProject(resType string) (*boskoscommon.Resource, error) {
 	for _, res := range c.resources {
 		if res.State == boskoscommon.Free {
 			res.State = boskoscommon.Busy
@@ -57,7 +55,7 @@ func (c *FakeBoskosClient) AcquireGKEProject(resType string) (*boskoscommon.Reso
 }
 
 // ReleaseGKEProject fakes to be no op
-func (c *FakeBoskosClient) ReleaseGKEProject(name string) error {
+func (c *BoskosClient) ReleaseGKEProject(name string) error {
 	owner := c.getOwner(nil)
 	for _, res := range c.resources {
 		if res.Name == name {
@@ -73,7 +71,7 @@ func (c *FakeBoskosClient) ReleaseGKEProject(name string) error {
 }
 
 // NewGKEProject adds Boskos resources for testing purpose
-func (c *FakeBoskosClient) NewGKEProject(name string) {
+func (c *BoskosClient) NewGKEProject(name string) {
 	c.resources = append(c.resources, &boskoscommon.Resource{
 		Type:  boskos.GKEProjectResource,
 		Name:  name,
diff --git a/testutils/clustermanager/e2e-tests/example_test.go b/testutils/clustermanager/e2e-tests/example_test.go
index 22c7daca9..1002fb493 100644
--- a/testutils/clustermanager/e2e-tests/example_test.go
+++ b/testutils/clustermanager/e2e-tests/example_test.go
@@ -53,5 +53,5 @@ func Example() {
 		log.Fatalf("failed acquire cluster: '%v'", err)
 	}
 	log.Printf("GKE project is: %q", gkeOps.Project)
-	log.Printf("GKE cluster is: %v", gkeOps.Cluster)
+	log.Print("GKE cluster is: ", gkeOps.Cluster)
 }
diff --git a/testutils/clustermanager/e2e-tests/gke_test.go b/testutils/clustermanager/e2e-tests/gke_test.go
index 6ee04488a..27219cac8 100644
--- a/testutils/clustermanager/e2e-tests/gke_test.go
+++ b/testutils/clustermanager/e2e-tests/gke_test.go
@@ -40,7 +40,7 @@ func setupFakeGKECluster() GKECluster {
 	return GKECluster{
 		Request:    &GKERequest{},
 		operations: gkeFake.NewGKESDKClient(),
-		boskosOps:  &boskosFake.FakeBoskosClient{},
+		boskosOps:  &boskosFake.BoskosClient{},
 	}
 }
 
@@ -600,7 +600,7 @@ func TestAcquire(t *testing.T) {
 			fgc := setupFakeGKECluster()
 			// Set up fake boskos
 			for _, bos := range data.boskosProjs {
-				fgc.boskosOps.(*boskosFake.FakeBoskosClient).NewGKEProject(bos)
+				fgc.boskosOps.(*boskosFake.BoskosClient).NewGKEProject(bos)
 			}
 			fgc.Request = &GKERequest{
 				Request: gke.Request{
@@ -783,9 +783,9 @@ func TestDelete(t *testing.T) {
 			}
 			// Set up fake boskos
 			for _, bos := range data.boskosState {
-				fgc.boskosOps.(*boskosFake.FakeBoskosClient).NewGKEProject(bos.Name)
+				fgc.boskosOps.(*boskosFake.BoskosClient).NewGKEProject(bos.Name)
 				// Acquire with default user
-				fgc.boskosOps.(*boskosFake.FakeBoskosClient).AcquireGKEProject(defaultResourceType)
+				fgc.boskosOps.(*boskosFake.BoskosClient).AcquireGKEProject(defaultResourceType)
 			}
 
 			err := fgc.Delete()
@@ -793,7 +793,7 @@ func TestDelete(t *testing.T) {
 			if data.cluster != nil {
 				gotCluster, _ = fgc.operations.GetCluster(fakeProj, data.cluster.Location, "", data.cluster.Name)
 			}
-			gotBoskos := fgc.boskosOps.(*boskosFake.FakeBoskosClient).GetResources()
+			gotBoskos := fgc.boskosOps.(*boskosFake.BoskosClient).GetResources()
 			errMsg := fmt.Sprintf("testing deleting cluster, with:\n\tIs Prow: '%v'\n\tIs Boskos: '%v'\n\t"+
 				"existing cluster: '%v'\n\tboskos state: '%v'",
 				data.isProw, data.isBoskos, data.cluster, data.boskosState)
diff --git a/testutils/clustermanager/e2e-tests/util.go b/testutils/clustermanager/e2e-tests/util.go
index 8d9eefa89..e816f3f60 100644
--- a/testutils/clustermanager/e2e-tests/util.go
+++ b/testutils/clustermanager/e2e-tests/util.go
@@ -69,7 +69,7 @@ func resolveGKEVersion(raw, location string) (string, error) {
 		if err != nil && version != "" {
 			return "", fmt.Errorf("failed getting the default version: %w", err)
 		}
-		log.Printf("Using default version, %s", version)
+		log.Print("Using default version, ", version)
 		return version, nil
 	case latestGKEVersion:
 		validCmd := "gcloud container get-server-config --format='value(validMasterVersions)' --zone=" + location
@@ -78,10 +78,10 @@ func resolveGKEVersion(raw, location string) (string, error) {
 			return "", fmt.Errorf("failed getting the list of valid versions: %w", err)
 		}
 		versions := strings.Split(versionsStr, ";")
-		log.Printf("Using the latest version, %s", strings.TrimSpace(versions[0]))
+		log.Print("Using the latest version, ", strings.TrimSpace(versions[0]))
 		return strings.TrimSpace(versions[0]), nil
 	default:
-		log.Printf("Using the custom version, %s", raw)
+		log.Print("Using the custom version, ", raw)
 		return raw, nil
 	}
 }
diff --git a/testutils/clustermanager/perf-tests/main.go b/testutils/clustermanager/perf-tests/main.go
index f8084c078..795d9f670 100644
--- a/testutils/clustermanager/perf-tests/main.go
+++ b/testutils/clustermanager/perf-tests/main.go
@@ -50,7 +50,7 @@ func main() {
 
 	client, err := testPkg.NewClient(gkeEnvironment)
 	if err != nil {
-		log.Fatalf("Failed setting up GKE client, cannot proceed: %v", err)
+		log.Fatal("Failed setting up GKE client, cannot proceed: ", err)
 	}
 	switch {
 	case isRecreate:
diff --git a/testutils/clustermanager/perf-tests/pkg/cluster.go b/testutils/clustermanager/perf-tests/pkg/cluster.go
index 473f072ba..5bde7719b 100644
--- a/testutils/clustermanager/perf-tests/pkg/cluster.go
+++ b/testutils/clustermanager/perf-tests/pkg/cluster.go
@@ -47,12 +47,13 @@ var (
 	serviceAccount         = flag.String("service-account", "", "service account that will be used on this cluster")
 )
 
-type gkeClient struct {
+// GKEClient is the GKE adapter to operate clusters.
+type GKEClient struct {
 	ops gke.SDKOperations
 }
 
 // NewClient will create a new gkeClient.
-func NewClient(environment string) (*gkeClient, error) {
+func NewClient(environment string) (*GKEClient, error) {
 	endpoint, err := gke.ServiceEndpoint(environment)
 	if err != nil {
 		return nil, err
@@ -63,7 +64,7 @@ func NewClient(environment string) (*gkeClient, error) {
 		return nil, fmt.Errorf("failed to set up GKE client: %w", err)
 	}
 
-	client := &gkeClient{
+	client := &GKEClient{
 		ops: operations,
 	}
 	return client, nil
@@ -71,7 +72,7 @@ func NewClient(environment string) (*gkeClient, error) {
 
 // RecreateClusters will delete and recreate the existing clusters, it will also create the clusters if they do
 // not exist for the corresponding benchmarks.
-func (gc *gkeClient) RecreateClusters(gcpProject, repo, benchmarkRoot string) error {
+func (gc *GKEClient) RecreateClusters(gcpProject, repo, benchmarkRoot string) error {
 	handleExistingCluster := func(cluster container.Cluster, configExists bool, config ClusterConfig) error {
 		// always delete the cluster, even if the cluster config is unchanged
 		return gc.handleExistingClusterHelper(gcpProject, cluster, configExists, config, false)
@@ -90,7 +91,7 @@ func (gc *gkeClient) RecreateClusters(gcpProject, repo, benchmarkRoot string) er
 // 2. If the benchmark's config is changed, delete the old cluster and create a new one with the new config
 // 3. If the benchmark is renamed, delete the old cluster and create a new one with the new name
 // 4. If the benchmark is deleted, delete the corresponding cluster
-func (gc *gkeClient) ReconcileClusters(gcpProject, repo, benchmarkRoot string) error {
+func (gc *GKEClient) ReconcileClusters(gcpProject, repo, benchmarkRoot string) error {
 	handleExistingCluster := func(cluster container.Cluster, configExists bool, config ClusterConfig) error {
 		// retain the cluster, if the cluster config is unchanged
 		return gc.handleExistingClusterHelper(gcpProject, cluster, configExists, config, true)
@@ -103,7 +104,7 @@ func (gc *gkeClient) ReconcileClusters(gcpProject, repo, benchmarkRoot string) e
 }
 
 // DeleteClusters will delete all existing clusters.
-func (gc *gkeClient) DeleteClusters(gcpProject, repo, benchmarkRoot string) error {
+func (gc *GKEClient) DeleteClusters(gcpProject, repo, benchmarkRoot string) error {
 	handleExistingCluster := func(cluster container.Cluster, configExists bool, config ClusterConfig) error {
 		// retain the cluster, if the cluster config is unchanged
 		return gc.deleteClusterWithRetries(gcpProject, cluster)
@@ -117,7 +118,7 @@ func (gc *gkeClient) DeleteClusters(gcpProject, repo, benchmarkRoot string) erro
 
 // processClusters will process existing clusters and configs for new clusters,
 // with the corresponding functions provided by callers.
-func (gc *gkeClient) processClusters(
+func (gc *GKEClient) processClusters(
 	gcpProject, repo, benchmarkRoot string,
 	handleExistingCluster func(cluster container.Cluster, configExists bool, config ClusterConfig) error,
 	handleNewClusterConfig func(name string, config ClusterConfig) error,
@@ -173,7 +174,7 @@ func (gc *gkeClient) processClusters(
 }
 
 // handleExistingClusterHelper is a helper function for handling an existing cluster.
-func (gc *gkeClient) handleExistingClusterHelper(
+func (gc *GKEClient) handleExistingClusterHelper(
 	gcpProject string,
 	cluster container.Cluster, configExists bool, config ClusterConfig,
 	retainIfUnchanged bool,
@@ -208,7 +209,7 @@ func (gc *gkeClient) handleExistingClusterHelper(
 }
 
 // listClustersForRepo will list all the clusters under the gcpProject that belong to the given repo.
-func (gc *gkeClient) listClustersForRepo(gcpProject, repo string) ([]container.Cluster, error) {
+func (gc *GKEClient) listClustersForRepo(gcpProject, repo string) ([]container.Cluster, error) {
 	allClusters, err := gc.ops.ListClustersInProject(gcpProject)
 	if err != nil {
 		return nil, fmt.Errorf("failed listing clusters in project %q: %w", gcpProject, err)
@@ -226,7 +227,7 @@ func (gc *gkeClient) listClustersForRepo(gcpProject, repo string) ([]container.C
 // deleteClusterWithRetries will delete the given cluster,
 // and retry for a maximum of retryTimes if there is an error.
 // TODO(chizhg): maybe move it to clustermanager library.
-func (gc *gkeClient) deleteClusterWithRetries(gcpProject string, cluster container.Cluster) error {
+func (gc *GKEClient) deleteClusterWithRetries(gcpProject string, cluster container.Cluster) error {
 	log.Printf("Deleting cluster %q under project %q", cluster.Name, gcpProject)
 	region, zone := gke.RegionZoneFromLoc(cluster.Location)
 	var err error
@@ -247,7 +248,7 @@ func (gc *gkeClient) deleteClusterWithRetries(gcpProject string, cluster contain
 // createClusterWithRetries will create a new cluster with the given config,
 // and retry for a maximum of retryTimes if there is an error.
 // TODO(chizhg): maybe move it to clustermanager library.
-func (gc *gkeClient) createClusterWithRetries(gcpProject, name string, config ClusterConfig) error {
+func (gc *GKEClient) createClusterWithRetries(gcpProject, name string, config ClusterConfig) error {
 	log.Printf("Creating cluster %q under project %q with config %v", name, gcpProject, config)
 	var addons []string
 	if strings.TrimSpace(config.Addons) != "" {
diff --git a/testutils/clustermanager/perf-tests/pkg/cluster_test.go b/testutils/clustermanager/perf-tests/pkg/cluster_test.go
index af30d127a..34f1e10c8 100644
--- a/testutils/clustermanager/perf-tests/pkg/cluster_test.go
+++ b/testutils/clustermanager/perf-tests/pkg/cluster_test.go
@@ -34,8 +34,8 @@ const (
 	testBenchmarkRoot = "testdir"
 )
 
-func setupFakeGKEClient() gkeClient {
-	return gkeClient{
+func setupFakeGKEClient() GKEClient {
+	return GKEClient{
 		ops: gkeFake.NewGKESDKClient(),
 	}
 }
diff --git a/testutils/metahelper/main.go b/testutils/metahelper/main.go
index 2b7838f8f..71586a44c 100644
--- a/testutils/metahelper/main.go
+++ b/testutils/metahelper/main.go
@@ -57,7 +57,7 @@ func main() {
 		if valOpt == "" {
 			log.Fatal("--val must be supplied when using --save")
 		}
-		log.Printf("Writing files to %s", c.Path)
+		log.Print("Writing files to ", c.Path)
 		if err := c.Set(saveKeyOpt, valOpt); err != nil {
 			log.Fatalf("Failed saving %q:%q to %q: '%v'", saveKeyOpt, valOpt, c.Path, err)
 		}
diff --git a/webhook/admission_integration_test.go b/webhook/admission_integration_test.go
index 6955eac74..8b53b11c5 100644
--- a/webhook/admission_integration_test.go
+++ b/webhook/admission_integration_test.go
@@ -19,7 +19,6 @@ import (
 	"bytes"
 	"context"
 	"encoding/json"
-	"fmt"
 	"io/ioutil"
 	"net/http"
 	"net/url"
@@ -116,7 +115,7 @@ func TestAdmissionValidResponseForResource(t *testing.T) {
 		t.Fatal("Failed to marshal admission review:", err)
 	}
 
-	u, err := url.Parse(fmt.Sprintf("https://%s", serverURL))
+	u, err := url.Parse("https://" + serverURL)
 	if err != nil {
 		t.Fatal("bad url", err)
 	}
@@ -246,7 +245,7 @@ func TestAdmissionInvalidResponseForResource(t *testing.T) {
 		t.Fatal("Failed to marshal admission review:", err)
 	}
 
-	u, err := url.Parse(fmt.Sprintf("https://%s", serverURL))
+	u, err := url.Parse("https://" + serverURL)
 	if err != nil {
 		t.Fatal("bad url", err)
 	}
diff --git a/webhook/resourcesemantics/defaulting/defaulting_test.go b/webhook/resourcesemantics/defaulting/defaulting_test.go
index 9e678dc18..a30dc4105 100644
--- a/webhook/resourcesemantics/defaulting/defaulting_test.go
+++ b/webhook/resourcesemantics/defaulting/defaulting_test.go
@@ -99,13 +99,13 @@ var (
 
 func newNonRunningTestResourceAdmissionController(t *testing.T) (
 	kubeClient *fakekubeclientset.Clientset,
-	ac *reconciler) {
+	ac webhook.AdmissionController) {
 
 	t.Helper()
 	// Create fake clients
 	kubeClient = fakekubeclientset.NewSimpleClientset(initialResourceWebhook)
 
-	ac = NewTestResourceAdmissionController(t)
+	ac = newTestResourceAdmissionController(t)
 	return
 }
 
@@ -505,7 +505,7 @@ func createInnerDefaultResourceWithoutSpec(t *testing.T) []byte {
 	return b
 }
 
-func NewTestResourceAdmissionController(t *testing.T) *reconciler {
+func newTestResourceAdmissionController(t *testing.T) webhook.AdmissionController {
 	ctx, _ := SetupFakeContext(t)
 	ctx = webhook.WithOptions(ctx, webhook.Options{
 		SecretName: "webhook-secret",
diff --git a/webhook/resourcesemantics/validation/validation_admit_test.go b/webhook/resourcesemantics/validation/validation_admit_test.go
index 1c906c5a2..fdd36dad0 100644
--- a/webhook/resourcesemantics/validation/validation_admit_test.go
+++ b/webhook/resourcesemantics/validation/validation_admit_test.go
@@ -115,13 +115,13 @@ var (
 
 func newNonRunningTestResourceAdmissionController(t *testing.T) (
 	kubeClient *fakekubeclientset.Clientset,
-	ac *reconciler) {
+	ac webhook.AdmissionController) {
 
 	t.Helper()
 	// Create fake clients
 	kubeClient = fakekubeclientset.NewSimpleClientset(initialResourceWebhook)
 
-	ac = NewTestResourceAdmissionController(t)
+	ac = newTestResourceAdmissionController(t)
 	return
 }
 
@@ -631,7 +631,7 @@ func TestNewResourceAdmissionControllerDuplciateVerb(t *testing.T) {
 		call)
 }
 
-func NewTestResourceAdmissionController(t *testing.T) *reconciler {
+func newTestResourceAdmissionController(t *testing.T) webhook.AdmissionController {
 	ctx, _ := SetupFakeContext(t)
 	ctx = webhook.WithOptions(ctx, webhook.Options{
 		SecretName: "webhook-secret",