Repository: knative/serving
Pull request #7985 (2020-05-19 01:37:13)

Use common logic  to handle observedgeneration for Revision
<!--
Request Prow to automatically lint any go code in this PR:

/lint
-->

Issue #4937

<!-- Please include the 'why' behind your changes if no issue exists -->
## Proposed Changes

- Use the generated code logic for automatically bumping generation
    - Note: I'm writing these on a per-resource basis so we can see e2e tests separately for each resource. This might make it easier than doing one larger PR for all serving resources.

**Release Note**

<!-- Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". -->

```release-note

```


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

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

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

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

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

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["mattmoor"]} -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-serving-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/apis/serving/v1/revision_types.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/7985/pull-knative-serving-go-coverage/1262558025684619266/artifacts/line-cov.html#file0) | 0.0% | 100.0% | 100.0
[pkg/reconciler/revision/revision.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/7985/pull-knative-serving-go-coverage/1262558025684619266/artifacts/line-cov.html#file1) | 98.1% | 97.9% | -0.2


Patch:
diff --git a/OWNERS b/OWNERS
index e90244b19..ef04db24b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,7 +1,4 @@
 # The OWNERS file is used by prow to automatically merge approved PRs.
 
 approvers:
-- evankanderson
-- mattmoor
-- mdemirhan
-- vaikas
+- serving-wg-leads
diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES
index 7a301f00b..662e1c14d 100644
--- a/OWNERS_ALIASES
+++ b/OWNERS_ALIASES
@@ -1,29 +1,42 @@
 aliases:
   serving-api-approvers:
-  - dgerd
   - dprotaso
   - markusthoemmes
   - mattmoor
   - tcnghia
   - vagababov
   serving-api-reviewers:
-  - dgerd
   - dprotaso
   - markusthoemmes
   - mattmoor
   - tcnghia
   - vagababov
 
+  toc:
+  - evankanderson
+  - grantr
+  - markusthoemmes
+  - mattmoor
+  - tcnghia
+
+  wg-leads:
+  - dprotaso        # API
+  - markusthoemmes  # Scaling
+  - mattmoor        # API
+  - mdemirhan       # Network
+  - tcnghia         # Network
+  - vagababov       # Scaling
+
   # TOC + WG leads
   serving-wg-leads:
-  - evankanderson  # TOC
-  - mattmoor       # TOC/Serving
-  - vaikas         # TOC
-  - dprotaso       # Serving
-  - markusthoemmes # Scaling
-  - mdemirhan      # Networking
-  - tcnghia        # Networking
-  - vagababov      # Scaling
+  - dprotaso        # API
+  - evankanderson   # TOC
+  - grantr          # TOC
+  - markusthoemmes  # Scaling/TOC
+  - mattmoor        # API/TOC
+  - mdemirhan       # Network
+  - tcnghia         # Network/TOC
+  - vagababov       # Scaling
 
   autoscaling-approvers:
   - markusthoemmes
diff --git a/cmd/autoscaler/main.go b/cmd/autoscaler/main.go
index 039e1c1f3..2f2ac4e8c 100644
--- a/cmd/autoscaler/main.go
+++ b/cmd/autoscaler/main.go
@@ -225,11 +225,17 @@ func uniScalerFactoryFunc(endpointsInformer corev1informers.EndpointsInformer,
 func statsScraperFactoryFunc(endpointsLister corev1listers.EndpointsLister,
 	podLister corev1listers.PodLister) asmetrics.StatsScraperFactory {
 	return func(metric *av1alpha1.Metric, logger *zap.SugaredLogger) (asmetrics.StatsScraper, error) {
-		podCounter := resources.NewScopedEndpointsCounter(
-			endpointsLister, metric.Namespace, metric.Spec.ScrapeTarget)
-		// TODO(vagababov): while metric name == revision name, we should utilize the proper
-		// values from the labels.
-		podAccessor := resources.NewPodAccessor(podLister, metric.Namespace, metric.Name)
+		if metric.Spec.ScrapeTarget == "" {
+			return nil, nil
+		}
+
+		revisionName := metric.Labels[serving.RevisionLabelKey]
+		if revisionName == "" {
+			return nil, fmt.Errorf("label %q not found or empty in Metric %s", serving.RevisionLabelKey, metric.Name)
+		}
+
+		podCounter := resources.NewScopedEndpointsCounter(endpointsLister, metric.Namespace, metric.Spec.ScrapeTarget)
+		podAccessor := resources.NewPodAccessor(podLister, metric.Namespace, revisionName)
 		return asmetrics.NewStatsScraper(metric, podCounter, podAccessor, logger)
 	}
 }
diff --git a/cmd/queue/main.go b/cmd/queue/main.go
index e533e4c66..95ef861fc 100644
--- a/cmd/queue/main.go
+++ b/cmd/queue/main.go
@@ -355,7 +355,7 @@ func main() {
 	reportTicker := time.NewTicker(reportingPeriod)
 	defer reportTicker.Stop()
 
-	queue.NewStats(time.Now(), reqChan, reportTicker.C, promStatReporter.Report)
+	go queue.ReportStats(time.Now(), reqChan, reportTicker.C, promStatReporter.Report)
 
 	// Setup probe to run for checking user-application healthiness.
 	probe := buildProbe(env.ServingReadinessProbe)
diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go
index fdf3a6007..1c59a288f 100644
--- a/cmd/webhook/main.go
+++ b/cmd/webhook/main.go
@@ -225,10 +225,11 @@ func NewConversionController(ctx context.Context, cmw configmap.Watcher) *contro
 }
 
 func main() {
+
 	// Set up a signal context with our webhook options
 	ctx := webhook.WithOptions(signals.NewContext(), webhook.Options{
 		ServiceName: "webhook",
-		Port:        8443,
+		Port:        webhook.PortFromEnv(8443),
 		SecretName:  "webhook-certs",
 	})
 
diff --git a/config/core/deployments/webhook.yaml b/config/core/deployments/webhook.yaml
index 663ab019e..eb361b147 100644
--- a/config/core/deployments/webhook.yaml
+++ b/config/core/deployments/webhook.yaml
@@ -42,11 +42,11 @@ spec:
 
         resources:
           requests:
-            cpu: 20m
-            memory: 20Mi
+            cpu: 100m
+            memory: 100Mi
           limits:
-            cpu: 200m
-            memory: 200Mi
+            cpu: 500m
+            memory: 500Mi
 
         env:
         - name: SYSTEM_NAMESPACE
@@ -57,6 +57,8 @@ spec:
           value: config-logging
         - name: CONFIG_OBSERVABILITY_NAME
           value: config-observability
+        - name: WEBHOOK_PORT
+          value: "8443"
 
         # TODO(https://github.com/knative/pkg/pull/953): Remove stackdriver specific config
         - name: METRICS_DOMAIN
diff --git a/config/core/webhooks/configmap-validation.yaml b/config/core/webhooks/configmap-validation.yaml
index 5da98fa56..18f9461f4 100644
--- a/config/core/webhooks/configmap-validation.yaml
+++ b/config/core/webhooks/configmap-validation.yaml
@@ -32,4 +32,4 @@ webhooks:
     matchExpressions:
     - key: serving.knative.dev/release
       operator: Exists
-  timeoutSeconds: 2
+  timeoutSeconds: 10
diff --git a/config/core/webhooks/defaulting.yaml b/config/core/webhooks/defaulting.yaml
index a29a0fa9d..18b66f43f 100644
--- a/config/core/webhooks/defaulting.yaml
+++ b/config/core/webhooks/defaulting.yaml
@@ -28,4 +28,4 @@ webhooks:
   failurePolicy: Fail
   sideEffects: None
   name: webhook.serving.knative.dev
-  timeoutSeconds: 2
+  timeoutSeconds: 10
diff --git a/config/core/webhooks/resource-validation.yaml b/config/core/webhooks/resource-validation.yaml
index 55001151d..2e7c9af8b 100644
--- a/config/core/webhooks/resource-validation.yaml
+++ b/config/core/webhooks/resource-validation.yaml
@@ -28,4 +28,4 @@ webhooks:
   failurePolicy: Fail
   sideEffects: None
   name: validation.webhook.serving.knative.dev
-  timeoutSeconds: 2
+  timeoutSeconds: 10
diff --git a/go.mod b/go.mod
index 470e81741..aab2a0681 100644
--- a/go.mod
+++ b/go.mod
@@ -43,9 +43,9 @@ require (
 	k8s.io/code-generator v0.18.0
 	k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
 	k8s.io/metrics v0.17.2
-	knative.dev/caching v0.0.0-20200515173804-a2fc9c5de2ef
-	knative.dev/pkg v0.0.0-20200517014302-3c29a1ce8906
-	knative.dev/test-infra v0.0.0-20200515184601-7a28f47cdbcb
+	knative.dev/caching v0.0.0-20200521155757-e78d17bc250e
+	knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7
+	knative.dev/test-infra v0.0.0-20200519161858-554a95a37986
 )
 
 replace (
diff --git a/go.sum b/go.sum
index bb516d99b..42ce87121 100644
--- a/go.sum
+++ b/go.sum
@@ -1430,26 +1430,25 @@ k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl
 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-20190719140829-2032732871ff/go.mod h1:dHXFU6CGlLlbzaWc32g80cR92iuBSpsslDNBWI8C7eg=
-knative.dev/caching v0.0.0-20200515173804-a2fc9c5de2ef h1:rX5pdlCet244RNg1SNbrO137EyAn7lMvuFu25R4YJfI=
-knative.dev/caching v0.0.0-20200515173804-a2fc9c5de2ef/go.mod h1:n5kQRfe9itUy2wpuik7d1CW6sXyPw0OJhs9xlmwB0H4=
+knative.dev/caching v0.0.0-20200521155757-e78d17bc250e h1:LN2scNClE8G4a9XzM0AGZkK/rT56A0ZlweaMjNzPMD0=
+knative.dev/caching v0.0.0-20200521155757-e78d17bc250e/go.mod h1:bsKPIG2XZFYL8uw5BAxg7EMBLp0vNpu9jZIGus8HUPU=
 knative.dev/eventing-contrib v0.6.1-0.20190723221543-5ce18048c08b/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
 knative.dev/pkg v0.0.0-20191101194912-56c2594e4f11/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
 knative.dev/pkg v0.0.0-20191111150521-6d806b998379/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
 knative.dev/pkg v0.0.0-20200428194351-90fc61bae7f7/go.mod h1:o+e8OVEJKIuvXPsGVPIautjXgs05xbos7G+QMRjuUps=
 knative.dev/pkg v0.0.0-20200505191044-3da93ebb24c2 h1:Qu2NlOHb9p3g+CSL/ok9+FySowN60URFEKRSXfWtDv4=
 knative.dev/pkg v0.0.0-20200505191044-3da93ebb24c2/go.mod h1:Q6sL35DdGs8hIQZKdaCXJGgY8f90BmNBKSb8z6d/BTM=
-knative.dev/pkg v0.0.0-20200514052058-c75d324f8b8b h1:lM0uwBgNcS9S4cWLqIS6Fvjt3kR/6oAPXgbXfEzBJwU=
-knative.dev/pkg v0.0.0-20200514052058-c75d324f8b8b/go.mod h1:tMOHGbxtRz8zYFGEGpV/bpoTEM1o89MwYFC4YJXl3GY=
 knative.dev/pkg v0.0.0-20200515002500-16d7b963416f/go.mod h1:tMOHGbxtRz8zYFGEGpV/bpoTEM1o89MwYFC4YJXl3GY=
-knative.dev/pkg v0.0.0-20200517014302-3c29a1ce8906 h1:Xo/FFyVEZic+75UZJ0MJMuXgdfh+99YixIMAd3nCVys=
-knative.dev/pkg v0.0.0-20200517014302-3c29a1ce8906/go.mod h1:GinFtW8LEi9rzCkzCMHAIlAnZjP1RzauxwhRAI3rimI=
+knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7 h1:9S2r59HZJF9nKvoRLg5zJzx6XpVlVyvVRqz/C/h6h2s=
+knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7/go.mod h1:QgNZTxnwpB/oSpNcfnLVlw+WpEwwyKAvJlvR3hgeltA=
 knative.dev/test-infra v0.0.0-20200407185800-1b88cb3b45a5/go.mod h1:xcdUkMJrLlBswIZqL5zCuBFOC22WIPMQoVX1L35i0vQ=
 knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55/go.mod h1:WqF1Azka+FxPZ20keR2zCNtiQA1MP9ZB4BH4HuI+SIU=
 knative.dev/test-infra v0.0.0-20200513011557-d03429a76034 h1:JxqONCZVS7or+Fv3ebVQoipuIBH7Ig3Qbx170hgIF+A=
 knative.dev/test-infra v0.0.0-20200513011557-d03429a76034/go.mod h1:aMif0KXL4g19YCYwsy4Ocjjz5xgPlseYV+B95Oo4JGE=
-knative.dev/test-infra v0.0.0-20200514223200-ef4fd3ad398f/go.mod h1:+uml4upluwrnWHRSezi4WVcKJ0OJHynF16Ot7fu0ky4=
-knative.dev/test-infra v0.0.0-20200515184601-7a28f47cdbcb h1:g1NE1OdGOh8vgLaKhi6O/fXFhlewq7wnL2EQNLKe+bE=
-knative.dev/test-infra v0.0.0-20200515184601-7a28f47cdbcb/go.mod h1:A5b2OAXTOeHT3hHhVQm3dmtbuWvIDP7qzgtqxA3/2pE=
+knative.dev/test-infra v0.0.0-20200519015156-82551620b0a9 h1:kKfV3QWsxugwXsqgjFd72MjZeAHJ381dWqAxH2KMknc=
+knative.dev/test-infra v0.0.0-20200519015156-82551620b0a9/go.mod h1:A5b2OAXTOeHT3hHhVQm3dmtbuWvIDP7qzgtqxA3/2pE=
+knative.dev/test-infra v0.0.0-20200519161858-554a95a37986 h1:ZDy43jkWPQ75d4l4DGy+ENQIXlNcnHIh4tB6XxgovNc=
+knative.dev/test-infra v0.0.0-20200519161858-554a95a37986/go.mod h1:LeNa1Wvn47efeQUkpkn3XG7Fx9Ga+rhAP13SZyjaEGg=
 modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
diff --git a/hack/generate-yamls.sh b/hack/generate-yamls.sh
index 6cb1e66d0..4343957b2 100755
--- a/hack/generate-yamls.sh
+++ b/hack/generate-yamls.sh
@@ -67,8 +67,6 @@ readonly MONITORING_TRACE_JAEGER_YAML=${YAML_OUTPUT_DIR}/monitoring-tracing-jaeg
 readonly MONITORING_TRACE_JAEGER_IN_MEM_YAML=${YAML_OUTPUT_DIR}/monitoring-tracing-jaeger-in-mem.yaml
 readonly MONITORING_LOG_ELASTICSEARCH_YAML=${YAML_OUTPUT_DIR}/monitoring-logs-elasticsearch.yaml
 
-readonly NET_ISTIO_YAML=${YAML_OUTPUT_DIR}/net-istio.yaml
-
 # Flags for all ko commands
 KO_YAML_FLAGS="-P"
 [[ "${KO_DOCKER_REPO}" != gcr.io/* ]] && KO_YAML_FLAGS=""
@@ -76,14 +74,10 @@ readonly KO_YAML_FLAGS="${KO_YAML_FLAGS} ${KO_FLAGS} --strict"
 
 if [[ -n "${TAG}" ]]; then
   LABEL_YAML_CMD=(sed -e "s|serving.knative.dev/release: devel|serving.knative.dev/release: \"${TAG}\"|")
-  NET_ISTIO_YAML_LINK=https://github.com/knative/net-istio/releases/download/${TAG}/release.yaml
 else
   LABEL_YAML_CMD=(cat)
-  NET_ISTIO_YAML_LINK=https://storage.googleapis.com/knative-nightly/net-istio/latest/release.yaml
 fi
 
-readonly NET_ISTIO_YAML_LINK
-
 : ${KO_DOCKER_REPO:="ko.local"}
 export KO_DOCKER_REPO
 
@@ -102,16 +96,16 @@ ko resolve ${KO_YAML_FLAGS} -f config/core/resources/ -f config/300-imagecache.y
 # Create hpa-class autoscaling related yaml
 ko resolve ${KO_YAML_FLAGS} -f config/hpa-autoscaling/ | "${LABEL_YAML_CMD[@]}" > "${SERVING_HPA_YAML}"
 
+# Create cert-manager related yaml
+cat "./third_party/cert-manager-0.12.0/net-certmanager.yaml" > "${SERVING_CERT_MANAGER_YAML}"
+
 # Create nscert related yaml
 ko resolve ${KO_YAML_FLAGS} -f config/namespace-wildcard-certs | "${LABEL_YAML_CMD[@]}" > "${SERVING_NSCERT_YAML}"
 
-# Download the net-istio.yaml
-wget "${NET_ISTIO_YAML_LINK}" -O "${NET_ISTIO_YAML}"
-
 # Create serving.yaml with all of the default components
 cat "${SERVING_CORE_YAML}" > "${SERVING_YAML}"
 cat "${SERVING_HPA_YAML}" >> "${SERVING_YAML}"
-cat "${NET_ISTIO_YAML}" >> "${SERVING_YAML}"
+cat "./third_party/net-istio.yaml" >> "${SERVING_YAML}"
 
 # Metrics via Prometheus & Grafana
 ko resolve ${KO_YAML_FLAGS} -R \
diff --git a/hack/update-deps.sh b/hack/update-deps.sh
index b277dd3ff..1774b9386 100755
--- a/hack/update-deps.sh
+++ b/hack/update-deps.sh
@@ -27,7 +27,7 @@ cd ${ROOT_DIR}
 export GO111MODULE=on
 
 # This controls the release branch we track.
-VERSION="master"
+VERSION="release-0.15"
 
 # The list of dependencies that we track at HEAD and periodically
 # float forward in this repository.
diff --git a/pkg/apis/autoscaling/annotation_validation.go b/pkg/apis/autoscaling/annotation_validation.go
index bf908fd95..518faae0a 100644
--- a/pkg/apis/autoscaling/annotation_validation.go
+++ b/pkg/apis/autoscaling/annotation_validation.go
@@ -31,9 +31,16 @@ func getIntGE0(m map[string]string, k string) (int64, *apis.FieldError) {
 		return 0, nil
 	}
 	i, err := strconv.ParseInt(v, 10, 32)
-	if err != nil || i < 0 {
-		return 0, apis.ErrOutOfBoundsValue(v, 1, math.MaxInt32, k)
+	if err == nil && i < 0 {
+		return 0, apis.ErrOutOfBoundsValue(v, 0, math.MaxInt32, k)
 	}
+	if err != nil {
+		if nerr, ok := err.(*strconv.NumError); ok && nerr.Err == strconv.ErrRange {
+			return 0, apis.ErrOutOfBoundsValue(v, 0, math.MaxInt32, k)
+		}
+		return 0, apis.ErrInvalidValue(v, k)
+	}
+
 	return i, nil
 }
 
@@ -43,8 +50,8 @@ func ValidateAnnotations(allowInitScaleZero bool, anns map[string]string) *apis.
 		return nil
 	}
 	return validateMinMaxScale(anns).Also(validateFloats(anns)).
-		Also(validateWindows(anns).Also(validateMetric(anns).
-			Also(validateInitialScale(allowInitScaleZero, anns))))
+		Also(validateWindow(anns).Also(validateLastPodRetention(anns)).
+			Also(validateMetric(anns).Also(validateInitialScale(allowInitScaleZero, anns))))
 }
 
 func validateFloats(annotations map[string]string) *apis.FieldError {
@@ -61,8 +68,8 @@ func validateFloats(annotations map[string]string) *apis.FieldError {
 		if fv, err := strconv.ParseFloat(v, 64); err != nil {
 			errs = errs.Also(apis.ErrInvalidValue(v, PanicThresholdPercentageAnnotationKey))
 		} else if fv < PanicThresholdPercentageMin || fv > PanicThresholdPercentageMax {
-			errs = errs.Also(apis.ErrOutOfBoundsValue(v, PanicThresholdPercentageMin, PanicThresholdPercentageMax,
-				PanicThresholdPercentageAnnotationKey))
+			errs = errs.Also(apis.ErrOutOfBoundsValue(v, PanicThresholdPercentageMin,
+				PanicThresholdPercentageMax, PanicThresholdPercentageAnnotationKey))
 		}
 	}
 
@@ -88,14 +95,27 @@ func validateFloats(annotations map[string]string) *apis.FieldError {
 	return errs
 }
 
-func validateWindows(annotations map[string]string) *apis.FieldError {
+func validateLastPodRetention(annotations map[string]string) *apis.FieldError {
+	var errs *apis.FieldError
+	if w, ok := annotations[ScaleToZeroPodRetentionPeriodKey]; ok {
+		if d, err := time.ParseDuration(w); err != nil {
+			errs = apis.ErrInvalidValue(w, ScaleToZeroPodRetentionPeriodKey)
+		} else if d < 0 || d > WindowMax {
+			// Since we disallow windows longer than WindowMax, so we should limit this
+			// as well.
+			errs = apis.ErrOutOfBoundsValue(w, 0*time.Second, WindowMax, ScaleToZeroPodRetentionPeriodKey)
+		}
+	}
+	return errs
+}
+
+func validateWindow(annotations map[string]string) *apis.FieldError {
 	var errs *apis.FieldError
 	if w, ok := annotations[WindowAnnotationKey]; ok {
 		if annotations[ClassAnnotationKey] == HPA && annotations[MetricAnnotationKey] == CPU {
 			return apis.ErrInvalidKeyName(WindowAnnotationKey, fmt.Sprintf("%s for %s %s", HPA, MetricAnnotationKey, CPU))
 		}
-		d, err := time.ParseDuration(w)
-		if err != nil {
+		if d, err := time.ParseDuration(w); err != nil {
 			errs = apis.ErrInvalidValue(w, WindowAnnotationKey)
 		} else if d < WindowMin || d > WindowMax {
 			errs = apis.ErrOutOfBoundsValue(w, WindowMin, WindowMax, WindowAnnotationKey)
@@ -105,11 +125,7 @@ func validateWindows(annotations map[string]string) *apis.FieldError {
 }
 
 func validateMinMaxScale(annotations map[string]string) *apis.FieldError {
-	var errs *apis.FieldError
-
-	min, err := getIntGE0(annotations, MinScaleAnnotationKey)
-	errs = errs.Also(err)
-
+	min, errs := getIntGE0(annotations, MinScaleAnnotationKey)
 	max, err := getIntGE0(annotations, MaxScaleAnnotationKey)
 	errs = errs.Also(err)
 
diff --git a/pkg/apis/autoscaling/register.go b/pkg/apis/autoscaling/register.go
index 91438920a..2c26d4dd5 100644
--- a/pkg/apis/autoscaling/register.go
+++ b/pkg/apis/autoscaling/register.go
@@ -71,6 +71,13 @@ const (
 	// below 1.
 	TargetMin = 0.01
 
+	// ScaleToZeroPodRetentionPeriodKey is the annotation to specify the minimum
+	// time duration the last pod will not be scaled down, after autoscaler has
+	// made the decision to scale to 0.
+	// This is the per-revision setting compliment to the
+	// scale-to-zero-pod-retention-period global setting.
+	ScaleToZeroPodRetentionPeriodKey = GroupName + "/scaleToZeroPodRetentionPeriod"
+
 	// WindowAnnotationKey is the annotation to specify the time
 	// interval over which to calculate the average metric.  Larger
 	// values result in more smoothing. For example,
diff --git a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go
index 4a5d187a7..7bca3dd52 100644
--- a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go
+++ b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go
@@ -112,16 +112,26 @@ func (pa *PodAutoscaler) TargetBC() (float64, bool) {
 	return pa.annotationFloat64(autoscaling.TargetBurstCapacityKey)
 }
 
-// Window returns the window annotation value or false if not present.
-func (pa *PodAutoscaler) Window() (window time.Duration, ok bool) {
-	// The value is validated in the webhook.
-	if s, ok := pa.Annotations[autoscaling.WindowAnnotationKey]; ok {
+func (pa *PodAutoscaler) annotationDuration(key string) (time.Duration, bool) {
+	if s, ok := pa.Annotations[key]; ok {
 		d, err := time.ParseDuration(s)
 		return d, err == nil
 	}
 	return 0, false
 }
 
+// ScaleToZeroPodRetention returns the window annotation value or false if not present.
+func (pa *PodAutoscaler) ScaleToZeroPodRetention() (time.Duration, bool) {
+	// The value is validated in the webhook.
+	return pa.annotationDuration(autoscaling.ScaleToZeroPodRetentionPeriodKey)
+}
+
+// Window returns the window annotation value or false if not present.
+func (pa *PodAutoscaler) Window() (time.Duration, bool) {
+	// The value is validated in the webhook.
+	return pa.annotationDuration(autoscaling.WindowAnnotationKey)
+}
+
 // PanicWindowPercentage returns panic window annotation value or false if not present.
 func (pa *PodAutoscaler) PanicWindowPercentage() (percentage float64, ok bool) {
 	// The value is validated in the webhook.
diff --git a/pkg/apis/serving/k8s_lifecycle.go b/pkg/apis/serving/k8s_lifecycle.go
index 23f7bc723..de5d60c14 100644
--- a/pkg/apis/serving/k8s_lifecycle.go
+++ b/pkg/apis/serving/k8s_lifecycle.go
@@ -40,7 +40,7 @@ const (
 	DeploymentConditionProgressing apis.ConditionType = "Progressing"
 )
 
-// transformDeploymentStatus transforms the kubernetes DeploymentStatus into a
+// TransformDeploymentStatus transforms the Kubernetes DeploymentStatus into a
 // duckv1.Status that uses ConditionSets to propagate failures and expose
 // a top-level happy state, per our condition conventions.
 func TransformDeploymentStatus(ds *appsv1.DeploymentStatus) *duckv1.Status {
diff --git a/pkg/apis/serving/v1/configuration_types.go b/pkg/apis/serving/v1/configuration_types.go
index fbf1ae155..cad8cfdf9 100644
--- a/pkg/apis/serving/v1/configuration_types.go
+++ b/pkg/apis/serving/v1/configuration_types.go
@@ -24,7 +24,7 @@ import (
 )
 
 // +genclient
-// +genreconciler
+// +genreconciler:krshapedlogic=true
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // Configuration represents the "floating HEAD" of a linear history of Revisions.
diff --git a/pkg/apis/serving/v1/revision_lifecycle.go b/pkg/apis/serving/v1/revision_lifecycle.go
index ca1b69da5..1b7649c3b 100644
--- a/pkg/apis/serving/v1/revision_lifecycle.go
+++ b/pkg/apis/serving/v1/revision_lifecycle.go
@@ -136,9 +136,6 @@ func (rs *RevisionStatus) MarkResourcesAvailableUnknown(reason, message string)
 func (rs *RevisionStatus) PropagateDeploymentStatus(original *appsv1.DeploymentStatus) {
 	ds := serving.TransformDeploymentStatus(original)
 	cond := ds.GetCondition(serving.DeploymentConditionReady)
-	if cond == nil {
-		return
-	}
 
 	m := revisionCondSet.Manage(rs)
 	switch cond.Status {
diff --git a/pkg/apis/serving/v1/revision_types.go b/pkg/apis/serving/v1/revision_types.go
index dbad280b1..72fc15fd4 100644
--- a/pkg/apis/serving/v1/revision_types.go
+++ b/pkg/apis/serving/v1/revision_types.go
@@ -25,7 +25,7 @@ import (
 )
 
 // +genclient
-// +genreconciler
+// +genreconciler:krshapedlogic=true
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // Revision is an immutable snapshot of code and configuration.  A revision
@@ -139,7 +139,7 @@ type RevisionStatus struct {
 	// may be empty if the image comes from a registry listed to skip resolution.
 	// If multiple containers specified then DeprecatedImageDigest holds the digest
 	// for serving container.
-	// DEPRECATED Use ContainerStatuses instead.
+	// DEPRECATED: Use ContainerStatuses instead.
 	// TODO(savitaashture) Remove deprecatedImageDigest.
 	// ref https://kubernetes.io/docs/reference/using-api/deprecation-policy for deprecation.
 	// +optional
diff --git a/pkg/apis/serving/v1/route_lifecycle.go b/pkg/apis/serving/v1/route_lifecycle.go
index 1942731f5..81de8ed8a 100644
--- a/pkg/apis/serving/v1/route_lifecycle.go
+++ b/pkg/apis/serving/v1/route_lifecycle.go
@@ -29,6 +29,7 @@ import (
 var routeCondSet = apis.NewLivingConditionSet(
 	RouteConditionAllTrafficAssigned,
 	RouteConditionIngressReady,
+	RouteConditionCertificateProvisioned,
 )
 
 // GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface.
@@ -104,43 +105,41 @@ func (rs *RouteStatus) MarkMissingTrafficTarget(kind, name string) {
 }
 
 func (rs *RouteStatus) MarkCertificateProvisionFailed(name string) {
-	routeCondSet.Manage(rs).SetCondition(apis.Condition{
-		Type:     RouteConditionCertificateProvisioned,
-		Status:   corev1.ConditionFalse,
-		Severity: apis.ConditionSeverityWarning,
-		Reason:   "CertificateProvisionFailed",
-		Message:  fmt.Sprintf("Certificate %s fails to be provisioned.", name),
-	})
+	routeCondSet.Manage(rs).MarkFalse(RouteConditionCertificateProvisioned,
+		"CertificateProvisionFailed",
+		"Certificate %s fails to be provisioned.", name)
 }
 
 func (rs *RouteStatus) MarkCertificateReady(name string) {
-	routeCondSet.Manage(rs).SetCondition(apis.Condition{
-		Type:     RouteConditionCertificateProvisioned,
-		Status:   corev1.ConditionTrue,
-		Severity: apis.ConditionSeverityWarning,
-		Reason:   "CertificateReady",
-		Message:  fmt.Sprintf("Certificate %s is successfully provisioned", name),
-	})
+	routeCondSet.Manage(rs).MarkTrue(RouteConditionCertificateProvisioned)
 }
 
 func (rs *RouteStatus) MarkCertificateNotReady(name string) {
-	routeCondSet.Manage(rs).SetCondition(apis.Condition{
-		Type:     RouteConditionCertificateProvisioned,
-		Status:   corev1.ConditionUnknown,
-		Severity: apis.ConditionSeverityWarning,
-		Reason:   "CertificateNotReady",
-		Message:  fmt.Sprintf("Certificate %s is not ready.", name),
-	})
+	routeCondSet.Manage(rs).MarkUnknown(RouteConditionCertificateProvisioned,
+		"CertificateNotReady",
+		"Certificate %s is not ready.", name)
 }
 
 func (rs *RouteStatus) MarkCertificateNotOwned(name string) {
-	routeCondSet.Manage(rs).SetCondition(apis.Condition{
-		Type:     RouteConditionCertificateProvisioned,
-		Status:   corev1.ConditionFalse,
-		Severity: apis.ConditionSeverityWarning,
-		Reason:   "CertificateNotOwned",
-		Message:  fmt.Sprintf("There is an existing certificate %s that we don't own.", name),
-	})
+	routeCondSet.Manage(rs).MarkFalse(RouteConditionCertificateProvisioned,
+		"CertificateNotOwned",
+		"There is an existing certificate %s that we don't own.", name)
+}
+
+// MarkAutoTLSNotEnabled sets RouteConditionCertificateProvisioned to true when
+// certificate config such as autoTLS is not enabled.
+func (rs *RouteStatus) MarkAutoTLSNotEnabled() {
+	routeCondSet.Manage(rs).MarkTrueWithReason(RouteConditionCertificateProvisioned,
+		"AutoTLSNotEnabled",
+		"autoTLS is not enabled")
+}
+
+// MarkHTTPDowngrade sets RouteConditionCertificateProvisioned to true when plain
+// HTTP is enabled even when Certificated is not ready.
+func (rs *RouteStatus) MarkHTTPDowngrade(name string) {
+	routeCondSet.Manage(rs).MarkTrueWithReason(RouteConditionCertificateProvisioned,
+		"HTTPDowngrade",
+		"Certificate %s is not ready downgrade HTTP.", name)
 }
 
 // PropagateIngressStatus update RouteConditionIngressReady condition
diff --git a/pkg/apis/serving/v1alpha1/revision_types.go b/pkg/apis/serving/v1alpha1/revision_types.go
index f0a7ca295..51cf78435 100644
--- a/pkg/apis/serving/v1alpha1/revision_types.go
+++ b/pkg/apis/serving/v1alpha1/revision_types.go
@@ -160,7 +160,7 @@ type RevisionStatus struct {
 	// may be empty if the image comes from a registry listed to skip resolution.
 	// If multiple containers specified then DeprecatedImageDigest holds the digest
 	// for serving container.
-	// DEPRECATED Use ImageDigests instead.
+	// DEPRECATED: Use ContainerStatuses instead.
 	// TODO(savitaashture) Remove deprecatedImageDigest.
 	// ref https://kubernetes.io/docs/reference/using-api/deprecation-policy for deprecation.
 	// +optional
diff --git a/pkg/apis/serving/v1alpha1/route_lifecycle.go b/pkg/apis/serving/v1alpha1/route_lifecycle.go
index 017e6dc42..523187d49 100644
--- a/pkg/apis/serving/v1alpha1/route_lifecycle.go
+++ b/pkg/apis/serving/v1alpha1/route_lifecycle.go
@@ -29,6 +29,7 @@ import (
 var routeCondSet = apis.NewLivingConditionSet(
 	RouteConditionAllTrafficAssigned,
 	RouteConditionIngressReady,
+	RouteConditionCertificateProvisioned,
 )
 
 func (r *Route) GetGroupVersionKind() schema.GroupVersionKind {
@@ -112,43 +113,41 @@ func (rs *RouteStatus) MarkMissingTrafficTarget(kind, name string) {
 }
 
 func (rs *RouteStatus) MarkCertificateProvisionFailed(name string) {
-	routeCondSet.Manage(rs).SetCondition(apis.Condition{
-		Type:     RouteConditionCertificateProvisioned,
-		Status:   corev1.ConditionFalse,
-		Severity: apis.ConditionSeverityWarning,
-		Reason:   "CertificateProvisionFailed",
-		Message:  fmt.Sprintf("Certificate %s fails to be provisioned.", name),
-	})
+	routeCondSet.Manage(rs).MarkFalse(RouteConditionCertificateProvisioned,
+		"CertificateProvisionFailed",
+		"Certificate %s fails to be provisioned.", name)
 }
 
 func (rs *RouteStatus) MarkCertificateReady(name string) {
-	routeCondSet.Manage(rs).SetCondition(apis.Condition{
-		Type:     RouteConditionCertificateProvisioned,
-		Status:   corev1.ConditionTrue,
-		Severity: apis.ConditionSeverityWarning,
-		Reason:   "CertificateReady",
-		Message:  fmt.Sprintf("Certificate %s is successfully provisioned", name),
-	})
+	routeCondSet.Manage(rs).MarkTrue(RouteConditionCertificateProvisioned)
 }
 
 func (rs *RouteStatus) MarkCertificateNotReady(name string) {
-	routeCondSet.Manage(rs).SetCondition(apis.Condition{
-		Type:     RouteConditionCertificateProvisioned,
-		Status:   corev1.ConditionUnknown,
-		Severity: apis.ConditionSeverityWarning,
-		Reason:   "CertificateNotReady",
-		Message:  fmt.Sprintf("Certificate %s is not ready.", name),
-	})
+	routeCondSet.Manage(rs).MarkUnknown(RouteConditionCertificateProvisioned,
+		"CertificateNotReady",
+		"Certificate %s is not ready.", name)
 }
 
 func (rs *RouteStatus) MarkCertificateNotOwned(name string) {
-	routeCondSet.Manage(rs).SetCondition(apis.Condition{
-		Type:     RouteConditionCertificateProvisioned,
-		Status:   corev1.ConditionFalse,
-		Severity: apis.ConditionSeverityWarning,
-		Reason:   "CertificateNotOwned",
-		Message:  fmt.Sprintf("There is an existing certificate %s that we don't own.", name),
-	})
+	routeCondSet.Manage(rs).MarkFalse(RouteConditionCertificateProvisioned,
+		"CertificateNotOwned",
+		"There is an existing certificate %s that we don't own.", name)
+}
+
+// MarkAutoTLSNotEnabled sets RouteConditionCertificateProvisioned to true when
+// certificate config such as autoTLS is not enabled.
+func (rs *RouteStatus) MarkAutoTLSNotEnabled() {
+	routeCondSet.Manage(rs).MarkTrueWithReason(RouteConditionCertificateProvisioned,
+		"AutoTLSNotEnabled",
+		"autoTLS is not enabled")
+}
+
+// MarkHTTPDowngrade sets RouteConditionCertificateProvisioned to true when plain
+// HTTP is enabled even when Certificated is not ready.
+func (rs *RouteStatus) MarkHTTPDowngrade(name string) {
+	routeCondSet.Manage(rs).MarkTrueWithReason(RouteConditionCertificateProvisioned,
+		"HTTPDowngrade",
+		"Certificate %s is not ready downgrade HTTP.", name)
 }
 
 // PropagateIngressStatus update RouteConditionIngressReady condition
diff --git a/pkg/autoscaler/config/config.go b/pkg/autoscaler/config/config.go
index 17f26acda..6fe41e1ca 100644
--- a/pkg/autoscaler/config/config.go
+++ b/pkg/autoscaler/config/config.go
@@ -193,9 +193,12 @@ func validate(lc *Config) (*Config, error) {
 	}
 
 	// We can't permit stable window be less than our aggregation window for correctness.
-	if lc.StableWindow < autoscaling.WindowMin {
-		return nil, fmt.Errorf("stable-window = %v, must be at least %v", lc.StableWindow, autoscaling.WindowMin)
+	// Or too big, so that our desisions are too imprecise.
+	if lc.StableWindow < autoscaling.WindowMin || lc.StableWindow > autoscaling.WindowMax {
+		return nil, fmt.Errorf("stable-window = %v, must be in [%v; %v] range", lc.StableWindow,
+			autoscaling.WindowMin, autoscaling.WindowMax)
 	}
+
 	if lc.StableWindow.Round(time.Second) != lc.StableWindow {
 		return nil, fmt.Errorf("stable-window = %v, must be specified with at most second precision", lc.StableWindow)
 	}
diff --git a/pkg/autoscaler/metrics/collector.go b/pkg/autoscaler/metrics/collector.go
index 21e659a6a..267d23aa9 100644
--- a/pkg/autoscaler/metrics/collector.go
+++ b/pkg/autoscaler/metrics/collector.go
@@ -184,7 +184,7 @@ func (c *MetricCollector) Watch(fn func(types.NamespacedName)) {
 	defer c.watcherMutex.Unlock()
 
 	if c.watcher != nil {
-		c.logger.Fatal("Multiple calls to Watch() not supported")
+		c.logger.Panic("Multiple calls to Watch() not supported")
 	}
 	c.watcher = fn
 }
@@ -302,15 +302,16 @@ func newCollection(metric *av1alpha1.Metric, scraper StatsScraper, tickFactory f
 			case <-c.stopCh:
 				return
 			case <-scrapeTicker.C:
-				currentMetric := c.currentMetric()
-				if currentMetric.Spec.ScrapeTarget == "" {
+				scraper := c.getScraper()
+				if scraper == nil {
 					// Don't scrape empty target service.
 					if c.updateLastError(nil) {
 						callback(key)
 					}
 					continue
 				}
-				stat, err := c.getScraper().Scrape(currentMetric.Spec.StableWindow)
+
+				stat, err := scraper.Scrape(c.currentMetric().Spec.StableWindow)
 				if err != nil {
 					logger.Errorw("Failed to scrape metrics", zap.Error(err))
 				}
diff --git a/pkg/autoscaler/metrics/stats_scraper.go b/pkg/autoscaler/metrics/stats_scraper.go
index 6c019570a..4387dba71 100644
--- a/pkg/autoscaler/metrics/stats_scraper.go
+++ b/pkg/autoscaler/metrics/stats_scraper.go
@@ -315,33 +315,37 @@ func (s *serviceScraper) scrapeService(window time.Duration, readyPods int) (Sta
 		grp.Go(func() error {
 			for tries := 1; ; tries++ {
 				stat, err := s.tryScrape(scrapedPods)
-				if err == nil {
-					if stat.ProcessUptime >= youngPodCutOffSecs {
-						// We run |sampleSize| goroutines and each of them terminates
-						// as soon as it sees stat from an `oldPod`.
-						// The channel is allocated to |sampleSize|, thus this will never
-						// deadlock.
-						oldStatCh <- stat
-						return nil
-					} else {
-						select {
-						// This in theory might loop over all the possible pods, thus might
-						// fill up the channel.
-						case youngStatCh <- stat:
-						default:
-							// If so, just return.
-							return nil
-						}
-					}
-				} else if tries >= scraperMaxRetries {
+				if err != nil {
 					// Return the error if we exhausted our retries and
 					// we had an error returned (we can end up here if
 					// all the pods were young, which is not an error condition).
-					return err
+					if tries >= scraperMaxRetries {
+						return err
+					}
+					continue
+				}
+
+				if stat.ProcessUptime >= youngPodCutOffSecs {
+					// We run |sampleSize| goroutines and each of them terminates
+					// as soon as it sees a stat from an `oldPod`.
+					// The channel is allocated to |sampleSize|, thus this will never
+					// deadlock.
+					oldStatCh <- stat
+					return nil
+				}
+
+				select {
+				// This in theory might loop over all the possible pods, thus might
+				// fill up the channel.
+				case youngStatCh <- stat:
+				default:
+					// If so, just return.
+					return nil
 				}
 			}
 		})
 	}
+
 	// Now at this point we have two possibilities.
 	// 1. We scraped |sampleSize| distinct pods, with the invariant of
 	// 		   sampleSize <= len(oldStatCh) + len(youngStatCh) <= sampleSize*2.
diff --git a/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go b/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go
index 868fdfd08..e2e4c5b6e 100644
--- a/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go
@@ -164,10 +164,14 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 			logger.Warnw("Failed to set finalizers", zap.Error(err))
 		}
 
+		reconciler.PreProcessReconcile(ctx, resource)
+
 		// 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)
 
+		reconciler.PostProcessReconcile(ctx, resource)
+
 	} else if fin, ok := r.reconciler.(Finalizer); ok {
 		// Append the target method to the logger.
 		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
diff --git a/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go b/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
index 25c59cd4c..ed3c6d2e3 100644
--- a/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
@@ -164,10 +164,14 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 			logger.Warnw("Failed to set finalizers", zap.Error(err))
 		}
 
+		reconciler.PreProcessReconcile(ctx, resource)
+
 		// 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)
 
+		reconciler.PostProcessReconcile(ctx, resource)
+
 	} else if fin, ok := r.reconciler.(Finalizer); ok {
 		// Append the target method to the logger.
 		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
diff --git a/pkg/deployment/config.go b/pkg/deployment/config.go
index 4000f05f1..21f65c6da 100644
--- a/pkg/deployment/config.go
+++ b/pkg/deployment/config.go
@@ -17,8 +17,8 @@ limitations under the License.
 package deployment
 
 import (
+	"errors"
 	"fmt"
-	"strings"
 	"time"
 
 	corev1 "k8s.io/api/core/v1"
@@ -56,15 +56,19 @@ func NewConfigFromMap(configMap map[string]string) (*Config, error) {
 	nc := defaultConfig()
 
 	if err := cm.Parse(configMap,
-		asRequiredString(QueueSidecarImageKey, &nc.QueueSidecarImage),
+		cm.AsString(QueueSidecarImageKey, &nc.QueueSidecarImage),
 		cm.AsDuration(ProgressDeadlineKey, &nc.ProgressDeadline),
-		asStringSet(registriesSkippingTagResolvingKey, &nc.RegistriesSkippingTagResolving),
+		cm.AsStringSet(registriesSkippingTagResolvingKey, &nc.RegistriesSkippingTagResolving),
 	); err != nil {
 		return nil, err
 	}
 
+	if nc.QueueSidecarImage == "" {
+		return nil, errors.New("queueSidecarImage cannot be empty or unset")
+	}
+
 	if nc.ProgressDeadline <= 0 {
-		return nil, fmt.Errorf("ProgressDeadline cannot be a non-positive duration, was %v", nc.ProgressDeadline)
+		return nil, fmt.Errorf("progressDeadline cannot be a non-positive duration, was %v", nc.ProgressDeadline)
 	}
 
 	return nc, nil
@@ -88,26 +92,3 @@ type Config struct {
 	// be ready before considering it failed.
 	ProgressDeadline time.Duration
 }
-
-// asRequiredString is the same as cm.AsString but errors if the key is not present.
-func asRequiredString(key string, target *string) cm.ParseFunc {
-	return func(data map[string]string) error {
-		if raw, ok := data[key]; ok {
-			*target = raw
-		} else {
-			return fmt.Errorf("%q is missing", key)
-		}
-		return nil
-	}
-}
-
-// asStringSet parses the value at key as a sets.String (splitting at ',') into the
-// target, if it exists.
-func asStringSet(key string, target *sets.String) cm.ParseFunc {
-	return func(data map[string]string) error {
-		if raw, ok := data[key]; ok {
-			*target = sets.NewString(strings.Split(raw, ",")...)
-		}
-		return nil
-	}
-}
diff --git a/pkg/queue/stats.go b/pkg/queue/stats.go
index 3118b2d55..b727e4bc0 100644
--- a/pkg/queue/stats.go
+++ b/pkg/queue/stats.go
@@ -22,24 +22,26 @@ import (
 	"knative.dev/serving/pkg/network"
 )
 
-// NewStats instantiates a new instance of Stats.
-func NewStats(startedAt time.Time, reqCh chan network.ReqEvent, reportCh <-chan time.Time, report func(float64, float64, float64, float64)) {
-	go func() {
-		state := network.NewRequestStats(startedAt)
-
-		for {
-			select {
-			case event := <-reqCh:
-				state.HandleEvent(event)
-			case now := <-reportCh:
-				stats := state.Report(now)
-				report(
-					stats.AverageConcurrency,
-					stats.AverageProxiedConcurrency,
-					stats.RequestCount,
-					stats.ProxiedRequestCount,
-				)
+// ReportStats continually processes network events from reqCh and reports
+// aggregated stats via the `report` function whenever reportCh ticks.
+func ReportStats(startedAt time.Time, reqCh chan network.ReqEvent, reportCh <-chan time.Time, report func(float64, float64, float64, float64)) {
+	state := network.NewRequestStats(startedAt)
+
+	for {
+		select {
+		case event, ok := <-reqCh:
+			if !ok {
+				return
 			}
+			state.HandleEvent(event)
+		case now := <-reportCh:
+			stats := state.Report(now)
+			report(
+				stats.AverageConcurrency,
+				stats.AverageProxiedConcurrency,
+				stats.RequestCount,
+				stats.ProxiedRequestCount,
+			)
 		}
-	}()
+	}
 }
diff --git a/pkg/reconciler/autoscaling/kpa/scaler.go b/pkg/reconciler/autoscaling/kpa/scaler.go
index 3a5999212..71a4d72c4 100644
--- a/pkg/reconciler/autoscaling/kpa/scaler.go
+++ b/pkg/reconciler/autoscaling/kpa/scaler.go
@@ -32,6 +32,7 @@ import (
 	pav1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
 	"knative.dev/serving/pkg/apis/networking"
 	nv1a1 "knative.dev/serving/pkg/apis/networking/v1alpha1"
+	asconfig "knative.dev/serving/pkg/autoscaler/config"
 	"knative.dev/serving/pkg/network"
 	"knative.dev/serving/pkg/reconciler/autoscaling/config"
 	aresources "knative.dev/serving/pkg/reconciler/autoscaling/resources"
@@ -129,6 +130,14 @@ func activatorProbe(pa *pav1alpha1.PodAutoscaler, transport http.RoundTripper) (
 	return prober.Do(context.Background(), transport, paToProbeTarget(pa), probeOptions...)
 }
 
+func lastPodRetention(pa *pav1alpha1.PodAutoscaler, cfg *asconfig.Config) time.Duration {
+	d, ok := pa.ScaleToZeroPodRetention()
+	if ok {
+		return d
+	}
+	return cfg.ScaleToZeroPodRetentionPeriod
+}
+
 // pre: 0 <= min <= max && 0 <= x
 func applyBounds(min, max, x int32) int32 {
 	if x < min {
@@ -213,7 +222,7 @@ func (ks *scaler) handleScaleToZero(ctx context.Context, pa *pav1alpha1.PodAutos
 			// And at least ScaleToZeroPodRetentionPeriod since PA became inactive.
 
 			// Most conservative check, if it passes we're good.
-			lastPodTimeout := cfgAS.ScaleToZeroPodRetentionPeriod
+			lastPodTimeout := lastPodRetention(pa, cfgAS)
 			lastPodMaxTimeout := durationMax(cfgAS.ScaleToZeroGracePeriod, lastPodTimeout)
 			if pa.Status.CanScaleToZero(now, lastPodMaxTimeout) {
 				return desiredScale, true
diff --git a/pkg/reconciler/configuration/configuration.go b/pkg/reconciler/configuration/configuration.go
index da44dc858..31f7ee8cf 100644
--- a/pkg/reconciler/configuration/configuration.go
+++ b/pkg/reconciler/configuration/configuration.go
@@ -61,10 +61,6 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, config *v1.Configuration
 	config.SetDefaults(ctx)
 	config.Status.InitializeConditions()
 
-	// Bump observed generation to denote that we have processed this
-	// generation regardless of success or failure.
-	config.Status.ObservedGeneration = config.Generation
-
 	// First, fetch the revision that should exist for the current generation.
 	lcr, err := c.latestCreatedRevision(config)
 	if errors.IsNotFound(err) {
diff --git a/pkg/reconciler/revision/revision.go b/pkg/reconciler/revision/revision.go
index caec98227..47546b435 100644
--- a/pkg/reconciler/revision/revision.go
+++ b/pkg/reconciler/revision/revision.go
@@ -168,7 +168,6 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, rev *v1.Revision) pkgrec
 			"Revision becomes ready upon all resources being ready")
 	}
 
-	rev.Status.ObservedGeneration = rev.Generation
 	return nil
 }
 
diff --git a/pkg/reconciler/route/route.go b/pkg/reconciler/route/route.go
index 3977b4502..52272d3bb 100644
--- a/pkg/reconciler/route/route.go
+++ b/pkg/reconciler/route/route.go
@@ -190,6 +190,7 @@ func (c *Reconciler) reconcileIngressResources(ctx context.Context, r *v1.Route,
 func (c *Reconciler) tls(ctx context.Context, host string, r *v1.Route, traffic *traffic.Config) ([]netv1alpha1.IngressTLS, []netv1alpha1.HTTP01Challenge, error) {
 	tls := []netv1alpha1.IngressTLS{}
 	if !config.FromContext(ctx).Network.AutoTLS {
+		r.Status.MarkAutoTLSNotEnabled()
 		return tls, nil, nil
 	}
 	domainToTagMap, err := domains.GetAllDomainsAndTags(ctx, r, getTrafficNames(traffic.Targets), traffic.Visibility)
@@ -249,7 +250,7 @@ func (c *Reconciler) tls(ctx context.Context, host string, r *v1.Route, traffic
 		} else {
 			acmeChallenges = append(acmeChallenges, cert.Status.HTTP01Challenges...)
 			r.Status.MarkCertificateNotReady(cert.Name)
-			// When httpProtocol is enabled, downward http scheme.
+			// When httpProtocol is enabled, downgrade http scheme.
 			if config.FromContext(ctx).Network.HTTPProtocol == network.HTTPEnabled {
 				if dnsNames.Has(host) {
 					r.Status.URL = &apis.URL{
@@ -258,6 +259,7 @@ func (c *Reconciler) tls(ctx context.Context, host string, r *v1.Route, traffic
 					}
 				}
 				setTargetsScheme(&r.Status, dnsNames.List(), "http")
+				r.Status.MarkHTTPDowngrade(cert.Name)
 			}
 		}
 	}
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 9450683e9..d3afecab7 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: "v20200501-507f8f4"
+    serving.knative.dev/release: "v20200525-e037dfb"
     serving.knative.dev/controller: "true"
     networking.knative.dev/certificate-provider: cert-manager
 rules:
@@ -44,13 +44,72 @@ rules:
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+apiVersion: admissionregistration.k8s.io/v1beta1
+kind: ValidatingWebhookConfiguration
+metadata:
+  name: config.webhook.net-certmanager.networking.internal.knative.dev
+  labels:
+    serving.knative.dev/release: "v20200525-e037dfb"
+webhooks:
+- admissionReviewVersions:
+  - v1beta1
+  clientConfig:
+    service:
+      name: net-certmanager-webhook
+      namespace: knative-serving
+  failurePolicy: Fail
+  sideEffects: None
+  name: config.webhook.net-certmanager.networking.internal.knative.dev
+  namespaceSelector:
+    matchExpressions:
+    - key: serving.knative.dev/release
+      operator: Exists
+
+---
+# 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.
+
+apiVersion: v1
+kind: Secret
+metadata:
+  name: net-certmanager-webhook-certs
+  namespace: knative-serving
+  labels:
+    serving.knative.dev/release: "v20200525-e037dfb"
+
+---
+# 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.
+
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: config-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200501-507f8f4"
+    serving.knative.dev/release: "v20200525-e037dfb"
     networking.knative.dev/certificate-provider: cert-manager
 data:
   _example: |
@@ -97,7 +156,7 @@ metadata:
   name: networking-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200501-507f8f4"
+    serving.knative.dev/release: "v20200525-e037dfb"
     networking.knative.dev/certificate-provider: cert-manager
 spec:
   selector:
@@ -109,14 +168,14 @@ spec:
         cluster-autoscaler.kubernetes.io/safe-to-evict: "true"
       labels:
         app: networking-certmanager
-        serving.knative.dev/release: "v20200501-507f8f4"
+        serving.knative.dev/release: "v20200525-e037dfb"
     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:4906e7b85bdd1427c3e2281bd1344a6b1d7e717fdca62a40e8bd1a04ab8461b2
+        image: gcr.io/knative-nightly/knative.dev/net-certmanager/cmd/controller@sha256:db89c7334f1c5d3fec39178f2a9eb50ded8ff6eb39e8a579403d447e2a583efe
         resources:
           requests:
             cpu: 30m
@@ -149,7 +208,7 @@ kind: Service
 metadata:
   labels:
     app: networking-certmanager
-    serving.knative.dev/release: "v20200501-507f8f4"
+    serving.knative.dev/release: "v20200525-e037dfb"
     networking.knative.dev/certificate-provider: cert-manager
   name: networking-certmanager
   namespace: knative-serving
@@ -166,3 +225,114 @@ spec:
     app: networking-certmanager
 
 ---
+# 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.
+
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: net-certmanager-webhook
+  namespace: knative-serving
+  labels:
+    serving.knative.dev/release: "v20200525-e037dfb"
+spec:
+  selector:
+    matchLabels:
+      app: net-certmanager-webhook
+      role: net-certmanager-webhook
+  template:
+    metadata:
+      annotations:
+        cluster-autoscaler.kubernetes.io/safe-to-evict: "false"
+      labels:
+        app: net-certmanager-webhook
+        role: net-certmanager-webhook
+        serving.knative.dev/release: "v20200525-e037dfb"
+    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:d7665647c960c714b07a53c6e1e91d0a3b2c76967aa0b6b0f292a13d11bd19d4
+        resources:
+          requests:
+            cpu: 20m
+            memory: 20Mi
+          limits:
+            cpu: 200m
+            memory: 200Mi
+        env:
+        - name: SYSTEM_NAMESPACE
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.namespace
+        - name: CONFIG_LOGGING_NAME
+          value: config-logging
+        - name: CONFIG_OBSERVABILITY_NAME
+          value: config-observability
+        - # TODO(https://github.com/knative/pkg/pull/953): Remove stackdriver specific config
+          name: METRICS_DOMAIN
+          value: knative.dev/net-certmanager
+        - name: WEBHOOK_NAME
+          value: net-certmanager-webhook
+        securityContext:
+          allowPrivilegeEscalation: false
+        ports:
+        - name: metrics
+          containerPort: 9090
+        - name: profiling
+          containerPort: 8008
+        - name: https-webhook
+          containerPort: 8443
+
+---
+# 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.
+
+apiVersion: v1
+kind: Service
+metadata:
+  name: net-certmanager-webhook
+  namespace: knative-serving
+  labels:
+    role: net-certmanager-webhook
+    serving.knative.dev/release: "v20200525-e037dfb"
+spec:
+  ports:
+  - # Define metrics and profiling for them to be accessible within service meshes.
+    name: http-metrics
+    port: 9090
+    targetPort: 9090
+  - name: http-profiling
+    port: 8008
+    targetPort: 8008
+  - name: https-webhook
+    port: 443
+    targetPort: 8443
+  selector:
+    app: net-certmanager-webhook
+
+---
diff --git a/third_party/net-istio.yaml b/third_party/net-istio.yaml
index 90163c06b..3a85c59df 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: "v20200514-0771529"
+    serving.knative.dev/release: "v20200522-dc16e1e"
     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: "v20200514-0771529"
+    serving.knative.dev/release: "v20200522-dc16e1e"
     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: "v20200514-0771529"
+    serving.knative.dev/release: "v20200522-dc16e1e"
     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: "v20200514-0771529"
+    serving.knative.dev/release: "v20200522-dc16e1e"
     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: "v20200514-0771529"
+    serving.knative.dev/release: "v20200522-dc16e1e"
     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: "v20200514-0771529"
+    serving.knative.dev/release: "v20200522-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 
 ---
@@ -216,7 +216,7 @@ metadata:
   name: config-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200514-0771529"
+    serving.knative.dev/release: "v20200522-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 data:
   _example: |
@@ -286,7 +286,7 @@ metadata:
   name: networking-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200514-0771529"
+    serving.knative.dev/release: "v20200522-dc16e1e"
     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: "v20200514-0771529"
+        serving.knative.dev/release: "v20200522-dc16e1e"
     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:918468e505cffc9e861eb9897a18829c8c45f2ed3a945ea027d5085e6f5842aa
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/controller@sha256:250c68ac1458f524fb8aaf271747aa047b72d16b4e5a8800390c344266380fc9
         resources:
           requests:
             cpu: 30m
@@ -361,7 +361,7 @@ metadata:
   name: istio-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200514-0771529"
+    serving.knative.dev/release: "v20200522-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -375,14 +375,14 @@ spec:
       labels:
         app: istio-webhook
         role: istio-webhook
-        serving.knative.dev/release: "v20200514-0771529"
+        serving.knative.dev/release: "v20200522-dc16e1e"
     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:2e62421e1c1f9bd1aef55eaf1664631b844c757ca958c3f3fd36c34db5ba8420
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/webhook@sha256:29978f12c8f25eeab4ecbcf1af73a9d65527f5a5331711197613d3b769089c6b
         resources:
           requests:
             cpu: 20m
@@ -436,7 +436,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: istio-webhook
-    serving.knative.dev/release: "v20200514-0771529"
+    serving.knative.dev/release: "v20200522-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 spec:
   ports:
diff --git a/vendor/knative.dev/pkg/configmap/parse.go b/vendor/knative.dev/pkg/configmap/parse.go
index 097b38f90..6479598b7 100644
--- a/vendor/knative.dev/pkg/configmap/parse.go
+++ b/vendor/knative.dev/pkg/configmap/parse.go
@@ -17,9 +17,12 @@ limitations under the License.
 package configmap
 
 import (
+	"fmt"
 	"strconv"
 	"strings"
 	"time"
+
+	"k8s.io/apimachinery/pkg/util/sets"
 )
 
 // ParseFunc is a function taking ConfigMap data and applying a parse operation to it.
@@ -51,7 +54,7 @@ func AsInt32(key string, target *int32) ParseFunc {
 		if raw, ok := data[key]; ok {
 			val, err := strconv.ParseInt(raw, 10, 32)
 			if err != nil {
-				return err
+				return fmt.Errorf("failed to parse %q: %w", key, err)
 			}
 			*target = int32(val)
 		}
@@ -65,7 +68,7 @@ func AsInt64(key string, target *int64) ParseFunc {
 		if raw, ok := data[key]; ok {
 			val, err := strconv.ParseInt(raw, 10, 64)
 			if err != nil {
-				return err
+				return fmt.Errorf("failed to parse %q: %w", key, err)
 			}
 			*target = val
 		}
@@ -79,7 +82,7 @@ func AsFloat64(key string, target *float64) ParseFunc {
 		if raw, ok := data[key]; ok {
 			val, err := strconv.ParseFloat(raw, 64)
 			if err != nil {
-				return err
+				return fmt.Errorf("failed to parse %q: %w", key, err)
 			}
 			*target = val
 		}
@@ -93,7 +96,7 @@ func AsDuration(key string, target *time.Duration) ParseFunc {
 		if raw, ok := data[key]; ok {
 			val, err := time.ParseDuration(raw)
 			if err != nil {
-				return err
+				return fmt.Errorf("failed to parse %q: %w", key, err)
 			}
 			*target = val
 		}
@@ -101,6 +104,16 @@ func AsDuration(key string, target *time.Duration) ParseFunc {
 	}
 }
 
+// AsStringSet parses the value at key as a sets.String (split by ',') into the target, if it exists.
+func AsStringSet(key string, target *sets.String) ParseFunc {
+	return func(data map[string]string) error {
+		if raw, ok := data[key]; ok {
+			*target = sets.NewString(strings.Split(raw, ",")...)
+		}
+		return nil
+	}
+}
+
 // Parse parses the given map using the parser functions passed in.
 func Parse(data map[string]string, parsers ...ParseFunc) error {
 	for _, parse := range parsers {
diff --git a/vendor/knative.dev/pkg/hack/update-deps.sh b/vendor/knative.dev/pkg/hack/update-deps.sh
index 8baa2558f..71b1bf26f 100644
--- a/vendor/knative.dev/pkg/hack/update-deps.sh
+++ b/vendor/knative.dev/pkg/hack/update-deps.sh
@@ -26,7 +26,7 @@ cd ${ROOT_DIR}
 # The list of dependencies that we track at HEAD and periodically
 # float forward in this repository.
 FLOATING_DEPS=(
-  "knative.dev/test-infra"
+  "knative.dev/test-infra@release-0.15"
 )
 
 # Parse flags to determine any we should pass to dep.
diff --git a/vendor/knative.dev/pkg/leaderelection/config.go b/vendor/knative.dev/pkg/leaderelection/config.go
index 0ec0377ed..59ab45905 100644
--- a/vendor/knative.dev/pkg/leaderelection/config.go
+++ b/vendor/knative.dev/pkg/leaderelection/config.go
@@ -19,12 +19,13 @@ package leaderelection
 import (
 	"fmt"
 	"os"
-	"strings"
 	"time"
 
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/apimachinery/pkg/util/uuid"
+
+	cm "knative.dev/pkg/configmap"
 )
 
 const ConfigMapNameEnv = "CONFIG_LEADERELECTION_NAME"
@@ -35,41 +36,23 @@ var validResourceLocks = sets.NewString("leases", "configmaps", "endpoints")
 func NewConfigFromMap(data map[string]string) (*Config, error) {
 	config := defaultConfig()
 
-	if resourceLock, ok := data["resourceLock"]; ok {
-		if !validResourceLocks.Has(resourceLock) {
-			return nil, fmt.Errorf(`resourceLock: invalid value %q: valid values are "leases","configmaps","endpoints"`, resourceLock)
-		}
-		config.ResourceLock = resourceLock
-	}
+	if err := cm.Parse(data,
+		cm.AsString("resourceLock", &config.ResourceLock),
 
-	for _, d := range []struct {
-		key string
-		val *time.Duration
-	}{{
-		"leaseDuration",
-		&config.LeaseDuration,
-	}, {
-		"renewDeadline",
-		&config.RenewDeadline,
-	}, {
-		"retryPeriod",
-		&config.RetryPeriod,
-	}} {
-		if v, ok := data[d.key]; ok {
-			dur, err := time.ParseDuration(v)
-			if err != nil {
-				return nil, fmt.Errorf("%s: invalid duration: %q", d.key, v)
-			}
-			*d.val = dur
-		}
+		cm.AsDuration("leaseDuration", &config.LeaseDuration),
+		cm.AsDuration("renewDeadline", &config.RenewDeadline),
+		cm.AsDuration("retryPeriod", &config.RetryPeriod),
+
+		// enabledComponents are not validated here, because they are dependent on
+		// the component. Components should provide additional validation for this
+		// field.
+		cm.AsStringSet("enabledComponents", &config.EnabledComponents),
+	); err != nil {
+		return nil, err
 	}
 
-	// enabledComponents are not validated here, because they are dependent on
-	// the component. Components should provide additional validation for this
-	// field.
-	if enabledComponents, ok := data["enabledComponents"]; ok {
-		tokens := strings.Split(enabledComponents, ",")
-		config.EnabledComponents = sets.NewString(tokens...)
+	if !validResourceLocks.Has(config.ResourceLock) {
+		return nil, fmt.Errorf(`resourceLock: invalid value %q: valid values are "leases","configmaps","endpoints"`, config.ResourceLock)
 	}
 
 	return config, nil
diff --git a/vendor/knative.dev/pkg/metrics/config_observability.go b/vendor/knative.dev/pkg/metrics/config_observability.go
index e40cb2fb2..e4560ede0 100644
--- a/vendor/knative.dev/pkg/metrics/config_observability.go
+++ b/vendor/knative.dev/pkg/metrics/config_observability.go
@@ -18,10 +18,10 @@ package metrics
 
 import (
 	"os"
-	"strings"
 	texttemplate "text/template"
 
 	corev1 "k8s.io/api/core/v1"
+	cm "knative.dev/pkg/configmap"
 )
 
 const (
@@ -71,32 +71,23 @@ func defaultConfig() *ObservabilityConfig {
 // NewObservabilityConfigFromConfigMap creates a ObservabilityConfig from the supplied ConfigMap
 func NewObservabilityConfigFromConfigMap(configMap *corev1.ConfigMap) (*ObservabilityConfig, error) {
 	oc := defaultConfig()
-	if evlc, ok := configMap.Data["logging.enable-var-log-collection"]; ok {
-		oc.EnableVarLogCollection = strings.EqualFold(evlc, "true")
-	}
 
-	if rut, ok := configMap.Data["logging.revision-url-template"]; ok {
-		oc.LoggingURLTemplate = rut
+	if err := cm.Parse(configMap.Data,
+		cm.AsBool("logging.enable-var-log-collection", &oc.EnableVarLogCollection),
+		cm.AsString("logging.revision-url-template", &oc.LoggingURLTemplate),
+		cm.AsString("logging.request-log-template", &oc.RequestLogTemplate),
+		cm.AsBool("logging.enable-probe-request-log", &oc.EnableProbeRequestLog),
+		cm.AsString("metrics.request-metrics-backend-destination", &oc.RequestMetricsBackend),
+		cm.AsBool("profiling.enable", &oc.EnableProfiling),
+	); err != nil {
+		return nil, err
 	}
 
-	if rlt, ok := configMap.Data["logging.request-log-template"]; ok {
+	if oc.RequestLogTemplate != "" {
 		// Verify that we get valid templates.
-		if _, err := texttemplate.New("requestLog").Parse(rlt); err != nil {
+		if _, err := texttemplate.New("requestLog").Parse(oc.RequestLogTemplate); err != nil {
 			return nil, err
 		}
-		oc.RequestLogTemplate = rlt
-	}
-
-	if eprl, ok := configMap.Data["logging.enable-probe-request-log"]; ok {
-		oc.EnableProbeRequestLog = strings.EqualFold(eprl, "true")
-	}
-
-	if mb, ok := configMap.Data["metrics.request-metrics-backend-destination"]; ok {
-		oc.RequestMetricsBackend = mb
-	}
-
-	if prof, ok := configMap.Data["profiling.enable"]; ok {
-		oc.EnableProfiling = strings.EqualFold(prof, "true")
 	}
 
 	return oc, nil
diff --git a/vendor/knative.dev/pkg/reconciler/reconcile_common.go b/vendor/knative.dev/pkg/reconciler/reconcile_common.go
index 8af08f61a..cc0e2d712 100644
--- a/vendor/knative.dev/pkg/reconciler/reconcile_common.go
+++ b/vendor/knative.dev/pkg/reconciler/reconcile_common.go
@@ -48,8 +48,9 @@ func PostProcessReconcile(ctx context.Context, resource duckv1.KRShaped) {
 	// generation regardless of success or failure.
 	newStatus.ObservedGeneration = resource.GetGeneration()
 
-	rc := mgr.GetTopLevelCondition()
-	if rc.Reason == failedGenerationBump {
+	if rc := mgr.GetTopLevelCondition(); rc == nil {
+		logger.Warn("A reconciliation included no top-level condition")
+	} else if rc.Reason == failedGenerationBump {
 		logger.Warn("A reconciler observed a new generation without updating the resource status")
 	}
 }
diff --git a/vendor/knative.dev/pkg/tracing/config/tracing.go b/vendor/knative.dev/pkg/tracing/config/tracing.go
index 67aec651c..b3aceee16 100644
--- a/vendor/knative.dev/pkg/tracing/config/tracing.go
+++ b/vendor/knative.dev/pkg/tracing/config/tracing.go
@@ -25,6 +25,7 @@ import (
 
 	"cloud.google.com/go/compute/metadata"
 	corev1 "k8s.io/api/core/v1"
+	cm "knative.dev/pkg/configmap"
 )
 
 const (
@@ -98,15 +99,20 @@ func NewTracingConfigFromMap(cfgMap map[string]string) (*Config, error) {
 		}
 	}
 
-	if endpoint, ok := cfgMap[zipkinEndpointKey]; !ok && tc.Backend == Zipkin {
+	if err := cm.Parse(cfgMap,
+		cm.AsString(zipkinEndpointKey, &tc.ZipkinEndpoint),
+		cm.AsString(stackdriverProjectIDKey, &tc.StackdriverProjectID),
+		cm.AsBool(debugKey, &tc.Debug),
+		cm.AsFloat64(sampleRateKey, &tc.SampleRate),
+	); err != nil {
+		return nil, err
+	}
+
+	if tc.Backend == Zipkin && tc.ZipkinEndpoint == "" {
 		return nil, errors.New("zipkin tracing enabled without a zipkin endpoint specified")
-	} else {
-		tc.ZipkinEndpoint = endpoint
 	}
 
-	if projectID, ok := cfgMap[stackdriverProjectIDKey]; ok {
-		tc.StackdriverProjectID = projectID
-	} else if tc.Backend == Stackdriver {
+	if tc.Backend == Stackdriver && tc.StackdriverProjectID == "" {
 		projectID, err := metadata.ProjectID()
 		if err != nil {
 			return nil, fmt.Errorf("stackdriver tracing enabled without a project-id specified: %w", err)
@@ -114,23 +120,8 @@ func NewTracingConfigFromMap(cfgMap map[string]string) (*Config, error) {
 		tc.StackdriverProjectID = projectID
 	}
 
-	if debug, ok := cfgMap[debugKey]; ok {
-		debugBool, err := strconv.ParseBool(debug)
-		if err != nil {
-			return nil, fmt.Errorf("failed parsing tracing config %q", debugKey)
-		}
-		tc.Debug = debugBool
-	}
-
-	if sampleRate, ok := cfgMap[sampleRateKey]; ok {
-		sampleRateFloat, err := strconv.ParseFloat(sampleRate, 64)
-		if err != nil {
-			return nil, fmt.Errorf("failed to parse sampleRate in tracing config: %w", err)
-		}
-		if sampleRateFloat < 0 || sampleRateFloat > 1 {
-			return nil, fmt.Errorf("sampleRate = %v must be in [0, 1] range", sampleRateFloat)
-		}
-		tc.SampleRate = sampleRateFloat
+	if tc.SampleRate < 0 || tc.SampleRate > 1 {
+		return nil, fmt.Errorf("sample-rate = %v must be in [0, 1] range", tc.SampleRate)
 	}
 
 	return tc, nil
diff --git a/vendor/modules.txt b/vendor/modules.txt
index aa4bc3fcc..32770bc3d 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1148,7 +1148,7 @@ k8s.io/utils/integer
 k8s.io/utils/path
 k8s.io/utils/pointer
 k8s.io/utils/trace
-# knative.dev/caching v0.0.0-20200515173804-a2fc9c5de2ef
+# knative.dev/caching v0.0.0-20200521155757-e78d17bc250e
 ## explicit
 knative.dev/caching/config
 knative.dev/caching/pkg/apis/caching
@@ -1169,7 +1169,7 @@ 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/pkg v0.0.0-20200517014302-3c29a1ce8906
+# knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7
 ## explicit
 knative.dev/pkg/apiextensions/storageversion
 knative.dev/pkg/apiextensions/storageversion/cmd/migrate
@@ -1285,7 +1285,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-20200515184601-7a28f47cdbcb
+# knative.dev/test-infra v0.0.0-20200519161858-554a95a37986
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/structured-merge-diff v1.0.1


Test patch:
diff --git a/cmd/queue/main_test.go b/cmd/queue/main_test.go
index 8e53a65bf..99282012c 100644
--- a/cmd/queue/main_test.go
+++ b/cmd/queue/main_test.go
@@ -517,7 +517,7 @@ func BenchmarkProxyHandler(b *testing.B) {
 	if err != nil {
 		b.Fatal("Failed to create stats reporter:", err)
 	}
-	queue.NewStats(time.Now(), reqChan, reportTicker.C, promStatReporter.Report)
+	go queue.ReportStats(time.Now(), reqChan, reportTicker.C, promStatReporter.Report)
 	req := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
 	req.Header.Set(network.OriginalHostHeader, wantHost)
 
diff --git a/pkg/activator/net/throttler_test.go b/pkg/activator/net/throttler_test.go
index 4933568da..624d5ab09 100644
--- a/pkg/activator/net/throttler_test.go
+++ b/pkg/activator/net/throttler_test.go
@@ -439,8 +439,15 @@ func TestThrottlerSuccesses(t *testing.T) {
 			}
 
 			// Make sure our informer event has fired.
+			// We send multiple updates in some tests, so make sure the capacity is exact.
+			wantCapacity := 1
+			cc := tc.revision.Spec.ContainerConcurrency
+			if cc != nil && *cc != 0 {
+				dests := tc.initUpdates[len(tc.initUpdates)-1].Dests.Len()
+				wantCapacity = dests * int(*cc)
+			}
 			if err := wait.PollImmediate(10*time.Millisecond, 3*time.Second, func() (bool, error) {
-				return atomic.LoadInt32(&rt.activatorIndex) != -1 && rt.breaker.Capacity() > 0, nil
+				return atomic.LoadInt32(&rt.activatorIndex) != -1 && rt.breaker.Capacity() == wantCapacity, nil
 			}); err != nil {
 				t.Fatal("Timed out waiting for the capacity to be updated")
 			}
@@ -449,11 +456,15 @@ func TestThrottlerSuccesses(t *testing.T) {
 			tryContext, cancel2 := context.WithTimeout(context.Background(), 1*time.Second)
 			defer cancel2()
 
+			waitGrp := sync.WaitGroup{}
+			waitGrp.Add(tc.requests)
 			resultChan := throttler.try(tryContext, tc.requests, func(string) error {
-				// Simulate proxying.
-				time.Sleep(50 * time.Millisecond)
+				waitGrp.Done()
+				// Wait for all requests to reach the Try function before proceeding.
+				waitGrp.Wait()
 				return nil
 			})
+
 			gotDests := sets.NewString()
 			for i := 0; i < tc.requests; i++ {
 				result := <-resultChan
diff --git a/pkg/apis/autoscaling/annotation_validation_test.go b/pkg/apis/autoscaling/annotation_validation_test.go
index 1d3a536c6..dbb18ea27 100644
--- a/pkg/apis/autoscaling/annotation_validation_test.go
+++ b/pkg/apis/autoscaling/annotation_validation_test.go
@@ -45,23 +45,27 @@ func TestValidateScaleBoundAnnotations(t *testing.T) {
 	}, {
 		name:        "minScale is -1",
 		annotations: map[string]string{MinScaleAnnotationKey: "-1"},
-		expectErr:   "expected 1 <= -1 <= 2147483647: " + MinScaleAnnotationKey,
+		expectErr:   "expected 0 <= -1 <= 2147483647: " + MinScaleAnnotationKey,
+	}, {
+		name:        "maxScale is huuuuuuuge",
+		annotations: map[string]string{MaxScaleAnnotationKey: "2147483648"},
+		expectErr:   "expected 0 <= 2147483648 <= 2147483647: " + MaxScaleAnnotationKey,
 	}, {
 		name:        "maxScale is -1",
 		annotations: map[string]string{MaxScaleAnnotationKey: "-1"},
-		expectErr:   "expected 1 <= -1 <= 2147483647: " + MaxScaleAnnotationKey,
+		expectErr:   "expected 0 <= -1 <= 2147483647: " + MaxScaleAnnotationKey,
 	}, {
 		name:        "minScale is foo",
 		annotations: map[string]string{MinScaleAnnotationKey: "foo"},
-		expectErr:   "expected 1 <= foo <= 2147483647: " + MinScaleAnnotationKey,
+		expectErr:   "invalid value: foo: " + MinScaleAnnotationKey,
 	}, {
 		name:        "maxScale is bar",
 		annotations: map[string]string{MaxScaleAnnotationKey: "bar"},
-		expectErr:   "expected 1 <= bar <= 2147483647: " + MaxScaleAnnotationKey,
+		expectErr:   "invalid value: bar: " + MaxScaleAnnotationKey,
 	}, {
 		name:        "max/minScale is bar",
 		annotations: map[string]string{MaxScaleAnnotationKey: "bar", MinScaleAnnotationKey: "bar"},
-		expectErr:   "expected 1 <= bar <= 2147483647: " + MaxScaleAnnotationKey + ", " + MinScaleAnnotationKey,
+		expectErr:   "invalid value: bar: " + MaxScaleAnnotationKey + ", " + MinScaleAnnotationKey,
 	}, {
 		name:        "minScale is 5",
 		annotations: map[string]string{MinScaleAnnotationKey: "5"},
@@ -192,6 +196,24 @@ func TestValidateScaleBoundAnnotations(t *testing.T) {
 		name:        "invalid format and valid class for /window annotation",
 		annotations: map[string]string{WindowAnnotationKey: "jerry-was-a-racecar-driver", ClassAnnotationKey: KPA},
 		expectErr:   "invalid value: jerry-was-a-racecar-driver: " + WindowAnnotationKey,
+	}, {
+		name:        "valid 0 last pod scaledown timeout",
+		annotations: map[string]string{ScaleToZeroPodRetentionPeriodKey: "0"},
+	}, {
+		name:        "valid positive last pod scaledown timeout",
+		annotations: map[string]string{ScaleToZeroPodRetentionPeriodKey: "21m31s"},
+	}, {
+		name:        "invalid positive last pod scaledown timeout",
+		annotations: map[string]string{ScaleToZeroPodRetentionPeriodKey: "311m"},
+		expectErr:   "expected 0s <= 311m <= 1h0m0s: " + ScaleToZeroPodRetentionPeriodKey,
+	}, {
+		name:        "invalid negative last pod scaledown timeout",
+		annotations: map[string]string{ScaleToZeroPodRetentionPeriodKey: "-42s"},
+		expectErr:   "expected 0s <= -42s <= 1h0m0s: " + ScaleToZeroPodRetentionPeriodKey,
+	}, {
+		name:        "invalid last pod scaledown timeout",
+		annotations: map[string]string{ScaleToZeroPodRetentionPeriodKey: "twenty-two-minutes-and-five-seconds"},
+		expectErr:   "invalid value: twenty-two-minutes-and-five-seconds: " + ScaleToZeroPodRetentionPeriodKey,
 	}, {
 		name: "all together now fail",
 		annotations: map[string]string{
@@ -200,7 +222,7 @@ func TestValidateScaleBoundAnnotations(t *testing.T) {
 			MinScaleAnnotationKey:                 "-4",
 			MaxScaleAnnotationKey:                 "never",
 		},
-		expectErr: "expected 1 <= -11 <= 100: " + PanicWindowPercentageAnnotationKey + "\nexpected 1 <= -4 <= 2147483647: " + MinScaleAnnotationKey + "\nexpected 1 <= never <= 2147483647: " + MaxScaleAnnotationKey + "\ninvalid value: fifty: " + PanicThresholdPercentageAnnotationKey,
+		expectErr: "expected 0 <= -4 <= 2147483647: " + MinScaleAnnotationKey + "\nexpected 1 <= -11 <= 100: " + PanicWindowPercentageAnnotationKey + "\ninvalid value: fifty: " + PanicThresholdPercentageAnnotationKey + "\ninvalid value: never: " + MaxScaleAnnotationKey,
 	}, {
 		name: "all together now, succeed",
 		annotations: map[string]string{
@@ -258,7 +280,7 @@ func TestValidateScaleBoundAnnotations(t *testing.T) {
 	for _, c := range cases {
 		t.Run(c.name, func(t *testing.T) {
 			if got, want := ValidateAnnotations(c.allowInitScaleZero, c.annotations).Error(), c.expectErr; !reflect.DeepEqual(got, want) {
-				t.Errorf("Err = %q, want: %q, diff:\n%s", got, want, cmp.Diff(got, want))
+				t.Errorf("\nErr = %q,\nwant: %q, diff(-want,+got):\n%s", got, want, cmp.Diff(want, got))
 			}
 		})
 	}
diff --git a/pkg/apis/autoscaling/v1alpha1/metric_types_test.go b/pkg/apis/autoscaling/v1alpha1/metric_types_test.go
index 8c0363982..3f1de95fc 100644
--- a/pkg/apis/autoscaling/v1alpha1/metric_types_test.go
+++ b/pkg/apis/autoscaling/v1alpha1/metric_types_test.go
@@ -27,7 +27,7 @@ func TestMetricGetStatus(t *testing.T) {
 	}
 
 	if got, want := r.GetStatus(), &r.Status.Status; got != want {
-		t.Errorf("GotStatus=%v, want=%v", got, want)
+		t.Errorf("GetStatus=%v, want=%v", got, want)
 	}
 }
 
diff --git a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle_test.go b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle_test.go
index 9272dac8c..24635dd3c 100644
--- a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle_test.go
+++ b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle_test.go
@@ -245,7 +245,7 @@ func TestInactiveFor(t *testing.T) {
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
-			if got, want := tc.status.InactiveFor(now), tc.result; absDiff(got, want) > 10*time.Millisecond {
+			if got, want := tc.status.InactiveFor(now), tc.result; got != want {
 				t.Errorf("InactiveFor = %v, want: %v", got, want)
 			}
 		})
@@ -329,21 +329,13 @@ func TestActiveFor(t *testing.T) {
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
-			if got, want := tc.status.ActiveFor(now), tc.result; absDiff(got, want) > 10*time.Millisecond {
+			if got, want := tc.status.ActiveFor(now), tc.result; got != want {
 				t.Errorf("ActiveFor = %v, want: %v", got, want)
 			}
 		})
 	}
 }
 
-func absDiff(a, b time.Duration) time.Duration {
-	a -= b
-	if a < 0 {
-		a *= -1
-	}
-	return a
-}
-
 func TestCanFailActivation(t *testing.T) {
 	now := time.Now()
 	cases := []struct {
@@ -417,8 +409,8 @@ func TestCanFailActivation(t *testing.T) {
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
-			if e, a := tc.result, tc.status.CanFailActivation(now, tc.grace); e != a {
-				t.Errorf("%q expected: %v got: %v", tc.name, e, a)
+			if got, want := tc.status.CanFailActivation(now, tc.grace), tc.result; got != want {
+				t.Errorf("CanFailActivation = %v, want: %v", got, want)
 			}
 		})
 	}
@@ -469,9 +461,11 @@ func TestIsActivating(t *testing.T) {
 	}}
 
 	for _, tc := range cases {
-		if e, a := tc.isActivating, tc.status.IsActivating(); e != a {
-			t.Errorf("%q expected: %v got: %v", tc.name, e, a)
-		}
+		t.Run(tc.name, func(t *testing.T) {
+			if got, want := tc.status.IsActivating(), tc.isActivating; got != want {
+				t.Errorf("IsActivating = %v, want: %v", got, want)
+			}
+		})
 	}
 }
 
@@ -569,9 +563,11 @@ func TestIsReady(t *testing.T) {
 	}}
 
 	for _, tc := range cases {
-		if e, a := tc.isReady, tc.status.IsReady(); e != a {
-			t.Errorf("%q expected: %v got: %v", tc.name, e, a)
-		}
+		t.Run(tc.name, func(t *testing.T) {
+			if got, want := tc.status.IsReady(), tc.isReady; got != want {
+				t.Errorf("IsReady = %v, want: %v", got, want)
+			}
+		})
 	}
 }
 
@@ -613,10 +609,10 @@ func TestTargetAnnotation(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			gotTarget, gotOK := tc.pa.Target()
 			if gotTarget != tc.wantTarget {
-				t.Errorf("got target: %v wanted: %v", gotTarget, tc.wantTarget)
+				t.Errorf("Target = %v; want: %v", gotTarget, tc.wantTarget)
 			}
 			if gotOK != tc.wantOK {
-				t.Errorf("got ok: %v wanted %v", gotOK, tc.wantOK)
+				t.Errorf("OK = %v; want: %v", gotOK, tc.wantOK)
 			}
 		})
 	}
@@ -823,7 +819,14 @@ func TestWindowAnnotation(t *testing.T) {
 		pa: pa(map[string]string{
 			autoscaling.WindowAnnotationKey: "120s",
 		}),
-		wantWindow: time.Second * 120,
+		wantWindow: 120 * time.Second,
+		wantOK:     true,
+	}, {
+		name: "complex",
+		pa: pa(map[string]string{
+			autoscaling.WindowAnnotationKey: "2m33s",
+		}),
+		wantWindow: 153 * time.Second,
 		wantOK:     true,
 	}, {
 		name: "invalid",
@@ -838,10 +841,10 @@ func TestWindowAnnotation(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			gotWindow, gotOK := tc.pa.Window()
 			if gotWindow != tc.wantWindow {
-				t.Errorf("%q expected target: %v got: %v", tc.name, tc.wantWindow, gotWindow)
+				t.Errorf("Window = %v, want: %v", gotWindow, tc.wantWindow)
 			}
 			if gotOK != tc.wantOK {
-				t.Errorf("%q expected ok: %v got %v", tc.name, tc.wantOK, gotOK)
+				t.Errorf("OK = %v, want: %v", gotOK, tc.wantOK)
 			}
 		})
 	}
@@ -878,10 +881,10 @@ func TestPanicWindowPercentageAnnotation(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			gotPercentage, gotOK := tc.pa.PanicWindowPercentage()
 			if gotPercentage != tc.wantPercentage {
-				t.Errorf("%q expected target: %v got: %v", tc.name, tc.wantPercentage, gotPercentage)
+				t.Errorf("PanicWindowPercentage = %v, want: %v", gotPercentage, tc.wantPercentage)
 			}
 			if gotOK != tc.wantOK {
-				t.Errorf("%q expected ok: %v got %v", tc.name, tc.wantOK, gotOK)
+				t.Errorf("OK = %v, want: %v", gotOK, tc.wantOK)
 			}
 		})
 	}
@@ -918,10 +921,10 @@ func TestTargetUtilization(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			got, gotOK := tc.pa.TargetUtilization()
 			if got, want := got, tc.want; got != want {
-				t.Errorf("%q target utilization: %v want: %v", tc.name, got, want)
+				t.Errorf("TU = %v, want: %v", got, want)
 			}
 			if gotOK != tc.wantOK {
-				t.Errorf("%q expected ok: %v got %v", tc.name, tc.wantOK, gotOK)
+				t.Errorf("OK = %v, want: %v", gotOK, tc.wantOK)
 			}
 		})
 	}
@@ -951,17 +954,17 @@ func TestPanicThresholdPercentage(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			gotPercentage, gotOK := tc.pa.PanicThresholdPercentage()
 			if gotPercentage != tc.wantPercentage {
-				t.Errorf("%q expected target: %v got: %v", tc.name, tc.wantPercentage, gotPercentage)
+				t.Errorf("PanicThresholdPercentage = %v, want: %v", gotPercentage, tc.wantPercentage)
 			}
 			if gotOK != tc.wantOK {
-				t.Errorf("%q expected ok: %v got %v", tc.name, tc.wantOK, gotOK)
+				t.Errorf("OK = %v, want: %v", gotOK, tc.wantOK)
 			}
 		})
 	}
 }
 
 func pa(annotations map[string]string) *PodAutoscaler {
-	p := &PodAutoscaler{
+	return &PodAutoscaler{
 		ObjectMeta: metav1.ObjectMeta{
 			Namespace:   "test-namespace",
 			Name:        "test-name",
@@ -972,7 +975,6 @@ func pa(annotations map[string]string) *PodAutoscaler {
 		},
 		Status: PodAutoscalerStatus{},
 	}
-	return p
 }
 
 func TestTypicalFlow(t *testing.T) {
@@ -1021,7 +1023,6 @@ func TestTargetBC(t *testing.T) {
 	}{{
 		name: "not present",
 		pa:   pa(map[string]string{}),
-		want: 0.0,
 	}, {
 		name: "present",
 		pa: pa(map[string]string{
@@ -1048,17 +1049,16 @@ func TestTargetBC(t *testing.T) {
 		pa: pa(map[string]string{
 			autoscaling.TargetBurstCapacityKey: "NPH",
 		}),
-		want: 0.0,
 	}}
 
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
 			got, gotOK := tc.pa.TargetBC()
 			if got, want := got, tc.want; got != want {
-				t.Errorf("%q target burst capacity: %v want: %v", tc.name, got, want)
+				t.Errorf("TBC = %v, want: %v", got, want)
 			}
 			if gotOK != tc.wantOK {
-				t.Errorf("%q expected ok: %v got %v", tc.name, tc.wantOK, gotOK)
+				t.Errorf("OK = %v, want: %v", gotOK, tc.wantOK)
 			}
 		})
 	}
@@ -1094,3 +1094,49 @@ func TestPodAutoscalerGetGroupVersionKind(t *testing.T) {
 		t.Errorf("got: %v, want: %v", got, want)
 	}
 }
+
+func TestScaleToZeroPodRetention(t *testing.T) {
+	cases := []struct {
+		name   string
+		pa     *PodAutoscaler
+		want   time.Duration
+		wantOK bool
+	}{{
+		name: "nil",
+		pa:   pa(nil),
+	}, {
+		name: "not present",
+		pa:   pa(map[string]string{}),
+	}, {
+		name: "present",
+		pa: pa(map[string]string{
+			autoscaling.ScaleToZeroPodRetentionPeriodKey: "311s",
+		}),
+		want:   311 * time.Second,
+		wantOK: true,
+	}, {
+		name: "complex",
+		pa: pa(map[string]string{
+			autoscaling.ScaleToZeroPodRetentionPeriodKey: "4m21s",
+		}),
+		want:   261 * time.Second,
+		wantOK: true,
+	}, {
+		name: "invalid",
+		pa: pa(map[string]string{
+			autoscaling.ScaleToZeroPodRetentionPeriodKey: "365d",
+		}),
+	}}
+
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, gotOK := tc.pa.ScaleToZeroPodRetention()
+			if got != tc.want {
+				t.Errorf("ScaleToZeroPodRetention = %v, want: %v", got, tc.want)
+			}
+			if gotOK != tc.wantOK {
+				t.Errorf("OK = %v, want: %v", gotOK, tc.wantOK)
+			}
+		})
+	}
+}
diff --git a/pkg/apis/autoscaling/v1alpha1/pa_types_test.go b/pkg/apis/autoscaling/v1alpha1/pa_types_test.go
index 865fb297b..c2b80debd 100644
--- a/pkg/apis/autoscaling/v1alpha1/pa_types_test.go
+++ b/pkg/apis/autoscaling/v1alpha1/pa_types_test.go
@@ -27,7 +27,7 @@ func TestPodAutoscalerGetStatus(t *testing.T) {
 	}
 
 	if got, want := r.GetStatus(), &r.Status.Status; got != want {
-		t.Errorf("GotStatus=%v, want=%v", got, want)
+		t.Errorf("GetStatus=%v, want=%v", got, want)
 	}
 }
 
diff --git a/pkg/apis/autoscaling/v1alpha1/pa_validation_test.go b/pkg/apis/autoscaling/v1alpha1/pa_validation_test.go
index 68574c2e8..b3defc968 100644
--- a/pkg/apis/autoscaling/v1alpha1/pa_validation_test.go
+++ b/pkg/apis/autoscaling/v1alpha1/pa_validation_test.go
@@ -18,7 +18,6 @@ package v1alpha1
 
 import (
 	"context"
-	"math"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
@@ -208,7 +207,7 @@ func TestPodAutoscalerValidation(t *testing.T) {
 				ProtocolType: net.ProtocolHTTP1,
 			},
 		},
-		want: apis.ErrOutOfBoundsValue("FOO", 1, math.MaxInt32, autoscaling.MinScaleAnnotationKey).ViaField("metadata", "annotations"),
+		want: apis.ErrInvalidValue("FOO", autoscaling.MinScaleAnnotationKey).ViaField("metadata", "annotations"),
 	}, {
 		name: "empty spec",
 		r: &PodAutoscaler{
diff --git a/pkg/apis/networking/v1alpha1/certificate_lifecycle_test.go b/pkg/apis/networking/v1alpha1/certificate_lifecycle_test.go
index eed31127a..066b1a563 100644
--- a/pkg/apis/networking/v1alpha1/certificate_lifecycle_test.go
+++ b/pkg/apis/networking/v1alpha1/certificate_lifecycle_test.go
@@ -49,7 +49,7 @@ func TestCertificateGetConditionSet(t *testing.T) {
 	r := &Certificate{}
 
 	if got, want := r.GetConditionSet().GetTopLevelConditionType(), apis.ConditionReady; got != want {
-		t.Errorf("GotConditionSet=%v, want=%v", got, want)
+		t.Errorf("GetConditionSet=%v, want=%v", got, want)
 	}
 }
 
diff --git a/pkg/apis/networking/v1alpha1/ingress_lifecycle_test.go b/pkg/apis/networking/v1alpha1/ingress_lifecycle_test.go
index c4bf343da..af2d8fa11 100644
--- a/pkg/apis/networking/v1alpha1/ingress_lifecycle_test.go
+++ b/pkg/apis/networking/v1alpha1/ingress_lifecycle_test.go
@@ -49,7 +49,7 @@ func TestIngressGetConditionSet(t *testing.T) {
 	r := &Ingress{}
 
 	if got, want := r.GetConditionSet().GetTopLevelConditionType(), apis.ConditionReady; got != want {
-		t.Errorf("GotConditionSet=%v, want=%v", got, want)
+		t.Errorf("GetConditionSet=%v, want=%v", got, want)
 	}
 }
 
diff --git a/pkg/apis/networking/v1alpha1/serverlessservice_lifecycle_test.go b/pkg/apis/networking/v1alpha1/serverlessservice_lifecycle_test.go
index 56408e07a..88205aaef 100644
--- a/pkg/apis/networking/v1alpha1/serverlessservice_lifecycle_test.go
+++ b/pkg/apis/networking/v1alpha1/serverlessservice_lifecycle_test.go
@@ -48,7 +48,7 @@ func TestServerlessServiceDuckTypes(t *testing.T) {
 func TestServerlessServiceGetConditionSet(t *testing.T) {
 	r := &ServerlessService{}
 	if got, want := r.GetConditionSet().GetTopLevelConditionType(), apis.ConditionReady; got != want {
-		t.Errorf("GotConditionSet=%v, want=%v", got, want)
+		t.Errorf("GetConditionSet=%v, want=%v", got, want)
 	}
 }
 
diff --git a/pkg/apis/serving/v1/revision_validation_test.go b/pkg/apis/serving/v1/revision_validation_test.go
index de865d96f..288594fb4 100644
--- a/pkg/apis/serving/v1/revision_validation_test.go
+++ b/pkg/apis/serving/v1/revision_validation_test.go
@@ -838,7 +838,7 @@ func TestRevisionTemplateSpecValidation(t *testing.T) {
 			ObjectMeta: metav1.ObjectMeta{
 				Annotations: map[string]string{
 					autoscaling.MinScaleAnnotationKey: "5",
-					autoscaling.MaxScaleAnnotationKey: "",
+					autoscaling.MaxScaleAnnotationKey: "covid-19",
 				},
 			},
 			Spec: RevisionSpec{
@@ -849,10 +849,8 @@ func TestRevisionTemplateSpecValidation(t *testing.T) {
 				},
 			},
 		},
-		want: (&apis.FieldError{
-			Message: "expected 1 <=  <= 2147483647",
-			Paths:   []string{autoscaling.MaxScaleAnnotationKey},
-		}).ViaField("annotations").ViaField("metadata"),
+		want: apis.ErrInvalidValue("covid-19", autoscaling.MaxScaleAnnotationKey).
+			ViaField("annotations").ViaField("metadata"),
 	}, {
 		name: "Queue sidecar resource percentage annotation more than 100",
 		rts: &RevisionTemplateSpec{
@@ -946,7 +944,7 @@ func TestRevisionTemplateSpecValidation(t *testing.T) {
 
 			got := test.rts.Validate(ctx)
 			if got, want := got.Error(), test.want.Error(); !cmp.Equal(got, want) {
-				t.Errorf("Validate (-want, +got): \n%s", cmp.Diff(want, got))
+				t.Errorf("Validate (-want, +got):\n%s", cmp.Diff(want, got))
 			}
 		})
 	}
diff --git a/pkg/apis/serving/v1/route_lifecycle_test.go b/pkg/apis/serving/v1/route_lifecycle_test.go
index aea593dfa..7f35cb6ab 100644
--- a/pkg/apis/serving/v1/route_lifecycle_test.go
+++ b/pkg/apis/serving/v1/route_lifecycle_test.go
@@ -177,6 +177,7 @@ func TestTypicalRouteFlow(t *testing.T) {
 	apistest.CheckConditionOngoing(r, RouteConditionReady, t)
 
 	r.MarkTrafficAssigned()
+	r.MarkAutoTLSNotEnabled()
 	apistest.CheckConditionSucceeded(r, RouteConditionAllTrafficAssigned, t)
 	apistest.CheckConditionOngoing(r, RouteConditionIngressReady, t)
 	apistest.CheckConditionOngoing(r, RouteConditionReady, t)
@@ -290,6 +291,7 @@ func TestIngressFailureRecovery(t *testing.T) {
 	apistest.CheckConditionOngoing(r, RouteConditionReady, t)
 
 	r.MarkTrafficAssigned()
+	r.MarkAutoTLSNotEnabled()
 	r.PropagateIngressStatus(netv1alpha1.IngressStatus{
 		Status: duckv1.Status{
 			Conditions: duckv1.Conditions{{
@@ -381,6 +383,22 @@ func TestRouteNotOwnCertificate(t *testing.T) {
 	apistest.CheckConditionFailed(r, RouteConditionCertificateProvisioned, t)
 }
 
+func TestRouteAutoTLSNotEnabled(t *testing.T) {
+	r := &RouteStatus{}
+	r.InitializeConditions()
+	r.MarkAutoTLSNotEnabled()
+
+	apistest.CheckConditionSucceeded(r, RouteConditionCertificateProvisioned, t)
+}
+
+func TestRouteHTTPDowngrade(t *testing.T) {
+	r := &RouteStatus{}
+	r.InitializeConditions()
+	r.MarkHTTPDowngrade("cert")
+
+	apistest.CheckConditionSucceeded(r, RouteConditionCertificateProvisioned, t)
+}
+
 func TestIngressNotConfigured(t *testing.T) {
 	r := &RouteStatus{}
 	r.InitializeConditions()
diff --git a/pkg/apis/serving/v1alpha1/revision_validation_test.go b/pkg/apis/serving/v1alpha1/revision_validation_test.go
index 419f69d3f..9e21de31f 100644
--- a/pkg/apis/serving/v1alpha1/revision_validation_test.go
+++ b/pkg/apis/serving/v1alpha1/revision_validation_test.go
@@ -427,7 +427,7 @@ func TestRevisionTemplateSpecValidation(t *testing.T) {
 			ObjectMeta: metav1.ObjectMeta{
 				Annotations: map[string]string{
 					autoscaling.MinScaleAnnotationKey: "5",
-					autoscaling.MaxScaleAnnotationKey: "",
+					autoscaling.MaxScaleAnnotationKey: "covid-19",
 				},
 			},
 			Spec: RevisionSpec{
@@ -436,10 +436,8 @@ func TestRevisionTemplateSpecValidation(t *testing.T) {
 				},
 			},
 		},
-		want: (&apis.FieldError{
-			Message: "expected 1 <=  <= 2147483647",
-			Paths:   []string{autoscaling.MaxScaleAnnotationKey},
-		}).ViaField("annotations").ViaField("metadata"),
+		want: apis.ErrInvalidValue("covid-19", autoscaling.MaxScaleAnnotationKey).
+			ViaField("annotations").ViaField("metadata"),
 	}, {
 		name: "Invalid initial scale when cluster doesn't allow zero",
 		ctx:  autoscalerConfigCtx(false, 1),
diff --git a/pkg/apis/serving/v1alpha1/route_lifecycle_test.go b/pkg/apis/serving/v1alpha1/route_lifecycle_test.go
index 04dec0bf6..b1685869e 100644
--- a/pkg/apis/serving/v1alpha1/route_lifecycle_test.go
+++ b/pkg/apis/serving/v1alpha1/route_lifecycle_test.go
@@ -160,6 +160,7 @@ func TestTypicalRouteFlow(t *testing.T) {
 	apistest.CheckConditionOngoing(r, RouteConditionReady, t)
 
 	r.MarkTrafficAssigned()
+	r.MarkAutoTLSNotEnabled()
 	apistest.CheckConditionSucceeded(r, RouteConditionAllTrafficAssigned, t)
 	apistest.CheckConditionOngoing(r, RouteConditionIngressReady, t)
 	apistest.CheckConditionOngoing(r, RouteConditionReady, t)
@@ -273,6 +274,7 @@ func TestIngressFailureRecovery(t *testing.T) {
 	apistest.CheckConditionOngoing(r, RouteConditionReady, t)
 
 	r.MarkTrafficAssigned()
+	r.MarkAutoTLSNotEnabled()
 	r.PropagateIngressStatus(netv1alpha1.IngressStatus{
 		Status: duckv1.Status{
 			Conditions: duckv1.Conditions{{
@@ -376,6 +378,22 @@ func TestRouteNotOwnCertificate(t *testing.T) {
 	apistest.CheckConditionFailed(r, RouteConditionCertificateProvisioned, t)
 }
 
+func TestRouteAutoTLSNotEnabled(t *testing.T) {
+	r := &RouteStatus{}
+	r.InitializeConditions()
+	r.MarkAutoTLSNotEnabled()
+
+	apistest.CheckConditionSucceeded(r, RouteConditionCertificateProvisioned, t)
+}
+
+func TestRouteHTTPDowngrade(t *testing.T) {
+	r := &RouteStatus{}
+	r.InitializeConditions()
+	r.MarkHTTPDowngrade("cert")
+
+	apistest.CheckConditionSucceeded(r, RouteConditionCertificateProvisioned, t)
+}
+
 func TestIngressNotConfigured(t *testing.T) {
 	r := &RouteStatus{}
 	r.InitializeConditions()
diff --git a/pkg/apis/serving/v1beta1/revision_validation_test.go b/pkg/apis/serving/v1beta1/revision_validation_test.go
index 217a0edec..4e08987c7 100644
--- a/pkg/apis/serving/v1beta1/revision_validation_test.go
+++ b/pkg/apis/serving/v1beta1/revision_validation_test.go
@@ -788,7 +788,7 @@ func TestRevisionTemplateSpecValidation(t *testing.T) {
 			ObjectMeta: metav1.ObjectMeta{
 				Annotations: map[string]string{
 					autoscaling.MinScaleAnnotationKey: "5",
-					autoscaling.MaxScaleAnnotationKey: "",
+					autoscaling.MaxScaleAnnotationKey: "covid-19",
 				},
 			},
 			Spec: v1.RevisionSpec{
@@ -799,10 +799,8 @@ func TestRevisionTemplateSpecValidation(t *testing.T) {
 				},
 			},
 		},
-		want: (&apis.FieldError{
-			Message: "expected 1 <=  <= 2147483647",
-			Paths:   []string{autoscaling.MaxScaleAnnotationKey},
-		}).ViaField("annotations").ViaField("metadata"),
+		want: apis.ErrInvalidValue("covid-19", autoscaling.MaxScaleAnnotationKey).
+			ViaField("annotations").ViaField("metadata"),
 	}, {
 		name: "Queue sidecar resource percentage annotation more than 100",
 		rts: &v1.RevisionTemplateSpec{
diff --git a/pkg/autoscaler/config/config_test.go b/pkg/autoscaler/config/config_test.go
index d03aaa40d..21133d3e6 100644
--- a/pkg/autoscaler/config/config_test.go
+++ b/pkg/autoscaler/config/config_test.go
@@ -203,6 +203,12 @@ func TestNewConfig(t *testing.T) {
 			"max-scale-down-rate": "1",
 		},
 		wantErr: true,
+	}, {
+		name: "stable window too big",
+		input: map[string]string{
+			"stable-window": "1h1s",
+		},
+		wantErr: true,
 	}, {
 		name: "stable window too small",
 		input: map[string]string{
diff --git a/pkg/autoscaler/metrics/collector_test.go b/pkg/autoscaler/metrics/collector_test.go
index 620d56d2d..90da18374 100644
--- a/pkg/autoscaler/metrics/collector_test.go
+++ b/pkg/autoscaler/metrics/collector_test.go
@@ -24,9 +24,11 @@ import (
 
 	"github.com/google/go-cmp/cmp"
 	"go.uber.org/zap"
+
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/wait"
+
 	. "knative.dev/pkg/logging/testing"
 	av1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
 	"knative.dev/serving/pkg/apis/serving"
@@ -224,12 +226,7 @@ func TestMetricCollectorNoScraper(t *testing.T) {
 		AverageConcurrentRequests: wantStat,
 		RequestCount:              wantStat,
 	}
-	scraper := &testScraper{
-		s: func() (Stat, error) {
-			return stat, nil
-		},
-	}
-	factory := scraperFactory(scraper, nil)
+	factory := scraperFactory(nil, nil)
 
 	coll := NewMetricCollector(factory, logger)
 	coll.tickProvider = mtp.NewTicker // custom ticker.
@@ -387,6 +384,19 @@ func TestMetricCollectorRecord(t *testing.T) {
 	}
 }
 
+func TestDoubleWatch(t *testing.T) {
+	defer func() {
+		if x := recover(); x == nil {
+			t.Error("Expected panic")
+		}
+	}()
+	logger := TestLogger(t)
+	factory := scraperFactory(nil, nil)
+	coll := NewMetricCollector(factory, logger)
+	coll.Watch(func(types.NamespacedName) {})
+	coll.Watch(func(types.NamespacedName) {})
+}
+
 func TestMetricCollectorError(t *testing.T) {
 	testMetric := &av1alpha1.Metric{
 		ObjectMeta: metav1.ObjectMeta{
diff --git a/pkg/deployment/config_test.go b/pkg/deployment/config_test.go
index d9ce7a737..4d97d08d8 100644
--- a/pkg/deployment/config_test.go
+++ b/pkg/deployment/config_test.go
@@ -31,7 +31,7 @@ import (
 	_ "knative.dev/pkg/system/testing"
 )
 
-const noSidecarImage = ""
+const defaultSidecarImage = "defaultImage"
 
 func TestControllerConfigurationFromFile(t *testing.T) {
 	cm, example := ConfigMapsFromTestFile(t, ConfigName, QueueSidecarImageKey)
@@ -62,33 +62,33 @@ func TestControllerConfiguration(t *testing.T) {
 		name: "controller configuration with bad registries",
 		wantConfig: &Config{
 			RegistriesSkippingTagResolving: sets.NewString("ko.local", ""),
-			QueueSidecarImage:              noSidecarImage,
+			QueueSidecarImage:              defaultSidecarImage,
 			ProgressDeadline:               ProgressDeadlineDefault,
 		},
 		data: map[string]string{
-			QueueSidecarImageKey:              noSidecarImage,
+			QueueSidecarImageKey:              defaultSidecarImage,
 			registriesSkippingTagResolvingKey: "ko.local,,",
 		},
 	}, {
 		name: "controller configuration good progress deadline",
 		wantConfig: &Config{
 			RegistriesSkippingTagResolving: sets.NewString("ko.local", "dev.local"),
-			QueueSidecarImage:              noSidecarImage,
+			QueueSidecarImage:              defaultSidecarImage,
 			ProgressDeadline:               444 * time.Second,
 		},
 		data: map[string]string{
-			QueueSidecarImageKey: noSidecarImage,
+			QueueSidecarImageKey: defaultSidecarImage,
 			ProgressDeadlineKey:  "444s",
 		},
 	}, {
 		name: "controller configuration with registries",
 		wantConfig: &Config{
 			RegistriesSkippingTagResolving: sets.NewString("ko.local", "ko.dev"),
-			QueueSidecarImage:              noSidecarImage,
+			QueueSidecarImage:              defaultSidecarImage,
 			ProgressDeadline:               ProgressDeadlineDefault,
 		},
 		data: map[string]string{
-			QueueSidecarImageKey:              noSidecarImage,
+			QueueSidecarImageKey:              defaultSidecarImage,
 			registriesSkippingTagResolvingKey: "ko.local,ko.dev",
 		},
 	}, {
@@ -99,21 +99,21 @@ func TestControllerConfiguration(t *testing.T) {
 		name:    "controller configuration invalid progress deadline",
 		wantErr: true,
 		data: map[string]string{
-			QueueSidecarImageKey: noSidecarImage,
+			QueueSidecarImageKey: defaultSidecarImage,
 			ProgressDeadlineKey:  "not-a-duration",
 		},
 	}, {
 		name:    "controller configuration invalid progress deadline II",
 		wantErr: true,
 		data: map[string]string{
-			QueueSidecarImageKey: noSidecarImage,
+			QueueSidecarImageKey: defaultSidecarImage,
 			ProgressDeadlineKey:  "-21s",
 		},
 	}, {
 		name:    "controller configuration invalid progress deadline III",
 		wantErr: true,
 		data: map[string]string{
-			QueueSidecarImageKey: noSidecarImage,
+			QueueSidecarImageKey: defaultSidecarImage,
 			ProgressDeadlineKey:  "0ms",
 		},
 	}}
diff --git a/pkg/leaderelection/config_test.go b/pkg/leaderelection/config_test.go
index e45ba435c..48f69077a 100644
--- a/pkg/leaderelection/config_test.go
+++ b/pkg/leaderelection/config_test.go
@@ -18,7 +18,6 @@ package leaderelection
 
 import (
 	"errors"
-	"reflect"
 	"testing"
 	"time"
 
@@ -71,7 +70,7 @@ func TestValidateConfig(t *testing.T) {
 			data["renewDeadline"] = "not a duration"
 			return data
 		}(),
-		err: errors.New(`renewDeadline: invalid duration: "not a duration"`),
+		err: errors.New(`failed to parse "renewDeadline": time: invalid duration not a duration`),
 	}, {
 		name: "invalid component",
 		data: func() map[string]string {
@@ -85,7 +84,7 @@ func TestValidateConfig(t *testing.T) {
 	for _, tc := range cases {
 		t.Run(tc.name, func(t *testing.T) {
 			actualConfig, actualErr := ValidateConfig(&corev1.ConfigMap{Data: tc.data})
-			if !reflect.DeepEqual(tc.err, actualErr) {
+			if tc.err != nil && tc.err.Error() != actualErr.Error() {
 				t.Fatalf("%v: expected error %v, got %v", tc.name, tc.err, actualErr)
 			}
 
diff --git a/pkg/queue/stats_test.go b/pkg/queue/stats_test.go
index ffadf15f5..2a2296ef5 100644
--- a/pkg/queue/stats_test.go
+++ b/pkg/queue/stats_test.go
@@ -33,7 +33,7 @@ type reportedStat struct {
 
 func TestNoData(t *testing.T) {
 	now := time.Now()
-	s := newTestStats(now)
+	s := newTestStats(t, now)
 
 	got := s.report(now)
 	want := reportedStat{
@@ -47,7 +47,7 @@ func TestNoData(t *testing.T) {
 
 func TestSingleRequestWholeTime(t *testing.T) {
 	now := time.Now()
-	s := newTestStats(now)
+	s := newTestStats(t, now)
 
 	s.requestStart(now)
 	now = now.Add(1 * time.Second)
@@ -66,7 +66,7 @@ func TestSingleRequestWholeTime(t *testing.T) {
 
 func TestSingleRequestHalfTime(t *testing.T) {
 	now := time.Now()
-	s := newTestStats(now)
+	s := newTestStats(t, now)
 
 	s.requestStart(now)
 	now = now.Add(1 * time.Second)
@@ -85,7 +85,7 @@ func TestSingleRequestHalfTime(t *testing.T) {
 
 func TestVeryShortLivedRequest(t *testing.T) {
 	now := time.Now()
-	s := newTestStats(now)
+	s := newTestStats(t, now)
 
 	s.requestStart(now)
 	now = now.Add(10 * time.Millisecond)
@@ -105,7 +105,7 @@ func TestVeryShortLivedRequest(t *testing.T) {
 
 func TestMultipleRequestsWholeTime(t *testing.T) {
 	now := time.Now()
-	s := newTestStats(now)
+	s := newTestStats(t, now)
 
 	s.requestStart(now)
 	now = now.Add(300 * time.Millisecond)
@@ -132,7 +132,7 @@ func TestMultipleRequestsWholeTime(t *testing.T) {
 
 func TestMultipleRequestsInterleaved(t *testing.T) {
 	now := time.Now()
-	s := newTestStats(now)
+	s := newTestStats(t, now)
 
 	s.requestStart(now)
 	now = now.Add(100 * time.Millisecond)
@@ -155,7 +155,7 @@ func TestMultipleRequestsInterleaved(t *testing.T) {
 
 func TestOneRequestAcrossReportings(t *testing.T) {
 	now := time.Now()
-	s := newTestStats(now)
+	s := newTestStats(t, now)
 
 	s.requestStart(now)
 	now = now.Add(1 * time.Second)
@@ -184,7 +184,7 @@ func TestOneRequestAcrossReportings(t *testing.T) {
 
 func TestOneProxiedRequest(t *testing.T) {
 	now := time.Now()
-	s := newTestStats(now)
+	s := newTestStats(t, now)
 	s.proxiedStart(now)
 	now = now.Add(1 * time.Second)
 	got := s.report(now)
@@ -201,7 +201,7 @@ func TestOneProxiedRequest(t *testing.T) {
 
 func TestOneEndedProxiedRequest(t *testing.T) {
 	now := time.Now()
-	s := newTestStats(now)
+	s := newTestStats(t, now)
 	s.proxiedStart(now)
 	now = now.Add(500 * time.Millisecond)
 	s.proxiedEnd(now)
@@ -220,7 +220,7 @@ func TestOneEndedProxiedRequest(t *testing.T) {
 
 func TestTwoRequestsOneProxied(t *testing.T) {
 	now := time.Now()
-	s := newTestStats(now)
+	s := newTestStats(t, now)
 	s.proxiedStart(now)
 	now = now.Add(500 * time.Millisecond)
 	s.proxiedEnd(now)
@@ -245,9 +245,11 @@ type testStats struct {
 	statChan     chan reportedStat
 }
 
-func newTestStats(now time.Time) *testStats {
-	reportBiChan := make(chan time.Time)
+func newTestStats(t *testing.T, now time.Time) *testStats {
 	reqChan := make(chan network.ReqEvent)
+	t.Cleanup(func() { close(reqChan) })
+
+	reportBiChan := make(chan time.Time)
 	statChan := make(chan reportedStat)
 	report := func(acr float64, apcr float64, rc float64, prc float64) {
 		statChan <- reportedStat{
@@ -257,7 +259,7 @@ func newTestStats(now time.Time) *testStats {
 			ProxiedRequestCount: prc,
 		}
 	}
-	NewStats(now, reqChan, reportBiChan, report)
+	go ReportStats(now, reqChan, reportBiChan, report)
 	return &testStats{
 		reqChan:      reqChan,
 		reportBiChan: reportBiChan,
diff --git a/pkg/reconciler/autoscaling/kpa/scaler_test.go b/pkg/reconciler/autoscaling/kpa/scaler_test.go
index 6001583f4..e9599f3cb 100644
--- a/pkg/reconciler/autoscaling/kpa/scaler_test.go
+++ b/pkg/reconciler/autoscaling/kpa/scaler_test.go
@@ -153,8 +153,18 @@ func TestScaler(t *testing.T) {
 			WithWindowAnnotation(paStableWindow.String())(k)
 			paMarkActive(k, time.Now().Add(-paStableWindow))
 		},
-		wantReplicas: 0,
-		wantScaling:  false,
+	}, {
+		label:         "can scale to zero after grace period, but 0 PA retention",
+		startReplicas: 1,
+		scaleTo:       0,
+		paMutation: func(k *pav1alpha1.PodAutoscaler) {
+			paMarkInactive(k, time.Now().Add(-gracePeriod))
+			k.Annotations[autoscaling.ScaleToZeroPodRetentionPeriodKey] = "0"
+		},
+		configMutator: func(c *config.Config) {
+			c.Autoscaler.ScaleToZeroPodRetentionPeriod = 2 * gracePeriod
+		},
+		wantScaling: true,
 	}, {
 		label:         "can't scale to zero after grace period, but before last pod retention",
 		startReplicas: 1,
@@ -168,6 +178,20 @@ func TestScaler(t *testing.T) {
 		wantReplicas: 0,
 		wantScaling:  false,
 		wantCBCount:  1,
+	}, {
+		label:         "can't scale to zero after grace period, but before last pod retention, pa defined",
+		startReplicas: 1,
+		scaleTo:       0,
+		paMutation: func(k *pav1alpha1.PodAutoscaler) {
+			paMarkInactive(k, time.Now().Add(-gracePeriod))
+			k.Annotations[autoscaling.ScaleToZeroPodRetentionPeriodKey] = (2 * gracePeriod).String()
+		},
+		configMutator: func(c *config.Config) {
+			c.Autoscaler.ScaleToZeroPodRetentionPeriod = 0 // Disabled in CM.
+		},
+		wantReplicas: 0,
+		wantScaling:  false,
+		wantCBCount:  1,
 	}, {
 		label:         "scale to zero after grace period, and after last pod retention",
 		startReplicas: 1,
diff --git a/pkg/reconciler/route/table_test.go b/pkg/reconciler/route/table_test.go
index 1ed2c6844..f8694c521 100644
--- a/pkg/reconciler/route/table_test.go
+++ b/pkg/reconciler/route/table_test.go
@@ -157,7 +157,7 @@ func TestReconcile(t *testing.T) {
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
 				WithRouteUID("12-34"),
 				// Populated by reconciliation when all traffic has been assigned.
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -204,7 +204,7 @@ func TestReconcile(t *testing.T) {
 			Object: Route("default", "ingress-failed", WithConfigTarget("config"),
 				WithRouteUID("12-34"),
 				// Populated by reconciliation when all traffic has been assigned.
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithInitRouteConditions,
 				MarkTrafficAssigned,
 				WithStatusTraffic(
 					v1.TrafficTarget{
@@ -255,7 +255,7 @@ func TestReconcile(t *testing.T) {
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
 				WithRouteUID("12-34"), WithIngressClass("custom-ingress-class"),
 				// Populated by reconciliation when all traffic has been assigned.
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -312,7 +312,7 @@ func TestReconcile(t *testing.T) {
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
 				WithRouteUID("65-23"),
 				// Populated by reconciliation when all traffic has been assigned.
-				WithLocalDomain, WithAddress, WithInitRouteConditions,
+				WithLocalDomain, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"}),
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
 					v1.TrafficTarget{
@@ -358,7 +358,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
 				// Populated by reconciliation when the route becomes ready.
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -448,7 +448,7 @@ func TestReconcile(t *testing.T) {
 			Object: Route("default", "ingress-create-failure", WithConfigTarget("config"),
 				WithRouteFinalizer,
 				// Populated by reconciliation when we fail to create the ingress.
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -466,7 +466,7 @@ func TestReconcile(t *testing.T) {
 		Name: "steady state",
 		Objects: []runtime.Object{
 			Route("default", "steady-state", WithConfigTarget("config"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady,
 				WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
@@ -503,7 +503,7 @@ func TestReconcile(t *testing.T) {
 		WantErr: true,
 		Objects: []runtime.Object{
 			Route("default", "unhappy-owner", WithConfigTarget("config"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName: "config-00001",
@@ -520,7 +520,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "unhappy-owner", WithConfigTarget("config"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -542,7 +542,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "different-domain", WithConfigTarget("config"),
 				WithAnotherDomain, WithAddress,
-				WithInitRouteConditions, MarkTrafficAssigned, MarkIngressReady,
+				WithRouteConditionsAutoTLSDisabled, MarkTrafficAssigned, MarkIngressReady,
 				WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -604,7 +604,7 @@ func TestReconcile(t *testing.T) {
 		Name: "new latest created revision",
 		Objects: []runtime.Object{
 			Route("default", "new-latest-created", WithConfigTarget("config"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -642,7 +642,7 @@ func TestReconcile(t *testing.T) {
 		Name: "new latest ready revision",
 		Objects: []runtime.Object{
 			Route("default", "new-latest-ready", WithConfigTarget("config"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName: "config-00001",
@@ -695,7 +695,7 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "new-latest-ready", WithConfigTarget("config"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00002",
@@ -760,7 +760,7 @@ func TestReconcile(t *testing.T) {
 			Object: Route("default", "becomes-local", WithConfigTarget("config"),
 				WithRouteUID("65-23"),
 				MarkTrafficAssigned, MarkIngressNotConfigured,
-				WithLocalDomain, WithAddress, WithInitRouteConditions,
+				WithLocalDomain, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"}),
 				WithStatusTraffic(
 					v1.TrafficTarget{
@@ -824,7 +824,7 @@ func TestReconcile(t *testing.T) {
 			Object: Route("default", "becomes-public", WithConfigTarget("config"),
 				WithRouteUID("65-23"),
 				MarkTrafficAssigned, MarkIngressNotConfigured,
-				WithAddress, WithInitRouteConditions, WithURL,
+				WithAddress, WithRouteConditionsAutoTLSDisabled, WithURL,
 				WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -842,7 +842,7 @@ func TestReconcile(t *testing.T) {
 		},
 		Objects: []runtime.Object{
 			Route("default", "update-ci-failure", WithConfigTarget("config"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName: "config-00001",
@@ -894,7 +894,7 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "update-ci-failure", WithConfigTarget("config"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00002",
@@ -910,7 +910,7 @@ func TestReconcile(t *testing.T) {
 		Name: "reconcile service mutation",
 		Objects: []runtime.Object{
 			Route("default", "svc-mutation", WithConfigTarget("config"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -954,7 +954,7 @@ func TestReconcile(t *testing.T) {
 		},
 		Objects: []runtime.Object{
 			Route("default", "svc-mutation", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -999,7 +999,7 @@ func TestReconcile(t *testing.T) {
 		Name: "drop cluster ip",
 		Objects: []runtime.Object{
 			Route("default", "cluster-ip", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -1039,7 +1039,7 @@ func TestReconcile(t *testing.T) {
 		Name: "fix external name",
 		Objects: []runtime.Object{
 			Route("default", "external-name", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -1078,7 +1078,7 @@ func TestReconcile(t *testing.T) {
 		Name: "reconcile ingress mutation",
 		Objects: []runtime.Object{
 			Route("default", "ingress-mutation", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -1133,7 +1133,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			// The status reflects "oldconfig", but the spec "newconfig".
 			Route("default", "change-configs", WithConfigTarget("newconfig"), WithRouteFinalizer,
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName: "oldconfig-00001",
@@ -1189,7 +1189,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			// Status updated to "newconfig"
 			Object: Route("default", "change-configs", WithConfigTarget("newconfig"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "newconfig-00001",
@@ -1270,7 +1270,7 @@ func TestReconcile(t *testing.T) {
 			Object: Route("default", "pinned-becomes-ready",
 				// Use the Revision name from the config
 				WithRevTarget("config-00001"), WithRouteFinalizer,
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -1352,7 +1352,7 @@ func TestReconcile(t *testing.T) {
 					ConfigurationName: "green",
 					Percent:           ptr.Int64(50),
 				}), WithRouteUID("34-78"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "blue-00001",
@@ -1489,7 +1489,7 @@ func TestReconcile(t *testing.T) {
 						RevisionName: "gray-00001",
 						Percent:      ptr.Int64(50),
 					}), WithRouteUID("1-2"), WithRouteFinalizer,
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
 					v1.TrafficTarget{
 						Tag:            "gray",
@@ -1524,7 +1524,7 @@ func TestReconcile(t *testing.T) {
 		// Start from a steady state referencing "blue", and modify the route spec to point to "green" instead.
 		Objects: []runtime.Object{
 			Route("default", "switch-configs", WithConfigTarget("green"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
 					v1.TrafficTarget{
 						Tag:          "blue",
@@ -1579,7 +1579,7 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "switch-configs", WithConfigTarget("green"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "green-00001",
@@ -1642,7 +1642,7 @@ func TestReconcile(t *testing.T) {
 		Name: "Update stale lastPinned",
 		Objects: []runtime.Object{
 			Route("default", "stale-lastpinned", WithConfigTarget("config"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -1682,7 +1682,7 @@ func TestReconcile(t *testing.T) {
 		Name: "check that we can find the ingress with old naming",
 		Objects: []runtime.Object{
 			Route("default", "old-naming", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -1718,7 +1718,7 @@ func TestReconcile(t *testing.T) {
 		Name: "deletes service when route no longer references service",
 		Objects: []runtime.Object{
 			Route("default", "my-route", WithConfigTarget("config"),
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady,
 				WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
@@ -1923,13 +1923,13 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
 				WithRouteUID("12-34"),
 				// Populated by reconciliation when all traffic has been assigned.
-				WithURL, WithAddress, WithInitRouteConditions,
+				WithURL, WithAddress, WithInitRouteConditions, WithRouteConditionsHTTPDowngrade,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
-					}), MarkCertificateNotReady),
+					})),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created placeholder service %q", "becomes-ready"),
@@ -2157,17 +2157,16 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 				WithRouteUID("12-34"),
 				// Populated by reconciliation when all traffic has been assigned.
 				WithAddress, WithInitRouteConditions,
+				// The certificate has to be created in the not ready state for the ACME challenge
+				// ingress rules to be added.
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					}),
-				// The certificate has to be created in the not ready state for the ACME challenge
-				// ingress rules to be added.
-				MarkCertificateNotReady,
 				// Which also means no HTTPS URL
-				WithURL,
+				WithURL, WithRouteConditionsHTTPDowngrade,
 			),
 		}},
 		Key: "default/becomes-ready",
@@ -2279,13 +2278,13 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
 				WithRouteUID("12-34"),
 				// Populated by reconciliation when all traffic has been assigned.
-				WithAddress, WithInitRouteConditions,
+				WithAddress, WithRouteConditionsHTTPDowngrade,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
-					}), MarkCertificateNotReady, MarkIngressNotConfigured,
+					}), MarkIngressNotConfigured,
 				// The certificate is not ready. So we want to have HTTP URL.
 				WithURL),
 		}},
diff --git a/pkg/testing/v1/route.go b/pkg/testing/v1/route.go
index 3cd22a476..54394f379 100644
--- a/pkg/testing/v1/route.go
+++ b/pkg/testing/v1/route.go
@@ -159,6 +159,18 @@ func WithInitRouteConditions(rt *v1.Route) {
 	rt.Status.InitializeConditions()
 }
 
+// WithRouteConditionsAutoTLSDisabled calls MarkAutoTLSNotEnabled after initialized the Service's conditions.
+func WithRouteConditionsAutoTLSDisabled(rt *v1.Route) {
+	rt.Status.InitializeConditions()
+	rt.Status.MarkAutoTLSNotEnabled()
+}
+
+// WithRouteConditionsHTTPDowngrade calls MarkHTTPDowngrade after initialized the Service's conditions.
+func WithRouteConditionsHTTPDowngrade(rt *v1.Route) {
+	rt.Status.InitializeConditions()
+	rt.Status.MarkHTTPDowngrade(routenames.Certificate(rt))
+}
+
 // MarkTrafficAssigned calls the method of the same name on .Status
 func MarkTrafficAssigned(r *v1.Route) {
 	r.Status.MarkTrafficAssigned()
diff --git a/test/e2e-tests.sh b/test/e2e-tests.sh
index d21fbc66c..a9a2dce34 100755
--- a/test/e2e-tests.sh
+++ b/test/e2e-tests.sh
@@ -85,11 +85,17 @@ add_trap "kubectl -n ${SYSTEM_NAMESPACE} patch configmap/config-autoscaler --typ
 # Run conformance and e2e tests.
 
 go_test_e2e -timeout=30m \
-  $(go list ./test/conformance/... | grep -v certificate) \
+  $(go list ./test/conformance/... | grep -v 'certificate\|ingress' ) \
   ./test/e2e ./test/e2e/hpa \
   ${parallelism} \
   "--resolvabledomain=$(use_resolvable_domain)" "${use_https}" "$(ingress_class)" || failed=1
 
+# We run KIngress conformance ingress separately, to make it easier to skip some tests.
+go_test_e2e -timeout=20m ./test/conformance/ingress ${parallelism}  \
+  `# Skip TestUpdate due to excessive flaking https://github.com/knative/serving/issues/8032` \
+  -run="Test[^U]" \
+  "--resolvabledomain=$(use_resolvable_domain)" "${use_https}" "$(ingress_class)" || failed=1
+
 if (( HTTPS )); then
   kubectl delete -f ${TMP_DIR}/test/config/autotls/certmanager/caissuer/ --ignore-not-found
   turn_off_auto_tls
diff --git a/test/ha/activator_test.go b/test/ha/activator_test.go
index cd8025ed4..80282dd08 100644
--- a/test/ha/activator_test.go
+++ b/test/ha/activator_test.go
@@ -42,6 +42,7 @@ const (
 )
 
 func TestActivatorHAGraceful(t *testing.T) {
+	t.Skip("TODO(8066): This was added too optimistically. Needs debugging and triaging.")
 	testActivatorHA(t, nil, 1)
 }
 
diff --git a/third_party/contour-latest/contour.yaml b/third_party/contour-latest/contour.yaml
index ddd207a20..150871ea2 100644
--- a/third_party/contour-latest/contour.yaml
+++ b/third_party/contour-latest/contour.yaml
@@ -2,6 +2,8 @@ apiVersion: rbac.authorization.k8s.io/v1beta1
 kind: ClusterRoleBinding
 metadata:
   name: contour-external
+  labels:
+    networking.knative.dev/ingress-provider: contour
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: ClusterRole
@@ -15,24 +17,32 @@ apiVersion: v1
 kind: Namespace
 metadata:
   name: contour-external
+  labels:
+    networking.knative.dev/ingress-provider: contour
 ---
 apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: contour
   namespace: contour-external
+  labels:
+    networking.knative.dev/ingress-provider: contour
 ---
 apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: envoy
   namespace: contour-external
+  labels:
+    networking.knative.dev/ingress-provider: contour
 ---
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: contour
   namespace: contour-external
+  labels:
+    networking.knative.dev/ingress-provider: contour
 data:
   contour.yaml: |
     leaderelection:
@@ -98,6 +108,8 @@ metadata:
     controller-gen.kubebuilder.io/version: v0.2.4
   creationTimestamp: null
   name: ingressroutes.contour.heptio.com
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   additionalPrinterColumns:
   - JSONPath: .spec.virtualhost.fqdn
@@ -467,6 +479,8 @@ metadata:
     controller-gen.kubebuilder.io/version: v0.2.4
   creationTimestamp: null
   name: tlscertificatedelegations.contour.heptio.com
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   group: contour.heptio.com
   names:
@@ -543,6 +557,8 @@ metadata:
     controller-gen.kubebuilder.io/version: v0.2.4
   creationTimestamp: null
   name: httpproxies.projectcontour.io
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   additionalPrinterColumns:
   - JSONPath: .spec.virtualhost.fqdn
@@ -1278,6 +1294,8 @@ metadata:
     controller-gen.kubebuilder.io/version: v0.2.4
   creationTimestamp: null
   name: tlscertificatedelegations.projectcontour.io
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   group: projectcontour.io
   names:
@@ -1354,12 +1372,16 @@ kind: ServiceAccount
 metadata:
   name: contour-certgen
   namespace: contour-external
+  labels:
+    networking.knative.dev/ingress-provider: contour
 ---
 apiVersion: rbac.authorization.k8s.io/v1beta1
 kind: RoleBinding
 metadata:
   name: contour
   namespace: contour-external
+  labels:
+    networking.knative.dev/ingress-provider: contour
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: Role
@@ -1374,6 +1396,8 @@ kind: Role
 metadata:
   name: contour-certgen
   namespace: contour-external
+  labels:
+    networking.knative.dev/ingress-provider: contour
 rules:
 - apiGroups:
   - ""
@@ -1393,6 +1417,8 @@ kind: Job
 metadata:
   name: contour-certgen
   namespace: contour-external
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   ttlSecondsAfterFinished: 0
   template:
@@ -1406,7 +1432,7 @@ spec:
         # if we change this on each version, you can no longer upgrade
         # just by applying the deployment YAML.
         # See #2423, #2395, #2150, and #2030 for earlier questions about this.
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:8d3d3f935e36974dfe5ec7f7499862c7a621325536b8c01df68bcfaaea98de33
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
         imagePullPolicy: Always
         command:
         - contour
@@ -1433,6 +1459,8 @@ apiVersion: rbac.authorization.k8s.io/v1beta1
 kind: ClusterRole
 metadata:
   name: contour
+  labels:
+    networking.knative.dev/ingress-provider: contour
 rules:
 - apiGroups:
   - ""
@@ -1511,6 +1539,8 @@ kind: Role
 metadata:
   name: contour-leaderelection
   namespace: contour-external
+  labels:
+    networking.knative.dev/ingress-provider: contour
 rules:
 - apiGroups:
   - ""
@@ -1536,6 +1566,8 @@ kind: RoleBinding
 metadata:
   name: contour-leaderelection
   namespace: contour-external
+  labels:
+    networking.knative.dev/ingress-provider: contour
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: Role
@@ -1550,6 +1582,8 @@ kind: Service
 metadata:
   name: contour
   namespace: contour-external
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   ports:
   - port: 8001
@@ -1573,6 +1607,8 @@ metadata:
     # for information about enabling the PROXY protocol on the ELB to recover
     # the original remote IP address.
     service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   externalTrafficPolicy: Local
   ports:
@@ -1591,6 +1627,7 @@ kind: Deployment
 metadata:
   labels:
     app: contour
+    networking.knative.dev/ingress-provider: contour
   name: contour
   namespace: contour-external
 spec:
@@ -1635,7 +1672,7 @@ spec:
         - --contour-key-file=/certs/tls.key
         - --config-path=/config/contour.yaml
         command: ["contour"]
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:8d3d3f935e36974dfe5ec7f7499862c7a621325536b8c01df68bcfaaea98de33
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
         imagePullPolicy: Always
         name: contour
         ports:
@@ -1701,6 +1738,7 @@ kind: DaemonSet
 metadata:
   labels:
     app: envoy
+    networking.knative.dev/ingress-provider: contour
   name: envoy
   namespace: contour-external
 spec:
@@ -1726,7 +1764,7 @@ spec:
         args:
         - envoy
         - shutdown-manager
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:8d3d3f935e36974dfe5ec7f7499862c7a621325536b8c01df68bcfaaea98de33
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
         imagePullPolicy: Always
         lifecycle:
           preStop:
@@ -1802,7 +1840,7 @@ spec:
         - --envoy-key-file=/certs/tls.key
         command:
         - contour
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:8d3d3f935e36974dfe5ec7f7499862c7a621325536b8c01df68bcfaaea98de33
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
         imagePullPolicy: Always
         name: envoy-initconfig
         volumeMounts:
@@ -1831,14 +1869,14 @@ spec:
         secret:
           secretName: cacert
       restartPolicy: Always
----
-
 
 ---
 apiVersion: rbac.authorization.k8s.io/v1beta1
 kind: ClusterRoleBinding
 metadata:
   name: contour-internal
+  labels:
+    networking.knative.dev/ingress-provider: contour
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: ClusterRole
@@ -1852,24 +1890,32 @@ apiVersion: v1
 kind: Namespace
 metadata:
   name: contour-internal
+  labels:
+    networking.knative.dev/ingress-provider: contour
 ---
 apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: contour
   namespace: contour-internal
+  labels:
+    networking.knative.dev/ingress-provider: contour
 ---
 apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: envoy
   namespace: contour-internal
+  labels:
+    networking.knative.dev/ingress-provider: contour
 ---
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: contour
   namespace: contour-internal
+  labels:
+    networking.knative.dev/ingress-provider: contour
 data:
   contour.yaml: |
     leaderelection:
@@ -1935,6 +1981,8 @@ metadata:
     controller-gen.kubebuilder.io/version: v0.2.4
   creationTimestamp: null
   name: ingressroutes.contour.heptio.com
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   additionalPrinterColumns:
   - JSONPath: .spec.virtualhost.fqdn
@@ -2304,6 +2352,8 @@ metadata:
     controller-gen.kubebuilder.io/version: v0.2.4
   creationTimestamp: null
   name: tlscertificatedelegations.contour.heptio.com
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   group: contour.heptio.com
   names:
@@ -2380,6 +2430,8 @@ metadata:
     controller-gen.kubebuilder.io/version: v0.2.4
   creationTimestamp: null
   name: httpproxies.projectcontour.io
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   additionalPrinterColumns:
   - JSONPath: .spec.virtualhost.fqdn
@@ -3115,6 +3167,8 @@ metadata:
     controller-gen.kubebuilder.io/version: v0.2.4
   creationTimestamp: null
   name: tlscertificatedelegations.projectcontour.io
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   group: projectcontour.io
   names:
@@ -3191,12 +3245,16 @@ kind: ServiceAccount
 metadata:
   name: contour-certgen
   namespace: contour-internal
+  labels:
+    networking.knative.dev/ingress-provider: contour
 ---
 apiVersion: rbac.authorization.k8s.io/v1beta1
 kind: RoleBinding
 metadata:
   name: contour
   namespace: contour-internal
+  labels:
+    networking.knative.dev/ingress-provider: contour
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: Role
@@ -3211,6 +3269,8 @@ kind: Role
 metadata:
   name: contour-certgen
   namespace: contour-internal
+  labels:
+    networking.knative.dev/ingress-provider: contour
 rules:
 - apiGroups:
   - ""
@@ -3230,6 +3290,8 @@ kind: Job
 metadata:
   name: contour-certgen
   namespace: contour-internal
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   ttlSecondsAfterFinished: 0
   template:
@@ -3243,7 +3305,7 @@ spec:
         # if we change this on each version, you can no longer upgrade
         # just by applying the deployment YAML.
         # See #2423, #2395, #2150, and #2030 for earlier questions about this.
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:8d3d3f935e36974dfe5ec7f7499862c7a621325536b8c01df68bcfaaea98de33
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
         imagePullPolicy: Always
         command:
         - contour
@@ -3270,6 +3332,8 @@ apiVersion: rbac.authorization.k8s.io/v1beta1
 kind: ClusterRole
 metadata:
   name: contour
+  labels:
+    networking.knative.dev/ingress-provider: contour
 rules:
 - apiGroups:
   - ""
@@ -3348,6 +3412,8 @@ kind: Role
 metadata:
   name: contour-leaderelection
   namespace: contour-internal
+  labels:
+    networking.knative.dev/ingress-provider: contour
 rules:
 - apiGroups:
   - ""
@@ -3373,6 +3439,8 @@ kind: RoleBinding
 metadata:
   name: contour-leaderelection
   namespace: contour-internal
+  labels:
+    networking.knative.dev/ingress-provider: contour
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: Role
@@ -3387,6 +3455,8 @@ kind: Service
 metadata:
   name: contour
   namespace: contour-internal
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   ports:
   - port: 8001
@@ -3410,6 +3480,8 @@ metadata:
     # for information about enabling the PROXY protocol on the ELB to recover
     # the original remote IP address.
     service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   # externalTrafficPolicy: Local
   ports:
@@ -3428,6 +3500,7 @@ kind: Deployment
 metadata:
   labels:
     app: contour
+    networking.knative.dev/ingress-provider: contour
   name: contour
   namespace: contour-internal
 spec:
@@ -3472,7 +3545,7 @@ spec:
         - --contour-key-file=/certs/tls.key
         - --config-path=/config/contour.yaml
         command: ["contour"]
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:8d3d3f935e36974dfe5ec7f7499862c7a621325536b8c01df68bcfaaea98de33
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
         imagePullPolicy: Always
         name: contour
         ports:
@@ -3538,6 +3611,7 @@ kind: DaemonSet
 metadata:
   labels:
     app: envoy
+    networking.knative.dev/ingress-provider: contour
   name: envoy
   namespace: contour-internal
 spec:
@@ -3563,7 +3637,7 @@ spec:
         args:
         - envoy
         - shutdown-manager
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:8d3d3f935e36974dfe5ec7f7499862c7a621325536b8c01df68bcfaaea98de33
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
         imagePullPolicy: Always
         lifecycle:
           preStop:
@@ -3639,7 +3713,7 @@ spec:
         - --envoy-key-file=/certs/tls.key
         command:
         - contour
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:8d3d3f935e36974dfe5ec7f7499862c7a621325536b8c01df68bcfaaea98de33
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
         imagePullPolicy: Always
         name: envoy-initconfig
         volumeMounts:
@@ -3668,7 +3742,5 @@ spec:
         secret:
           secretName: cacert
       restartPolicy: Always
----
-
 
 ---
diff --git a/third_party/contour-latest/net-contour.yaml b/third_party/contour-latest/net-contour.yaml
index 78a55da15..0a5e9c3d9 100644
--- a/third_party/contour-latest/net-contour.yaml
+++ b/third_party/contour-latest/net-contour.yaml
@@ -5,6 +5,7 @@ apiVersion: rbac.authorization.k8s.io/v1
 metadata:
   name: knative-contour-core
   labels:
+    networking.knative.dev/ingress-provider: contour
     serving.knative.dev/controller: "true"
 rules:
 - apiGroups: ["projectcontour.io"]
@@ -33,7 +34,8 @@ metadata:
   name: config-contour
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200518-f3b733a"
+    networking.knative.dev/ingress-provider: contour
+    serving.knative.dev/release: "v20200525-d02a1c4"
 data:
   _example: |
     ################################
@@ -75,6 +77,8 @@ kind: Deployment
 metadata:
   name: contour-ingress-controller
   namespace: knative-serving
+  labels:
+    networking.knative.dev/ingress-provider: contour
 spec:
   replicas: 1
   selector:
@@ -90,7 +94,7 @@ spec:
       - name: controller
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:adf935d27fdfc204e862abff0ced57471ae7b6fbacc6e9c9522b4c43daca614e
+        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:b6528a96949370697e2b7a96ad2ab847cf0c5a1ba0b5d5884241021f73cf3700
         resources:
           requests:
             cpu: 40m
diff --git a/third_party/kourier-latest/kourier.yaml b/third_party/kourier-latest/kourier.yaml
index 6c0031d76..c016164c8 100644
--- a/third_party/kourier-latest/kourier.yaml
+++ b/third_party/kourier-latest/kourier.yaml
@@ -2,24 +2,32 @@ apiVersion: v1
 kind: Namespace
 metadata:
   name: kourier-system
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 ---
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: config-logging
   namespace: kourier-system
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 ---
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: config-observability
   namespace: kourier-system
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 ---
 apiVersion: v1
 kind: ConfigMap
 metadata:
   name: config-leader-election
   namespace: kourier-system
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 data:
   _example: |
     ################################
@@ -69,6 +77,8 @@ kind: Service
 metadata:
   name: kourier
   namespace: kourier-system
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 spec:
   ports:
   - name: http2
@@ -88,6 +98,8 @@ kind: Deployment
 metadata:
   name: 3scale-kourier-gateway
   namespace: kourier-system
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 spec:
   selector:
     matchLabels:
@@ -103,7 +115,7 @@ spec:
         - /tmp/config/envoy-bootstrap.yaml
         command:
         - /usr/local/bin/envoy
-        image: docker.io/maistra/proxyv2-ubi8:1.0.8
+        image: docker.io/maistra/proxyv2-ubi8:1.1.0
         imagePullPolicy: Always
         name: kourier-gateway
         ports:
@@ -116,16 +128,20 @@ spec:
         volumeMounts:
         - name: config-volume
           mountPath: /tmp/config
+        lifecycle:
+          preStop:
+            exec:
+              command: ["curl", "-X", "POST", "--unix", "/tmp/envoy.admin", "http://localhost/healthcheck/fail"]
         readinessProbe:
           httpGet:
             httpHeaders:
             - name: Host
               value: internalkourier
-            path: /__internalkouriersnapshot
+            path: /ready
             port: 8081
             scheme: HTTP
-          initialDelaySeconds: 5
-          periodSeconds: 2
+          initialDelaySeconds: 10
+          periodSeconds: 5
       volumes:
       - name: config-volume
         configMap:
@@ -137,6 +153,8 @@ kind: Deployment
 metadata:
   name: 3scale-kourier-control
   namespace: kourier-system
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 spec:
   replicas: 1
   selector:
@@ -148,7 +166,7 @@ spec:
         app: 3scale-kourier-control
     spec:
       containers:
-      - image: gcr.io/knative-nightly/knative.dev/net-kourier/cmd/kourier@sha256:4c1bba191b7751a31d4bf7679a0e7cf5ce47abadb1fa8983b7064218f7a2fadb
+      - image: gcr.io/knative-nightly/knative.dev/net-kourier/cmd/kourier@sha256:f1ec90d11071ec5fbe9e83fa8fa340bf1b338a395e52e4dd3de2f5ce89ad5679
         imagePullPolicy: Always
         name: kourier-control
         env:
@@ -168,6 +186,8 @@ kind: ClusterRole
 metadata:
   name: 3scale-kourier
   namespace: kourier-system
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 rules:
 - apiGroups: [""]
   resources: ["pods", "endpoints", "namespaces", "services", "secrets", "configmaps"]
@@ -190,11 +210,15 @@ kind: ServiceAccount
 metadata:
   name: 3scale-kourier
   namespace: kourier-system
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 ---
 apiVersion: rbac.authorization.k8s.io/v1beta1
 kind: ClusterRoleBinding
 metadata:
   name: 3scale-kourier
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: ClusterRole
@@ -209,6 +233,8 @@ kind: Service
 metadata:
   name: kourier-internal
   namespace: kourier-system
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 spec:
   ports:
   - name: http2
@@ -224,6 +250,8 @@ kind: Service
 metadata:
   name: kourier-control
   namespace: kourier-system
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 spec:
   ports:
   - port: 18000
@@ -238,6 +266,8 @@ kind: ConfigMap
 metadata:
   name: kourier-bootstrap
   namespace: kourier-system
+  labels:
+    networking.knative.dev/ingress-provider: kourier
 data:
   envoy-bootstrap.yaml: |
     dynamic_resources:
diff --git a/vendor/knative.dev/pkg/test/prow/prow.go b/vendor/knative.dev/pkg/test/prow/prow.go
index a6e7f3a50..2af8a0095 100644
--- a/vendor/knative.dev/pkg/test/prow/prow.go
+++ b/vendor/knative.dev/pkg/test/prow/prow.go
@@ -34,9 +34,6 @@ import (
 )
 
 const (
-	// OrgName is the name of knative org
-	OrgName = "knative"
-
 	// BucketName is the gcs bucket for all knative builds
 	BucketName = "knative-prow"
 	// Latest is the filename storing latest build number
@@ -71,6 +68,7 @@ type Job struct {
 	Name        string
 	Type        string
 	Bucket      string  // optional
+	Org         string  // optional
 	Repo        string  // optional
 	StoragePath string  // optional
 	PullID      int     // only for Presubmit jobs
@@ -135,11 +133,13 @@ func Initialize(serviceAccount string) error {
 
 // NewJob creates new job struct
 // pullID is only saved by Presubmit job for determining StoragePath
-func NewJob(jobName, jobType, repoName string, pullID int) *Job {
+func NewJob(jobName, jobType, orgName, repoName string, pullID int) *Job {
 	job := Job{
 		Name:   jobName,
 		Type:   jobType,
 		Bucket: BucketName,
+		Org:    orgName,
+		Repo:   repoName,
 	}
 
 	switch jobType {
@@ -147,7 +147,7 @@ func NewJob(jobName, jobType, repoName string, pullID int) *Job {
 		job.StoragePath = path.Join("logs", jobName)
 	case PresubmitJob:
 		job.PullID = pullID
-		job.StoragePath = path.Join("pr-logs", "pull", OrgName+"_"+repoName, strconv.Itoa(pullID), jobName)
+		job.StoragePath = path.Join("pr-logs", "pull", orgName+"_"+repoName, strconv.Itoa(pullID), jobName)
 	case BatchJob:
 		job.StoragePath = path.Join("pr-logs", "pull", "batch", jobName)
 	default: