Repository: knative/serving
Pull request #8786 (2020-07-23 14:01:15)

[master] Update net-istio nightly
<!--[master] Update net-istio nightly-->
<!---->
Produced via:
  `curl https://storage.googleapis.com/knative-nightly/net-istio/latest/$x > /workspace/source/./third_party//$x`
/assign tcnghia
/cc tcnghia
/test pull-knative-serving-istio-1.4-mesh


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

This pull-request has been approved by: *<a href="https://github.com/knative/serving/pull/8786#" 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%2Fserving).

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:

- ~~[third_party/OWNERS](https://github.com/knative/serving/blob/master/third_party/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":[]} -->

Patch:
diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES
index e9023468f..8ace497e6 100644
--- a/OWNERS_ALIASES
+++ b/OWNERS_ALIASES
@@ -43,6 +43,7 @@ aliases:
   - markusthoemmes
   - mdemirhan
   - vagababov
+  - julz
   autoscaling-reviewers:
   - julz
   - markusthoemmes
diff --git a/go.mod b/go.mod
index 6055c34dd..f1edc8034 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
 	github.com/ghodss/yaml v1.0.0
 	github.com/gogo/protobuf v1.3.1
 	github.com/golang/protobuf v1.4.2
-	github.com/google/go-cmp v0.5.0
+	github.com/google/go-cmp v0.5.1
 	github.com/google/go-containerregistry v0.1.2-0.20200717224239-a84993334b27
 	github.com/google/gofuzz v1.1.0
 	github.com/google/mako v0.0.0-20190821191249-122f8dcef9e3
@@ -38,10 +38,10 @@ require (
 	k8s.io/client-go v11.0.1-0.20190805182717-6502b5e7b1b5+incompatible
 	k8s.io/code-generator v0.18.0
 	k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29
-	knative.dev/caching v0.0.0-20200721163356-191470591e4c
-	knative.dev/networking v0.0.0-20200721203054-c218540746c9
-	knative.dev/pkg v0.0.0-20200721211453-557b6826ef85
-	knative.dev/test-infra v0.0.0-20200721175154-c98db9bd4d5d
+	knative.dev/caching v0.0.0-20200722161057-641864dd528b
+	knative.dev/networking v0.0.0-20200722170957-facd9a20c21d
+	knative.dev/pkg v0.0.0-20200723060257-ae9c3f7fa8d3
+	knative.dev/test-infra v0.0.0-20200722142057-3ca910b5a25e
 )
 
 replace (
diff --git a/go.sum b/go.sum
index 571b020cc..c41b36ca6 100644
--- a/go.sum
+++ b/go.sum
@@ -515,6 +515,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
+github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-containerregistry v0.0.0-20200115214256-379933c9c22b/go.mod h1:Wtl/v6YdQxv397EREtzwgd9+Ud7Q5D8XMbi3Zazgkrs=
 github.com/google/go-containerregistry v0.1.1 h1:AG8FSAfXglim2l5qSrqp5VK2Xl03PiBf25NiTGGamws=
 github.com/google/go-containerregistry v0.1.1/go.mod h1:npTSyywOeILcgWqd+rvtzGWflIPPcBQhYoOONaY4ltM=
@@ -1384,6 +1386,8 @@ golang.org/x/tools v0.0.0-20200714190737-9048b464a08d h1:hYhnolbefSSt3WZp66sgmgn
 golang.org/x/tools v0.0.0-20200714190737-9048b464a08d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200721032237-77f530d86f9a h1:kVMPw4f6EVqYdfGQTedjrpw1dbE2PEMfw4jwXsNdn9s=
 golang.org/x/tools v0.0.0-20200721032237-77f530d86f9a/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
+golang.org/x/tools v0.0.0-20200721223218-6123e77877b2 h1:kxDWg8KNMtpGjI/XVKGgOtSljTnVg/PrjhS8+0pxjLE=
+golang.org/x/tools v0.0.0-20200721223218-6123e77877b2/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
@@ -1475,6 +1479,8 @@ google.golang.org/genproto v0.0.0-20200715011427-11fb19a81f2c h1:6DWnZZ6EY/59QRR
 google.golang.org/genproto v0.0.0-20200715011427-11fb19a81f2c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986 h1:10ohwcLf82I55O/aQxYqmWKoOdNbQTYYComeP1KDOS4=
 google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20200722002428-88e341933a54 h1:ASrBgpl9XvkNTP0m39/j18mid7aoF21npu2ioIBxYnY=
+google.golang.org/genproto v0.0.0-20200722002428-88e341933a54/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -1619,24 +1625,26 @@ k8s.io/test-infra v0.0.0-20200514184223-ba32c8aae783/go.mod h1:bW6thaPZfL2hW7ecj
 k8s.io/test-infra v0.0.0-20200617221206-ea73eaeab7ff/go.mod h1:L3+cRvwftUq8IW1TrHji5m3msnc4uck/7LsE/GR/aZk=
 k8s.io/test-infra v0.0.0-20200715094037-cc150f5ae724/go.mod h1:D2jUSuQFYy6McY2qbknsLUE9stqN0yIuJ+rjdUAxSCs=
 k8s.io/test-infra v0.0.0-20200721115715-1af01ef6b4c8/go.mod h1:4cRZlOy5Ka3Ym/orCmNWL2dsE39pN0xHFT0WFrZe2HQ=
+k8s.io/test-infra v0.0.0-20200722010006-526277bee528/go.mod h1:4cRZlOy5Ka3Ym/orCmNWL2dsE39pN0xHFT0WFrZe2HQ=
 k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
 k8s.io/utils v0.0.0-20200124190032-861946025e34 h1:HjlUD6M0K3P8nRXmr2B9o4F9dUy9TCj/aEpReeyi6+k=
 k8s.io/utils v0.0.0-20200124190032-861946025e34/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
 knative.dev/caching v0.0.0-20200116200605-67bca2c83dfa/go.mod h1:dHXFU6CGlLlbzaWc32g80cR92iuBSpsslDNBWI8C7eg=
-knative.dev/caching v0.0.0-20200721163356-191470591e4c h1:aDeMztPpV4WeBDX4AuPY8g18GG+RBk2aFVzvqgj0b/8=
-knative.dev/caching v0.0.0-20200721163356-191470591e4c/go.mod h1:yCEJufJ5fN3jfqak39ZiCoPv8gnUXKc3zf1ShXznG50=
+knative.dev/caching v0.0.0-20200722161057-641864dd528b h1:5myzqPEGzwwjTBs4TrC9eN9dr6GgKMVpgexrYQWmc9Y=
+knative.dev/caching v0.0.0-20200722161057-641864dd528b/go.mod h1:PyEAi2yV6/ZijlYPI3N3LvKWub7VaAMvoGN5aR+hkY8=
 knative.dev/eventing-contrib v0.11.2/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
-knative.dev/networking v0.0.0-20200721203054-c218540746c9 h1:ZXdQiiK+8zP/uaiPIR+ftn5Zr3IP9kBOkucahWd/Hnw=
-knative.dev/networking v0.0.0-20200721203054-c218540746c9/go.mod h1:o5vF7egCdods9D0/SJKDVPtN2B12DeQ/kcwlZV68RPI=
+knative.dev/networking v0.0.0-20200722170957-facd9a20c21d h1:eYqwM1cYXvDmDT8tkWhma2SZx1tc02vIEqeuGi6H038=
+knative.dev/networking v0.0.0-20200722170957-facd9a20c21d/go.mod h1:uZezjyCfUDhhvhX9J86zSMG+uPcCb6m893lF9800p78=
 knative.dev/pkg v0.0.0-20200207155214-fef852970f43/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
-knative.dev/pkg v0.0.0-20200721091635-3b7ca76a63e4/go.mod h1:uiDEJrNciKWl5fTSURoGXekCN5Wz9yo9ambLR80ISL0=
-knative.dev/pkg v0.0.0-20200721211453-557b6826ef85 h1:Yq3+57GZOg/EpRaoEUazq+BiM9g5CTIqgeDdFeIByc8=
-knative.dev/pkg v0.0.0-20200721211453-557b6826ef85/go.mod h1:uiDEJrNciKWl5fTSURoGXekCN5Wz9yo9ambLR80ISL0=
+knative.dev/pkg v0.0.0-20200722085354-ba0f3cb7cf84/go.mod h1:uiDEJrNciKWl5fTSURoGXekCN5Wz9yo9ambLR80ISL0=
+knative.dev/pkg v0.0.0-20200723060257-ae9c3f7fa8d3 h1:A4Q7ylydTjfl8Wj1bbyVCwVC0msBDJeqhaHNZZIc47Y=
+knative.dev/pkg v0.0.0-20200723060257-ae9c3f7fa8d3/go.mod h1:pOJ+tu5k/SgHPGmecWqFguxJcIZozyDWlTlyldGcYGM=
 knative.dev/test-infra v0.0.0-20200719034534-5adf654f5ed5 h1:ZspjtLBz7pzkB9PvPxSgDNHVNftTU1mmjtYh+j5LZJE=
 knative.dev/test-infra v0.0.0-20200719034534-5adf654f5ed5/go.mod h1:mAsPDmFmlsTJjRWplWBz8xtEiarSgvGiiOjkGj4Or1g=
-knative.dev/test-infra v0.0.0-20200720224135-d2706240545c/go.mod h1:mAsPDmFmlsTJjRWplWBz8xtEiarSgvGiiOjkGj4Or1g=
 knative.dev/test-infra v0.0.0-20200721175154-c98db9bd4d5d h1:GFzUmKNa8SVL3uMwa3R+NOjaSY1oUzxNdNWW3j5HrZ4=
 knative.dev/test-infra v0.0.0-20200721175154-c98db9bd4d5d/go.mod h1:kzRhTm5L08eDQFRl8NKSAN93lz6IZWQMs+2TjTCN+VA=
+knative.dev/test-infra v0.0.0-20200722142057-3ca910b5a25e h1:KXg2xCMLWa+J4+mA8aerqe+6Utw7NXlbPAUypKGeHnY=
+knative.dev/test-infra v0.0.0-20200722142057-3ca910b5a25e/go.mod h1:oHmDsPmq+zcc3b+Z94Kgmrz1JnmZEz36jmKuvL2Lw7o=
 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/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/reconciler.go b/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/reconciler.go
index fc340dc69..f576e0f3f 100644
--- a/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/reconciler.go
+++ b/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/reconciler.go
@@ -32,7 +32,6 @@ import (
 	labels "k8s.io/apimachinery/pkg/labels"
 	types "k8s.io/apimachinery/pkg/types"
 	sets "k8s.io/apimachinery/pkg/util/sets"
-	cache "k8s.io/client-go/tools/cache"
 	record "k8s.io/client-go/tools/record"
 	controller "knative.dev/pkg/controller"
 	logging "knative.dev/pkg/logging"
@@ -84,6 +83,15 @@ type ReadOnlyFinalizer interface {
 	ObserveFinalizeKind(ctx context.Context, o *v1alpha1.Metric) reconciler.Event
 }
 
+type doReconcile func(ctx context.Context, o *v1alpha1.Metric) reconciler.Event
+
+const (
+	doReconcileKind       = "ReconcileKind"
+	doFinalizeKind        = "FinalizeKind"
+	doObserveKind         = "ObserveKind"
+	doObserveFinalizeKind = "ObserveFinalizeKind"
+)
+
 // reconcilerImpl implements controller.Reconciler for v1alpha1.Metric resources.
 type reconcilerImpl struct {
 	// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware
@@ -176,22 +184,19 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	logger := logging.FromContext(ctx)
 
-	// Convert the namespace/name string into a distinct namespace and name
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	// Initialize the reconciler state. This will convert the namespace/name
+	// string into a distinct namespace and name, determin if this instance of
+	// the reconciler is the leader, and any additional interfaces implemented
+	// by the reconciler. Returns an error is the resource key is invalid.
+	s, err := newState(key, r)
 	if err != nil {
 		logger.Errorf("invalid resource key: %s", key)
 		return nil
 	}
-	// Establish whether we are the leader for use below.
-	isLeader := r.IsLeaderFor(types.NamespacedName{
-		Namespace: namespace,
-		Name:      name,
-	})
-	roi, isROI := r.reconciler.(ReadOnlyInterface)
-	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
-	if !isLeader && !isROI && !isROF {
-		// If we are not the leader, and we don't implement either ReadOnly
-		// interface, then take a fast-path out.
+
+	// If we are not the leader, and we don't implement either ReadOnly
+	// observer interfaces, then take a fast-path out.
+	if s.isNotLeaderNorObserver() {
 		return nil
 	}
 
@@ -205,9 +210,9 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 
 	// Get the resource with this namespace/name.
 
-	getter := r.Lister.Metrics(namespace)
+	getter := r.Lister.Metrics(s.namespace)
 
-	original, err := getter.Get(name)
+	original, err := getter.Get(s.name)
 
 	if errors.IsNotFound(err) {
 		// The resource may no longer exist, in which case we stop processing.
@@ -221,48 +226,42 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	resource := original.DeepCopy()
 
 	var reconcileEvent reconciler.Event
-	if resource.GetDeletionTimestamp().IsZero() {
-		if isLeader {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
-
-			// Set and update the finalizer on resource if r.reconciler
-			// implements Finalizer.
-			if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
-				return fmt.Errorf("failed to set finalizers: %w", err)
-			}
 
-			reconciler.PreProcessReconcile(ctx, resource)
+	name, do := s.reconcileMethodFor(resource)
+	// Append the target method to the logger.
+	logger = logger.With(zap.String("targetMethod", name))
+	switch name {
+	case doReconcileKind:
+		// Append the target method to the logger.
+		logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
 
-			// Reconcile this copy of the resource and then write back any status
-			// updates regardless of whether the reconciliation errored out.
-			reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
+		// Set and update the finalizer on resource if r.reconciler
+		// implements Finalizer.
+		if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
+			return fmt.Errorf("failed to set finalizers: %w", err)
+		}
 
-			reconciler.PostProcessReconcile(ctx, resource, original)
+		reconciler.PreProcessReconcile(ctx, resource)
 
-		} else if isROI {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ObserveKind"))
+		// Reconcile this copy of the resource and then write back any status
+		// updates regardless of whether the reconciliation errored out.
+		reconcileEvent = do(ctx, resource)
 
-			// Observe any changes to this resource, since we are not the leader.
-			reconcileEvent = roi.ObserveKind(ctx, resource)
-		}
-	} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
+		reconciler.PostProcessReconcile(ctx, resource, original)
 
+	case "FinalizeKind":
 		// For finalizing reconcilers, if this resource being marked for deletion
 		// and reconciled cleanly (nil or normal event), remove the finalizer.
-		reconcileEvent = fin.FinalizeKind(ctx, resource)
+		reconcileEvent = do(ctx, resource)
+
 		if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
 			return fmt.Errorf("failed to clear finalizers: %w", err)
 		}
-	} else if !isLeader && isROF {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
 
-		// For finalizing reconcilers, just observe when we aren't the leader.
-		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
+	case "ObserveKind", "ObserveFinalizeKind":
+		// Observe any changes to this resource, since we are not the leader.
+		reconcileEvent = do(ctx, resource)
+
 	}
 
 	// Synchronize the status.
@@ -275,7 +274,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		// This is important because the copy we loaded from the injectionInformer's
 		// cache may be stale and we don't want to overwrite a prior update
 		// to status with this stale state.
-	case !isLeader:
+	case !s.isLeader:
 		// High-availability reconcilers may have many replicas watching the resource, but only
 		// the elected leader is expected to write modifications.
 		logger.Warn("Saw status changes when we aren't the leader!")
diff --git a/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/state.go b/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/state.go
new file mode 100644
index 000000000..7cf1b131b
--- /dev/null
+++ b/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/state.go
@@ -0,0 +1,105 @@
+/*
+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 metric
+
+import (
+	fmt "fmt"
+
+	types "k8s.io/apimachinery/pkg/types"
+	cache "k8s.io/client-go/tools/cache"
+	v1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
+)
+
+// state is used to track the state of a reconciler in a single run.
+type state struct {
+	// Key is the original reconciliation key from the queue.
+	key string
+	// Namespace is the namespace split from the reconciliation key.
+	namespace string
+	// Namespace is the name split from the reconciliation key.
+	name string
+	// reconciler is the reconciler.
+	reconciler Interface
+	// rof is the read only interface cast of the reconciler.
+	roi ReadOnlyInterface
+	// IsROI (Read Only Interface) the reconciler only observes reconciliation.
+	isROI bool
+	// rof is the read only finalizer cast of the reconciler.
+	rof ReadOnlyFinalizer
+	// IsROF (Read Only Finalizer) the reconciler only observes finalize.
+	isROF bool
+	// IsLeader the instance of the reconciler is the elected leader.
+	isLeader bool
+}
+
+func newState(key string, r *reconcilerImpl) (*state, error) {
+	// Convert the namespace/name string into a distinct namespace and name
+	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	if err != nil {
+		return nil, fmt.Errorf("invalid resource key: %s", key)
+	}
+
+	roi, isROI := r.reconciler.(ReadOnlyInterface)
+	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
+
+	isLeader := r.IsLeaderFor(types.NamespacedName{
+		Namespace: namespace,
+		Name:      name,
+	})
+
+	return &state{
+		key:        key,
+		namespace:  namespace,
+		name:       name,
+		reconciler: r.reconciler,
+		roi:        roi,
+		isROI:      isROI,
+		rof:        rof,
+		isROF:      isROF,
+		isLeader:   isLeader,
+	}, nil
+}
+
+// isNotLeaderNorObserver checks to see if this reconciler with the current
+// state is enabled to do any work or not.
+// isNotLeaderNorObserver returns true when there is no work possible for the
+// reconciler.
+func (s *state) isNotLeaderNorObserver() bool {
+	if !s.isLeader && !s.isROI && !s.isROF {
+		// If we are not the leader, and we don't implement either ReadOnly
+		// interface, then take a fast-path out.
+		return true
+	}
+	return false
+}
+
+func (s *state) reconcileMethodFor(o *v1alpha1.Metric) (string, doReconcile) {
+	if o.GetDeletionTimestamp().IsZero() {
+		if s.isLeader {
+			return doReconcileKind, s.reconciler.ReconcileKind
+		} else if s.isROI {
+			return doObserveKind, s.roi.ObserveKind
+		}
+	} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
+		return doFinalizeKind, fin.FinalizeKind
+	} else if !s.isLeader && s.isROF {
+		return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
+	}
+	return "unknown", nil
+}
diff --git a/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/reconciler.go b/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/reconciler.go
index 202f0a5fe..c2213126b 100644
--- a/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/reconciler.go
+++ b/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/reconciler.go
@@ -32,7 +32,6 @@ import (
 	labels "k8s.io/apimachinery/pkg/labels"
 	types "k8s.io/apimachinery/pkg/types"
 	sets "k8s.io/apimachinery/pkg/util/sets"
-	cache "k8s.io/client-go/tools/cache"
 	record "k8s.io/client-go/tools/record"
 	controller "knative.dev/pkg/controller"
 	logging "knative.dev/pkg/logging"
@@ -84,6 +83,15 @@ type ReadOnlyFinalizer interface {
 	ObserveFinalizeKind(ctx context.Context, o *v1alpha1.PodAutoscaler) reconciler.Event
 }
 
+type doReconcile func(ctx context.Context, o *v1alpha1.PodAutoscaler) reconciler.Event
+
+const (
+	doReconcileKind       = "ReconcileKind"
+	doFinalizeKind        = "FinalizeKind"
+	doObserveKind         = "ObserveKind"
+	doObserveFinalizeKind = "ObserveFinalizeKind"
+)
+
 // reconcilerImpl implements controller.Reconciler for v1alpha1.PodAutoscaler resources.
 type reconcilerImpl struct {
 	// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware
@@ -180,22 +188,19 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	logger := logging.FromContext(ctx)
 
-	// Convert the namespace/name string into a distinct namespace and name
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	// Initialize the reconciler state. This will convert the namespace/name
+	// string into a distinct namespace and name, determin if this instance of
+	// the reconciler is the leader, and any additional interfaces implemented
+	// by the reconciler. Returns an error is the resource key is invalid.
+	s, err := newState(key, r)
 	if err != nil {
 		logger.Errorf("invalid resource key: %s", key)
 		return nil
 	}
-	// Establish whether we are the leader for use below.
-	isLeader := r.IsLeaderFor(types.NamespacedName{
-		Namespace: namespace,
-		Name:      name,
-	})
-	roi, isROI := r.reconciler.(ReadOnlyInterface)
-	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
-	if !isLeader && !isROI && !isROF {
-		// If we are not the leader, and we don't implement either ReadOnly
-		// interface, then take a fast-path out.
+
+	// If we are not the leader, and we don't implement either ReadOnly
+	// observer interfaces, then take a fast-path out.
+	if s.isNotLeaderNorObserver() {
 		return nil
 	}
 
@@ -209,9 +214,9 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 
 	// Get the resource with this namespace/name.
 
-	getter := r.Lister.PodAutoscalers(namespace)
+	getter := r.Lister.PodAutoscalers(s.namespace)
 
-	original, err := getter.Get(name)
+	original, err := getter.Get(s.name)
 
 	if errors.IsNotFound(err) {
 		// The resource may no longer exist, in which case we stop processing.
@@ -232,48 +237,42 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	resource := original.DeepCopy()
 
 	var reconcileEvent reconciler.Event
-	if resource.GetDeletionTimestamp().IsZero() {
-		if isLeader {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
-
-			// Set and update the finalizer on resource if r.reconciler
-			// implements Finalizer.
-			if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
-				return fmt.Errorf("failed to set finalizers: %w", err)
-			}
 
-			reconciler.PreProcessReconcile(ctx, resource)
+	name, do := s.reconcileMethodFor(resource)
+	// Append the target method to the logger.
+	logger = logger.With(zap.String("targetMethod", name))
+	switch name {
+	case doReconcileKind:
+		// Append the target method to the logger.
+		logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
 
-			// Reconcile this copy of the resource and then write back any status
-			// updates regardless of whether the reconciliation errored out.
-			reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
+		// Set and update the finalizer on resource if r.reconciler
+		// implements Finalizer.
+		if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
+			return fmt.Errorf("failed to set finalizers: %w", err)
+		}
 
-			reconciler.PostProcessReconcile(ctx, resource, original)
+		reconciler.PreProcessReconcile(ctx, resource)
 
-		} else if isROI {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ObserveKind"))
+		// Reconcile this copy of the resource and then write back any status
+		// updates regardless of whether the reconciliation errored out.
+		reconcileEvent = do(ctx, resource)
 
-			// Observe any changes to this resource, since we are not the leader.
-			reconcileEvent = roi.ObserveKind(ctx, resource)
-		}
-	} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
+		reconciler.PostProcessReconcile(ctx, resource, original)
 
+	case "FinalizeKind":
 		// For finalizing reconcilers, if this resource being marked for deletion
 		// and reconciled cleanly (nil or normal event), remove the finalizer.
-		reconcileEvent = fin.FinalizeKind(ctx, resource)
+		reconcileEvent = do(ctx, resource)
+
 		if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
 			return fmt.Errorf("failed to clear finalizers: %w", err)
 		}
-	} else if !isLeader && isROF {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
 
-		// For finalizing reconcilers, just observe when we aren't the leader.
-		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
+	case "ObserveKind", "ObserveFinalizeKind":
+		// Observe any changes to this resource, since we are not the leader.
+		reconcileEvent = do(ctx, resource)
+
 	}
 
 	// Synchronize the status.
@@ -286,7 +285,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		// This is important because the copy we loaded from the injectionInformer's
 		// cache may be stale and we don't want to overwrite a prior update
 		// to status with this stale state.
-	case !isLeader:
+	case !s.isLeader:
 		// High-availability reconcilers may have many replicas watching the resource, but only
 		// the elected leader is expected to write modifications.
 		logger.Warn("Saw status changes when we aren't the leader!")
diff --git a/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/state.go b/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/state.go
new file mode 100644
index 000000000..72e9fed19
--- /dev/null
+++ b/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/state.go
@@ -0,0 +1,105 @@
+/*
+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 podautoscaler
+
+import (
+	fmt "fmt"
+
+	types "k8s.io/apimachinery/pkg/types"
+	cache "k8s.io/client-go/tools/cache"
+	v1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
+)
+
+// state is used to track the state of a reconciler in a single run.
+type state struct {
+	// Key is the original reconciliation key from the queue.
+	key string
+	// Namespace is the namespace split from the reconciliation key.
+	namespace string
+	// Namespace is the name split from the reconciliation key.
+	name string
+	// reconciler is the reconciler.
+	reconciler Interface
+	// rof is the read only interface cast of the reconciler.
+	roi ReadOnlyInterface
+	// IsROI (Read Only Interface) the reconciler only observes reconciliation.
+	isROI bool
+	// rof is the read only finalizer cast of the reconciler.
+	rof ReadOnlyFinalizer
+	// IsROF (Read Only Finalizer) the reconciler only observes finalize.
+	isROF bool
+	// IsLeader the instance of the reconciler is the elected leader.
+	isLeader bool
+}
+
+func newState(key string, r *reconcilerImpl) (*state, error) {
+	// Convert the namespace/name string into a distinct namespace and name
+	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	if err != nil {
+		return nil, fmt.Errorf("invalid resource key: %s", key)
+	}
+
+	roi, isROI := r.reconciler.(ReadOnlyInterface)
+	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
+
+	isLeader := r.IsLeaderFor(types.NamespacedName{
+		Namespace: namespace,
+		Name:      name,
+	})
+
+	return &state{
+		key:        key,
+		namespace:  namespace,
+		name:       name,
+		reconciler: r.reconciler,
+		roi:        roi,
+		isROI:      isROI,
+		rof:        rof,
+		isROF:      isROF,
+		isLeader:   isLeader,
+	}, nil
+}
+
+// isNotLeaderNorObserver checks to see if this reconciler with the current
+// state is enabled to do any work or not.
+// isNotLeaderNorObserver returns true when there is no work possible for the
+// reconciler.
+func (s *state) isNotLeaderNorObserver() bool {
+	if !s.isLeader && !s.isROI && !s.isROF {
+		// If we are not the leader, and we don't implement either ReadOnly
+		// interface, then take a fast-path out.
+		return true
+	}
+	return false
+}
+
+func (s *state) reconcileMethodFor(o *v1alpha1.PodAutoscaler) (string, doReconcile) {
+	if o.GetDeletionTimestamp().IsZero() {
+		if s.isLeader {
+			return doReconcileKind, s.reconciler.ReconcileKind
+		} else if s.isROI {
+			return doObserveKind, s.roi.ObserveKind
+		}
+	} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
+		return doFinalizeKind, fin.FinalizeKind
+	} else if !s.isLeader && s.isROF {
+		return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
+	}
+	return "unknown", nil
+}
diff --git a/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go b/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go
index 65f252328..d9c30f4d9 100644
--- a/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go
@@ -32,7 +32,6 @@ import (
 	labels "k8s.io/apimachinery/pkg/labels"
 	types "k8s.io/apimachinery/pkg/types"
 	sets "k8s.io/apimachinery/pkg/util/sets"
-	cache "k8s.io/client-go/tools/cache"
 	record "k8s.io/client-go/tools/record"
 	controller "knative.dev/pkg/controller"
 	logging "knative.dev/pkg/logging"
@@ -84,6 +83,15 @@ type ReadOnlyFinalizer interface {
 	ObserveFinalizeKind(ctx context.Context, o *v1.Configuration) reconciler.Event
 }
 
+type doReconcile func(ctx context.Context, o *v1.Configuration) reconciler.Event
+
+const (
+	doReconcileKind       = "ReconcileKind"
+	doFinalizeKind        = "FinalizeKind"
+	doObserveKind         = "ObserveKind"
+	doObserveFinalizeKind = "ObserveFinalizeKind"
+)
+
 // reconcilerImpl implements controller.Reconciler for v1.Configuration resources.
 type reconcilerImpl struct {
 	// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware
@@ -176,22 +184,19 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	logger := logging.FromContext(ctx)
 
-	// Convert the namespace/name string into a distinct namespace and name
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	// Initialize the reconciler state. This will convert the namespace/name
+	// string into a distinct namespace and name, determin if this instance of
+	// the reconciler is the leader, and any additional interfaces implemented
+	// by the reconciler. Returns an error is the resource key is invalid.
+	s, err := newState(key, r)
 	if err != nil {
 		logger.Errorf("invalid resource key: %s", key)
 		return nil
 	}
-	// Establish whether we are the leader for use below.
-	isLeader := r.IsLeaderFor(types.NamespacedName{
-		Namespace: namespace,
-		Name:      name,
-	})
-	roi, isROI := r.reconciler.(ReadOnlyInterface)
-	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
-	if !isLeader && !isROI && !isROF {
-		// If we are not the leader, and we don't implement either ReadOnly
-		// interface, then take a fast-path out.
+
+	// If we are not the leader, and we don't implement either ReadOnly
+	// observer interfaces, then take a fast-path out.
+	if s.isNotLeaderNorObserver() {
 		return nil
 	}
 
@@ -205,9 +210,9 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 
 	// Get the resource with this namespace/name.
 
-	getter := r.Lister.Configurations(namespace)
+	getter := r.Lister.Configurations(s.namespace)
 
-	original, err := getter.Get(name)
+	original, err := getter.Get(s.name)
 
 	if errors.IsNotFound(err) {
 		// The resource may no longer exist, in which case we stop processing.
@@ -221,48 +226,42 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	resource := original.DeepCopy()
 
 	var reconcileEvent reconciler.Event
-	if resource.GetDeletionTimestamp().IsZero() {
-		if isLeader {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
-
-			// Set and update the finalizer on resource if r.reconciler
-			// implements Finalizer.
-			if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
-				return fmt.Errorf("failed to set finalizers: %w", err)
-			}
 
-			reconciler.PreProcessReconcile(ctx, resource)
+	name, do := s.reconcileMethodFor(resource)
+	// Append the target method to the logger.
+	logger = logger.With(zap.String("targetMethod", name))
+	switch name {
+	case doReconcileKind:
+		// Append the target method to the logger.
+		logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
 
-			// Reconcile this copy of the resource and then write back any status
-			// updates regardless of whether the reconciliation errored out.
-			reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
+		// Set and update the finalizer on resource if r.reconciler
+		// implements Finalizer.
+		if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
+			return fmt.Errorf("failed to set finalizers: %w", err)
+		}
 
-			reconciler.PostProcessReconcile(ctx, resource, original)
+		reconciler.PreProcessReconcile(ctx, resource)
 
-		} else if isROI {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ObserveKind"))
+		// Reconcile this copy of the resource and then write back any status
+		// updates regardless of whether the reconciliation errored out.
+		reconcileEvent = do(ctx, resource)
 
-			// Observe any changes to this resource, since we are not the leader.
-			reconcileEvent = roi.ObserveKind(ctx, resource)
-		}
-	} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
+		reconciler.PostProcessReconcile(ctx, resource, original)
 
+	case "FinalizeKind":
 		// For finalizing reconcilers, if this resource being marked for deletion
 		// and reconciled cleanly (nil or normal event), remove the finalizer.
-		reconcileEvent = fin.FinalizeKind(ctx, resource)
+		reconcileEvent = do(ctx, resource)
+
 		if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
 			return fmt.Errorf("failed to clear finalizers: %w", err)
 		}
-	} else if !isLeader && isROF {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
 
-		// For finalizing reconcilers, just observe when we aren't the leader.
-		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
+	case "ObserveKind", "ObserveFinalizeKind":
+		// Observe any changes to this resource, since we are not the leader.
+		reconcileEvent = do(ctx, resource)
+
 	}
 
 	// Synchronize the status.
@@ -275,7 +274,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		// This is important because the copy we loaded from the injectionInformer's
 		// cache may be stale and we don't want to overwrite a prior update
 		// to status with this stale state.
-	case !isLeader:
+	case !s.isLeader:
 		// High-availability reconcilers may have many replicas watching the resource, but only
 		// the elected leader is expected to write modifications.
 		logger.Warn("Saw status changes when we aren't the leader!")
diff --git a/pkg/client/injection/reconciler/serving/v1/configuration/state.go b/pkg/client/injection/reconciler/serving/v1/configuration/state.go
new file mode 100644
index 000000000..535157a76
--- /dev/null
+++ b/pkg/client/injection/reconciler/serving/v1/configuration/state.go
@@ -0,0 +1,105 @@
+/*
+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 configuration
+
+import (
+	fmt "fmt"
+
+	types "k8s.io/apimachinery/pkg/types"
+	cache "k8s.io/client-go/tools/cache"
+	v1 "knative.dev/serving/pkg/apis/serving/v1"
+)
+
+// state is used to track the state of a reconciler in a single run.
+type state struct {
+	// Key is the original reconciliation key from the queue.
+	key string
+	// Namespace is the namespace split from the reconciliation key.
+	namespace string
+	// Namespace is the name split from the reconciliation key.
+	name string
+	// reconciler is the reconciler.
+	reconciler Interface
+	// rof is the read only interface cast of the reconciler.
+	roi ReadOnlyInterface
+	// IsROI (Read Only Interface) the reconciler only observes reconciliation.
+	isROI bool
+	// rof is the read only finalizer cast of the reconciler.
+	rof ReadOnlyFinalizer
+	// IsROF (Read Only Finalizer) the reconciler only observes finalize.
+	isROF bool
+	// IsLeader the instance of the reconciler is the elected leader.
+	isLeader bool
+}
+
+func newState(key string, r *reconcilerImpl) (*state, error) {
+	// Convert the namespace/name string into a distinct namespace and name
+	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	if err != nil {
+		return nil, fmt.Errorf("invalid resource key: %s", key)
+	}
+
+	roi, isROI := r.reconciler.(ReadOnlyInterface)
+	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
+
+	isLeader := r.IsLeaderFor(types.NamespacedName{
+		Namespace: namespace,
+		Name:      name,
+	})
+
+	return &state{
+		key:        key,
+		namespace:  namespace,
+		name:       name,
+		reconciler: r.reconciler,
+		roi:        roi,
+		isROI:      isROI,
+		rof:        rof,
+		isROF:      isROF,
+		isLeader:   isLeader,
+	}, nil
+}
+
+// isNotLeaderNorObserver checks to see if this reconciler with the current
+// state is enabled to do any work or not.
+// isNotLeaderNorObserver returns true when there is no work possible for the
+// reconciler.
+func (s *state) isNotLeaderNorObserver() bool {
+	if !s.isLeader && !s.isROI && !s.isROF {
+		// If we are not the leader, and we don't implement either ReadOnly
+		// interface, then take a fast-path out.
+		return true
+	}
+	return false
+}
+
+func (s *state) reconcileMethodFor(o *v1.Configuration) (string, doReconcile) {
+	if o.GetDeletionTimestamp().IsZero() {
+		if s.isLeader {
+			return doReconcileKind, s.reconciler.ReconcileKind
+		} else if s.isROI {
+			return doObserveKind, s.roi.ObserveKind
+		}
+	} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
+		return doFinalizeKind, fin.FinalizeKind
+	} else if !s.isLeader && s.isROF {
+		return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
+	}
+	return "unknown", nil
+}
diff --git a/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go b/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
index 22fcbffdf..78bc6806c 100644
--- a/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
@@ -32,7 +32,6 @@ import (
 	labels "k8s.io/apimachinery/pkg/labels"
 	types "k8s.io/apimachinery/pkg/types"
 	sets "k8s.io/apimachinery/pkg/util/sets"
-	cache "k8s.io/client-go/tools/cache"
 	record "k8s.io/client-go/tools/record"
 	controller "knative.dev/pkg/controller"
 	logging "knative.dev/pkg/logging"
@@ -84,6 +83,15 @@ type ReadOnlyFinalizer interface {
 	ObserveFinalizeKind(ctx context.Context, o *v1.Revision) reconciler.Event
 }
 
+type doReconcile func(ctx context.Context, o *v1.Revision) reconciler.Event
+
+const (
+	doReconcileKind       = "ReconcileKind"
+	doFinalizeKind        = "FinalizeKind"
+	doObserveKind         = "ObserveKind"
+	doObserveFinalizeKind = "ObserveFinalizeKind"
+)
+
 // reconcilerImpl implements controller.Reconciler for v1.Revision resources.
 type reconcilerImpl struct {
 	// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware
@@ -176,22 +184,19 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	logger := logging.FromContext(ctx)
 
-	// Convert the namespace/name string into a distinct namespace and name
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	// Initialize the reconciler state. This will convert the namespace/name
+	// string into a distinct namespace and name, determin if this instance of
+	// the reconciler is the leader, and any additional interfaces implemented
+	// by the reconciler. Returns an error is the resource key is invalid.
+	s, err := newState(key, r)
 	if err != nil {
 		logger.Errorf("invalid resource key: %s", key)
 		return nil
 	}
-	// Establish whether we are the leader for use below.
-	isLeader := r.IsLeaderFor(types.NamespacedName{
-		Namespace: namespace,
-		Name:      name,
-	})
-	roi, isROI := r.reconciler.(ReadOnlyInterface)
-	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
-	if !isLeader && !isROI && !isROF {
-		// If we are not the leader, and we don't implement either ReadOnly
-		// interface, then take a fast-path out.
+
+	// If we are not the leader, and we don't implement either ReadOnly
+	// observer interfaces, then take a fast-path out.
+	if s.isNotLeaderNorObserver() {
 		return nil
 	}
 
@@ -205,9 +210,9 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 
 	// Get the resource with this namespace/name.
 
-	getter := r.Lister.Revisions(namespace)
+	getter := r.Lister.Revisions(s.namespace)
 
-	original, err := getter.Get(name)
+	original, err := getter.Get(s.name)
 
 	if errors.IsNotFound(err) {
 		// The resource may no longer exist, in which case we stop processing.
@@ -221,48 +226,42 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	resource := original.DeepCopy()
 
 	var reconcileEvent reconciler.Event
-	if resource.GetDeletionTimestamp().IsZero() {
-		if isLeader {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
-
-			// Set and update the finalizer on resource if r.reconciler
-			// implements Finalizer.
-			if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
-				return fmt.Errorf("failed to set finalizers: %w", err)
-			}
 
-			reconciler.PreProcessReconcile(ctx, resource)
+	name, do := s.reconcileMethodFor(resource)
+	// Append the target method to the logger.
+	logger = logger.With(zap.String("targetMethod", name))
+	switch name {
+	case doReconcileKind:
+		// Append the target method to the logger.
+		logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
 
-			// Reconcile this copy of the resource and then write back any status
-			// updates regardless of whether the reconciliation errored out.
-			reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
+		// Set and update the finalizer on resource if r.reconciler
+		// implements Finalizer.
+		if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
+			return fmt.Errorf("failed to set finalizers: %w", err)
+		}
 
-			reconciler.PostProcessReconcile(ctx, resource, original)
+		reconciler.PreProcessReconcile(ctx, resource)
 
-		} else if isROI {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ObserveKind"))
+		// Reconcile this copy of the resource and then write back any status
+		// updates regardless of whether the reconciliation errored out.
+		reconcileEvent = do(ctx, resource)
 
-			// Observe any changes to this resource, since we are not the leader.
-			reconcileEvent = roi.ObserveKind(ctx, resource)
-		}
-	} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
+		reconciler.PostProcessReconcile(ctx, resource, original)
 
+	case "FinalizeKind":
 		// For finalizing reconcilers, if this resource being marked for deletion
 		// and reconciled cleanly (nil or normal event), remove the finalizer.
-		reconcileEvent = fin.FinalizeKind(ctx, resource)
+		reconcileEvent = do(ctx, resource)
+
 		if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
 			return fmt.Errorf("failed to clear finalizers: %w", err)
 		}
-	} else if !isLeader && isROF {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
 
-		// For finalizing reconcilers, just observe when we aren't the leader.
-		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
+	case "ObserveKind", "ObserveFinalizeKind":
+		// Observe any changes to this resource, since we are not the leader.
+		reconcileEvent = do(ctx, resource)
+
 	}
 
 	// Synchronize the status.
@@ -275,7 +274,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		// This is important because the copy we loaded from the injectionInformer's
 		// cache may be stale and we don't want to overwrite a prior update
 		// to status with this stale state.
-	case !isLeader:
+	case !s.isLeader:
 		// High-availability reconcilers may have many replicas watching the resource, but only
 		// the elected leader is expected to write modifications.
 		logger.Warn("Saw status changes when we aren't the leader!")
diff --git a/pkg/client/injection/reconciler/serving/v1/revision/state.go b/pkg/client/injection/reconciler/serving/v1/revision/state.go
new file mode 100644
index 000000000..ceb20b57d
--- /dev/null
+++ b/pkg/client/injection/reconciler/serving/v1/revision/state.go
@@ -0,0 +1,105 @@
+/*
+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 revision
+
+import (
+	fmt "fmt"
+
+	types "k8s.io/apimachinery/pkg/types"
+	cache "k8s.io/client-go/tools/cache"
+	v1 "knative.dev/serving/pkg/apis/serving/v1"
+)
+
+// state is used to track the state of a reconciler in a single run.
+type state struct {
+	// Key is the original reconciliation key from the queue.
+	key string
+	// Namespace is the namespace split from the reconciliation key.
+	namespace string
+	// Namespace is the name split from the reconciliation key.
+	name string
+	// reconciler is the reconciler.
+	reconciler Interface
+	// rof is the read only interface cast of the reconciler.
+	roi ReadOnlyInterface
+	// IsROI (Read Only Interface) the reconciler only observes reconciliation.
+	isROI bool
+	// rof is the read only finalizer cast of the reconciler.
+	rof ReadOnlyFinalizer
+	// IsROF (Read Only Finalizer) the reconciler only observes finalize.
+	isROF bool
+	// IsLeader the instance of the reconciler is the elected leader.
+	isLeader bool
+}
+
+func newState(key string, r *reconcilerImpl) (*state, error) {
+	// Convert the namespace/name string into a distinct namespace and name
+	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	if err != nil {
+		return nil, fmt.Errorf("invalid resource key: %s", key)
+	}
+
+	roi, isROI := r.reconciler.(ReadOnlyInterface)
+	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
+
+	isLeader := r.IsLeaderFor(types.NamespacedName{
+		Namespace: namespace,
+		Name:      name,
+	})
+
+	return &state{
+		key:        key,
+		namespace:  namespace,
+		name:       name,
+		reconciler: r.reconciler,
+		roi:        roi,
+		isROI:      isROI,
+		rof:        rof,
+		isROF:      isROF,
+		isLeader:   isLeader,
+	}, nil
+}
+
+// isNotLeaderNorObserver checks to see if this reconciler with the current
+// state is enabled to do any work or not.
+// isNotLeaderNorObserver returns true when there is no work possible for the
+// reconciler.
+func (s *state) isNotLeaderNorObserver() bool {
+	if !s.isLeader && !s.isROI && !s.isROF {
+		// If we are not the leader, and we don't implement either ReadOnly
+		// interface, then take a fast-path out.
+		return true
+	}
+	return false
+}
+
+func (s *state) reconcileMethodFor(o *v1.Revision) (string, doReconcile) {
+	if o.GetDeletionTimestamp().IsZero() {
+		if s.isLeader {
+			return doReconcileKind, s.reconciler.ReconcileKind
+		} else if s.isROI {
+			return doObserveKind, s.roi.ObserveKind
+		}
+	} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
+		return doFinalizeKind, fin.FinalizeKind
+	} else if !s.isLeader && s.isROF {
+		return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
+	}
+	return "unknown", nil
+}
diff --git a/pkg/client/injection/reconciler/serving/v1/route/reconciler.go b/pkg/client/injection/reconciler/serving/v1/route/reconciler.go
index dbffab42c..e8d1a4575 100644
--- a/pkg/client/injection/reconciler/serving/v1/route/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/route/reconciler.go
@@ -32,7 +32,6 @@ import (
 	labels "k8s.io/apimachinery/pkg/labels"
 	types "k8s.io/apimachinery/pkg/types"
 	sets "k8s.io/apimachinery/pkg/util/sets"
-	cache "k8s.io/client-go/tools/cache"
 	record "k8s.io/client-go/tools/record"
 	controller "knative.dev/pkg/controller"
 	logging "knative.dev/pkg/logging"
@@ -84,6 +83,15 @@ type ReadOnlyFinalizer interface {
 	ObserveFinalizeKind(ctx context.Context, o *v1.Route) reconciler.Event
 }
 
+type doReconcile func(ctx context.Context, o *v1.Route) reconciler.Event
+
+const (
+	doReconcileKind       = "ReconcileKind"
+	doFinalizeKind        = "FinalizeKind"
+	doObserveKind         = "ObserveKind"
+	doObserveFinalizeKind = "ObserveFinalizeKind"
+)
+
 // reconcilerImpl implements controller.Reconciler for v1.Route resources.
 type reconcilerImpl struct {
 	// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware
@@ -176,22 +184,19 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	logger := logging.FromContext(ctx)
 
-	// Convert the namespace/name string into a distinct namespace and name
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	// Initialize the reconciler state. This will convert the namespace/name
+	// string into a distinct namespace and name, determin if this instance of
+	// the reconciler is the leader, and any additional interfaces implemented
+	// by the reconciler. Returns an error is the resource key is invalid.
+	s, err := newState(key, r)
 	if err != nil {
 		logger.Errorf("invalid resource key: %s", key)
 		return nil
 	}
-	// Establish whether we are the leader for use below.
-	isLeader := r.IsLeaderFor(types.NamespacedName{
-		Namespace: namespace,
-		Name:      name,
-	})
-	roi, isROI := r.reconciler.(ReadOnlyInterface)
-	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
-	if !isLeader && !isROI && !isROF {
-		// If we are not the leader, and we don't implement either ReadOnly
-		// interface, then take a fast-path out.
+
+	// If we are not the leader, and we don't implement either ReadOnly
+	// observer interfaces, then take a fast-path out.
+	if s.isNotLeaderNorObserver() {
 		return nil
 	}
 
@@ -205,9 +210,9 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 
 	// Get the resource with this namespace/name.
 
-	getter := r.Lister.Routes(namespace)
+	getter := r.Lister.Routes(s.namespace)
 
-	original, err := getter.Get(name)
+	original, err := getter.Get(s.name)
 
 	if errors.IsNotFound(err) {
 		// The resource may no longer exist, in which case we stop processing.
@@ -221,48 +226,42 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	resource := original.DeepCopy()
 
 	var reconcileEvent reconciler.Event
-	if resource.GetDeletionTimestamp().IsZero() {
-		if isLeader {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
-
-			// Set and update the finalizer on resource if r.reconciler
-			// implements Finalizer.
-			if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
-				return fmt.Errorf("failed to set finalizers: %w", err)
-			}
 
-			reconciler.PreProcessReconcile(ctx, resource)
+	name, do := s.reconcileMethodFor(resource)
+	// Append the target method to the logger.
+	logger = logger.With(zap.String("targetMethod", name))
+	switch name {
+	case doReconcileKind:
+		// Append the target method to the logger.
+		logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
 
-			// Reconcile this copy of the resource and then write back any status
-			// updates regardless of whether the reconciliation errored out.
-			reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
+		// Set and update the finalizer on resource if r.reconciler
+		// implements Finalizer.
+		if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
+			return fmt.Errorf("failed to set finalizers: %w", err)
+		}
 
-			reconciler.PostProcessReconcile(ctx, resource, original)
+		reconciler.PreProcessReconcile(ctx, resource)
 
-		} else if isROI {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ObserveKind"))
+		// Reconcile this copy of the resource and then write back any status
+		// updates regardless of whether the reconciliation errored out.
+		reconcileEvent = do(ctx, resource)
 
-			// Observe any changes to this resource, since we are not the leader.
-			reconcileEvent = roi.ObserveKind(ctx, resource)
-		}
-	} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
+		reconciler.PostProcessReconcile(ctx, resource, original)
 
+	case "FinalizeKind":
 		// For finalizing reconcilers, if this resource being marked for deletion
 		// and reconciled cleanly (nil or normal event), remove the finalizer.
-		reconcileEvent = fin.FinalizeKind(ctx, resource)
+		reconcileEvent = do(ctx, resource)
+
 		if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
 			return fmt.Errorf("failed to clear finalizers: %w", err)
 		}
-	} else if !isLeader && isROF {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
 
-		// For finalizing reconcilers, just observe when we aren't the leader.
-		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
+	case "ObserveKind", "ObserveFinalizeKind":
+		// Observe any changes to this resource, since we are not the leader.
+		reconcileEvent = do(ctx, resource)
+
 	}
 
 	// Synchronize the status.
@@ -275,7 +274,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		// This is important because the copy we loaded from the injectionInformer's
 		// cache may be stale and we don't want to overwrite a prior update
 		// to status with this stale state.
-	case !isLeader:
+	case !s.isLeader:
 		// High-availability reconcilers may have many replicas watching the resource, but only
 		// the elected leader is expected to write modifications.
 		logger.Warn("Saw status changes when we aren't the leader!")
diff --git a/pkg/client/injection/reconciler/serving/v1/route/state.go b/pkg/client/injection/reconciler/serving/v1/route/state.go
new file mode 100644
index 000000000..62227dd8e
--- /dev/null
+++ b/pkg/client/injection/reconciler/serving/v1/route/state.go
@@ -0,0 +1,105 @@
+/*
+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 route
+
+import (
+	fmt "fmt"
+
+	types "k8s.io/apimachinery/pkg/types"
+	cache "k8s.io/client-go/tools/cache"
+	v1 "knative.dev/serving/pkg/apis/serving/v1"
+)
+
+// state is used to track the state of a reconciler in a single run.
+type state struct {
+	// Key is the original reconciliation key from the queue.
+	key string
+	// Namespace is the namespace split from the reconciliation key.
+	namespace string
+	// Namespace is the name split from the reconciliation key.
+	name string
+	// reconciler is the reconciler.
+	reconciler Interface
+	// rof is the read only interface cast of the reconciler.
+	roi ReadOnlyInterface
+	// IsROI (Read Only Interface) the reconciler only observes reconciliation.
+	isROI bool
+	// rof is the read only finalizer cast of the reconciler.
+	rof ReadOnlyFinalizer
+	// IsROF (Read Only Finalizer) the reconciler only observes finalize.
+	isROF bool
+	// IsLeader the instance of the reconciler is the elected leader.
+	isLeader bool
+}
+
+func newState(key string, r *reconcilerImpl) (*state, error) {
+	// Convert the namespace/name string into a distinct namespace and name
+	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	if err != nil {
+		return nil, fmt.Errorf("invalid resource key: %s", key)
+	}
+
+	roi, isROI := r.reconciler.(ReadOnlyInterface)
+	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
+
+	isLeader := r.IsLeaderFor(types.NamespacedName{
+		Namespace: namespace,
+		Name:      name,
+	})
+
+	return &state{
+		key:        key,
+		namespace:  namespace,
+		name:       name,
+		reconciler: r.reconciler,
+		roi:        roi,
+		isROI:      isROI,
+		rof:        rof,
+		isROF:      isROF,
+		isLeader:   isLeader,
+	}, nil
+}
+
+// isNotLeaderNorObserver checks to see if this reconciler with the current
+// state is enabled to do any work or not.
+// isNotLeaderNorObserver returns true when there is no work possible for the
+// reconciler.
+func (s *state) isNotLeaderNorObserver() bool {
+	if !s.isLeader && !s.isROI && !s.isROF {
+		// If we are not the leader, and we don't implement either ReadOnly
+		// interface, then take a fast-path out.
+		return true
+	}
+	return false
+}
+
+func (s *state) reconcileMethodFor(o *v1.Route) (string, doReconcile) {
+	if o.GetDeletionTimestamp().IsZero() {
+		if s.isLeader {
+			return doReconcileKind, s.reconciler.ReconcileKind
+		} else if s.isROI {
+			return doObserveKind, s.roi.ObserveKind
+		}
+	} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
+		return doFinalizeKind, fin.FinalizeKind
+	} else if !s.isLeader && s.isROF {
+		return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
+	}
+	return "unknown", nil
+}
diff --git a/pkg/client/injection/reconciler/serving/v1/service/reconciler.go b/pkg/client/injection/reconciler/serving/v1/service/reconciler.go
index 3fc87a12b..65d5bc875 100644
--- a/pkg/client/injection/reconciler/serving/v1/service/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/service/reconciler.go
@@ -32,7 +32,6 @@ import (
 	labels "k8s.io/apimachinery/pkg/labels"
 	types "k8s.io/apimachinery/pkg/types"
 	sets "k8s.io/apimachinery/pkg/util/sets"
-	cache "k8s.io/client-go/tools/cache"
 	record "k8s.io/client-go/tools/record"
 	controller "knative.dev/pkg/controller"
 	logging "knative.dev/pkg/logging"
@@ -84,6 +83,15 @@ type ReadOnlyFinalizer interface {
 	ObserveFinalizeKind(ctx context.Context, o *v1.Service) reconciler.Event
 }
 
+type doReconcile func(ctx context.Context, o *v1.Service) reconciler.Event
+
+const (
+	doReconcileKind       = "ReconcileKind"
+	doFinalizeKind        = "FinalizeKind"
+	doObserveKind         = "ObserveKind"
+	doObserveFinalizeKind = "ObserveFinalizeKind"
+)
+
 // reconcilerImpl implements controller.Reconciler for v1.Service resources.
 type reconcilerImpl struct {
 	// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware
@@ -176,22 +184,19 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	logger := logging.FromContext(ctx)
 
-	// Convert the namespace/name string into a distinct namespace and name
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	// Initialize the reconciler state. This will convert the namespace/name
+	// string into a distinct namespace and name, determin if this instance of
+	// the reconciler is the leader, and any additional interfaces implemented
+	// by the reconciler. Returns an error is the resource key is invalid.
+	s, err := newState(key, r)
 	if err != nil {
 		logger.Errorf("invalid resource key: %s", key)
 		return nil
 	}
-	// Establish whether we are the leader for use below.
-	isLeader := r.IsLeaderFor(types.NamespacedName{
-		Namespace: namespace,
-		Name:      name,
-	})
-	roi, isROI := r.reconciler.(ReadOnlyInterface)
-	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
-	if !isLeader && !isROI && !isROF {
-		// If we are not the leader, and we don't implement either ReadOnly
-		// interface, then take a fast-path out.
+
+	// If we are not the leader, and we don't implement either ReadOnly
+	// observer interfaces, then take a fast-path out.
+	if s.isNotLeaderNorObserver() {
 		return nil
 	}
 
@@ -205,9 +210,9 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 
 	// Get the resource with this namespace/name.
 
-	getter := r.Lister.Services(namespace)
+	getter := r.Lister.Services(s.namespace)
 
-	original, err := getter.Get(name)
+	original, err := getter.Get(s.name)
 
 	if errors.IsNotFound(err) {
 		// The resource may no longer exist, in which case we stop processing.
@@ -221,48 +226,42 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	resource := original.DeepCopy()
 
 	var reconcileEvent reconciler.Event
-	if resource.GetDeletionTimestamp().IsZero() {
-		if isLeader {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
-
-			// Set and update the finalizer on resource if r.reconciler
-			// implements Finalizer.
-			if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
-				return fmt.Errorf("failed to set finalizers: %w", err)
-			}
 
-			reconciler.PreProcessReconcile(ctx, resource)
+	name, do := s.reconcileMethodFor(resource)
+	// Append the target method to the logger.
+	logger = logger.With(zap.String("targetMethod", name))
+	switch name {
+	case doReconcileKind:
+		// Append the target method to the logger.
+		logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
 
-			// Reconcile this copy of the resource and then write back any status
-			// updates regardless of whether the reconciliation errored out.
-			reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
+		// Set and update the finalizer on resource if r.reconciler
+		// implements Finalizer.
+		if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
+			return fmt.Errorf("failed to set finalizers: %w", err)
+		}
 
-			reconciler.PostProcessReconcile(ctx, resource, original)
+		reconciler.PreProcessReconcile(ctx, resource)
 
-		} else if isROI {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ObserveKind"))
+		// Reconcile this copy of the resource and then write back any status
+		// updates regardless of whether the reconciliation errored out.
+		reconcileEvent = do(ctx, resource)
 
-			// Observe any changes to this resource, since we are not the leader.
-			reconcileEvent = roi.ObserveKind(ctx, resource)
-		}
-	} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
+		reconciler.PostProcessReconcile(ctx, resource, original)
 
+	case "FinalizeKind":
 		// For finalizing reconcilers, if this resource being marked for deletion
 		// and reconciled cleanly (nil or normal event), remove the finalizer.
-		reconcileEvent = fin.FinalizeKind(ctx, resource)
+		reconcileEvent = do(ctx, resource)
+
 		if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
 			return fmt.Errorf("failed to clear finalizers: %w", err)
 		}
-	} else if !isLeader && isROF {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
 
-		// For finalizing reconcilers, just observe when we aren't the leader.
-		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
+	case "ObserveKind", "ObserveFinalizeKind":
+		// Observe any changes to this resource, since we are not the leader.
+		reconcileEvent = do(ctx, resource)
+
 	}
 
 	// Synchronize the status.
@@ -275,7 +274,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		// This is important because the copy we loaded from the injectionInformer's
 		// cache may be stale and we don't want to overwrite a prior update
 		// to status with this stale state.
-	case !isLeader:
+	case !s.isLeader:
 		// High-availability reconcilers may have many replicas watching the resource, but only
 		// the elected leader is expected to write modifications.
 		logger.Warn("Saw status changes when we aren't the leader!")
diff --git a/pkg/client/injection/reconciler/serving/v1/service/state.go b/pkg/client/injection/reconciler/serving/v1/service/state.go
new file mode 100644
index 000000000..be375cf18
--- /dev/null
+++ b/pkg/client/injection/reconciler/serving/v1/service/state.go
@@ -0,0 +1,105 @@
+/*
+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 service
+
+import (
+	fmt "fmt"
+
+	types "k8s.io/apimachinery/pkg/types"
+	cache "k8s.io/client-go/tools/cache"
+	v1 "knative.dev/serving/pkg/apis/serving/v1"
+)
+
+// state is used to track the state of a reconciler in a single run.
+type state struct {
+	// Key is the original reconciliation key from the queue.
+	key string
+	// Namespace is the namespace split from the reconciliation key.
+	namespace string
+	// Namespace is the name split from the reconciliation key.
+	name string
+	// reconciler is the reconciler.
+	reconciler Interface
+	// rof is the read only interface cast of the reconciler.
+	roi ReadOnlyInterface
+	// IsROI (Read Only Interface) the reconciler only observes reconciliation.
+	isROI bool
+	// rof is the read only finalizer cast of the reconciler.
+	rof ReadOnlyFinalizer
+	// IsROF (Read Only Finalizer) the reconciler only observes finalize.
+	isROF bool
+	// IsLeader the instance of the reconciler is the elected leader.
+	isLeader bool
+}
+
+func newState(key string, r *reconcilerImpl) (*state, error) {
+	// Convert the namespace/name string into a distinct namespace and name
+	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	if err != nil {
+		return nil, fmt.Errorf("invalid resource key: %s", key)
+	}
+
+	roi, isROI := r.reconciler.(ReadOnlyInterface)
+	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
+
+	isLeader := r.IsLeaderFor(types.NamespacedName{
+		Namespace: namespace,
+		Name:      name,
+	})
+
+	return &state{
+		key:        key,
+		namespace:  namespace,
+		name:       name,
+		reconciler: r.reconciler,
+		roi:        roi,
+		isROI:      isROI,
+		rof:        rof,
+		isROF:      isROF,
+		isLeader:   isLeader,
+	}, nil
+}
+
+// isNotLeaderNorObserver checks to see if this reconciler with the current
+// state is enabled to do any work or not.
+// isNotLeaderNorObserver returns true when there is no work possible for the
+// reconciler.
+func (s *state) isNotLeaderNorObserver() bool {
+	if !s.isLeader && !s.isROI && !s.isROF {
+		// If we are not the leader, and we don't implement either ReadOnly
+		// interface, then take a fast-path out.
+		return true
+	}
+	return false
+}
+
+func (s *state) reconcileMethodFor(o *v1.Service) (string, doReconcile) {
+	if o.GetDeletionTimestamp().IsZero() {
+		if s.isLeader {
+			return doReconcileKind, s.reconciler.ReconcileKind
+		} else if s.isROI {
+			return doObserveKind, s.roi.ObserveKind
+		}
+	} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
+		return doFinalizeKind, fin.FinalizeKind
+	} else if !s.isLeader && s.isROF {
+		return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
+	}
+	return "unknown", nil
+}
diff --git a/third_party/cert-manager-0.12.0/net-certmanager.yaml b/third_party/cert-manager-0.12.0/net-certmanager.yaml
index 694923017..2d82205c0 100644
--- a/third_party/cert-manager-0.12.0/net-certmanager.yaml
+++ b/third_party/cert-manager-0.12.0/net-certmanager.yaml
@@ -18,7 +18,7 @@ metadata:
   # These are the permissions needed by the `cert-manager` `Certificate` implementation.
   name: knative-serving-certmanager
   labels:
-    serving.knative.dev/release: "v20200629-029ac30"
+    serving.knative.dev/release: "v20200723-0b87370"
     serving.knative.dev/controller: "true"
     networking.knative.dev/certificate-provider: cert-manager
 rules:
@@ -49,7 +49,7 @@ kind: ValidatingWebhookConfiguration
 metadata:
   name: config.webhook.net-certmanager.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200629-029ac30"
+    serving.knative.dev/release: "v20200723-0b87370"
 webhooks:
 - admissionReviewVersions:
   - v1beta1
@@ -86,7 +86,7 @@ metadata:
   name: net-certmanager-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200629-029ac30"
+    serving.knative.dev/release: "v20200723-0b87370"
 
 ---
 # Copyright 2020 The Knative Authors
@@ -109,7 +109,7 @@ metadata:
   name: config-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200629-029ac30"
+    serving.knative.dev/release: "v20200723-0b87370"
     networking.knative.dev/certificate-provider: cert-manager
 data:
   _example: |
@@ -156,7 +156,7 @@ metadata:
   name: networking-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200629-029ac30"
+    serving.knative.dev/release: "v20200723-0b87370"
     networking.knative.dev/certificate-provider: cert-manager
 spec:
   selector:
@@ -168,14 +168,14 @@ spec:
         cluster-autoscaler.kubernetes.io/safe-to-evict: "true"
       labels:
         app: networking-certmanager
-        serving.knative.dev/release: "v20200629-029ac30"
+        serving.knative.dev/release: "v20200723-0b87370"
     spec:
       serviceAccountName: controller
       containers:
       - name: networking-certmanager
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-certmanager/cmd/controller@sha256:cad309b6d01f3be1581440a624931a56744e6c60b467c373f8652d65cbb96511
+        image: gcr.io/knative-nightly/knative.dev/net-certmanager/cmd/controller@sha256:b73d6d3fa5e47e6b779d56f086e94a140e91069af8a024e213a8003cc65e21a3
         resources:
           requests:
             cpu: 30m
@@ -208,7 +208,7 @@ kind: Service
 metadata:
   labels:
     app: networking-certmanager
-    serving.knative.dev/release: "v20200629-029ac30"
+    serving.knative.dev/release: "v20200723-0b87370"
     networking.knative.dev/certificate-provider: cert-manager
   name: networking-certmanager
   namespace: knative-serving
@@ -245,7 +245,7 @@ metadata:
   name: net-certmanager-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200629-029ac30"
+    serving.knative.dev/release: "v20200723-0b87370"
 spec:
   selector:
     matchLabels:
@@ -258,14 +258,14 @@ spec:
       labels:
         app: net-certmanager-webhook
         role: net-certmanager-webhook
-        serving.knative.dev/release: "v20200629-029ac30"
+        serving.knative.dev/release: "v20200723-0b87370"
     spec:
       serviceAccountName: controller
       containers:
       - name: webhook
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-certmanager/cmd/webhook@sha256:ab3bde3b28b622cafbd6b027312175e0aab27b9a4316ba942c039000a95ecb0a
+        image: gcr.io/knative-nightly/knative.dev/net-certmanager/cmd/webhook@sha256:4b1c531d8f2a930de994df13c4b91e488398e064ee33fad4b0e344b2e9dcadcb
         resources:
           requests:
             cpu: 20m
@@ -319,7 +319,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: net-certmanager-webhook
-    serving.knative.dev/release: "v20200629-029ac30"
+    serving.knative.dev/release: "v20200723-0b87370"
 spec:
   ports:
   - # Define metrics and profiling for them to be accessible within service meshes.
diff --git a/third_party/net-istio.yaml b/third_party/net-istio.yaml
index 839c10e5d..1f32910f1 100644
--- a/third_party/net-istio.yaml
+++ b/third_party/net-istio.yaml
@@ -18,7 +18,7 @@ metadata:
   # These are the permissions needed by the Istio Ingress implementation.
   name: knative-serving-istio
   labels:
-    serving.knative.dev/release: "v20200722-368e852"
+    serving.knative.dev/release: "v20200723-56f23ab"
     serving.knative.dev/controller: "true"
     networking.knative.dev/ingress-provider: istio
 rules:
@@ -48,7 +48,7 @@ metadata:
   name: knative-ingress-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200722-368e852"
+    serving.knative.dev/release: "v20200723-56f23ab"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -85,7 +85,7 @@ metadata:
   name: cluster-local-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200722-368e852"
+    serving.knative.dev/release: "v20200723-56f23ab"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -118,7 +118,7 @@ kind: MutatingWebhookConfiguration
 metadata:
   name: webhook.istio.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200722-368e852"
+    serving.knative.dev/release: "v20200723-56f23ab"
     networking.knative.dev/ingress-provider: istio
 webhooks:
 - admissionReviewVersions:
@@ -154,7 +154,7 @@ kind: ValidatingWebhookConfiguration
 metadata:
   name: config.webhook.istio.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200722-368e852"
+    serving.knative.dev/release: "v20200723-56f23ab"
     networking.knative.dev/ingress-provider: istio
 webhooks:
 - admissionReviewVersions:
@@ -192,7 +192,7 @@ metadata:
   name: istio-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200722-368e852"
+    serving.knative.dev/release: "v20200723-56f23ab"
     networking.knative.dev/ingress-provider: istio
 
 ---
@@ -216,7 +216,7 @@ metadata:
   name: config-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200722-368e852"
+    serving.knative.dev/release: "v20200723-56f23ab"
     networking.knative.dev/ingress-provider: istio
 data:
   _example: |
@@ -286,7 +286,7 @@ metadata:
   name: networking-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200722-368e852"
+    serving.knative.dev/release: "v20200723-56f23ab"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -302,14 +302,14 @@ spec:
         sidecar.istio.io/inject: "false"
       labels:
         app: networking-istio
-        serving.knative.dev/release: "v20200722-368e852"
+        serving.knative.dev/release: "v20200723-56f23ab"
     spec:
       serviceAccountName: controller
       containers:
       - name: networking-istio
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/controller@sha256:66bc4943b78ae66b993629c34fb40e14a1a4d8c24cb38457fda49448b8a13b4f
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/controller@sha256:c473606d6bf5bb11d0ec154742e1e3a26b09a0c864a609606ef0e0c529cb95d1
         resources:
           requests:
             cpu: 30m
@@ -361,7 +361,7 @@ metadata:
   name: istio-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200722-368e852"
+    serving.knative.dev/release: "v20200723-56f23ab"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -375,14 +375,14 @@ spec:
       labels:
         app: istio-webhook
         role: istio-webhook
-        serving.knative.dev/release: "v20200722-368e852"
+        serving.knative.dev/release: "v20200723-56f23ab"
     spec:
       serviceAccountName: controller
       containers:
       - name: webhook
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/webhook@sha256:b90ca87807d775ec71762ad5329cdcf7fb83a52640d50556d6326fe912c31120
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/webhook@sha256:e52a92524b9ce8e43f885146c31cc1f2d98f1550cb7d865a6f40dd5509bdaf8e
         resources:
           requests:
             cpu: 20m
@@ -436,7 +436,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: istio-webhook
-    serving.knative.dev/release: "v20200722-368e852"
+    serving.knative.dev/release: "v20200723-56f23ab"
     networking.knative.dev/ingress-provider: istio
 spec:
   ports:
diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go
index afd36beed..48787dd1a 100644
--- a/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go
+++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go
@@ -128,7 +128,7 @@ func newUnexportedFilter(typs ...interface{}) unexportedFilter {
 	for _, typ := range typs {
 		t := reflect.TypeOf(typ)
 		if t == nil || t.Kind() != reflect.Struct {
-			panic(fmt.Sprintf("invalid struct type: %T", typ))
+			panic(fmt.Sprintf("%T must be a non-pointer struct", typ))
 		}
 		ux.m[t] = true
 	}
diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go
index dae7ced5c..fe8d1b9cc 100644
--- a/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go
+++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go
@@ -42,7 +42,7 @@ func newStructFilter(typ interface{}, names ...string) structFilter {
 
 	t := reflect.TypeOf(typ)
 	if t == nil || t.Kind() != reflect.Struct {
-		panic(fmt.Sprintf("%T must be a struct", typ))
+		panic(fmt.Sprintf("%T must be a non-pointer struct", typ))
 	}
 	var ft fieldTree
 	for _, name := range names {
diff --git a/vendor/github.com/google/go-cmp/cmp/report_reflect.go b/vendor/github.com/google/go-cmp/cmp/report_reflect.go
index 2d722ea51..786f67126 100644
--- a/vendor/github.com/google/go-cmp/cmp/report_reflect.go
+++ b/vendor/github.com/google/go-cmp/cmp/report_reflect.go
@@ -5,6 +5,7 @@
 package cmp
 
 import (
+	"bytes"
 	"fmt"
 	"reflect"
 	"strconv"
@@ -125,21 +126,20 @@ func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind,
 		// implementations crash when doing so.
 		if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() {
 			var prefix, strVal string
-			switch v := v.Interface().(type) {
-			case error:
-				prefix, strVal = "e", v.Error()
-			case fmt.Stringer:
-				prefix, strVal = "s", v.String()
-			}
-			if prefix != "" {
-				maxLen := len(strVal)
-				if opts.LimitVerbosity {
-					maxLen = (1 << opts.verbosity()) << 5 // 32, 64, 128, 256, etc...
+			func() {
+				// Swallow and ignore any panics from String or Error.
+				defer func() { recover() }()
+				switch v := v.Interface().(type) {
+				case error:
+					strVal = v.Error()
+					prefix = "e"
+				case fmt.Stringer:
+					strVal = v.String()
+					prefix = "s"
 				}
-				if len(strVal) > maxLen+len(textEllipsis) {
-					return textLine(prefix + formatString(strVal[:maxLen]) + string(textEllipsis))
-				}
-				return textLine(prefix + formatString(strVal))
+			}()
+			if prefix != "" {
+				return opts.formatString(prefix, strVal)
 			}
 		}
 	}
@@ -171,14 +171,7 @@ func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind,
 	case reflect.Complex64, reflect.Complex128:
 		return textLine(fmt.Sprint(v.Complex()))
 	case reflect.String:
-		maxLen := v.Len()
-		if opts.LimitVerbosity {
-			maxLen = (1 << opts.verbosity()) << 5 // 32, 64, 128, 256, etc...
-		}
-		if v.Len() > maxLen+len(textEllipsis) {
-			return textLine(formatString(v.String()[:maxLen]) + string(textEllipsis))
-		}
-		return textLine(formatString(v.String()))
+		return opts.formatString("", v.String())
 	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
 		return textLine(formatPointer(value.PointerOf(v), true))
 	case reflect.Struct:
@@ -210,6 +203,17 @@ func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind,
 		if v.IsNil() {
 			return textNil
 		}
+
+		// Check whether this is a []byte of text data.
+		if t.Elem() == reflect.TypeOf(byte(0)) {
+			b := v.Bytes()
+			isPrintSpace := func(r rune) bool { return unicode.IsPrint(r) && unicode.IsSpace(r) }
+			if len(b) > 0 && utf8.Valid(b) && len(bytes.TrimFunc(b, isPrintSpace)) == 0 {
+				out = opts.formatString("", string(b))
+				return opts.WithTypeMode(emitType).FormatType(t, out)
+			}
+		}
+
 		fallthrough
 	case reflect.Array:
 		maxLen := v.Len()
@@ -295,6 +299,49 @@ func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind,
 	}
 }
 
+func (opts formatOptions) formatString(prefix, s string) textNode {
+	maxLen := len(s)
+	maxLines := strings.Count(s, "\n") + 1
+	if opts.LimitVerbosity {
+		maxLen = (1 << opts.verbosity()) << 5   // 32, 64, 128, 256, etc...
+		maxLines = (1 << opts.verbosity()) << 2 //  4, 8, 16, 32, 64, etc...
+	}
+
+	// For multiline strings, use the triple-quote syntax,
+	// but only use it when printing removed or inserted nodes since
+	// we only want the extra verbosity for those cases.
+	lines := strings.Split(strings.TrimSuffix(s, "\n"), "\n")
+	isTripleQuoted := len(lines) >= 4 && (opts.DiffMode == '-' || opts.DiffMode == '+')
+	for i := 0; i < len(lines) && isTripleQuoted; i++ {
+		lines[i] = strings.TrimPrefix(strings.TrimSuffix(lines[i], "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support
+		isPrintable := func(r rune) bool {
+			return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable
+		}
+		line := lines[i]
+		isTripleQuoted = !strings.HasPrefix(strings.TrimPrefix(line, prefix), `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" && len(line) <= maxLen
+	}
+	if isTripleQuoted {
+		var list textList
+		list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true})
+		for i, line := range lines {
+			if numElided := len(lines) - i; i == maxLines-1 && numElided > 1 {
+				comment := commentString(fmt.Sprintf("%d elided lines", numElided))
+				list = append(list, textRecord{Diff: opts.DiffMode, Value: textEllipsis, ElideComma: true, Comment: comment})
+				break
+			}
+			list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(line), ElideComma: true})
+		}
+		list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true})
+		return &textWrap{Prefix: "(", Value: list, Suffix: ")"}
+	}
+
+	// Format the string as a single-line quoted string.
+	if len(s) > maxLen+len(textEllipsis) {
+		return textLine(prefix + formatString(s[:maxLen]) + string(textEllipsis))
+	}
+	return textLine(prefix + formatString(s))
+}
+
 // formatMapKey formats v as if it were a map key.
 // The result is guaranteed to be a single line.
 func formatMapKey(v reflect.Value, disambiguate bool, ptrs *pointerReferences) string {
diff --git a/vendor/knative.dev/networking/pkg/apis/config/defaults.go b/vendor/knative.dev/networking/pkg/apis/config/defaults.go
new file mode 100644
index 000000000..b23e4c89d
--- /dev/null
+++ b/vendor/knative.dev/networking/pkg/apis/config/defaults.go
@@ -0,0 +1,88 @@
+/*
+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
+
+    https://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 config
+
+import (
+	"fmt"
+
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/api/resource"
+
+	cm "knative.dev/pkg/configmap"
+)
+
+const (
+	// DefaultsConfigName is the name of config map for the defaults.
+	DefaultsConfigName = "config-defaults"
+
+	// DefaultRevisionTimeoutSeconds will be set if timeoutSeconds not specified.
+	DefaultRevisionTimeoutSeconds = 5 * 60
+
+	// DefaultMaxRevisionTimeoutSeconds will be set if MaxRevisionTimeoutSeconds is not specified.
+	DefaultMaxRevisionTimeoutSeconds = 10 * 60
+)
+
+func defaultConfig() *Defaults {
+	return &Defaults{
+		RevisionTimeoutSeconds:    DefaultRevisionTimeoutSeconds,
+		MaxRevisionTimeoutSeconds: DefaultMaxRevisionTimeoutSeconds,
+	}
+}
+
+// NewDefaultsConfigFromMap creates a Defaults from the supplied Map.
+func NewDefaultsConfigFromMap(data map[string]string) (*Defaults, error) {
+	nc := defaultConfig()
+
+	if err := cm.Parse(data,
+		cm.AsInt64("revision-timeout-seconds", &nc.RevisionTimeoutSeconds),
+		cm.AsInt64("max-revision-timeout-seconds", &nc.MaxRevisionTimeoutSeconds),
+	); err != nil {
+		return nil, err
+	}
+
+	if nc.RevisionTimeoutSeconds > nc.MaxRevisionTimeoutSeconds {
+		return nil, fmt.Errorf("revision-timeout-seconds (%d) cannot be greater than max-revision-timeout-seconds (%d)", nc.RevisionTimeoutSeconds, nc.MaxRevisionTimeoutSeconds)
+	}
+	return nc, nil
+}
+
+// NewDefaultsConfigFromConfigMap creates a Defaults from the supplied configMap.
+func NewDefaultsConfigFromConfigMap(config *corev1.ConfigMap) (*Defaults, error) {
+	return NewDefaultsConfigFromMap(config.Data)
+}
+
+// Defaults includes the default values to be populated by the webhook.
+type Defaults struct {
+	RevisionTimeoutSeconds int64
+	// This is the timeout set for ingress.
+	// RevisionTimeoutSeconds must be less than this value.
+	MaxRevisionTimeoutSeconds int64
+}
+
+// asQuantity parses the value at key as a *resource.Quantity into the target, if it exists.
+func asQuantity(key string, target **resource.Quantity) cm.ParseFunc {
+	return func(data map[string]string) error {
+		if raw, ok := data[key]; !ok {
+			*target = nil
+		} else if val, err := resource.ParseQuantity(raw); err != nil {
+			return err
+		} else {
+			*target = &val
+		}
+		return nil
+	}
+}
diff --git a/vendor/knative.dev/networking/pkg/apis/config/doc.go b/vendor/knative.dev/networking/pkg/apis/config/doc.go
new file mode 100644
index 000000000..a0d4319ef
--- /dev/null
+++ b/vendor/knative.dev/networking/pkg/apis/config/doc.go
@@ -0,0 +1,21 @@
+/*
+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.
+*/
+
+// +k8s:deepcopy-gen=package
+
+// Package config holds the typed objects that define the schemas for
+// ConfigMap objects that pertain to our API objects.
+package config
diff --git a/vendor/knative.dev/networking/pkg/apis/config/store.go b/vendor/knative.dev/networking/pkg/apis/config/store.go
new file mode 100644
index 000000000..a0dfc8645
--- /dev/null
+++ b/vendor/knative.dev/networking/pkg/apis/config/store.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 config
+
+import (
+	"context"
+
+	"knative.dev/pkg/configmap"
+)
+
+type cfgKey struct{}
+
+// Config holds the collection of configurations that we attach to contexts.
+// +k8s:deepcopy-gen=false
+type Config struct {
+	Defaults *Defaults
+}
+
+// FromContext extracts a Config from the provided context.
+func FromContext(ctx context.Context) *Config {
+	x, ok := ctx.Value(cfgKey{}).(*Config)
+	if ok {
+		return x
+	}
+	return nil
+}
+
+// FromContextOrDefaults is like FromContext, but when no Config is attached it
+// returns a Config populated with the defaults for each of the Config fields.
+func FromContextOrDefaults(ctx context.Context) *Config {
+	if cfg := FromContext(ctx); cfg != nil {
+		return cfg
+	}
+	defaults, _ := NewDefaultsConfigFromMap(map[string]string{})
+	return &Config{
+		Defaults: defaults,
+	}
+}
+
+// ToContext attaches the provided Config to the provided context, returning the
+// new context with the Config attached.
+func ToContext(ctx context.Context, c *Config) context.Context {
+	return context.WithValue(ctx, cfgKey{}, c)
+}
+
+// Store is a typed wrapper around configmap.Untyped store to handle our configmaps.
+// +k8s:deepcopy-gen=false
+type Store struct {
+	*configmap.UntypedStore
+}
+
+// NewStore creates a new store of Configs and optionally calls functions when ConfigMaps are updated.
+func NewStore(logger configmap.Logger, onAfterStore ...func(name string, value interface{})) *Store {
+	store := &Store{
+		UntypedStore: configmap.NewUntypedStore(
+			"defaults",
+			logger,
+			configmap.Constructors{
+				DefaultsConfigName: NewDefaultsConfigFromConfigMap,
+			},
+			onAfterStore...,
+		),
+	}
+
+	return store
+}
+
+// ToContext attaches the current Config state to the provided context.
+func (s *Store) ToContext(ctx context.Context) context.Context {
+	return ToContext(ctx, s.Load())
+}
+
+// Load creates a Config from the current config state of the Store.
+func (s *Store) Load() *Config {
+	return &Config{
+		Defaults: s.UntypedLoad(DefaultsConfigName).(*Defaults).DeepCopy(),
+	}
+}
diff --git a/vendor/knative.dev/networking/pkg/apis/config/zz_generated.deepcopy.go b/vendor/knative.dev/networking/pkg/apis/config/zz_generated.deepcopy.go
new file mode 100644
index 000000000..5169897dd
--- /dev/null
+++ b/vendor/knative.dev/networking/pkg/apis/config/zz_generated.deepcopy.go
@@ -0,0 +1,37 @@
+// +build !ignore_autogenerated
+
+/*
+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 deepcopy-gen. DO NOT EDIT.
+
+package config
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Defaults) DeepCopyInto(out *Defaults) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Defaults.
+func (in *Defaults) DeepCopy() *Defaults {
+	if in == nil {
+		return nil
+	}
+	out := new(Defaults)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/vendor/knative.dev/networking/pkg/apis/networking/v1alpha1/ingress_defaults.go b/vendor/knative.dev/networking/pkg/apis/networking/v1alpha1/ingress_defaults.go
index 4e3b40362..eeee8a36e 100644
--- a/vendor/knative.dev/networking/pkg/apis/networking/v1alpha1/ingress_defaults.go
+++ b/vendor/knative.dev/networking/pkg/apis/networking/v1alpha1/ingress_defaults.go
@@ -18,8 +18,12 @@ package v1alpha1
 
 import (
 	"context"
+	"time"
 
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"knative.dev/pkg/apis"
+
+	"knative.dev/networking/pkg/apis/config"
 )
 
 // SetDefaults populates default values in Ingress
@@ -65,4 +69,11 @@ func (p *HTTPIngressPath) SetDefaults(ctx context.Context) {
 	if len(p.Splits) == 1 && p.Splits[0].Percent == 0 {
 		p.Splits[0].Percent = 100
 	}
+
+	cfg := config.FromContextOrDefaults(ctx)
+	maxTimeout := time.Duration(cfg.Defaults.MaxRevisionTimeoutSeconds) * time.Second
+
+	if p.Timeout == nil {
+		p.Timeout = &metav1.Duration{Duration: maxTimeout}
+	}
 }
diff --git a/vendor/knative.dev/networking/pkg/apis/networking/v1alpha1/ingress_types.go b/vendor/knative.dev/networking/pkg/apis/networking/v1alpha1/ingress_types.go
index af992d761..da3f5a54b 100644
--- a/vendor/knative.dev/networking/pkg/apis/networking/v1alpha1/ingress_types.go
+++ b/vendor/knative.dev/networking/pkg/apis/networking/v1alpha1/ingress_types.go
@@ -25,7 +25,7 @@ import (
 )
 
 // +genclient
-// +genreconciler:krshapedlogic=true
+// +genreconciler:class=networking.knative.dev/ingress.class,krshapedlogic=true
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // Ingress is a collection of rules that allow inbound connections to reach the endpoints defined
diff --git a/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/reconciler.go b/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/reconciler.go
index 8081d59b3..17dd45424 100644
--- a/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/reconciler.go
+++ b/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/reconciler.go
@@ -34,7 +34,6 @@ import (
 	sets "k8s.io/apimachinery/pkg/util/sets"
 	kubernetes "k8s.io/client-go/kubernetes"
 	corev1 "k8s.io/client-go/listers/core/v1"
-	cache "k8s.io/client-go/tools/cache"
 	record "k8s.io/client-go/tools/record"
 	controller "knative.dev/pkg/controller"
 	logging "knative.dev/pkg/logging"
@@ -83,6 +82,15 @@ type ReadOnlyFinalizer interface {
 	ObserveFinalizeKind(ctx context.Context, o *v1.Namespace) reconciler.Event
 }
 
+type doReconcile func(ctx context.Context, o *v1.Namespace) reconciler.Event
+
+const (
+	doReconcileKind       = "ReconcileKind"
+	doFinalizeKind        = "FinalizeKind"
+	doObserveKind         = "ObserveKind"
+	doObserveFinalizeKind = "ObserveFinalizeKind"
+)
+
 // reconcilerImpl implements controller.Reconciler for v1.Namespace resources.
 type reconcilerImpl struct {
 	// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware
@@ -175,22 +183,19 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client kubern
 func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	logger := logging.FromContext(ctx)
 
-	// Convert the namespace/name string into a distinct namespace and name
-	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	// Initialize the reconciler state. This will convert the namespace/name
+	// string into a distinct namespace and name, determin if this instance of
+	// the reconciler is the leader, and any additional interfaces implemented
+	// by the reconciler. Returns an error is the resource key is invalid.
+	s, err := newState(key, r)
 	if err != nil {
 		logger.Errorf("invalid resource key: %s", key)
 		return nil
 	}
-	// Establish whether we are the leader for use below.
-	isLeader := r.IsLeaderFor(types.NamespacedName{
-		Namespace: namespace,
-		Name:      name,
-	})
-	roi, isROI := r.reconciler.(ReadOnlyInterface)
-	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
-	if !isLeader && !isROI && !isROF {
-		// If we are not the leader, and we don't implement either ReadOnly
-		// interface, then take a fast-path out.
+
+	// If we are not the leader, and we don't implement either ReadOnly
+	// observer interfaces, then take a fast-path out.
+	if s.isNotLeaderNorObserver() {
 		return nil
 	}
 
@@ -206,7 +211,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 
 	getter := r.Lister
 
-	original, err := getter.Get(name)
+	original, err := getter.Get(s.name)
 
 	if errors.IsNotFound(err) {
 		// The resource may no longer exist, in which case we stop processing.
@@ -220,44 +225,38 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 	resource := original.DeepCopy()
 
 	var reconcileEvent reconciler.Event
-	if resource.GetDeletionTimestamp().IsZero() {
-		if isLeader {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
-
-			// Set and update the finalizer on resource if r.reconciler
-			// implements Finalizer.
-			if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
-				return fmt.Errorf("failed to set finalizers: %w", err)
-			}
 
-			// Reconcile this copy of the resource and then write back any status
-			// updates regardless of whether the reconciliation errored out.
-			reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
-
-		} else if isROI {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ObserveKind"))
+	name, do := s.reconcileMethodFor(resource)
+	// Append the target method to the logger.
+	logger = logger.With(zap.String("targetMethod", name))
+	switch name {
+	case doReconcileKind:
+		// Append the target method to the logger.
+		logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
 
-			// Observe any changes to this resource, since we are not the leader.
-			reconcileEvent = roi.ObserveKind(ctx, resource)
+		// Set and update the finalizer on resource if r.reconciler
+		// implements Finalizer.
+		if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
+			return fmt.Errorf("failed to set finalizers: %w", err)
 		}
-	} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
 
+		// Reconcile this copy of the resource and then write back any status
+		// updates regardless of whether the reconciliation errored out.
+		reconcileEvent = do(ctx, resource)
+
+	case "FinalizeKind":
 		// For finalizing reconcilers, if this resource being marked for deletion
 		// and reconciled cleanly (nil or normal event), remove the finalizer.
-		reconcileEvent = fin.FinalizeKind(ctx, resource)
+		reconcileEvent = do(ctx, resource)
+
 		if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
 			return fmt.Errorf("failed to clear finalizers: %w", err)
 		}
-	} else if !isLeader && isROF {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
 
-		// For finalizing reconcilers, just observe when we aren't the leader.
-		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
+	case "ObserveKind", "ObserveFinalizeKind":
+		// Observe any changes to this resource, since we are not the leader.
+		reconcileEvent = do(ctx, resource)
+
 	}
 
 	// Synchronize the status.
@@ -270,7 +269,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		// This is important because the copy we loaded from the injectionInformer's
 		// cache may be stale and we don't want to overwrite a prior update
 		// to status with this stale state.
-	case !isLeader:
+	case !s.isLeader:
 		// High-availability reconcilers may have many replicas watching the resource, but only
 		// the elected leader is expected to write modifications.
 		logger.Warn("Saw status changes when we aren't the leader!")
diff --git a/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/state.go b/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/state.go
new file mode 100644
index 000000000..2370803c9
--- /dev/null
+++ b/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/state.go
@@ -0,0 +1,105 @@
+/*
+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 namespace
+
+import (
+	fmt "fmt"
+
+	v1 "k8s.io/api/core/v1"
+	types "k8s.io/apimachinery/pkg/types"
+	cache "k8s.io/client-go/tools/cache"
+)
+
+// state is used to track the state of a reconciler in a single run.
+type state struct {
+	// Key is the original reconciliation key from the queue.
+	key string
+	// Namespace is the namespace split from the reconciliation key.
+	namespace string
+	// Namespace is the name split from the reconciliation key.
+	name string
+	// reconciler is the reconciler.
+	reconciler Interface
+	// rof is the read only interface cast of the reconciler.
+	roi ReadOnlyInterface
+	// IsROI (Read Only Interface) the reconciler only observes reconciliation.
+	isROI bool
+	// rof is the read only finalizer cast of the reconciler.
+	rof ReadOnlyFinalizer
+	// IsROF (Read Only Finalizer) the reconciler only observes finalize.
+	isROF bool
+	// IsLeader the instance of the reconciler is the elected leader.
+	isLeader bool
+}
+
+func newState(key string, r *reconcilerImpl) (*state, error) {
+	// Convert the namespace/name string into a distinct namespace and name
+	namespace, name, err := cache.SplitMetaNamespaceKey(key)
+	if err != nil {
+		return nil, fmt.Errorf("invalid resource key: %s", key)
+	}
+
+	roi, isROI := r.reconciler.(ReadOnlyInterface)
+	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
+
+	isLeader := r.IsLeaderFor(types.NamespacedName{
+		Namespace: namespace,
+		Name:      name,
+	})
+
+	return &state{
+		key:        key,
+		namespace:  namespace,
+		name:       name,
+		reconciler: r.reconciler,
+		roi:        roi,
+		isROI:      isROI,
+		rof:        rof,
+		isROF:      isROF,
+		isLeader:   isLeader,
+	}, nil
+}
+
+// isNotLeaderNorObserver checks to see if this reconciler with the current
+// state is enabled to do any work or not.
+// isNotLeaderNorObserver returns true when there is no work possible for the
+// reconciler.
+func (s *state) isNotLeaderNorObserver() bool {
+	if !s.isLeader && !s.isROI && !s.isROF {
+		// If we are not the leader, and we don't implement either ReadOnly
+		// interface, then take a fast-path out.
+		return true
+	}
+	return false
+}
+
+func (s *state) reconcileMethodFor(o *v1.Namespace) (string, doReconcile) {
+	if o.GetDeletionTimestamp().IsZero() {
+		if s.isLeader {
+			return doReconcileKind, s.reconciler.ReconcileKind
+		} else if s.isROI {
+			return doObserveKind, s.roi.ObserveKind
+		}
+	} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
+		return doFinalizeKind, fin.FinalizeKind
+	} else if !s.isLeader && s.isROF {
+		return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
+	}
+	return "unknown", nil
+}
diff --git a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/packages.go b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/packages.go
index 50f3cb461..ab2ee1205 100644
--- a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/packages.go
+++ b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/packages.go
@@ -213,6 +213,15 @@ func isNonNamespaced(tags map[string]map[string]string) bool {
 	return has
 }
 
+func stubs(tags map[string]map[string]string) bool {
+	vals, has := tags["genreconciler"]
+	if !has {
+		return false
+	}
+	_, has = vals["stubs"]
+	return has
+}
+
 func vendorless(p string) string {
 	if pos := strings.LastIndex(p, "/vendor/"); pos != -1 {
 		return p[pos+len("/vendor/"):]
@@ -414,6 +423,7 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
 		reconcilerClass, hasReconcilerClass := extractReconcilerClassTag(extracted)
 		nonNamespaced := isNonNamespaced(extracted)
 		isKRShaped := isKRShaped(extracted)
+		stubs := stubs(extracted)
 
 		packagePath := filepath.Join(packagePath, strings.ToLower(t.Name.Name))
 
@@ -449,32 +459,34 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
 			},
 		})
 
-		// Controller Stub
-		vers = append(vers, &generator.DefaultPackage{
-			PackageName: strings.ToLower(t.Name.Name),
-			PackagePath: filepath.Join(packagePath, "stub"),
-			HeaderText:  boilerplate,
-			GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
-				// Impl
-				generators = append(generators, &reconcilerControllerStubGenerator{
-					DefaultGen: generator.DefaultGen{
-						OptionalName: "controller",
-					},
-					typeToGenerate:      t,
-					reconcilerPkg:       packagePath,
-					outputPackage:       filepath.Join(packagePath, "stub"),
-					imports:             generator.NewImportTracker(),
-					informerPackagePath: informerPackagePath,
-					reconcilerClass:     reconcilerClass,
-					hasReconcilerClass:  hasReconcilerClass,
-				})
-				return generators
-			},
-			FilterFunc: func(c *generator.Context, t *types.Type) bool {
-				tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
-				return tags.NeedsReconciler(t, customArgs)
-			},
-		})
+		if stubs {
+			// Controller Stub
+			vers = append(vers, &generator.DefaultPackage{
+				PackageName: strings.ToLower(t.Name.Name),
+				PackagePath: filepath.Join(packagePath, "stub"),
+				HeaderText:  boilerplate,
+				GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
+					// Impl
+					generators = append(generators, &reconcilerControllerStubGenerator{
+						DefaultGen: generator.DefaultGen{
+							OptionalName: "controller",
+						},
+						typeToGenerate:      t,
+						reconcilerPkg:       packagePath,
+						outputPackage:       filepath.Join(packagePath, "stub"),
+						imports:             generator.NewImportTracker(),
+						informerPackagePath: informerPackagePath,
+						reconcilerClass:     reconcilerClass,
+						hasReconcilerClass:  hasReconcilerClass,
+					})
+					return generators
+				},
+				FilterFunc: func(c *generator.Context, t *types.Type) bool {
+					tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
+					return tags.NeedsReconciler(t, customArgs)
+				},
+			})
+		}
 
 		// Reconciler
 		vers = append(vers, &generator.DefaultPackage{
@@ -508,20 +520,45 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
 			},
 		})
 
-		// Reconciler Stub
+		if stubs {
+			// Reconciler Stub
+			vers = append(vers, &generator.DefaultPackage{
+				PackageName: strings.ToLower(t.Name.Name),
+				PackagePath: filepath.Join(packagePath, "stub"),
+				HeaderText:  boilerplate,
+				GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
+					// Impl
+					generators = append(generators, &reconcilerReconcilerStubGenerator{
+						DefaultGen: generator.DefaultGen{
+							OptionalName: "reconciler",
+						},
+						typeToGenerate: t,
+						reconcilerPkg:  packagePath,
+						outputPackage:  filepath.Join(packagePath, "stub"),
+						imports:        generator.NewImportTracker(),
+					})
+					return generators
+				},
+				FilterFunc: func(c *generator.Context, t *types.Type) bool {
+					tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
+					return tags.NeedsReconciler(t, customArgs)
+				},
+			})
+		}
+
+		// Reconciler State
 		vers = append(vers, &generator.DefaultPackage{
 			PackageName: strings.ToLower(t.Name.Name),
-			PackagePath: filepath.Join(packagePath, "stub"),
+			PackagePath: packagePath,
 			HeaderText:  boilerplate,
 			GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
-				// Impl
-				generators = append(generators, &reconcilerReconcilerStubGenerator{
+				// state
+				generators = append(generators, &reconcilerStateGenerator{
 					DefaultGen: generator.DefaultGen{
-						OptionalName: "reconciler",
+						OptionalName: "state",
 					},
 					typeToGenerate: t,
-					reconcilerPkg:  packagePath,
-					outputPackage:  filepath.Join(packagePath, "stub"),
+					outputPackage:  packagePath,
 					imports:        generator.NewImportTracker(),
 				})
 				return generators
diff --git a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler.go b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler.go
index 91af9a7e8..e05d78f20 100644
--- a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler.go
+++ b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler.go
@@ -230,6 +230,15 @@ type ReadOnlyFinalizer interface {
 	ObserveFinalizeKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}}
 }
 
+type doReconcile func(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}}
+
+const (
+	doReconcileKind       = "ReconcileKind"
+	doFinalizeKind        = "FinalizeKind"
+	doObserveKind         = "ObserveKind"
+	doObserveFinalizeKind = "ObserveFinalizeKind"
+)
+
 // reconcilerImpl implements controller.Reconciler for {{.type|raw}} resources.
 type reconcilerImpl struct {
 	// LeaderAwareFuncs is inlined to help us implement {{.reconcilerLeaderAware|raw}}
@@ -332,22 +341,19 @@ var reconcilerImplFactory = `
 func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) error {
 	logger := {{.loggingFromContext|raw}}(ctx)
 
-	// Convert the namespace/name string into a distinct namespace and name
-	namespace, name, err := {{.cacheSplitMetaNamespaceKey|raw}}(key)
+	// Initialize the reconciler state. This will convert the namespace/name 
+	// string into a distinct namespace and name, determin if this instance of
+	// the reconciler is the leader, and any additional interfaces implemented
+	// by the reconciler. Returns an error is the resource key is invalid.
+	s, err := newState(key, r)
 	if err != nil {
 		logger.Errorf("invalid resource key: %s", key)
 		return nil
 	}
-	// Establish whether we are the leader for use below.
-	isLeader := r.IsLeaderFor({{.typesNamespacedName|raw}}{
-		Namespace: namespace,
-		Name: name,
-	})
-	roi, isROI := r.reconciler.(ReadOnlyInterface)
-	rof, isROF := r.reconciler.(ReadOnlyFinalizer);
-	if !isLeader && !isROI && !isROF {
-		// If we are not the leader, and we don't implement either ReadOnly
-		// interface, then take a fast-path out.
+	
+	// If we are not the leader, and we don't implement either ReadOnly
+	// observer interfaces, then take a fast-path out.
+	if s.isNotLeaderNorObserver() {
 		return nil
 	}
 
@@ -363,9 +369,9 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro
 	{{if .nonNamespaced}}
 	getter := r.Lister
 	{{else}}
-	getter := r.Lister.{{.type|apiGroup}}(namespace)
+	getter := r.Lister.{{.type|apiGroup}}(s.namespace)
 	{{end}}
-	original, err := getter.Get(name)
+	original, err := getter.Get(s.name)
 
 	if {{.apierrsIsNotFound|raw}}(err) {
 		// The resource may no longer exist, in which case we stop processing.
@@ -387,50 +393,45 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro
 	resource := original.DeepCopy()
 
 	var reconcileEvent {{.reconcilerEvent|raw}}
-	if resource.GetDeletionTimestamp().IsZero() {
-		if isLeader {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
-
-			// Set and update the finalizer on resource if r.reconciler
-			// implements Finalizer.
-			if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
-				return {{.fmtErrorf|raw}}("failed to set finalizers: %w", err)
-			}
-			{{if .isKRShaped}}
-			reconciler.PreProcessReconcile(ctx, resource)
-			{{end}}
 
-			// Reconcile this copy of the resource and then write back any status
-			// updates regardless of whether the reconciliation errored out.
-			reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
-
-			{{if .isKRShaped}}
-			reconciler.PostProcessReconcile(ctx, resource, original)
-			{{end}}
-		} else if isROI {
-			// Append the target method to the logger.
-			logger = logger.With(zap.String("targetMethod", "ObserveKind"))
+	name, do := s.reconcileMethodFor(resource)
+	// Append the target method to the logger.
+	logger = logger.With(zap.String("targetMethod", name))
+	switch name {
+	case doReconcileKind:
+		// Append the target method to the logger.
+		logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
 
-			// Observe any changes to this resource, since we are not the leader.
-			reconcileEvent = roi.ObserveKind(ctx, resource)
+		// Set and update the finalizer on resource if r.reconciler
+		// implements Finalizer.
+		if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
+			return {{.fmtErrorf|raw}}("failed to set finalizers: %w", err)
 		}
-	} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
+		{{if .isKRShaped}}
+		reconciler.PreProcessReconcile(ctx, resource)
+		{{end}}
 
+		// Reconcile this copy of the resource and then write back any status
+		// updates regardless of whether the reconciliation errored out.
+		reconcileEvent = do(ctx, resource)
+
+		{{if .isKRShaped}}
+		reconciler.PostProcessReconcile(ctx, resource, original)
+		{{end}}
+
+	case "FinalizeKind":
 		// For finalizing reconcilers, if this resource being marked for deletion
 		// and reconciled cleanly (nil or normal event), remove the finalizer.
-		reconcileEvent = fin.FinalizeKind(ctx, resource)
+		reconcileEvent = do(ctx, resource)
+
 		if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
 			return {{.fmtErrorf|raw}}("failed to clear finalizers: %w", err)
 		}
-	} else if !isLeader && isROF {
-		// Append the target method to the logger.
-		logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
 
-		// For finalizing reconcilers, just observe when we aren't the leader.
-		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
+	case "ObserveKind", "ObserveFinalizeKind":
+		// Observe any changes to this resource, since we are not the leader.
+		reconcileEvent = do(ctx, resource)
+
 	}
 
 	// Synchronize the status.
@@ -443,7 +444,7 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro
 		// This is important because the copy we loaded from the injectionInformer's
 		// cache may be stale and we don't want to overwrite a prior update
 		// to status with this stale state.
-	case !isLeader:
+	case !s.isLeader:
 		// High-availability reconcilers may have many replicas watching the resource, but only
 		// the elected leader is expected to write modifications.
 		logger.Warn("Saw status changes when we aren't the leader!")
diff --git a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler_stub.go b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler_stub.go
index 62805e3d2..24bc74ff8 100644
--- a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler_stub.go
+++ b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler_stub.go
@@ -139,8 +139,9 @@ func (r *Reconciler) ReconcileKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}
 
 	{{if not .isKRShaped}}
 	// TODO: use this if the object has .status.ObservedGeneration.
-	// o.Status.ObservedGeneration = o.Generation{{end}}
-	return newReconciledNormal(o.Namespace, o.Name)
+	// o.Status.ObservedGeneration = o.Generation
+	{{end}}
+	return nil
 }
 
 // Optionally, use FinalizeKind to add finalizers. FinalizeKind will be called
diff --git a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_state.go b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_state.go
new file mode 100644
index 000000000..d04fef703
--- /dev/null
+++ b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_state.go
@@ -0,0 +1,163 @@
+/*
+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 generators
+
+import (
+	"io"
+
+	"k8s.io/gengo/generator"
+	"k8s.io/gengo/namer"
+	"k8s.io/gengo/types"
+	"k8s.io/klog"
+)
+
+// reconcilerStateGenerator produces a reconciler state object to manage reconcilation runs.
+type reconcilerStateGenerator struct {
+	generator.DefaultGen
+	outputPackage  string
+	imports        namer.ImportTracker
+	typeToGenerate *types.Type
+}
+
+var _ generator.Generator = (*reconcilerStateGenerator)(nil)
+
+func (g *reconcilerStateGenerator) Filter(c *generator.Context, t *types.Type) bool {
+	// Only process the type for this generator.
+	return t == g.typeToGenerate
+}
+
+func (g *reconcilerStateGenerator) Namers(c *generator.Context) namer.NameSystems {
+	return namer.NameSystems{
+		"raw": namer.NewRawNamer(g.outputPackage, g.imports),
+	}
+}
+
+func (g *reconcilerStateGenerator) Imports(c *generator.Context) (imports []string) {
+	imports = append(imports, g.imports.ImportLines()...)
+	return
+}
+
+func (g *reconcilerStateGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
+	sw := generator.NewSnippetWriter(w, c, "{{", "}}")
+
+	klog.V(5).Infof("processing type %v", t)
+
+	m := map[string]interface{}{
+		"type": t,
+		// methods
+		"cacheSplitMetaNamespaceKey": c.Universe.Function(types.Name{
+			Package: "k8s.io/client-go/tools/cache",
+			Name:    "SplitMetaNamespaceKey",
+		}),
+		"fmtErrorf": c.Universe.Package("fmt").Function("Errorf"),
+		"reconcilerLeaderAware": c.Universe.Type(types.Name{
+			Package: "knative.dev/pkg/reconciler",
+			Name:    "LeaderAware",
+		}),
+		"typesNamespacedName": c.Universe.Type(types.Name{
+			Package: "k8s.io/apimachinery/pkg/types",
+			Name:    "NamespacedName",
+		}),
+	}
+
+	sw.Do(reconcilerStateType, m)
+	sw.Do(reconcilerStateMethods, m)
+
+	return sw.Error()
+}
+
+var reconcilerStateType = `
+// state is used to track the state of a reconciler in a single run.
+type state struct {
+	// Key is the original reconciliation key from the queue.
+	key string
+	// Namespace is the namespace split from the reconciliation key.
+	namespace string
+	// Namespace is the name split from the reconciliation key.
+	name string
+	// reconciler is the reconciler.
+	reconciler Interface
+	// rof is the read only interface cast of the reconciler.
+	roi ReadOnlyInterface
+	// IsROI (Read Only Interface) the reconciler only observes reconciliation.
+	isROI bool
+	// rof is the read only finalizer cast of the reconciler.
+	rof ReadOnlyFinalizer
+	// IsROF (Read Only Finalizer) the reconciler only observes finalize.
+	isROF bool
+	// IsLeader the instance of the reconciler is the elected leader.
+	isLeader bool
+}
+
+func newState(key string, r *reconcilerImpl) (*state, error) {
+	// Convert the namespace/name string into a distinct namespace and name
+	namespace, name, err := {{.cacheSplitMetaNamespaceKey|raw}}(key)
+	if err != nil {
+		return nil, {{.fmtErrorf|raw}}("invalid resource key: %s", key)
+	}
+
+	roi, isROI := r.reconciler.(ReadOnlyInterface)
+	rof, isROF := r.reconciler.(ReadOnlyFinalizer)
+
+	isLeader := r.IsLeaderFor({{.typesNamespacedName|raw}}{
+		Namespace: namespace,
+		Name:      name,
+	})
+
+	return &state{
+		key:        key,
+		namespace:  namespace,
+		name:       name,
+		reconciler: r.reconciler,
+		roi:        roi,
+		isROI:      isROI,
+		rof:        rof,
+		isROF:      isROF,
+		isLeader:   isLeader,
+	}, nil
+}
+`
+
+var reconcilerStateMethods = `
+// isNotLeaderNorObserver checks to see if this reconciler with the current
+// state is enabled to do any work or not.
+// isNotLeaderNorObserver returns true when there is no work possible for the
+// reconciler.
+func (s *state) isNotLeaderNorObserver() bool {
+	if !s.isLeader && !s.isROI && !s.isROF {
+		// If we are not the leader, and we don't implement either ReadOnly
+		// interface, then take a fast-path out.
+		return true
+	}
+	return false
+}
+
+func (s *state) reconcileMethodFor(o *{{.type|raw}}) (string, doReconcile) {
+	if o.GetDeletionTimestamp().IsZero() {
+		if s.isLeader {
+			return doReconcileKind, s.reconciler.ReconcileKind
+		} else if s.isROI {
+			return doObserveKind, s.roi.ObserveKind
+		}
+	} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
+		return doFinalizeKind, fin.FinalizeKind
+	} else if !s.isLeader && s.isROF {
+		return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
+	}
+	return "unknown", nil
+}
+`
diff --git a/vendor/knative.dev/pkg/hack/generate-knative.sh b/vendor/knative.dev/pkg/hack/generate-knative.sh
old mode 100755
new mode 100644
diff --git a/vendor/knative.dev/pkg/injection/README.md b/vendor/knative.dev/pkg/injection/README.md
index 30d75866e..8be10cd55 100644
--- a/vendor/knative.dev/pkg/injection/README.md
+++ b/vendor/knative.dev/pkg/injection/README.md
@@ -476,8 +476,20 @@ reconciler.PostProcessReconcile(ctx, resource, oldResource)
 
 #### Stubs
 
-To get started, or to use as reference. It is intended to be copied out of the
-`client` dir.
+To enable stubs generation, add the stubs flag:
+
+```go
+// +genreconciler:stubs
+```
+
+Or with the class annotation:
+
+```go
+// +genreconciler:class=example.com/filter.class,stubs
+```
+
+The stubs are intended to be used to get started, or to use as reference. It is
+intended to be copied out of the `client` dir. 
 
 `knative.dev/<repo>/pkg/client/injection/reconciler/<clientgroup>/<version>/<kind>/stubs/controller.go`
 
diff --git a/vendor/knative.dev/pkg/injection/sharedmain/main.go b/vendor/knative.dev/pkg/injection/sharedmain/main.go
index 00354fd80..1cbba0264 100644
--- a/vendor/knative.dev/pkg/injection/sharedmain/main.go
+++ b/vendor/knative.dev/pkg/injection/sharedmain/main.go
@@ -35,7 +35,6 @@ import (
 	"k8s.io/apimachinery/pkg/util/wait"
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/tools/clientcmd"
-	"k8s.io/client-go/transport"
 	"k8s.io/klog"
 
 	"go.uber.org/zap"
@@ -69,15 +68,6 @@ func GetConfig(masterURL, kubeconfig string) (*rest.Config, error) {
 	// 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 {
-		// Augment the rest.Config with a "wrapper" around the transport that
-		// will emit varying levels of debug logging when -v is passed with
-		// levels 6 to 9.
-		wt := transport.DebugWrappers
-		if cfg.WrapTransport != nil {
-			wt = transport.Wrappers(wt, cfg.WrapTransport)
-		}
-		cfg.WrapTransport = wt
-
 		return cfg
 	}
 
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 701b2ec86..54159d67d 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -163,7 +163,7 @@ github.com/golang/protobuf/ptypes/empty
 github.com/golang/protobuf/ptypes/struct
 github.com/golang/protobuf/ptypes/timestamp
 github.com/golang/protobuf/ptypes/wrappers
-# github.com/google/go-cmp v0.5.0
+# github.com/google/go-cmp v0.5.1
 ## explicit
 github.com/google/go-cmp/cmp
 github.com/google/go-cmp/cmp/cmpopts
@@ -410,7 +410,7 @@ golang.org/x/text/width
 # golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
 ## explicit
 golang.org/x/time/rate
-# golang.org/x/tools v0.0.0-20200721032237-77f530d86f9a
+# golang.org/x/tools v0.0.0-20200721223218-6123e77877b2
 golang.org/x/tools/cmd/goimports
 golang.org/x/tools/go/ast/astutil
 golang.org/x/tools/go/gcexportdata
@@ -485,7 +485,7 @@ google.golang.org/appengine/internal/socket
 google.golang.org/appengine/internal/urlfetch
 google.golang.org/appengine/socket
 google.golang.org/appengine/urlfetch
-# google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986
+# google.golang.org/genproto v0.0.0-20200722002428-88e341933a54
 google.golang.org/genproto/googleapis/api
 google.golang.org/genproto/googleapis/api/annotations
 google.golang.org/genproto/googleapis/api/distribution
@@ -1004,7 +1004,7 @@ k8s.io/utils/buffer
 k8s.io/utils/integer
 k8s.io/utils/pointer
 k8s.io/utils/trace
-# knative.dev/caching v0.0.0-20200721163356-191470591e4c
+# knative.dev/caching v0.0.0-20200722161057-641864dd528b
 ## explicit
 knative.dev/caching/config
 knative.dev/caching/pkg/apis/caching
@@ -1025,8 +1025,9 @@ knative.dev/caching/pkg/client/injection/informers/caching/v1alpha1/image/fake
 knative.dev/caching/pkg/client/injection/informers/factory
 knative.dev/caching/pkg/client/injection/informers/factory/fake
 knative.dev/caching/pkg/client/listers/caching/v1alpha1
-# knative.dev/networking v0.0.0-20200721203054-c218540746c9
+# knative.dev/networking v0.0.0-20200722170957-facd9a20c21d
 ## explicit
+knative.dev/networking/pkg/apis/config
 knative.dev/networking/pkg/apis/networking
 knative.dev/networking/pkg/apis/networking/v1alpha1
 knative.dev/networking/pkg/client/clientset/versioned
@@ -1051,7 +1052,7 @@ knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/server
 knative.dev/networking/pkg/client/injection/reconciler/networking/v1alpha1/serverlessservice
 knative.dev/networking/pkg/client/istio/listers/networking/v1alpha3
 knative.dev/networking/pkg/client/listers/networking/v1alpha1
-# knative.dev/pkg v0.0.0-20200721211453-557b6826ef85
+# knative.dev/pkg v0.0.0-20200723060257-ae9c3f7fa8d3
 ## explicit
 knative.dev/pkg/apiextensions/storageversion
 knative.dev/pkg/apiextensions/storageversion/cmd/migrate
@@ -1175,7 +1176,7 @@ knative.dev/pkg/webhook/resourcesemantics/conversion
 knative.dev/pkg/webhook/resourcesemantics/defaulting
 knative.dev/pkg/webhook/resourcesemantics/validation
 knative.dev/pkg/websocket
-# knative.dev/test-infra v0.0.0-20200721175154-c98db9bd4d5d
+# knative.dev/test-infra v0.0.0-20200722142057-3ca910b5a25e
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/yaml v1.2.0


Test patch:
diff --git a/pkg/activator/handler/concurrency_reporter_test.go b/pkg/activator/handler/concurrency_reporter_test.go
index 594734c52..92d20b270 100644
--- a/pkg/activator/handler/concurrency_reporter_test.go
+++ b/pkg/activator/handler/concurrency_reporter_test.go
@@ -19,6 +19,7 @@ package handler
 import (
 	"context"
 	"sort"
+	"strconv"
 	"testing"
 	"time"
 
@@ -30,6 +31,7 @@ import (
 	rtesting "knative.dev/pkg/reconciler/testing"
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
 	"knative.dev/serving/pkg/autoscaler/metrics"
+	asmetrics "knative.dev/serving/pkg/autoscaler/metrics"
 	fakeservingclient "knative.dev/serving/pkg/client/injection/client/fake"
 	fakerevisioninformer "knative.dev/serving/pkg/client/injection/informers/serving/v1/revision/fake"
 	"knative.dev/serving/pkg/network"
@@ -425,3 +427,68 @@ func revisionInformer(ctx context.Context, revs ...*v1.Revision) {
 		revisions.Informer().GetIndexer().Add(rev)
 	}
 }
+
+func BenchmarkConcurrencyReporter(b *testing.B) {
+	ctx, cancel, _ := rtesting.SetupFakeContextWithCancel(b)
+	defer cancel()
+
+	// Buffer equal to the activator.
+	reqCh := make(chan network.ReqEvent, 100)
+	statCh := make(chan []asmetrics.StatMessage)
+	cr := NewConcurrencyReporter(ctx, activatorPodName, reqCh, statCh)
+
+	stopCh := make(chan struct{})
+	defer close(stopCh)
+	go cr.Run(stopCh)
+
+	// Just read and ignore all stat messages.
+	go func() {
+		for {
+			select {
+			case <-statCh:
+			case <-stopCh:
+				return
+			}
+		}
+	}()
+
+	// Spread the load across 100 revisions.
+	keys := make([]types.NamespacedName, 0, 100)
+	for i := 0; i < cap(keys); i++ {
+		keys = append(keys, types.NamespacedName{
+			Namespace: testNamespace,
+			Name:      testRevName + strconv.Itoa(i),
+		})
+	}
+
+	request := func(key types.NamespacedName) {
+		reqCh <- network.ReqEvent{
+			Time: time.Now(),
+			Type: network.ReqIn,
+			Key:  key,
+		}
+		reqCh <- network.ReqEvent{
+			Time: time.Now(),
+			Type: network.ReqOut,
+			Key:  key,
+		}
+	}
+
+	b.Run("sequential", func(b *testing.B) {
+		for j := 0; j < b.N; j++ {
+			key := keys[j%len(keys)]
+			request(key)
+		}
+	})
+
+	b.Run("parallel", func(b *testing.B) {
+		b.RunParallel(func(pb *testing.PB) {
+			var j int
+			for pb.Next() {
+				key := keys[j%len(keys)]
+				request(key)
+				j++
+			}
+		})
+	})
+}
diff --git a/test/config/chaosduck.yaml b/test/config/chaosduck.yaml
index 3ae8d37a1..45c84b7f9 100644
--- a/test/config/chaosduck.yaml
+++ b/test/config/chaosduck.yaml
@@ -81,7 +81,9 @@ spec:
         # Disable chaos on the Istio components.
         - "-disable=istio-webhook"
         - "-disable=networking-istio"
-
+        # Disable chaos on webhooks until https://github.com/knative/pkg/issues/1509 is
+        # sorted out.
+        - "-disable=webhook"
         env:
         - name: SYSTEM_NAMESPACE
           valueFrom:
diff --git a/test/e2e-common.sh b/test/e2e-common.sh
index dfa6bf91d..e14cbd677 100644
--- a/test/e2e-common.sh
+++ b/test/e2e-common.sh
@@ -648,18 +648,18 @@ function toggle_feature() {
 function scale_controlplane() {
   for deployment in "$@"; do
     # Make sure all pods run in leader-elected mode.
-    kubectl -n "${SYSTEM_NAMESPACE}" scale deployment "$deployment" --replicas=0 || failed=1
+    kubectl -n "${SYSTEM_NAMESPACE}" scale deployment "$deployment" --replicas=0 || fail_test
     # Give it time to kill the pods.
     sleep 5
     # Scale up components for HA tests
-    kubectl -n "${SYSTEM_NAMESPACE}" scale deployment "$deployment" --replicas="${REPLICAS}" || failed=1
+    kubectl -n "${SYSTEM_NAMESPACE}" scale deployment "$deployment" --replicas="${REPLICAS}" || fail_test
   done
 }
 
 function disable_chaosduck() {
-  kubectl -n "${SYSTEM_NAMESPACE}" scale deployment "chaosduck" --replicas=0 || failed=1
+  kubectl -n "${SYSTEM_NAMESPACE}" scale deployment "chaosduck" --replicas=0 || fail_test
 }
 
 function enable_chaosduck() {
-  kubectl -n "${SYSTEM_NAMESPACE}" scale deployment "chaosduck" --replicas=1 || failed=1
+  kubectl -n "${SYSTEM_NAMESPACE}" scale deployment "chaosduck" --replicas=1 || fail_test
 }
diff --git a/test/e2e-tests.sh b/test/e2e-tests.sh
index 2881f1e25..9a647a35c 100755
--- a/test/e2e-tests.sh
+++ b/test/e2e-tests.sh
@@ -62,15 +62,15 @@ if (( HTTPS )); then
 fi
 
 # Enable allow-zero-initial-scale before running e2e tests (for test/e2e/initial_scale_test.go)
-kubectl -n ${SYSTEM_NAMESPACE} patch configmap/config-autoscaler --type=merge --patch='{"data":{"allow-zero-initial-scale":"true"}}' || failed=1
+kubectl -n ${SYSTEM_NAMESPACE} patch configmap/config-autoscaler --type=merge --patch='{"data":{"allow-zero-initial-scale":"true"}}' || fail_test
 
 # Keep the bucket count in sync with test/ha/ha.go
 kubectl -n "${SYSTEM_NAMESPACE}" patch configmap/config-leader-election --type=merge \
-  --patch='{"data":{"buckets": "'${BUCKETS}'"}}' || failed=1
+  --patch='{"data":{"buckets": "'${BUCKETS}'"}}' || fail_test
 
 kubectl patch hpa activator -n "${SYSTEM_NAMESPACE}" \
   --type "merge" \
-  --patch '{"spec": {"minReplicas": '${REPLICAS}', "maxReplicas": '${REPLICAS}'}}' || failed=1
+  --patch '{"spec": {"minReplicas": '${REPLICAS}', "maxReplicas": '${REPLICAS}'}}' || fail_test
 
 # Scale up all of the HA components in knative-serving.
 scale_controlplane "${HA_COMPONENTS[@]}"
@@ -80,7 +80,7 @@ scale_controlplane "${HA_COMPONENTS[@]}"
 kubectl -n ${SYSTEM_NAMESPACE} delete leases --all
 
 # Wait for a new leader Controller to prevent race conditions during service reconciliation
-wait_for_leader_controller || failed=1
+wait_for_leader_controller || fail_test
 
 # Dump the leases post-setup.
 header "Leaders"
diff --git a/vendor/knative.dev/pkg/metrics/metricstest/resource_metrics.go b/vendor/knative.dev/pkg/metrics/metricstest/resource_metrics.go
new file mode 100644
index 000000000..eeaeda6d7
--- /dev/null
+++ b/vendor/knative.dev/pkg/metrics/metricstest/resource_metrics.go
@@ -0,0 +1,315 @@
+/*
+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 metricstest simplifies some of the common boilerplate around testing
+// metrics exports. It should work with or without the code in metrics, but this
+// code particularly knows how to deal with metrics which are exported for
+// multiple Resources in the same process.
+package metricstest
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"go.opencensus.io/metric/metricdata"
+	"go.opencensus.io/metric/metricproducer"
+	"go.opencensus.io/resource"
+)
+
+// Value provides a simplified implementation of a metric Value suitable for
+// easy testing.
+type Value struct {
+	Tags map[string]string
+	// union interface, only one of these will be set
+	Int64        *int64
+	Float64      *float64
+	Distribution *metricdata.Distribution
+}
+
+// Metric provides a simplified (for testing) implementation of a metric report
+// for a given metric name in a given Resource.
+type Metric struct {
+	// Name is the exported name of the metric, probably from the View's name.
+	Name string
+	// Unit is the units of measure of the metric. This is only checked for
+	// equality if Unit is non-empty or VerifyMetadata is true on both Metrics.
+	Unit metricdata.Unit
+	// Type is the type of measurement represented by the metric. This is only
+	// checked for equality if VerifyMetadata is true on both Metrics.
+	Type metricdata.Type
+
+	// Resource is the reported Resource (if any) for this metric. This is only
+	// checked for equality if Resource is non-nil or VerifyResource is true on
+	// both Metrics.
+	Resource *resource.Resource
+
+	// Values contains the values recorded for different Key=Value Tag
+	// combinations. Value is checked for equality if present.
+	Values []Value
+
+	// Equality testing/validation settings on the Metric. These are used to
+	// allow simple construction and usage with github.com/google/go-cmp/cmp
+
+	// VerifyMetadata makes Equal compare Unit and Type if it is true on both
+	// Metrics.
+	VerifyMetadata bool
+	// VerifyResource makes Equal compare Resource if it is true on Metrics with
+	// nil Resource. Metrics with non-nil Resource are always compared.
+	VerifyResource bool
+}
+
+// NewMetric creates a Metric from a metricdata.Metric, which is designed for
+// compact wire representation.
+func NewMetric(metric *metricdata.Metric) Metric {
+	value := Metric{
+		Name:     metric.Descriptor.Name,
+		Unit:     metric.Descriptor.Unit,
+		Type:     metric.Descriptor.Type,
+		Resource: metric.Resource,
+
+		VerifyMetadata: true,
+		VerifyResource: true,
+
+		Values: make([]Value, 0, len(metric.TimeSeries)),
+	}
+
+	for _, ts := range metric.TimeSeries {
+		tags := make(map[string]string, len(metric.Descriptor.LabelKeys))
+		for i, k := range metric.Descriptor.LabelKeys {
+			tags[k.Key] = ts.LabelValues[i].Value
+		}
+		v := Value{Tags: tags}
+		ts.Points[0].ReadValue(&v)
+		value.Values = append(value.Values, v)
+	}
+
+	return value
+}
+
+// GetMetric returns all values for the named metric.
+func GetMetric(name string) []Metric {
+	producers := metricproducer.GlobalManager().GetAll()
+	retval := make([]Metric, 0, len(producers))
+	for _, p := range producers {
+		for _, m := range p.Read() {
+			if m.Descriptor.Name == name && len(m.TimeSeries) > 0 {
+				retval = append(retval, NewMetric(m))
+			}
+		}
+	}
+	return retval
+}
+
+// GetOneMetric is like GetMetric, but it panics if more than a single Metric is
+// found.
+func GetOneMetric(name string) Metric {
+	m := GetMetric(name)
+	if len(m) != 1 {
+		panic(fmt.Sprint("Got wrong number of metrics:", m))
+	}
+	return m[0]
+}
+
+func genericMetricFactory(name string, v Value, keyvalues ...string) Metric {
+	if len(keyvalues)%2 != 0 {
+		panic("Odd number of arguments to CountMetric")
+	}
+	if v.Tags == nil {
+		v.Tags = make(map[string]string, len(keyvalues)/2)
+	}
+	for i := 0; i < len(keyvalues); i += 2 {
+		v.Tags[keyvalues[i]] = keyvalues[i+1]
+	}
+	return Metric{
+		Name:   name,
+		Values: []Value{v},
+	}
+}
+
+// IntMetric is a shortcut factory for creating an Int64 metric.
+func IntMetric(name string, value int64, keyvalues ...string) Metric {
+	return genericMetricFactory(name, Value{Int64: &value}, keyvalues...)
+}
+
+// FloatMetric is a shortcut factor for creating a Float64 metric
+func FloatMetric(name string, value float64, keyvalues ...string) Metric {
+	return genericMetricFactory(name, Value{Float64: &value}, keyvalues...)
+}
+
+// AssertMetric verifies that the metrics have the specified values.
+func AssertMetric(t *testing.T, values ...Metric) {
+	t.Helper()
+	for _, v := range values {
+		if diff := cmp.Diff(v, GetOneMetric(v.Name)); diff != "" {
+			t.Errorf("Wrong adds (-want +got): %s", diff)
+		}
+	}
+}
+
+// AssertMetricExists verifies that at least one metric values has been reported for
+// each of metric names.
+func AssertMetricExists(t *testing.T, names ...string) {
+	t.Helper()
+	for _, name := range names {
+		if len(GetMetric(name)) == 0 {
+			t.Errorf("No metrics found for %q", name)
+		}
+	}
+}
+
+// AssertNoMetric verifies that no metrics have been reported for any of the
+// metric names.
+func AssertNoMetric(t *testing.T, names ...string) {
+	t.Helper()
+	for _, name := range names {
+		if m := GetMetric(name); len(m) != 0 {
+			t.Error("Found unexpected data for:", m)
+		}
+	}
+}
+
+// VisitFloat64Value implements metricdata.ValueVisitor.
+func (v *Value) VisitFloat64Value(f float64) {
+	v.Float64 = &f
+	v.Int64 = nil
+	v.Distribution = nil
+}
+
+// VisitInt64Value implements metricdata.ValueVisitor.
+func (v *Value) VisitInt64Value(i int64) {
+	v.Int64 = &i
+	v.Float64 = nil
+	v.Distribution = nil
+}
+
+// VisitDistributionValue implements metricdata.ValueVisitor.
+func (v *Value) VisitDistributionValue(d *metricdata.Distribution) {
+	v.Distribution = d
+	v.Int64 = nil
+	v.Float64 = nil
+}
+
+// VisitSummaryValue implements metricdata.ValueVisitor.
+func (v *Value) VisitSummaryValue(*metricdata.Summary) {
+	panic("Attempted to fetch summary value, which we never use!")
+}
+
+// Equal provides a contract for use with github.com/google/go-cmp/cmp. Due to
+// the reflection in cmp, it only works if the type of the two arguments to cmp
+// are the same.
+func (m Metric) Equal(other Metric) bool {
+	if m.Name != other.Name {
+		return false
+	}
+	if (m.Unit != "" || m.VerifyMetadata) && (other.Unit != "" || other.VerifyMetadata) {
+		if m.Unit != other.Unit {
+			return false
+		}
+	}
+	if m.VerifyMetadata && other.VerifyMetadata {
+		if m.Type != other.Type {
+			return false
+		}
+	}
+
+	if (m.Resource != nil || m.VerifyResource) && (other.Resource != nil || other.VerifyResource) {
+		if !cmp.Equal(m.Resource, other.Resource) {
+			return false
+		}
+	}
+
+	if len(m.Values) > 0 && len(other.Values) > 0 {
+		if len(m.Values) != len(other.Values) {
+			return false
+		}
+		myValues := make(map[string]Value, len(m.Values))
+		for _, v := range m.Values {
+			myValues[tagsToString(v.Tags)] = v
+		}
+		for _, v := range other.Values {
+			myV, ok := myValues[tagsToString(v.Tags)]
+			if !ok || !myV.Equal(v) {
+				return false
+			}
+		}
+	}
+
+	return true
+}
+
+// Equal provides a contract for github.com/google/go-cmp/cmp. It compares two
+// values, including deep comparison of Distributions. (Exemplars are
+// intentional not included in the comparison, but other fields are considered).
+func (v Value) Equal(other Value) bool {
+	if len(v.Tags) != len(other.Tags) {
+		return false
+	}
+	for k, v := range v.Tags {
+		if v != other.Tags[k] {
+			return false
+		}
+	}
+	if v.Int64 != nil {
+		return other.Int64 != nil && *v.Int64 == *other.Int64
+	}
+	if v.Float64 != nil {
+		return other.Float64 != nil && *v.Float64 == *other.Float64
+	}
+
+	if v.Distribution != nil {
+		if other.Distribution == nil {
+			return false
+		}
+		if v.Distribution.Count != other.Distribution.Count {
+			return false
+		}
+		if v.Distribution.Sum != other.Distribution.Sum {
+			return false
+		}
+		if v.Distribution.SumOfSquaredDeviation != other.Distribution.SumOfSquaredDeviation {
+			return false
+		}
+		if v.Distribution.BucketOptions != nil {
+			if other.Distribution.BucketOptions == nil {
+				return false
+			}
+			for i, bo := range v.Distribution.BucketOptions.Bounds {
+				if bo != other.Distribution.BucketOptions.Bounds[i] {
+					return false
+				}
+			}
+		}
+		for i, b := range v.Distribution.Buckets {
+			if b.Count != other.Distribution.Buckets[i].Count {
+				return false
+			}
+		}
+	}
+
+	return true
+}
+
+func tagsToString(tags map[string]string) string {
+	pairs := make([]string, 0, len(tags))
+	for k, v := range tags {
+		pairs = append(pairs, fmt.Sprintf("%s=%s", k, v))
+	}
+	sort.Strings(pairs)
+	return strings.Join(pairs, ",")
+}
diff --git a/vendor/knative.dev/pkg/test/logging/logger_writer.go b/vendor/knative.dev/pkg/test/logging/logger_writer.go
deleted file mode 100644
index 92bcc1d83..000000000
--- a/vendor/knative.dev/pkg/test/logging/logger_writer.go
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-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 logging
-
-import (
-	"bytes"
-	"io"
-	"strings"
-)
-
-type loggerWriter struct {
-	prepend string
-	logf    FormatLogger
-
-	buf bytes.Buffer
-}
-
-func NewLoggerWriter(prepend string, logf FormatLogger) io.Writer {
-	return &loggerWriter{
-		prepend: prepend,
-		logf:    logf,
-	}
-}
-
-func (l *loggerWriter) Write(p []byte) (n int, err error) {
-	s := string(p)
-	splitted := strings.Split(s, "\n")
-
-	// Get the remaining of the previous write
-	splitted[0] = l.buf.String() + splitted[0]
-	l.buf.Reset()
-
-	// Write out the lines
-	for i := 0; i < len(splitted)-1; i++ {
-		l.logf(l.prepend + splitted[i])
-	}
-
-	n, err = l.buf.WriteString(splitted[len(splitted)-1])
-	return
-}
diff --git a/vendor/knative.dev/pkg/test/monitoring/monitoring.go b/vendor/knative.dev/pkg/test/monitoring/monitoring.go
index f0bbafb68..2dbaf958c 100644
--- a/vendor/knative.dev/pkg/test/monitoring/monitoring.go
+++ b/vendor/knative.dev/pkg/test/monitoring/monitoring.go
@@ -18,7 +18,6 @@ package monitoring
 
 import (
 	"fmt"
-	"io"
 	"net"
 	"os"
 	"os/exec"
@@ -60,10 +59,10 @@ func Cleanup(pid int) error {
 }
 
 // PortForward sets up local port forward to the pod specified by the "app" label in the given namespace
-func PortForward(logf logging.FormatLogger, podList *v1.PodList, localPort, remotePort int, namespace string, stdout io.Writer, stderr io.Writer) (int, error) {
+func PortForward(logf logging.FormatLogger, podList *v1.PodList, localPort, remotePort int, namespace string) (int, error) {
 	podName := podList.Items[0].Name
 	portFwdCmd := fmt.Sprintf("kubectl port-forward %s %d:%d -n %s", podName, localPort, remotePort, namespace)
-	portFwdProcess, err := executeCmdBackground(logf, stdout, stderr, portFwdCmd)
+	portFwdProcess, err := executeCmdBackground(logf, portFwdCmd)
 
 	if err != nil {
 		return 0, fmt.Errorf("failed to port forward: %w", err)
@@ -74,13 +73,11 @@ func PortForward(logf logging.FormatLogger, podList *v1.PodList, localPort, remo
 }
 
 // RunBackground starts a background process and returns the Process if succeed
-func executeCmdBackground(logf logging.FormatLogger, stdout io.Writer, stderr io.Writer, format string, args ...interface{}) (*os.Process, error) {
+func executeCmdBackground(logf logging.FormatLogger, format string, args ...interface{}) (*os.Process, error) {
 	cmd := fmt.Sprintf(format, args...)
 	logf("Executing command: %s", cmd)
 	parts := strings.Split(cmd, " ")
 	c := exec.Command(parts[0], parts[1:]...) // #nosec
-	c.Stdout = stdout
-	c.Stderr = stderr
 	if err := c.Start(); err != nil {
 		return nil, fmt.Errorf("%s command failed: %w", cmd, err)
 	}
diff --git a/vendor/knative.dev/pkg/test/zipkin/util.go b/vendor/knative.dev/pkg/test/zipkin/util.go
index 51ecf5c59..b5b574911 100644
--- a/vendor/knative.dev/pkg/test/zipkin/util.go
+++ b/vendor/knative.dev/pkg/test/zipkin/util.go
@@ -126,15 +126,7 @@ func SetupZipkinTracing(kubeClientset *kubernetes.Clientset, logf logging.Format
 			return
 		}
 
-		zipkinPortForwardPID, e = monitoring.PortForward(
-			logf,
-			zipkinPods,
-			ZipkinPort,
-			zipkinRemotePort,
-			zipkinNamespace,
-			logging.NewLoggerWriter("zipkin-port-forward-stdout", logf),
-			logging.NewLoggerWriter("zipkin-port-forward-stderr", logf),
-		)
+		zipkinPortForwardPID, e = monitoring.PortForward(logf, zipkinPods, ZipkinPort, zipkinRemotePort, zipkinNamespace)
 		if e != nil {
 			err = fmt.Errorf("error starting kubectl port-forward command: %w", err)
 			return