Repository: knative/pkg
Pull request #1602 (2020-08-10 21:40:54)

Support customizing lease name and identity
1. Add a field called `LeaseName` to customize lease name.
1. Add a field called `Identity` to specify the identify to use to gain the lock for leader election.

This is for the autoscaler scaleout work. See the [feature track doc](https://docs.google.com/document/d/1FyjB1GIJW259nMaNOc7nlQVjvgAZHlQ-fD6tbfUsaz8/edit#)

/assign @vagababov @mattmoor 

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

This pull-request has been approved by: *<a href="https://github.com/knative/pkg/pull/1602#" title="Author self-approved">yanweiguo</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **tcnghia**
You can assign the PR to them by writing `/assign @tcnghia` in a comment when ready.

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

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

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

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":["tcnghia"]} -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-pkg-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[leaderelection/context.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_pkg/1602/pull-knative-pkg-go-coverage/1292939235233370114/artifacts/line-cov.html#file1) | 94.2% | 95.5% | 1.3

knative-prow-robot: @yanweiguo: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-pkg-unit-tests | 9bce8669c7223f7619df732d61cfe23b1829a406 | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_pkg/1602/pull-knative-pkg-unit-tests/1292939235233370112) | `/test pull-knative-pkg-unit-tests`

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

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-prow-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/pkg/pull/1602#" title="Author self-approved">yanweiguo</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **mattmoor**
You can assign the PR to them by writing `/assign @mattmoor` in a comment when ready.

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

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

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

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":["mattmoor"]} -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-pkg-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[leaderelection/context.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_pkg/1602/pull-knative-pkg-go-coverage/1293310721844580354/artifacts/line-cov.html#file1) | 94.2% | 95.2% | 1.0

knative-prow-robot: @yanweiguo: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-pkg-unit-tests | 41fb83c4d5278b381ab71174861b9e9a032ccca6 | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_pkg/1602/pull-knative-pkg-unit-tests/1293310721844580352) | `/test pull-knative-pkg-unit-tests`

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

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
mattmoor: @vagababov looks like a race in the two-lane-queue

knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-pkg-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[leaderelection/context.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_pkg/1602/pull-knative-pkg-go-coverage/1294393356725522434/artifacts/line-cov.html#file1) | 94.2% | 93.5% | -0.7

vagababov: > @vagababov looks like a race in the two-lane-queue

It's the test @Cynocracy  added :|
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-pkg-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[leaderelection/context.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_pkg/1602/pull-knative-pkg-go-coverage/1294428714464972802/artifacts/line-cov.html#file1) | 94.2% | 94.7% | 0.5

yanweiguo: Talked with @mattmoor in slack. We still have to do the update endpoints part in AS.

1. If using endpoints as the resource lock, LE package helps us create the endpoints, we need to 1) watch the endpoints 2) update the endpoints.

2. If using lease as the resource lock, we need to 1) watch the leases 2) create/update the endpoints.

There are multiple places reconciling the same endpoints resource, which can result in conflicts.

So we choose solution 2. PTAL.
knative-prow-robot: @yanweiguo: The following test **failed**, say `/retest` to rerun all failed tests:

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

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

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-pkg-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[leaderelection/context.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_pkg/1602/pull-knative-pkg-go-coverage/1295789603583692802/artifacts/line-cov.html#file1) | 94.2% | 94.7% | 0.5


Patch:
diff --git a/.github/workflows/go-build.yaml b/.github/workflows/knative-go-build.yaml
similarity index 100%
rename from .github/workflows/go-build.yaml
rename to .github/workflows/knative-go-build.yaml
diff --git a/.github/workflows/go-test.yaml b/.github/workflows/knative-go-test.yaml
similarity index 100%
rename from .github/workflows/go-test.yaml
rename to .github/workflows/knative-go-test.yaml
diff --git a/.github/workflows/knative-stale.yaml b/.github/workflows/knative-stale.yaml
new file mode 100644
index 000000000..3983efbe8
--- /dev/null
+++ b/.github/workflows/knative-stale.yaml
@@ -0,0 +1,31 @@
+name: 'Close stale'
+on:
+  schedule:
+  - cron: '* * * * *'
+
+jobs:
+  stale:
+    runs-on: 'ubuntu-latest'
+    steps:
+    - uses: 'actions/stale@v3'
+      with:
+        repo-token: '${{ secrets.GITHUB_TOKEN }}' # No need to setup
+
+        stale-issue-message: |-
+          This issue is stale because it has been open for 90 days with no
+          activity. It will automatically close after 30 more days of
+          inactivity. Reopen the issue with `/reopen`. Mark the issue as
+          fresh by adding the comment `/remove-lifecycle stale`.
+        stale-issue-label: 'lifecycle/stale'
+        exempt-issue-labels: 'lifecycle/frozen'
+
+        stale-pr-message: |-
+          This Pull Request is stale because it has been open for 90 days with
+          no activity. It will automatically close after 30 more days of
+          inactivity. Reopen with `/reopen`. Mark as fresh by adding the
+          comment `/remove-lifecycle stale`.
+        stale-pr-label: 'lifecycle/stale'
+        exempt-pr-labels: 'lifecycle/frozen'
+
+        days-before-stale: 90
+        days-before-close: 30
diff --git a/.github/workflows/go-style.yaml b/.github/workflows/knative-style.yaml
similarity index 100%
rename from .github/workflows/go-style.yaml
rename to .github/workflows/knative-style.yaml
diff --git a/.github/workflows/go-verify.yaml b/.github/workflows/knative-verify.yaml
similarity index 100%
rename from .github/workflows/go-verify.yaml
rename to .github/workflows/knative-verify.yaml
diff --git a/RELEASE-LEADS.md b/RELEASE-LEADS.md
index f3294ebe0..4c6b37272 100644
--- a/RELEASE-LEADS.md
+++ b/RELEASE-LEADS.md
@@ -227,6 +227,16 @@ automation to kick in (runs on a 2 hour interval). Once the release automation
 passed, it will create a release tag in both repositories. Enhance the
 respective tags with the collected release-notes using the Github UI.
 
+### Cut `release-x.y` branches of `net-*`
+
+Cut a `release-x.y` branch in each of the following repositories which do not
+depend on `serving` or `eventing`:
+
+- [knative-sandbox/net-certmanager](https://github.com/knative-sandbox/net-certmanager)
+- [knative-sandbox/net-contour](https://github.com/knative-sandbox/net-contour)
+- [knative-sandbox/net-http01](https://github.com/knative-sandbox/net-http01)
+- [knative-sandbox/net-istio](https://github.com/knative-sandbox/net-istio)
+
 ### Pin `serving` and `eventing` releases in dependent repositories
 
 **After** the tags for `serving` and `eventing` are created, their version needs
@@ -234,10 +244,6 @@ to be pinned in all repositories that depend on them.
 
 For **serving** that is:
 
-- [knative-sandbox/net-certmanager](https://github.com/knative-sandbox/net-certmanager)
-- [knative-sandbox/net-contour](https://github.com/knative-sandbox/net-contour)
-- [knative-sandbox/net-http01](https://github.com/knative-sandbox/net-http01)
-- [knative-sandbox/net-istio](https://github.com/knative-sandbox/net-istio)
 - [knative-sandbox/net-kourier](https://github.com/knative-sandbox/net-kourier)
 - [knative/eventing-contrib](https://github.com/knative/eventing-contrib)
 
@@ -255,10 +261,6 @@ branch of each repository respectively.
 After the pin PRs are merged, cut the `release-x.y` branch in each of the
 remaining repositories (except `operator`):
 
-- [knative-sandbox/net-certmanager](https://github.com/knative-sandbox/net-certmanager)
-- [knative-sandbox/net-contour](https://github.com/knative-sandbox/net-contour)
-- [knative-sandbox/net-http01](https://github.com/knative-sandbox/net-http01)
-- [knative-sandbox/net-istio](https://github.com/knative-sandbox/net-istio)
 - [knative-sandbox/net-kourier](https://github.com/knative-sandbox/net-kourier)
 - [knative/eventing-contrib](https://github.com/knative/eventing-contrib)
 - [knative-sandbox/eventing-kafka-broker](https://github.com/knative-sandbox/eventing-kafka-broker)
diff --git a/apis/duck/README.md b/apis/duck/README.md
index ab9a59030..688f54246 100644
--- a/apis/duck/README.md
+++ b/apis/duck/README.md
@@ -1,8 +1,9 @@
 # Duck Types
 
 Knative leverages duck-typing to interact with resources inside of Kubernetes
-without explicit knowlage of the full resource shape. `knative/pkg` defines two
-duck types that are used throughout Knative: `Addressable` and `Source`.
+without explicit knowledge of the full resource shape. `knative/pkg` defines
+three duck types that are used throughout Knative: `Addressable`, `Binding`, and
+`Source`.
 
 For APIs leveraging `ObjectReference`, the context of the resource in question
 identifies the duck-type. To enable the case where no `ObjectRefrence` is used,
diff --git a/client/injection/kube/informers/coordination/v1/lease/fake/fake.go b/client/injection/kube/informers/coordination/v1/lease/fake/fake.go
new file mode 100644
index 000000000..89763152a
--- /dev/null
+++ b/client/injection/kube/informers/coordination/v1/lease/fake/fake.go
@@ -0,0 +1,40 @@
+/*
+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 (
+	context "context"
+
+	lease "knative.dev/pkg/client/injection/kube/informers/coordination/v1/lease"
+	fake "knative.dev/pkg/client/injection/kube/informers/factory/fake"
+	controller "knative.dev/pkg/controller"
+	injection "knative.dev/pkg/injection"
+)
+
+var Get = lease.Get
+
+func init() {
+	injection.Fake.RegisterInformer(withInformer)
+}
+
+func withInformer(ctx context.Context) (context.Context, controller.Informer) {
+	f := fake.Get(ctx)
+	inf := f.Coordination().V1().Leases()
+	return context.WithValue(ctx, lease.Key{}, inf), inf.Informer()
+}
diff --git a/client/injection/kube/informers/coordination/v1/lease/lease.go b/client/injection/kube/informers/coordination/v1/lease/lease.go
new file mode 100644
index 000000000..46f77c470
--- /dev/null
+++ b/client/injection/kube/informers/coordination/v1/lease/lease.go
@@ -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.
+*/
+
+// Code generated by injection-gen. DO NOT EDIT.
+
+package lease
+
+import (
+	context "context"
+
+	v1 "k8s.io/client-go/informers/coordination/v1"
+	factory "knative.dev/pkg/client/injection/kube/informers/factory"
+	controller "knative.dev/pkg/controller"
+	injection "knative.dev/pkg/injection"
+	logging "knative.dev/pkg/logging"
+)
+
+func init() {
+	injection.Default.RegisterInformer(withInformer)
+}
+
+// Key is used for associating the Informer inside the context.Context.
+type Key struct{}
+
+func withInformer(ctx context.Context) (context.Context, controller.Informer) {
+	f := factory.Get(ctx)
+	inf := f.Coordination().V1().Leases()
+	return context.WithValue(ctx, Key{}, inf), inf.Informer()
+}
+
+// Get extracts the typed informer from the context.
+func Get(ctx context.Context) v1.LeaseInformer {
+	untyped := ctx.Value(Key{})
+	if untyped == nil {
+		logging.FromContext(ctx).Panic(
+			"Unable to fetch k8s.io/client-go/informers/coordination/v1.LeaseInformer from context.")
+	}
+	return untyped.(v1.LeaseInformer)
+}
diff --git a/configmap/parse.go b/configmap/parse.go
index eba04a11c..d4fa5e875 100644
--- a/configmap/parse.go
+++ b/configmap/parse.go
@@ -45,7 +45,9 @@ func AsString(key string, target *string) ParseFunc {
 func AsBool(key string, target *bool) ParseFunc {
 	return func(data map[string]string) error {
 		if raw, ok := data[key]; ok {
-			*target = strings.EqualFold(raw, "true")
+			val, err := strconv.ParseBool(raw)
+			*target = val // If err != nil — this is always false.
+			return err
 		}
 		return nil
 	}
diff --git a/controller/controller.go b/controller/controller.go
index 895b05d01..300f76e82 100644
--- a/controller/controller.go
+++ b/controller/controller.go
@@ -216,10 +216,13 @@ type ControllerOptions struct {
 
 // NewImpl instantiates an instance of our controller that will feed work to the
 // provided Reconciler as it is enqueued.
+// Deprecated: use NewImplFull.
 func NewImpl(r Reconciler, logger *zap.SugaredLogger, workQueueName string) *Impl {
 	return NewImplFull(r, ControllerOptions{WorkQueueName: workQueueName, Logger: logger})
 }
 
+// NewImplWithStats creates a controller.Impl with stats reporter.
+// Deprecated: use NewImplFull.
 func NewImplWithStats(r Reconciler, logger *zap.SugaredLogger, workQueueName string, reporter StatsReporter) *Impl {
 	return NewImplFull(r, ControllerOptions{WorkQueueName: workQueueName, Logger: logger, Reporter: reporter})
 }
@@ -416,15 +419,14 @@ func (c *Impl) EnqueueKeyAfter(key types.NamespacedName, delay time.Duration) {
 // internal work queue and waits for workers to finish processing their current
 // work items.
 func (c *Impl) RunContext(ctx context.Context, threadiness int) error {
-	logger := c.logger
-	defer runtime.HandleCrash()
 	sg := sync.WaitGroup{}
-	defer sg.Wait()
 	defer func() {
 		c.workQueue.ShutDown()
 		for c.workQueue.Len() > 0 {
 			time.Sleep(time.Millisecond * 100)
 		}
+		sg.Wait()
+		runtime.HandleCrash()
 	}()
 
 	if la, ok := c.Reconciler.(reconciler.LeaderAware); ok {
@@ -441,7 +443,7 @@ func (c *Impl) RunContext(ctx context.Context, threadiness int) error {
 	}
 
 	// Launch workers to process resources that get enqueued to our workqueue.
-	logger.Info("Starting controller and workers")
+	c.logger.Info("Starting controller and workers")
 	for i := 0; i < threadiness; i++ {
 		sg.Add(1)
 		go func() {
@@ -451,9 +453,9 @@ func (c *Impl) RunContext(ctx context.Context, threadiness int) error {
 		}()
 	}
 
-	logger.Info("Started workers")
+	c.logger.Info("Started workers")
 	<-ctx.Done()
-	logger.Info("Shutting down workers")
+	c.logger.Info("Shutting down workers")
 
 	return nil
 }
@@ -485,13 +487,6 @@ func (c *Impl) processNextWorkItem() bool {
 	// Send the metrics for the current queue depth
 	c.statsReporter.ReportQueueDepth(int64(c.workQueue.Len()))
 
-	// We call Done here so the workqueue knows we have finished
-	// processing this item. We also must remember to call Forget if
-	// reconcile succeeds. If a transient error occurs, we do not call
-	// Forget and put the item back to the queue with an increased
-	// delay.
-	defer c.workQueue.Done(key)
-
 	var err error
 	defer func() {
 		status := trueString
@@ -499,6 +494,13 @@ func (c *Impl) processNextWorkItem() bool {
 			status = falseString
 		}
 		c.statsReporter.ReportReconcile(time.Since(startTime), status)
+
+		// We call Done here so the workqueue knows we have finished
+		// processing this item. We also must remember to call Forget if
+		// reconcile succeeds. If a transient error occurs, we do not call
+		// Forget and put the item back to the queue with an increased
+		// delay.
+		c.workQueue.Done(key)
 	}()
 
 	// Embed the key into the logger and attach that to the context we pass
@@ -544,8 +546,8 @@ func (c *Impl) GlobalResync(si cache.SharedInformer) {
 	c.FilteredGlobalResync(alwaysTrue, si)
 }
 
-// FilteredGlobalResync enqueues (with a delay) all objects from the
-// SharedInformer that pass the filter function
+// FilteredGlobalResync enqueues all objects from the
+// SharedInformer that pass the filter function in to the slow queue.
 func (c *Impl) FilteredGlobalResync(f func(interface{}) bool, si cache.SharedInformer) {
 	if c.workQueue.ShuttingDown() {
 		return
diff --git a/go.mod b/go.mod
index 14eb6336f..d88d63c08 100644
--- a/go.mod
+++ b/go.mod
@@ -52,7 +52,7 @@ require (
 	k8s.io/code-generator v0.18.6
 	k8s.io/gengo v0.0.0-20200205140755-e0e292d8aa12
 	k8s.io/klog v1.0.0
-	knative.dev/test-infra v0.0.0-20200817225313-c0ea00ce480c
+	knative.dev/test-infra v0.0.0-20200825022047-cb4bb218c5e5
 	sigs.k8s.io/boskos v0.0.0-20200729174948-794df80db9c9
 )
 
diff --git a/go.sum b/go.sum
index f49b0f630..3ac56adc6 100644
--- a/go.sum
+++ b/go.sum
@@ -1836,8 +1836,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-20200817225313-c0ea00ce480c h1:AT7+D1fWJlqcCpMDgYYLNPa0//gBTRtX8bnCz4Ar4Jg=
-knative.dev/test-infra v0.0.0-20200817225313-c0ea00ce480c/go.mod h1:Pmg2c7Z7q7BGFUV/GOpU5BlrD3ePJft4MPqx8AYBplc=
+knative.dev/test-infra v0.0.0-20200825022047-cb4bb218c5e5 h1:iqbpe5rccMW2r1Mu1Prw4gJ9xi7yqahmtxzckTUHBeI=
+knative.dev/test-infra v0.0.0-20200825022047-cb4bb218c5e5/go.mod h1:Pmg2c7Z7q7BGFUV/GOpU5BlrD3ePJft4MPqx8AYBplc=
 modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh
index 3e030bfac..0743ad689 100755
--- a/hack/update-codegen.sh
+++ b/hack/update-codegen.sh
@@ -47,7 +47,7 @@ EXTERNAL_INFORMER_PKG="k8s.io/client-go/informers" \
   ${REPO_ROOT_DIR}/hack/generate-knative.sh "injection" \
     k8s.io/client-go \
     k8s.io/api \
-    "admissionregistration:v1beta1,v1 apps:v1 autoscaling:v1,v2beta1 batch:v1,v1beta1 core:v1 rbac:v1" \
+    "admissionregistration:v1beta1,v1 apps:v1 autoscaling:v1,v2beta1 batch:v1,v1beta1 core:v1 rbac:v1 coordination:v1" \
     --go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt \
     --force-genreconciler-kinds "Namespace,Deployment"
 
diff --git a/leaderelection/config.go b/leaderelection/config.go
index 326956790..c364d4f3d 100644
--- a/leaderelection/config.go
+++ b/leaderelection/config.go
@@ -34,8 +34,8 @@ import (
 
 const (
 	configMapNameEnv = "CONFIG_LEADERELECTION_NAME"
-	// KnativeResourceLock is the only supported lock mechanism for Knative.
-	KnativeResourceLock = resourcelock.LeasesResourceLock
+	// knativeResourceLock is the only supported lock mechanism for Knative.
+	knativeResourceLock = resourcelock.LeasesResourceLock
 )
 
 // MaxBuckets is the maximum number of buckets to allow users to define.
@@ -112,6 +112,18 @@ type ComponentConfig struct {
 	LeaseDuration time.Duration
 	RenewDeadline time.Duration
 	RetryPeriod   time.Duration
+	// LeaseName is a function to customize the lease name given the index i.
+	// If not present, a name in format {Component}.{queue-name}.{i}-of-{Buckets}
+	// will be use.
+	// Autoscaler need to know the Lease names to filter out Leases which are not
+	// used for Autoscaler. Instead of exposing the names from leadelection package,
+	// we let Autoscaler to pass them in.
+	LeaseName func(i uint32) string
+	// Identity is the unique string identifying a resource lock holder across
+	// all participants in an election. If not present, a new unique string will
+	// be generated to be used as identity for each BuildElector call.
+	// Autoscaler uses the pod IP as identity.
+	Identity string
 }
 
 // statefulSetID is a envconfig Decodable controller ordinal and name.
diff --git a/leaderelection/context.go b/leaderelection/context.go
index 5e897bcc7..df617d807 100644
--- a/leaderelection/context.go
+++ b/leaderelection/context.go
@@ -109,9 +109,13 @@ func (b *standardBuilder) buildElector(ctx context.Context, la reconciler.Leader
 	queueName string, enq func(reconciler.Bucket, types.NamespacedName)) (Elector, error) {
 	logger := logging.FromContext(ctx)
 
-	id, err := UniqueID()
-	if err != nil {
-		return nil, err
+	id := b.lec.Identity
+	if id == "" {
+		uid, err := UniqueID()
+		if err != nil {
+			return nil, err
+		}
+		id = uid
 	}
 
 	bkts := newStandardBuckets(queueName, b.lec)
@@ -120,7 +124,7 @@ func (b *standardBuilder) buildElector(ctx context.Context, la reconciler.Leader
 		// Use a local var which won't change across the for loop since it is
 		// used in a callback asynchronously.
 		bkt := bkt
-		rl, err := resourcelock.New(KnativeResourceLock,
+		rl, err := resourcelock.New(knativeResourceLock,
 			system.Namespace(), // use namespace we are running in
 			bkt.Name(),
 			b.kc.CoreV1(),
@@ -169,9 +173,15 @@ func (b *standardBuilder) buildElector(ctx context.Context, la reconciler.Leader
 }
 
 func newStandardBuckets(queueName string, cc ComponentConfig) []reconciler.Bucket {
+	ln := cc.LeaseName
+	if ln == nil {
+		ln = func(i uint32) string {
+			return standardBucketName(i, queueName, cc)
+		}
+	}
 	names := make(sets.String, cc.Buckets)
 	for i := uint32(0); i < cc.Buckets; i++ {
-		names.Insert(standardBucketName(i, queueName, cc))
+		names.Insert(ln(i))
 	}
 
 	return hash.NewBucketSet(names).Buckets()
diff --git a/logging/config.go b/logging/config.go
index 7cc7a3e18..2fbd235df 100644
--- a/logging/config.go
+++ b/logging/config.go
@@ -108,7 +108,7 @@ func newLoggerFromConfig(configJSON string, levelOverride string, opts []zap.Opt
 	}
 
 	logger.Info("Successfully created the logger.")
-	logger.Sugar().Infof("Logging level set to %v", loggingCfg.Level)
+	logger.Info("Logging level set to: " + loggingCfg.Level.String())
 	return logger, loggingCfg.Level, nil
 }
 
@@ -232,11 +232,10 @@ func UpdateLevelFromConfigMap(logger *zap.SugaredLogger, atomicLevel zap.AtomicL
 
 // ConfigMapName gets the name of the logging ConfigMap
 func ConfigMapName() string {
-	cm := os.Getenv(configMapNameEnv)
-	if cm == "" {
-		return "config-logging"
+	if cm := os.Getenv(configMapNameEnv); cm != "" {
+		return cm
 	}
-	return cm
+	return "config-logging"
 }
 
 // JsonToLoggingConfig converts a json string of a Config.
diff --git a/metrics/config_observability.go b/metrics/config_observability.go
index d17fba89c..12df827de 100644
--- a/metrics/config_observability.go
+++ b/metrics/config_observability.go
@@ -17,8 +17,8 @@ limitations under the License.
 package metrics
 
 import (
+	"fmt"
 	"os"
-	"strings"
 	texttemplate "text/template"
 
 	corev1 "k8s.io/api/core/v1"
@@ -28,12 +28,16 @@ import (
 const (
 	// The following is used to set the default log url template
 	DefaultLogURLTemplate = "http://localhost:8001/api/v1/namespaces/knative-monitoring/services/kibana-logging/proxy/app/kibana#/discover?_a=(query:(match:(kubernetes.labels.knative-dev%2FrevisionUID:(query:'${REVISION_UID}',type:phrase))))"
+	// DefaultRequestLogTemplate is the default format for emitting request logs.
+	DefaultRequestLogTemplate = `{"httpRequest": {"requestMethod": "{{.Request.Method}}", "requestUrl": "{{js .Request.RequestURI}}", "requestSize": "{{.Request.ContentLength}}", "status": {{.Response.Code}}, "responseSize": "{{.Response.Size}}", "userAgent": "{{js .Request.UserAgent}}", "remoteIp": "{{js .Request.RemoteAddr}}", "serverIp": "{{.Revision.PodIP}}", "referer": "{{js .Request.Referer}}", "latency": "{{.Response.Latency}}s", "protocol": "{{.Request.Proto}}"}, "traceId": "{{index .Request.Header "X-B3-Traceid"}}"}`
 
 	// The following is used to set the default metrics backend
 	defaultRequestMetricsBackend = "prometheus"
 
 	// The env var name for config-observability
-	configMapNameEnv = "CONFIG_OBSERVABILITY_NAME"
+	configMapNameEnv  = "CONFIG_OBSERVABILITY_NAME"
+	reqLogTemplateKey = "logging.request-log-template"
+	enableReqLogKey   = "logging.enable-request-log"
 )
 
 // ObservabilityConfig contains the configuration defined in the observability ConfigMap.
@@ -63,11 +67,16 @@ type ObservabilityConfig struct {
 
 	// EnableRequestLog enables activator/queue-proxy to write request logs.
 	EnableRequestLog bool
+
+	// MetricsCollectorAddress specifies the metrics collector address. This is only used
+	// when the metrics backend is opencensus.
+	MetricsCollectorAddress string
 }
 
 func defaultConfig() *ObservabilityConfig {
 	return &ObservabilityConfig{
 		LoggingURLTemplate:    DefaultLogURLTemplate,
+		RequestLogTemplate:    DefaultRequestLogTemplate,
 		RequestMetricsBackend: defaultRequestMetricsBackend,
 	}
 }
@@ -79,23 +88,18 @@ func NewObservabilityConfigFromConfigMap(configMap *corev1.ConfigMap) (*Observab
 	if err := cm.Parse(configMap.Data,
 		cm.AsBool("logging.enable-var-log-collection", &oc.EnableVarLogCollection),
 		cm.AsString("logging.revision-url-template", &oc.LoggingURLTemplate),
-		cm.AsString("logging.request-log-template", &oc.RequestLogTemplate),
+		cm.AsString(reqLogTemplateKey, &oc.RequestLogTemplate),
+		cm.AsBool(enableReqLogKey, &oc.EnableRequestLog),
 		cm.AsBool("logging.enable-probe-request-log", &oc.EnableProbeRequestLog),
 		cm.AsString("metrics.request-metrics-backend-destination", &oc.RequestMetricsBackend),
 		cm.AsBool("profiling.enable", &oc.EnableProfiling),
+		cm.AsString("metrics.opencensus-address", &oc.MetricsCollectorAddress),
 	); err != nil {
 		return nil, err
 	}
 
-	if raw, ok := configMap.Data["logging.enable-request-log"]; ok {
-		if strings.EqualFold(raw, "true") && oc.RequestLogTemplate != "" {
-			oc.EnableRequestLog = true
-		}
-	} else if oc.RequestLogTemplate != "" {
-		// TODO: remove this after 0.17 cuts, this is meant only for smooth transition to the new flag.
-		// Once 0.17 cuts we should set a proper default value and users will need to set the flag explicitly
-		// to enable request logging.
-		oc.EnableRequestLog = true
+	if oc.RequestLogTemplate == "" && oc.EnableRequestLog {
+		return nil, fmt.Errorf("%q was set to true, but no %q was specified", enableReqLogKey, reqLogTemplateKey)
 	}
 
 	if oc.RequestLogTemplate != "" {
@@ -110,9 +114,8 @@ func NewObservabilityConfigFromConfigMap(configMap *corev1.ConfigMap) (*Observab
 
 // ConfigMapName gets the name of the metrics ConfigMap
 func ConfigMapName() string {
-	cm := os.Getenv(configMapNameEnv)
-	if cm == "" {
-		return "config-observability"
+	if cm := os.Getenv(configMapNameEnv); cm != "" {
+		return cm
 	}
-	return cm
+	return "config-observability"
 }
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 5f39f26f4..5e4ab94a8 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -997,7 +997,7 @@ k8s.io/utils/buffer
 k8s.io/utils/integer
 k8s.io/utils/pointer
 k8s.io/utils/trace
-# knative.dev/test-infra v0.0.0-20200817225313-c0ea00ce480c
+# knative.dev/test-infra v0.0.0-20200825022047-cb4bb218c5e5
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/boskos v0.0.0-20200729174948-794df80db9c9


Test patch:
diff --git a/apis/duck/patch_test.go b/apis/duck/patch_test.go
index f17dfb4ab..69cda2a50 100644
--- a/apis/duck/patch_test.go
+++ b/apis/duck/patch_test.go
@@ -70,7 +70,7 @@ func TestCreateMergePatch(t *testing.T) {
 		},
 		want: []byte(`{"status":{"patchable":{"field1":42,"field2":null}}}`),
 	}, {
-		name: "patch array",
+		name: "patch slice bigger",
 		before: &Patch{
 			Spec: PatchSpec{
 				Patchable: &Patchable{
@@ -86,6 +86,23 @@ func TestCreateMergePatch(t *testing.T) {
 			},
 		},
 		want: []byte(`{"status":{"patchable":{"array":["foo","bar","baz"]}}}`),
+	}, {
+		name: "patch array smaller",
+		before: &Patch{
+			Spec: PatchSpec{
+				Patchable: &Patchable{
+					Array: []string{"foo", "bar", "baz", "jimmy"},
+				},
+			},
+		},
+		after: &Patch{
+			Spec: PatchSpec{
+				Patchable: &Patchable{
+					Array: []string{"foo", "baz"},
+				},
+			},
+		},
+		want: []byte(`{"status":{"patchable":{"array":["foo","baz"]}}}`),
 	}, {
 		name:    "before doesn't marshal",
 		before:  &DoesntMarshal{},
@@ -111,8 +128,8 @@ func TestCreateMergePatch(t *testing.T) {
 				return
 			}
 
-			if diff := cmp.Diff(test.want, got); diff != "" {
-				t.Errorf("CreatePatch (-want, +got) = %v, %s", diff, got)
+			if !cmp.Equal(test.want, got) {
+				t.Errorf("CreatePatch = ->%s<-, diff: (-want, +got) =\n%s", string(got), cmp.Diff(string(test.want), string(got)))
 			}
 		})
 	}
diff --git a/configmap/parse_test.go b/configmap/parse_test.go
index 8bf6cb98e..c7706c87e 100644
--- a/configmap/parse_test.go
+++ b/configmap/parse_test.go
@@ -27,15 +27,15 @@ import (
 )
 
 type testConfig struct {
-	str string
-	boo bool
-	i32 int32
-	i64 int64
-	u32 uint32
-	f64 float64
-	dur time.Duration
-	set sets.String
-	qua *resource.Quantity
+	str    string
+	toggle bool
+	i32    int32
+	i64    int64
+	u32    uint32
+	f64    float64
+	dur    time.Duration
+	set    sets.String
+	qua    *resource.Quantity
 
 	nsn  types.NamespacedName
 	onsn *types.NamespacedName
@@ -66,15 +66,15 @@ func TestParse(t *testing.T) {
 			"test-optional-namespaced-name": "some-other-namespace/some-other-name",
 		},
 		want: testConfig{
-			str: "foo.bar",
-			boo: true,
-			i32: 1,
-			i64: 2,
-			u32: 3,
-			f64: 1.0,
-			dur: time.Minute,
-			set: sets.NewString("a", "b", "c"),
-			qua: &fiveHundredM,
+			str:    "foo.bar",
+			toggle: true,
+			i32:    1,
+			i64:    2,
+			u32:    3,
+			f64:    1.0,
+			dur:    time.Minute,
+			set:    sets.NewString("a", "b", "c"),
+			qua:    &fiveHundredM,
 			nsn: types.NamespacedName{
 				Name:      "some-name",
 				Namespace: "some-namespace",
@@ -87,31 +87,29 @@ func TestParse(t *testing.T) {
 	}, {
 		name: "respect defaults",
 		conf: testConfig{
-			str: "foo.bar",
-			boo: true,
-			i32: 1,
-			i64: 2,
-			f64: 1.0,
-			dur: time.Minute,
-			qua: &fiveHundredM,
+			str:    "foo.bar",
+			toggle: true,
+			i32:    1,
+			i64:    2,
+			f64:    1.0,
+			dur:    time.Minute,
+			qua:    &fiveHundredM,
 		},
 		want: testConfig{
-			str: "foo.bar",
-			boo: true,
-			i32: 1,
-			i64: 2,
-			f64: 1.0,
-			dur: time.Minute,
-			qua: &fiveHundredM,
+			str:    "foo.bar",
+			toggle: true,
+			i32:    1,
+			i64:    2,
+			f64:    1.0,
+			dur:    time.Minute,
+			qua:    &fiveHundredM,
 		},
 	}, {
-		name: "bool defaults to false",
+		name: "junk bool fails",
 		data: map[string]string{
 			"test-bool": "foo",
 		},
-		want: testConfig{
-			boo: false,
-		},
+		expectErr: true,
 	}, {
 		name: "int32 error",
 		data: map[string]string{
@@ -166,7 +164,7 @@ func TestParse(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 			if err := Parse(test.data,
 				AsString("test-string", &test.conf.str),
-				AsBool("test-bool", &test.conf.boo),
+				AsBool("test-bool", &test.conf.toggle),
 				AsInt32("test-int32", &test.conf.i32),
 				AsInt64("test-int64", &test.conf.i64),
 				AsUint32("test-uint32", &test.conf.u32),
diff --git a/controller/controller_test.go b/controller/controller_test.go
index 70ec2e5f0..13d660177 100644
--- a/controller/controller_test.go
+++ b/controller/controller_test.go
@@ -29,6 +29,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/apimachinery/pkg/util/wait"
 	fakekube "k8s.io/client-go/kubernetes/fake"
 	"k8s.io/client-go/tools/cache"
 	"k8s.io/client-go/tools/record"
@@ -435,7 +436,7 @@ func (t testRateLimiter) NumRequeues(interface{}) int    { return 0 }
 
 var _ workqueue.RateLimiter = (*testRateLimiter)(nil)
 
-func TestEnqueues(t *testing.T) {
+func TestEnqueue(t *testing.T) {
 	tests := []struct {
 		name      string
 		work      func(*Impl)
@@ -741,8 +742,6 @@ func TestEnqueues(t *testing.T) {
 			impl := NewImplFull(&NopReconciler{}, ControllerOptions{WorkQueueName: "Testing", Logger: TestLogger(t), RateLimiter: rl})
 			test.work(impl)
 
-			// The rate limit on our queue delays when things are added to the queue.
-			time.Sleep(150 * time.Millisecond)
 			impl.WorkQueue().ShutDown()
 			gotQueue := drainWorkQueue(impl.WorkQueue())
 
@@ -758,8 +757,6 @@ func TestEnqueues(t *testing.T) {
 			impl := NewImplWithStats(&NopReconciler{}, TestLogger(t), "Testing", &FakeStatsReporter{})
 			test.work(impl)
 
-			// The rate limit on our queue delays when things are added to the queue.
-			time.Sleep(50 * time.Millisecond)
 			impl.WorkQueue().ShutDown()
 			gotQueue := drainWorkQueue(impl.WorkQueue())
 
@@ -771,59 +768,149 @@ func TestEnqueues(t *testing.T) {
 }
 
 func TestEnqueueAfter(t *testing.T) {
+	const (
+		// longDelay is longer than we expect the test to run.
+		longDelay = time.Minute
+		// shortDelay is short enough for the test to execute quickly, but long
+		// enough to reasonably delay the enqueuing of an item.
+		shortDelay = 50 * time.Millisecond
+
+		// time we allow the queue length checker to keep polling the
+		// workqueue.
+		queueCheckTimeout = shortDelay + 500*time.Millisecond
+	)
+
 	t.Cleanup(ClearAll)
+
 	impl := NewImplWithStats(&NopReconciler{}, TestLogger(t), "Testing", &FakeStatsReporter{})
+	t.Cleanup(func() {
+		impl.WorkQueue().ShutDown()
+	})
+
+	// Enqueue two items with a long delay.
 	impl.EnqueueAfter(&Resource{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      "for",
 			Namespace: "waiting",
 		},
-	}, 5*time.Second)
+	}, longDelay)
 	impl.EnqueueAfter(&Resource{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      "waterfall",
 			Namespace: "the",
 		},
-	}, 500*time.Millisecond)
+	}, longDelay)
+
+	// Enqueue one item with a short delay.
+	enqueueTime := time.Now()
 	impl.EnqueueAfter(&Resource{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      "to",
-			Namespace: "fall",
+			Name:      "fall",
+			Namespace: "to",
 		},
-	}, 20*time.Second)
-	time.Sleep(10 * time.Millisecond)
-	if got, want := impl.WorkQueue().Len(), 0; got != want {
-		t.Errorf("|Queue| = %d, want: %d", got, want)
+	}, shortDelay)
+
+	// Keep checking the queue length until 'to/fall' gets enqueued, send to channel to indicate success.
+	queuePopulated := make(chan struct{})
+	ctx, cancel := context.WithTimeout(context.Background(), queueCheckTimeout)
+
+	t.Cleanup(func() {
+		close(queuePopulated)
+		cancel()
+	})
+
+	var successCheck wait.ConditionFunc = func() (bool, error) {
+		if impl.WorkQueue().Len() > 0 {
+			queuePopulated <- struct{}{}
+			return true, nil
+		}
+		return false, nil
 	}
-	// Sleep the remaining time.
-	time.Sleep(time.Second)
-	if got, want := impl.WorkQueue().Len(), 1; got != want {
-		t.Errorf("|Queue| = %d, want: %d", got, want)
+	go wait.PollImmediateUntil(5*time.Millisecond, successCheck, ctx.Done())
+
+	select {
+	case <-queuePopulated:
+		if enqueueDelay := time.Since(enqueueTime); enqueueDelay < shortDelay {
+			t.Errorf("Item enqueued within %v, expected at least a %v delay", enqueueDelay, shortDelay)
+		}
+		if got, want := impl.WorkQueue().Len(), 1; got != want {
+			t.Errorf("|Queue| = %d, want: %d", got, want)
+		}
+
+	case <-ctx.Done():
+		t.Fatal("Timed out waiting for item to be put onto the workqueue")
 	}
+
 	impl.WorkQueue().ShutDown()
-	if got, want := drainWorkQueue(impl.WorkQueue()), []types.NamespacedName{{Namespace: "the", Name: "waterfall"}}; !cmp.Equal(got, want) {
-		t.Errorf("Queue = %v, want: %v, diff: %s", got, want, cmp.Diff(got, want))
+
+	got, want := drainWorkQueue(impl.WorkQueue()), []types.NamespacedName{{Namespace: "to", Name: "fall"}}
+	if diff := cmp.Diff(want, got); diff != "" {
+		t.Errorf("Unexpected workqueue state (-:expect, +:got):\n%s", diff)
 	}
 }
 
-func TestEnqeueKeyAfter(t *testing.T) {
+func TestEnqueueKeyAfter(t *testing.T) {
+	const (
+		// longDelay is longer than we expect the test to run.
+		longDelay = time.Minute
+		// shortDelay is short enough for the test to execute quickly, but long
+		// enough to reasonably delay the enqueuing of an item.
+		shortDelay = 50 * time.Millisecond
+	)
+
 	t.Cleanup(ClearAll)
+
 	impl := NewImplWithStats(&NopReconciler{}, TestLogger(t), "Testing", &FakeStatsReporter{})
-	impl.EnqueueKeyAfter(types.NamespacedName{Namespace: "waiting", Name: "for"}, 5*time.Second)
-	impl.EnqueueKeyAfter(types.NamespacedName{Namespace: "the", Name: "waterfall"}, 500*time.Millisecond)
-	impl.EnqueueKeyAfter(types.NamespacedName{Namespace: "to", Name: "fall"}, 20*time.Second)
-	time.Sleep(10 * time.Millisecond)
-	if got, want := impl.WorkQueue().Len(), 0; got != want {
-		t.Errorf("|Queue| = %d, want: %d", got, want)
-	}
-	// Sleep the remaining time.
-	time.Sleep(time.Second)
-	if got, want := impl.WorkQueue().Len(), 1; got != want {
-		t.Errorf("|Queue| = %d, want: %d", got, want)
+	t.Cleanup(func() {
+		impl.WorkQueue().ShutDown()
+	})
+
+	// Enqueue two items with a long delay.
+	impl.EnqueueKeyAfter(types.NamespacedName{Namespace: "waiting", Name: "for"}, longDelay)
+	impl.EnqueueKeyAfter(types.NamespacedName{Namespace: "the", Name: "waterfall"}, longDelay)
+
+	// Enqueue one item with a short delay.
+	enqueueTime := time.Now()
+	impl.EnqueueKeyAfter(types.NamespacedName{Namespace: "to", Name: "fall"}, shortDelay)
+
+	// Keep checking the queue length until 'to/fall' gets enqueued, send to channel to indicate success.
+	queuePopulated := make(chan struct{})
+
+	const queueCheckTimeout = shortDelay + 500*time.Millisecond
+	ctx, cancel := context.WithTimeout(context.Background(), queueCheckTimeout)
+
+	t.Cleanup(func() {
+		close(queuePopulated)
+		cancel()
+	})
+
+	var successCheck wait.ConditionFunc = func() (bool, error) {
+		if impl.WorkQueue().Len() > 0 {
+			queuePopulated <- struct{}{}
+			return true, nil
+		}
+		return false, nil
+	}
+	go wait.PollImmediateUntil(5*time.Millisecond, successCheck, ctx.Done())
+
+	select {
+	case <-queuePopulated:
+		if enqueueDelay := time.Since(enqueueTime); enqueueDelay < shortDelay {
+			t.Errorf("Item enqueued within %v, expected at least a %v delay", enqueueDelay, shortDelay)
+		}
+		if got, want := impl.WorkQueue().Len(), 1; got != want {
+			t.Errorf("|Queue| = %d, want: %d", got, want)
+		}
+
+	case <-ctx.Done():
+		t.Fatal("Timed out waiting for item to be put onto the workqueue")
 	}
+
 	impl.WorkQueue().ShutDown()
-	if got, want := drainWorkQueue(impl.WorkQueue()), []types.NamespacedName{{Namespace: "the", Name: "waterfall"}}; !cmp.Equal(got, want) {
-		t.Errorf("Queue = %v, want: %v, diff: %s", got, want, cmp.Diff(got, want))
+
+	got, want := drainWorkQueue(impl.WorkQueue()), []types.NamespacedName{{Namespace: "to", Name: "fall"}}
+	if diff := cmp.Diff(want, got); diff != "" {
+		t.Errorf("Unexpected workqueue state (-:expect, +:got):\n%s", diff)
 	}
 }
 
@@ -1090,17 +1177,19 @@ func (er *ErrorReconciler) Reconcile(context.Context, string) error {
 }
 
 func TestStartAndShutdownWithErroringWork(t *testing.T) {
+	const testTimeout = 500 * time.Millisecond
+
+	item := types.NamespacedName{Namespace: "", Name: "bar"}
+
 	t.Cleanup(ClearAll)
-	r := &ErrorReconciler{}
-	reporter := &FakeStatsReporter{}
-	impl := NewImplWithStats(r, TestLogger(t), "Testing", reporter)
 
-	ctx, cancel := context.WithCancel(context.Background())
-	t.Cleanup(cancel)
-	doneCh := make(chan struct{})
+	impl := NewImplWithStats(&ErrorReconciler{}, TestLogger(t), "Testing", &FakeStatsReporter{})
+	impl.EnqueueKey(item)
 
-	impl.EnqueueKey(types.NamespacedName{Namespace: "", Name: "bar"})
+	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
+	t.Cleanup(cancel)
 
+	doneCh := make(chan struct{})
 	go func() {
 		defer close(doneCh)
 		// StartAll blocks until all the worker threads finish, which shouldn't
@@ -1108,33 +1197,33 @@ func TestStartAndShutdownWithErroringWork(t *testing.T) {
 		StartAll(ctx, impl)
 	}()
 
-	select {
-	case <-time.After(1 * time.Second):
-		// We don't expect completion before the context is cancelled,
-		// but the workers should spin on the erroring work.
-
-	case <-doneCh:
-		t.Error("StartAll finished early.")
+	// Keep checking the number of requeues, send to channel to indicate success.
+	itemRequeued := make(chan struct{})
+	defer close(itemRequeued)
+
+	var successCheck wait.ConditionFunc = func() (bool, error) {
+		// Check that the work was requeued in RateLimiter, as NumRequeues
+		// can't fully reflect the real state of queue length.
+		// Here we need to wait for NumRequeues to be more than 1, to ensure
+		// the key get re-queued and reprocessed as expect.
+		if impl.WorkQueue().NumRequeues(item) > 1 {
+			itemRequeued <- struct{}{}
+			return true, nil
+		}
+		return false, nil
 	}
-
-	// By cancelling the context all the workers should complete and
-	// we should close the doneCh.
-	cancel()
+	go wait.PollImmediateUntil(5*time.Millisecond, successCheck, ctx.Done())
 
 	select {
-	case <-time.After(1 * time.Second):
-		t.Error("Timed out waiting for controller to finish.")
+	case <-itemRequeued:
+		// shut down reconciler
+		cancel()
+
 	case <-doneCh:
-		// We expect any outstanding work to complete, for the worker
-		// threads to complete and for doneCh to close in a timely manner.
-	}
+		t.Fatal("StartAll finished early")
 
-	// Check that the work was requeued in RateLimiter.
-	// As NumRequeues can't fully reflect the real state of queue length.
-	// Here we need to wait for NumRequeues to be more than 1, to ensure
-	// the key get re-queued and reprocessed as expect.
-	if got, wantAtLeast := impl.WorkQueue().NumRequeues(types.NamespacedName{Namespace: "", Name: "bar"}), 2; got < wantAtLeast {
-		t.Errorf("Requeue count = %v, wanted at least %v", got, wantAtLeast)
+	case <-ctx.Done():
+		t.Fatal("Timed out waiting for item to be requeued")
 	}
 }
 
@@ -1254,9 +1343,10 @@ func TestImplGlobalResync(t *testing.T) {
 	impl.GlobalResync(&dummyInformer{})
 
 	// The global resync delays enqueuing things by a second with a jitter that
-	// goes up to len(dummyObjs) times a second.
+	// goes up to len(dummyObjs) times a second: time.Duration(1+len(dummyObjs)) * time.Second.
+	// In this test, the fast lane is empty, so we can assume immediate enqueuing.
 	select {
-	case <-time.After((1 + 3) * time.Second):
+	case <-time.After(50 * time.Millisecond):
 		// We don't expect completion before the context is cancelled.
 	case <-doneCh:
 		t.Error("StartAll finished early.")
@@ -1361,10 +1451,10 @@ func TestStartInformersEventualSuccess(t *testing.T) {
 	}()
 
 	select {
-	case err := <-errCh:
-		t.Errorf("Unexpected send on errCh: %v", err)
-	case <-time.After(1 * time.Second):
+	case <-time.After(50 * time.Millisecond):
 		// Wait a brief period to ensure nothing is sent.
+	case err := <-errCh:
+		t.Fatal("Unexpected send on errCh:", err)
 	}
 
 	// Let the Sync complete.
@@ -1392,10 +1482,10 @@ func TestStartInformersFailure(t *testing.T) {
 	}()
 
 	select {
-	case err := <-errCh:
-		t.Errorf("Unexpected send on errCh: %v", err)
-	case <-time.After(1 * time.Second):
+	case <-time.After(50 * time.Millisecond):
 		// Wait a brief period to ensure nothing is sent.
+	case err := <-errCh:
+		t.Fatal("Unexpected send on errCh:", err)
 	}
 
 	// Now close the stopCh and we should see an error sent.
@@ -1448,10 +1538,10 @@ func TestRunInformersEventualSuccess(t *testing.T) {
 	}()
 
 	select {
-	case err := <-errCh:
-		t.Fatalf("Unexpected send on errCh: %v", err)
-	case <-time.After(1 * time.Second):
+	case <-time.After(50 * time.Millisecond):
 		// Wait a brief period to ensure nothing is sent.
+	case err := <-errCh:
+		t.Fatal("Unexpected send on errCh:", err)
 	}
 
 	// Let the Sync complete.
@@ -1482,10 +1572,10 @@ func TestRunInformersFailure(t *testing.T) {
 	}()
 
 	select {
-	case err := <-errCh:
-		t.Errorf("Unexpected send on errCh: %v", err)
-	case <-time.After(1 * time.Second):
+	case <-time.After(50 * time.Millisecond):
 		// Wait a brief period to ensure nothing is sent.
+	case err := <-errCh:
+		t.Fatal("Unexpected send on errCh:", err)
 	}
 
 	// Now close the stopCh and we should see an error sent.
diff --git a/controller/stats_reporter_test.go b/controller/stats_reporter_test.go
index ff56d99a3..35895f764 100644
--- a/controller/stats_reporter_test.go
+++ b/controller/stats_reporter_test.go
@@ -38,7 +38,6 @@ func TestNewStatsReporterErrors(t *testing.T) {
 		if err == nil {
 			t.Errorf("Expected err to not be nil for value %q, got nil", v)
 		}
-
 	}
 }
 
diff --git a/controller/two_lane_queue_test.go b/controller/two_lane_queue_test.go
index a6998e735..187cd2fb1 100644
--- a/controller/two_lane_queue_test.go
+++ b/controller/two_lane_queue_test.go
@@ -29,64 +29,60 @@ type chanRateLimiter struct {
 	t *testing.T
 	// Called when this ratelimiter is consulted for when to process a value.
 	whenCalled chan interface{}
-	retryCount map[interface{}]int
 }
 
-func (r chanRateLimiter) When(item interface{}) time.Duration {
+func (r *chanRateLimiter) When(item interface{}) time.Duration {
 	r.whenCalled <- item
-	return 0 * time.Second
+	return 0
 }
-func (r chanRateLimiter) Forget(item interface{}) {
+
+func (r *chanRateLimiter) Forget(item interface{}) {
 	r.t.Fatalf("Forgetting item %+v, we should not be forgetting any items.", item)
 }
-func (r chanRateLimiter) NumRequeues(item interface{}) int {
-	return r.retryCount[item]
+
+func (r *chanRateLimiter) NumRequeues(item interface{}) int {
+	return 0
 }
 
 var _ workqueue.RateLimiter = &chanRateLimiter{}
 
 func TestRateLimit(t *testing.T) {
-	whenCalled := make(chan interface{}, 2)
-	rl := chanRateLimiter{
-		t,
-		whenCalled,
-		make(map[interface{}]int),
+	// Verifies that we properly pass the rate limiter to the queue.
+	rl := &chanRateLimiter{
+		t:          t,
+		whenCalled: make(chan interface{}, 1),
 	}
-	q := newTwoLaneWorkQueue("live-in-the-limited-lane", rl)
-	q.SlowLane().Add("1")
-	q.Add("2")
 
-	k, done := q.Get()
-	q.Done(k)
-	q.AddRateLimited(k)
-	rlK := <-whenCalled
-	if got, want := k.(string), rlK.(string); got != want {
-		t.Errorf(`Got = %q, want: "%q"`, got, want)
-	}
-	if done {
-		t.Error("The queue is unexpectedly shutdown")
+	q := newTwoLaneWorkQueue("live-in-the-limited-lane", rl)
+	// Verify the slow lane has the proper RL.
+	q.SlowLane().AddRateLimited("1")
+	select {
+	case <-rl.whenCalled:
+		// As desired.
+	default:
+		t.Error("Didn't go to the proper rate limiter.")
 	}
 
-	k2, done := q.Get()
-	q.Done(k2)
-	q.AddRateLimited(k2)
-	rlK2 := <-whenCalled
-	if got, want := k2.(string), rlK2.(string); got != want {
-		t.Errorf(`Got = %q, want: "%q"`, got, want)
-	}
-	if s1, s2 := k.(string), k2.(string); (s1 != "1" || s2 != "2") && (s1 != "2" || s2 != "1") {
-		t.Errorf("Expected to see 1 and 2, instead saw %q and %q", s1, s2)
-	}
-	if done {
-		t.Error("The queue is unexpectedly shutdown")
+	// Verify the fast lane has the proper RL.
+	q.AddRateLimited("2")
+	select {
+	case <-rl.whenCalled:
+		// As desired.
+	default:
+		t.Error("Didn't go to the proper rate limiter.")
 	}
 
-	// Queue has async moving parts so if we check at the wrong moment, this might still be 0 or 1.
+	// Verify the items were properly added for consumption.
 	if wait.PollImmediate(10*time.Millisecond, 250*time.Millisecond, func() (bool, error) {
 		return q.Len() == 2, nil
 	}) != nil {
 		t.Error("Queue length was never 2")
 	}
+	// And drain.
+	q.ShutDown()
+	for q.Len() > 0 {
+		q.Get()
+	}
 }
 
 func TestSlowQueue(t *testing.T) {
diff --git a/leaderelection/context_test.go b/leaderelection/context_test.go
index 76cf4bb3a..1b20e1d98 100644
--- a/leaderelection/context_test.go
+++ b/leaderelection/context_test.go
@@ -21,6 +21,7 @@ package leaderelection
 
 import (
 	"context"
+	"fmt"
 	"os"
 	"sort"
 	"testing"
@@ -166,6 +167,60 @@ func TestWithBuilder(t *testing.T) {
 	}
 }
 
+func TestBuilderWithCustomizedLeaseName(t *testing.T) {
+	const buckets = 3
+	cc := ComponentConfig{
+		Component:     "the-component",
+		Buckets:       buckets,
+		LeaseDuration: 15 * time.Second,
+		RenewDeadline: 10 * time.Second,
+		RetryPeriod:   2 * time.Second,
+		LeaseName: func(i uint32) string {
+			return fmt.Sprintf("bucket-%02d", i)
+		},
+	}
+	kc := fakekube.NewSimpleClientset()
+	ctx := context.Background()
+
+	gotNames := make(sets.String, buckets)
+	promoted := make(chan string)
+	laf := &reconciler.LeaderAwareFuncs{
+		PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error {
+			promoted <- bkt.Name()
+			return nil
+		},
+	}
+	enq := func(reconciler.Bucket, types.NamespacedName) {}
+	ctx = WithDynamicLeaderElectorBuilder(ctx, kc, cc)
+	le, err := BuildElector(ctx, laf, "name", enq)
+	if err != nil {
+		t.Fatalf("BuildElector() = %v", err)
+	}
+
+	ctx, cancel := context.WithCancel(context.Background())
+	t.Cleanup(cancel)
+	go le.Run(ctx)
+
+	// We expect to have been promoted 3 times.
+	for i := 0; i < buckets; i++ {
+		select {
+		case s := <-promoted:
+			gotNames.Insert(s)
+		case <-time.After(time.Second):
+			t.Fatal("Timed out waiting for promotion.")
+		}
+	}
+
+	want := sets.NewString(
+		"bucket-00",
+		"bucket-01",
+		"bucket-02",
+	)
+	if !gotNames.Equal(want) {
+		t.Errorf("BucketSet.BucketList() = %q, want: %q", gotNames, want)
+	}
+}
+
 func TestNewStatefulSetBucketAndSet(t *testing.T) {
 	wantNames := []string{
 		"http://as-0.autoscaler.knative-testing.svc.cluster.local:80",
diff --git a/logging/config_test.go b/logging/config_test.go
index a86c73a38..6fca9fb3d 100644
--- a/logging/config_test.go
+++ b/logging/config_test.go
@@ -18,6 +18,7 @@ package logging
 
 import (
 	"fmt"
+	"os"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
@@ -476,3 +477,20 @@ func withComponentLevel(name, lvl string) testConfigOption {
 		return cfg
 	}
 }
+
+func TestConfigMapName(t *testing.T) {
+	if got, want := ConfigMapName(), "config-logging"; got != want {
+		t.Errorf("ConfigMapName = %q, want: %q", got, want)
+	}
+	t.Cleanup(func() {
+		os.Unsetenv(configMapNameEnv)
+	})
+	os.Setenv(configMapNameEnv, "")
+	if got, want := ConfigMapName(), "config-logging"; got != want {
+		t.Errorf("ConfigMapName = %q, want: %q", got, want)
+	}
+	os.Setenv(configMapNameEnv, "slowly-dying-inside")
+	if got, want := ConfigMapName(), "slowly-dying-inside"; got != want {
+		t.Errorf("ConfigMapName = %q, want: %q", got, want)
+	}
+}
diff --git a/metrics/config_observability_test.go b/metrics/config_observability_test.go
index 6c90f291d..9e752bd5a 100644
--- a/metrics/config_observability_test.go
+++ b/metrics/config_observability_test.go
@@ -17,6 +17,7 @@ limitations under the License.
 package metrics
 
 import (
+	"os"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
@@ -32,13 +33,12 @@ func TestObservabilityConfiguration(t *testing.T) {
 		wantErr    bool
 		wantConfig *ObservabilityConfig
 	}{{
-		name:    "observability configuration with all inputs",
-		wantErr: false,
+		name: "observability configuration with all inputs",
 		wantConfig: &ObservabilityConfig{
 			EnableProbeRequestLog:  true,
 			EnableProfiling:        true,
-			EnableRequestLog:       false,
 			EnableVarLogCollection: true,
+			EnableRequestLog:       true,
 			LoggingURLTemplate:     "https://logging.io",
 			RequestLogTemplate:     `{"requestMethod": "{{.Request.Method}}"}`,
 			RequestMetricsBackend:  "stackdriver",
@@ -48,38 +48,65 @@ func TestObservabilityConfiguration(t *testing.T) {
 			"logging.enable-var-log-collection":           "true",
 			"logging.request-log-template":                `{"requestMethod": "{{.Request.Method}}"}`,
 			"logging.revision-url-template":               "https://logging.io",
-			"logging.enable-request-log":                  "false",
+			"logging.enable-request-log":                  "true",
 			"metrics.request-metrics-backend-destination": "stackdriver",
 			"profiling.enable":                            "true",
 		},
 	}, {
 		name:       "observability config with no map",
-		wantErr:    false,
 		wantConfig: defaultConfig(),
 	}, {
-		name:       "invalid request log template",
-		wantErr:    true,
-		wantConfig: nil,
+		name:    "invalid request log template",
+		wantErr: true,
 		data: map[string]string{
 			"logging.request-log-template": `{{ something }}`,
 		},
 	}, {
-		name:    "observability configuration with request log set and with template not set",
-		wantErr: false,
+		name: "observability configuration with request log set and template default",
+		data: map[string]string{
+			"logging.enable-probe-request-log":            "true",
+			"logging.enable-request-log":                  "true",
+			"logging.enable-var-log-collection":           "true",
+			"logging.revision-url-template":               "https://logging.io",
+			"metrics.request-metrics-backend-destination": "stackdriver",
+			"profiling.enable":                            "true",
+		},
+		wantConfig: &ObservabilityConfig{
+			EnableProbeRequestLog:  true,
+			EnableProfiling:        true,
+			EnableRequestLog:       true,
+			EnableVarLogCollection: true,
+			LoggingURLTemplate:     "https://logging.io",
+			RequestLogTemplate:     DefaultRequestLogTemplate,
+			RequestMetricsBackend:  "stackdriver",
+		},
+	}, {
+		name: "observability configuration with request log and template not set",
 		wantConfig: &ObservabilityConfig{
 			EnableProbeRequestLog:  true,
 			EnableProfiling:        true,
-			EnableRequestLog:       false,
 			EnableVarLogCollection: true,
 			LoggingURLTemplate:     "https://logging.io",
-			RequestLogTemplate:     "",
 			RequestMetricsBackend:  "stackdriver",
 		},
 		data: map[string]string{
 			"logging.enable-probe-request-log":            "true",
+			"logging.enable-request-log":                  "false",
 			"logging.enable-var-log-collection":           "true",
+			"logging.request-log-template":                "",
+			"logging.revision-url-template":               "https://logging.io",
+			"metrics.request-metrics-backend-destination": "stackdriver",
+			"profiling.enable":                            "true",
+		},
+	}, {
+		name:    "observability configuration with request log set and template not set",
+		wantErr: true,
+		data: map[string]string{
+			"logging.enable-probe-request-log":            "true",
+			"logging.enable-request-log":                  "true",
+			"logging.enable-var-log-collection":           "true",
+			"logging.request-log-template":                "",
 			"logging.revision-url-template":               "https://logging.io",
-			"logging.enable-request-log":                  "false",
 			"metrics.request-metrics-backend-destination": "stackdriver",
 			"profiling.enable":                            "true",
 		},
@@ -89,7 +116,6 @@ func TestObservabilityConfiguration(t *testing.T) {
 		wantConfig: &ObservabilityConfig{
 			EnableProbeRequestLog:  true,
 			EnableProfiling:        true,
-			EnableRequestLog:       true,
 			EnableVarLogCollection: true,
 			LoggingURLTemplate:     "https://logging.io",
 			RequestLogTemplate:     `{"requestMethod": "{{.Request.Method}}"}`,
@@ -103,6 +129,18 @@ func TestObservabilityConfiguration(t *testing.T) {
 			"metrics.request-metrics-backend-destination": "stackdriver",
 			"profiling.enable":                            "true",
 		},
+	}, {
+		name: "observability configuration with collector address",
+		wantConfig: &ObservabilityConfig{
+			LoggingURLTemplate:      DefaultLogURLTemplate,
+			RequestLogTemplate:      DefaultRequestLogTemplate,
+			RequestMetricsBackend:   "opencensus",
+			MetricsCollectorAddress: "otel:55678",
+		},
+		data: map[string]string{
+			"metrics.request-metrics-backend-destination": "opencensus",
+			"metrics.opencensus-address":                  "otel:55678",
+		},
 	}}
 
 	for _, tt := range observabilityConfigTests {
@@ -121,3 +159,20 @@ func TestObservabilityConfiguration(t *testing.T) {
 		})
 	}
 }
+
+func TestConfigMapName(t *testing.T) {
+	if got, want := ConfigMapName(), "config-observability"; got != want {
+		t.Errorf("ConfigMapName = %q, want: %q", got, want)
+	}
+	t.Cleanup(func() {
+		os.Unsetenv(configMapNameEnv)
+	})
+	os.Setenv(configMapNameEnv, "")
+	if got, want := ConfigMapName(), "config-observability"; got != want {
+		t.Errorf("ConfigMapName = %q, want: %q", got, want)
+	}
+	os.Setenv(configMapNameEnv, "why-is-living-so-hard?")
+	if got, want := ConfigMapName(), "why-is-living-so-hard?"; got != want {
+		t.Errorf("ConfigMapName = %q, want: %q", got, want)
+	}
+}
diff --git a/test/monitoring/monitoring.go b/test/monitoring/monitoring.go
index 2dbaf958c..2e618f247 100644
--- a/test/monitoring/monitoring.go
+++ b/test/monitoring/monitoring.go
@@ -22,6 +22,7 @@ import (
 	"os"
 	"os/exec"
 	"strings"
+	"time"
 
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -55,7 +56,22 @@ func GetPods(kubeClientset *kubernetes.Clientset, app, namespace string) (*v1.Po
 // Cleanup will clean the background process used for port forwarding
 func Cleanup(pid int) error {
 	ps := os.Process{Pid: pid}
-	return ps.Kill()
+	if err := ps.Kill(); err != nil {
+		return err
+	}
+
+	errCh := make(chan error)
+	go func() {
+		_, err := ps.Wait()
+		errCh <- err
+	}()
+
+	select {
+	case err := <-errCh:
+		return err
+	case <-time.After(30 * time.Second):
+		return fmt.Errorf("timed out waiting for process %d to exit", pid)
+	}
 }
 
 // PortForward sets up local port forward to the pod specified by the "app" label in the given namespace
diff --git a/vendor/knative.dev/test-infra/scripts/README.md b/vendor/knative.dev/test-infra/scripts/README.md
index 01361fd4a..6fc264849 100644
--- a/vendor/knative.dev/test-infra/scripts/README.md
+++ b/vendor/knative.dev/test-infra/scripts/README.md
@@ -113,12 +113,13 @@ This is a helper script for Knative E2E test scripts. To use it:
 1. [optional] Customize the test cluster. Set the following environment
    variables if the default values don't fit your needs:
 
-   - `E2E_GCP_PROJECT_ID`: GCP project ID for creating the clusters, defaults to none.
+   - `E2E_GCP_PROJECT_ID`: GCP project ID for creating the clusters, defaults to
+     none.
    - `E2E_GKE_CLUSTER_REGION`: Cluster region, defaults to `us-central1`.
-   - `E2E_GKE_CLUSTER_BACKUP_REGIONS`: Space-separated list of regions to retry test
-     cluster creation in case of stockout. Defaults to `us-west1 us-east1`.
-   - `E2E_GKE_CLUSTER_ZONE`: Cluster zone (e.g., `a`), defaults to none (i.e. use a
-     regional cluster).
+   - `E2E_GKE_CLUSTER_BACKUP_REGIONS`: Space-separated list of regions to retry
+     test cluster creation in case of stockout. Defaults to `us-west1 us-east1`.
+   - `E2E_GKE_CLUSTER_ZONE`: Cluster zone (e.g., `a`), defaults to none (i.e.
+     use a regional cluster).
    - `E2E_GKE_CLUSTER_BACKUP_ZONES`: Space-separated list of zones to retry test
      cluster creation in case of stockout. If defined,
      `E2E_GKE_CLUSTER_BACKUP_REGIONS` will be ignored thus it defaults to none.
@@ -128,7 +129,8 @@ This is a helper script for Knative E2E test scripts. To use it:
      autoscaling, defaults to 1.
    - `E2E_MAX_CLUSTER_NODES`: Maximum number of nodes in the cluster when
      autoscaling, defaults to 3.
-   - `E2E_GKE_SCOPES`: Scopes for the GKE node instances, defaults to `cloud-platform`.
+   - `E2E_GKE_SCOPES`: Scopes for the GKE node instances, defaults to
+     `cloud-platform`.
    - `E2E_CLUSTER_VERSION`: Version for the cluster, defaults to `latest`.
 
 1. Source the script.
diff --git a/vendor/knative.dev/test-infra/scripts/e2e-tests.sh b/vendor/knative.dev/test-infra/scripts/e2e-tests.sh
index 590adb1f3..e644a5c33 100644
--- a/vendor/knative.dev/test-infra/scripts/e2e-tests.sh
+++ b/vendor/knative.dev/test-infra/scripts/e2e-tests.sh
@@ -129,15 +129,15 @@ function fail_test() {
 
 SKIP_TEARDOWNS=0
 SKIP_ISTIO_ADDON=0
+E2E_SCRIPT=""
 
 # Parse flags and initialize the test cluster.
 function initialize() {
   local run_tests=0
   local extra_kubetest2_flags=()
   local extra_cluster_creation_flags=()
-  local e2e_script
-  e2e_script="$(get_canonical_path "$0")"
-  local e2e_script_command=( "${e2e_script}" "--run-tests" )
+  E2E_SCRIPT="$(get_canonical_path "$0")"
+  local e2e_script_command=( "${E2E_SCRIPT}" "--run-tests" )
 
   cd "${REPO_ROOT_DIR}"
   while [[ $# -ne 0 ]]; do