Repository: knative/serving
Pull request #7980 (2020-05-18 21:01:14)

Use common generation bump for Configuration
<!--
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/7980#" 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 **vaikas**
You can assign the PR to them by writing `/assign @vaikas` 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":["vaikas"]} -->
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/configuration_types.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/7980/pull-knative-serving-go-coverage/1262488567838412800/artifacts/line-cov.html#file0) | 0.0% | 100.0% | 100.0
[pkg/reconciler/configuration/configuration.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/7980/pull-knative-serving-go-coverage/1262488567838412800/artifacts/line-cov.html#file1) | 93.5% | 93.5% | 0.1


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/networking/v1alpha1/certificate_types.go b/pkg/apis/networking/v1alpha1/certificate_types.go
index 4691da7cb..3a78d007e 100644
--- a/pkg/apis/networking/v1alpha1/certificate_types.go
+++ b/pkg/apis/networking/v1alpha1/certificate_types.go
@@ -25,7 +25,7 @@ import (
 )
 
 // +genclient
-// +genreconciler:class=networking.knative.dev/certificate.class
+// +genreconciler:class=networking.knative.dev/certificate.class,krshapedlogic=true
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // Certificate is responsible for provisioning a SSL certificate for the
@@ -120,11 +120,6 @@ type HTTP01Challenge struct {
 	ServicePort intstr.IntOrString `json:"servicePort,omitempty"`
 }
 
-// GetTypeMeta retrieves the ObjectMeta of the Certificate. Implements the KRShaped interface.
-func (t *Certificate) GetTypeMeta() *metav1.TypeMeta {
-	return &t.TypeMeta
-}
-
 // GetStatus retrieves the status of the Certificate. Implements the KRShaped interface.
 func (t *Certificate) GetStatus() *duckv1.Status {
 	return &t.Status.Status
diff --git a/pkg/apis/networking/v1alpha1/ingress_types.go b/pkg/apis/networking/v1alpha1/ingress_types.go
index 1ddf40e87..de465c013 100644
--- a/pkg/apis/networking/v1alpha1/ingress_types.go
+++ b/pkg/apis/networking/v1alpha1/ingress_types.go
@@ -25,7 +25,7 @@ import (
 )
 
 // +genclient
-// +genreconciler
+// +genreconciler:krshapedlogic=true
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // Ingress is a collection of rules that allow inbound connections to reach the endpoints defined
@@ -346,11 +346,6 @@ const (
 	IngressConditionLoadBalancerReady apis.ConditionType = "LoadBalancerReady"
 )
 
-// GetTypeMeta retrieves the ObjectMeta of the Ingress. Implements the KRShaped interface.
-func (t *Ingress) GetTypeMeta() *metav1.TypeMeta {
-	return &t.TypeMeta
-}
-
 // GetStatus retrieves the status of the Ingress. Implements the KRShaped interface.
 func (t *Ingress) GetStatus() *duckv1.Status {
 	return &t.Status.Status
diff --git a/pkg/apis/networking/v1alpha1/serverlessservice_types.go b/pkg/apis/networking/v1alpha1/serverlessservice_types.go
index 8c2d0cd5a..f61525e62 100644
--- a/pkg/apis/networking/v1alpha1/serverlessservice_types.go
+++ b/pkg/apis/networking/v1alpha1/serverlessservice_types.go
@@ -26,7 +26,7 @@ import (
 )
 
 // +genclient
-// +genreconciler
+// +genreconciler:krshapedlogic=true
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // ServerlessService is a proxy for the K8s service objects containing the
@@ -143,11 +143,6 @@ const (
 	ActivatorEndpointsPopulated apis.ConditionType = "ActivatorEndpointsPopulated"
 )
 
-// GetTypeMeta retrieves the ObjectMeta of the ServerlessService. Implements the KRShaped interface.
-func (t *ServerlessService) GetTypeMeta() *metav1.TypeMeta {
-	return &t.TypeMeta
-}
-
 // GetStatus retrieves the status of the ServerlessService. Implements the KRShaped interface.
 func (t *ServerlessService) GetStatus() *duckv1.Status {
 	return &t.Status.Status
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/metadata_validation.go b/pkg/apis/serving/metadata_validation.go
index 30798d9fc..36c138617 100644
--- a/pkg/apis/serving/metadata_validation.go
+++ b/pkg/apis/serving/metadata_validation.go
@@ -25,18 +25,20 @@ import (
 	"k8s.io/apimachinery/pkg/api/equality"
 	"k8s.io/apimachinery/pkg/api/validation"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/sets"
+
 	"knative.dev/pkg/apis"
 	"knative.dev/serving/pkg/apis/autoscaling"
 	"knative.dev/serving/pkg/apis/config"
 )
 
 var (
-	allowedAnnotations = map[string]struct{}{
-		UpdaterAnnotation:                {},
-		CreatorAnnotation:                {},
-		RevisionLastPinnedAnnotationKey:  {},
-		GroupNamePrefix + "forceUpgrade": {},
-	}
+	allowedAnnotations = sets.NewString(
+		UpdaterAnnotation,
+		CreatorAnnotation,
+		RevisionLastPinnedAnnotationKey,
+		GroupNamePrefix+"forceUpgrade",
+	)
 )
 
 // ValidateObjectMetadata validates that `metadata` stanza of the
@@ -51,7 +53,7 @@ func ValidateObjectMetadata(ctx context.Context, meta metav1.Object) *apis.Field
 
 func validateKnativeAnnotations(annotations map[string]string) (errs *apis.FieldError) {
 	for key := range annotations {
-		if _, ok := allowedAnnotations[key]; !ok && strings.HasPrefix(key, GroupNamePrefix) {
+		if !allowedAnnotations.Has(key) && strings.HasPrefix(key, GroupNamePrefix) {
 			errs = errs.Also(apis.ErrInvalidKeyName(key, apis.CurrentField))
 		}
 	}
@@ -71,8 +73,8 @@ func ValidateQueueSidecarAnnotation(annotations map[string]string) *apis.FieldEr
 	if err != nil {
 		return apis.ErrInvalidValue(v, apis.CurrentField).ViaKey(QueueSideCarResourcePercentageAnnotation)
 	}
-	if value <= 0.1 || value > 100 {
-		return apis.ErrOutOfBoundsValue(value, 0.1, 100.0, QueueSideCarResourcePercentageAnnotation)
+	if value < 0.1 || value > 100 {
+		return apis.ErrOutOfBoundsValue(value, 0.1, 100.0, apis.CurrentField).ViaKey(QueueSideCarResourcePercentageAnnotation)
 	}
 	return nil
 }
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..36696ea15 100644
--- a/pkg/apis/serving/v1/revision_types.go
+++ b/pkg/apis/serving/v1/revision_types.go
@@ -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 31a047d75..267d23aa9 100644
--- a/pkg/autoscaler/metrics/collector.go
+++ b/pkg/autoscaler/metrics/collector.go
@@ -112,8 +112,8 @@ type MetricCollector struct {
 	statsScraperFactory StatsScraperFactory
 	tickProvider        func(time.Duration) *time.Ticker
 
-	collections      map[types.NamespacedName]*collection
 	collectionsMutex sync.RWMutex
+	collections      map[types.NamespacedName]*collection
 
 	watcherMutex sync.RWMutex
 	watcher      func(types.NamespacedName)
@@ -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
 }
@@ -239,8 +239,10 @@ func (c *MetricCollector) StableAndPanicRPS(key types.NamespacedName, now time.T
 
 // collection represents the collection of metrics for one specific entity.
 type collection struct {
-	metricMutex sync.RWMutex
-	metric      *av1alpha1.Metric
+	// mux guards access to all of the collection's state.
+	mux sync.RWMutex
+
+	metric *av1alpha1.Metric
 
 	// Fields relevant to metric collection in general.
 	concurrencyBuckets      *aggregation.TimedFloat64Buckets
@@ -249,23 +251,21 @@ type collection struct {
 	rpsPanicBuckets         *aggregation.TimedFloat64Buckets
 
 	// Fields relevant for metric scraping specifically.
-	scraperMutex sync.RWMutex
-	scraper      StatsScraper
-	errMutex     sync.RWMutex
-	lastErr      error
-	grp          sync.WaitGroup
-	stopCh       chan struct{}
+	scraper StatsScraper
+	lastErr error
+	grp     sync.WaitGroup
+	stopCh  chan struct{}
 }
 
 func (c *collection) updateScraper(ss StatsScraper) {
-	c.scraperMutex.Lock()
-	defer c.scraperMutex.Unlock()
+	c.mux.Lock()
+	defer c.mux.Unlock()
 	c.scraper = ss
 }
 
 func (c *collection) getScraper() StatsScraper {
-	c.scraperMutex.RLock()
-	defer c.scraperMutex.RUnlock()
+	c.mux.RLock()
+	defer c.mux.RUnlock()
 	return c.scraper
 }
 
@@ -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))
 				}
@@ -335,8 +336,8 @@ func (c *collection) close() {
 
 // updateMetric safely updates the metric stored in the collection.
 func (c *collection) updateMetric(metric *av1alpha1.Metric) {
-	c.metricMutex.Lock()
-	defer c.metricMutex.Unlock()
+	c.mux.Lock()
+	defer c.mux.Unlock()
 
 	c.metric = metric
 	c.concurrencyBuckets.ResizeWindow(metric.Spec.StableWindow)
@@ -347,8 +348,8 @@ func (c *collection) updateMetric(metric *av1alpha1.Metric) {
 
 // currentMetric safely returns the current metric stored in the collection.
 func (c *collection) currentMetric() *av1alpha1.Metric {
-	c.metricMutex.RLock()
-	defer c.metricMutex.RUnlock()
+	c.mux.RLock()
+	defer c.mux.RUnlock()
 
 	return c.metric
 }
@@ -356,8 +357,8 @@ func (c *collection) currentMetric() *av1alpha1.Metric {
 // updateLastError updates the last error returned from the scraper
 // and returns true if the error or error state changed.
 func (c *collection) updateLastError(err error) bool {
-	c.errMutex.Lock()
-	defer c.errMutex.Unlock()
+	c.mux.Lock()
+	defer c.mux.Unlock()
 
 	if c.lastErr == err {
 		return false
@@ -367,8 +368,8 @@ func (c *collection) updateLastError(err error) bool {
 }
 
 func (c *collection) lastError() error {
-	c.errMutex.RLock()
-	defer c.errMutex.RUnlock()
+	c.mux.RLock()
+	defer c.mux.RUnlock()
 
 	return c.lastErr
 }
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/networking/v1alpha1/certificate/reconciler.go b/pkg/client/injection/reconciler/networking/v1alpha1/certificate/reconciler.go
index 3a4483101..7ef6fa43a 100644
--- a/pkg/client/injection/reconciler/networking/v1alpha1/certificate/reconciler.go
+++ b/pkg/client/injection/reconciler/networking/v1alpha1/certificate/reconciler.go
@@ -175,10 +175,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/networking/v1alpha1/ingress/reconciler.go b/pkg/client/injection/reconciler/networking/v1alpha1/ingress/reconciler.go
index 2560e7511..0938d5b00 100644
--- a/pkg/client/injection/reconciler/networking/v1alpha1/ingress/reconciler.go
+++ b/pkg/client/injection/reconciler/networking/v1alpha1/ingress/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/networking/v1alpha1/serverlessservice/reconciler.go b/pkg/client/injection/reconciler/networking/v1alpha1/serverlessservice/reconciler.go
index 0d8d91383..478e647ee 100644
--- a/pkg/client/injection/reconciler/networking/v1alpha1/serverlessservice/reconciler.go
+++ b/pkg/client/injection/reconciler/networking/v1alpha1/serverlessservice/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/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/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/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/pkg/reconciler/serverlessservice/serverlessservice.go b/pkg/reconciler/serverlessservice/serverlessservice.go
index 5e1e99146..16fd92427 100644
--- a/pkg/reconciler/serverlessservice/serverlessservice.go
+++ b/pkg/reconciler/serverlessservice/serverlessservice.go
@@ -82,7 +82,6 @@ func (r *reconciler) ReconcileKind(ctx context.Context, sks *netv1alpha1.Serverl
 			return err
 		}
 	}
-	sks.Status.ObservedGeneration = sks.Generation
 	return nil
 }
 
diff --git a/third_party/cert-manager-0.12.0/net-certmanager.yaml b/third_party/cert-manager-0.12.0/net-certmanager.yaml
index 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/certificate_types_test.go b/pkg/apis/networking/v1alpha1/certificate_types_test.go
index f14385455..8764b0dc8 100644
--- a/pkg/apis/networking/v1alpha1/certificate_types_test.go
+++ b/pkg/apis/networking/v1alpha1/certificate_types_test.go
@@ -17,8 +17,6 @@ package v1alpha1
 
 import (
 	"testing"
-
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 func TestCertificateGetStatus(t *testing.T) {
@@ -27,16 +25,6 @@ func TestCertificateGetStatus(t *testing.T) {
 	}
 
 	if got, want := r.GetStatus(), &r.Status.Status; got != want {
-		t.Errorf("GotStatus=%v, want=%v", got, want)
-	}
-}
-
-func TestCertificateGetObjectMeta(t *testing.T) {
-	r := &Certificate{
-		TypeMeta: metav1.TypeMeta{},
-	}
-
-	if got, want := r.GetTypeMeta(), &r.TypeMeta; got != want {
-		t.Errorf("GotTypeMeta=%v, want=%v", got, want)
+		t.Errorf("GetStatus=%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/ingress_types_test.go b/pkg/apis/networking/v1alpha1/ingress_types_test.go
index 9ee4bc096..1f9e91ec5 100644
--- a/pkg/apis/networking/v1alpha1/ingress_types_test.go
+++ b/pkg/apis/networking/v1alpha1/ingress_types_test.go
@@ -17,8 +17,6 @@ package v1alpha1
 
 import (
 	"testing"
-
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 func TestIngressGetStatus(t *testing.T) {
@@ -27,16 +25,6 @@ func TestIngressGetStatus(t *testing.T) {
 	}
 
 	if got, want := r.GetStatus(), &r.Status.Status; got != want {
-		t.Errorf("GotStatus=%v, want=%v", got, want)
-	}
-}
-
-func TestIngressGetObjectMeta(t *testing.T) {
-	r := &Ingress{
-		TypeMeta: metav1.TypeMeta{},
-	}
-
-	if got, want := r.GetTypeMeta(), &r.TypeMeta; got != want {
-		t.Errorf("GotTypeMeta=%v, want=%v", got, want)
+		t.Errorf("GetStatus=%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/networking/v1alpha1/serverlessservice_types_test.go b/pkg/apis/networking/v1alpha1/serverlessservice_types_test.go
index b862630a2..662025263 100644
--- a/pkg/apis/networking/v1alpha1/serverlessservice_types_test.go
+++ b/pkg/apis/networking/v1alpha1/serverlessservice_types_test.go
@@ -17,8 +17,6 @@ package v1alpha1
 
 import (
 	"testing"
-
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
 func TestServerlessServiceGetStatus(t *testing.T) {
@@ -27,16 +25,6 @@ func TestServerlessServiceGetStatus(t *testing.T) {
 	}
 
 	if got, want := r.GetStatus(), &r.Status.Status; got != want {
-		t.Errorf("GotStatus=%v, want=%v", got, want)
-	}
-}
-
-func TestServerlessServiceGetObjectMeta(t *testing.T) {
-	r := &ServerlessService{
-		TypeMeta: metav1.TypeMeta{},
-	}
-
-	if got, want := r.GetTypeMeta(), &r.TypeMeta; got != want {
-		t.Errorf("GotTypeMeta=%v, want=%v", got, want)
+		t.Errorf("GetStatus=%v, want=%v", got, want)
 	}
 }
diff --git a/pkg/apis/serving/metadata_validation_test.go b/pkg/apis/serving/metadata_validation_test.go
index 5ca9a4c6d..2c862ced8 100644
--- a/pkg/apis/serving/metadata_validation_test.go
+++ b/pkg/apis/serving/metadata_validation_test.go
@@ -209,13 +209,22 @@ func TestValidateQueueSidecarAnnotation(t *testing.T) {
 		annotation map[string]string
 		expectErr  *apis.FieldError
 	}{{
-		name: "Queue sidecar resource percentage annotation more than 100",
+		name: "too small",
 		annotation: map[string]string{
-			QueueSideCarResourcePercentageAnnotation: "200",
+			QueueSideCarResourcePercentageAnnotation: "0.01982",
 		},
 		expectErr: &apis.FieldError{
-			Message: "expected 0.1 <= 200 <= 100",
-			Paths:   []string{QueueSideCarResourcePercentageAnnotation},
+			Message: "expected 0.1 <= 0.01982 <= 100",
+			Paths:   []string{fmt.Sprintf("[%s]", QueueSideCarResourcePercentageAnnotation)},
+		},
+	}, {
+		name: "too big for Queue sidecar resource percentage annotation",
+		annotation: map[string]string{
+			QueueSideCarResourcePercentageAnnotation: "100.0001",
+		},
+		expectErr: &apis.FieldError{
+			Message: "expected 0.1 <= 100.0001 <= 100",
+			Paths:   []string{fmt.Sprintf("[%s]", QueueSideCarResourcePercentageAnnotation)},
 		},
 	}, {
 		name: "Invalid queue sidecar resource percentage annotation",
@@ -232,7 +241,12 @@ func TestValidateQueueSidecarAnnotation(t *testing.T) {
 	}, {
 		name: "different annotation other than QueueSideCarResourcePercentageAnnotation",
 		annotation: map[string]string{
-			CreatorAnnotation: "",
+			CreatorAnnotation: "umph",
+		},
+	}, {
+		name: "valid value for Queue sidecar resource percentage annotation",
+		annotation: map[string]string{
+			QueueSideCarResourcePercentageAnnotation: "0.1",
 		},
 	}, {
 		name: "valid value for Queue sidecar resource percentage annotation",
diff --git a/pkg/apis/serving/v1/revision_validation_test.go b/pkg/apis/serving/v1/revision_validation_test.go
index b5f313066..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{
@@ -871,7 +869,7 @@ func TestRevisionTemplateSpecValidation(t *testing.T) {
 		},
 		want: (&apis.FieldError{
 			Message: "expected 0.1 <= 200 <= 100",
-			Paths:   []string{serving.QueueSideCarResourcePercentageAnnotation},
+			Paths:   []string{"[" + serving.QueueSideCarResourcePercentageAnnotation + "]"},
 		}).ViaField("metadata.annotations"),
 	}, {
 		name: "Invalid queue sidecar resource percentage annotation",
@@ -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 ed6c35a70..9e21de31f 100644
--- a/pkg/apis/serving/v1alpha1/revision_validation_test.go
+++ b/pkg/apis/serving/v1alpha1/revision_validation_test.go
@@ -401,7 +401,7 @@ func TestRevisionTemplateSpecValidation(t *testing.T) {
 		},
 		want: (&apis.FieldError{
 			Message: "expected 0.1 <= 200 <= 100",
-			Paths:   []string{serving.QueueSideCarResourcePercentageAnnotation},
+			Paths:   []string{"[" + serving.QueueSideCarResourcePercentageAnnotation + "]"},
 		}).ViaField("metadata.annotations"),
 	}, {
 		name: "Invalid queue sidecar resource percentage annotation",
@@ -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 c7c56dff9..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{
@@ -821,7 +819,7 @@ func TestRevisionTemplateSpecValidation(t *testing.T) {
 		},
 		want: (&apis.FieldError{
 			Message: "expected 0.1 <= 200 <= 100",
-			Paths:   []string{serving.QueueSideCarResourcePercentageAnnotation},
+			Paths:   []string{"[" + serving.QueueSideCarResourcePercentageAnnotation + "]"},
 		}).ViaField("metadata.annotations"),
 	}, {
 		name: "Invalid queue sidecar resource percentage annotation",
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/resources/service_test.go b/pkg/reconciler/route/resources/service_test.go
index 81ccb3fc8..df6d8f5e5 100644
--- a/pkg/reconciler/route/resources/service_test.go
+++ b/pkg/reconciler/route/resources/service_test.go
@@ -53,193 +53,192 @@ var (
 )
 
 func TestNewMakeK8SService(t *testing.T) {
-	scenarios := map[string]struct {
-		// Inputs
+	tests := []struct {
+		name         string
 		route        *v1.Route
 		ingress      *netv1alpha1.Ingress
 		targetName   string
 		expectedSpec corev1.ServiceSpec
 		expectedMeta metav1.ObjectMeta
 		shouldFail   bool
-	}{
-		"no-loadbalancer": {
-			route: r,
-			ingress: &netv1alpha1.Ingress{
-				Status: netv1alpha1.IngressStatus{},
-			},
-			expectedMeta: expectedMeta,
-			shouldFail:   true,
+	}{{
+		name:  "no-loadbalancer",
+		route: r,
+		ingress: &netv1alpha1.Ingress{
+			Status: netv1alpha1.IngressStatus{},
 		},
-		"empty-loadbalancer": {
-			route: r,
-			ingress: &netv1alpha1.Ingress{
-				Status: netv1alpha1.IngressStatus{
-					LoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{}},
-					},
-					PublicLoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{}},
-					},
-					PrivateLoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{}},
-					},
+		expectedMeta: expectedMeta,
+		shouldFail:   true,
+	}, {
+		name:  "empty-loadbalancer",
+		route: r,
+		ingress: &netv1alpha1.Ingress{
+			Status: netv1alpha1.IngressStatus{
+				LoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{}},
+				},
+				PublicLoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{}},
+				},
+				PrivateLoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{}},
 				},
 			},
-			expectedMeta: expectedMeta,
-			shouldFail:   true,
 		},
-		"multi-loadbalancer": {
-			route: r,
-			ingress: &netv1alpha1.Ingress{
-				Status: netv1alpha1.IngressStatus{
-					LoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{
-							Domain: "domain.com",
-						}, {
-							DomainInternal: "domain.com",
-						}},
-					},
-					PublicLoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{
-							Domain: "domain.com",
-						}, {
-							DomainInternal: "domain.com",
-						}},
-					},
-					PrivateLoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{
-							Domain: "domain.com",
-						}, {
-							DomainInternal: "domain.com",
-						}},
-					},
+		expectedMeta: expectedMeta,
+		shouldFail:   true,
+	}, {
+		name:  "multi-loadbalancer",
+		route: r,
+		ingress: &netv1alpha1.Ingress{
+			Status: netv1alpha1.IngressStatus{
+				LoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{
+						Domain: "domain.com",
+					}, {
+						DomainInternal: "domain.com",
+					}},
+				},
+				PublicLoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{
+						Domain: "domain.com",
+					}, {
+						DomainInternal: "domain.com",
+					}},
+				},
+				PrivateLoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{
+						Domain: "domain.com",
+					}, {
+						DomainInternal: "domain.com",
+					}},
 				},
 			},
-			expectedMeta: expectedMeta,
-			shouldFail:   true,
 		},
-		"ingress-with-domain": {
-			route: r,
-			ingress: &netv1alpha1.Ingress{
-				Status: netv1alpha1.IngressStatus{
-					LoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{Domain: "domain.com"}},
-					},
-					PublicLoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{Domain: "domain.com"}},
-					},
-					PrivateLoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{Domain: "domain.com"}},
-					},
+		expectedMeta: expectedMeta,
+		shouldFail:   true,
+	}, {
+		name:  "ingress-with-domain",
+		route: r,
+		ingress: &netv1alpha1.Ingress{
+			Status: netv1alpha1.IngressStatus{
+				LoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{Domain: "domain.com"}},
+				},
+				PublicLoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{Domain: "domain.com"}},
+				},
+				PrivateLoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{Domain: "domain.com"}},
 				},
 			},
-			expectedMeta: expectedMeta,
-			expectedSpec: corev1.ServiceSpec{
-				Type:         corev1.ServiceTypeExternalName,
-				ExternalName: "domain.com",
-			},
 		},
-		"ingress-with-domaininternal": {
-			route: r,
-			ingress: &netv1alpha1.Ingress{
-				Status: netv1alpha1.IngressStatus{
-					LoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{DomainInternal: "istio-ingressgateway.istio-system.svc.cluster.local"}},
-					},
-					PublicLoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{DomainInternal: "istio-ingressgateway.istio-system.svc.cluster.local"}},
-					},
-					PrivateLoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{DomainInternal: "private-istio-ingressgateway.istio-system.svc.cluster.local"}},
-					},
+		expectedMeta: expectedMeta,
+		expectedSpec: corev1.ServiceSpec{
+			Type:         corev1.ServiceTypeExternalName,
+			ExternalName: "domain.com",
+		},
+	}, {
+		name:  "ingress-with-domaininternal",
+		route: r,
+		ingress: &netv1alpha1.Ingress{
+			Status: netv1alpha1.IngressStatus{
+				LoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{DomainInternal: "istio-ingressgateway.istio-system.svc.cluster.local"}},
+				},
+				PublicLoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{DomainInternal: "istio-ingressgateway.istio-system.svc.cluster.local"}},
+				},
+				PrivateLoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{DomainInternal: "private-istio-ingressgateway.istio-system.svc.cluster.local"}},
 				},
-			},
-			expectedMeta: expectedMeta,
-			expectedSpec: corev1.ServiceSpec{
-				Type:            corev1.ServiceTypeExternalName,
-				ExternalName:    "private-istio-ingressgateway.istio-system.svc.cluster.local",
-				SessionAffinity: corev1.ServiceAffinityNone,
 			},
 		},
-		"ingress-with-only-mesh": {
-			route: r,
-			ingress: &netv1alpha1.Ingress{
-				Status: netv1alpha1.IngressStatus{
-					LoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{MeshOnly: true}},
-					},
-					PublicLoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{MeshOnly: true}},
-					},
-					PrivateLoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{MeshOnly: true}},
-					},
+		expectedMeta: expectedMeta,
+		expectedSpec: corev1.ServiceSpec{
+			Type:            corev1.ServiceTypeExternalName,
+			ExternalName:    "private-istio-ingressgateway.istio-system.svc.cluster.local",
+			SessionAffinity: corev1.ServiceAffinityNone,
+		},
+	}, {
+		name:  "ingress-with-only-mesh",
+		route: r,
+		ingress: &netv1alpha1.Ingress{
+			Status: netv1alpha1.IngressStatus{
+				LoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{MeshOnly: true}},
+				},
+				PublicLoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{MeshOnly: true}},
+				},
+				PrivateLoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{MeshOnly: true}},
 				},
 			},
-			expectedMeta: expectedMeta,
-			expectedSpec: corev1.ServiceSpec{
-				Type: corev1.ServiceTypeClusterIP,
-				Ports: []corev1.ServicePort{{
-					Name: "http",
-					Port: 80,
-				}},
-			},
 		},
-		"with-target-name-specified": {
-			route:      r,
-			targetName: "my-target-name",
-			ingress: &netv1alpha1.Ingress{
-				Status: netv1alpha1.IngressStatus{
-					LoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{MeshOnly: true}},
-					},
-					PublicLoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{MeshOnly: true}},
-					},
-					PrivateLoadBalancer: &netv1alpha1.LoadBalancerStatus{
-						Ingress: []netv1alpha1.LoadBalancerIngressStatus{{MeshOnly: true}},
-					},
+		expectedMeta: expectedMeta,
+		expectedSpec: corev1.ServiceSpec{
+			Type: corev1.ServiceTypeClusterIP,
+			Ports: []corev1.ServicePort{{
+				Name: "http",
+				Port: 80,
+			}},
+		},
+	}, {
+		name:       "with-target-name-specified",
+		route:      r,
+		targetName: "my-target-name",
+		ingress: &netv1alpha1.Ingress{
+			Status: netv1alpha1.IngressStatus{
+				LoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{MeshOnly: true}},
 				},
-			},
-			expectedMeta: metav1.ObjectMeta{
-				Name:      "my-target-name-test-route",
-				Namespace: r.Namespace,
-				OwnerReferences: []metav1.OwnerReference{
-					*kmeta.NewControllerRef(r),
+				PublicLoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{MeshOnly: true}},
 				},
-				Labels: map[string]string{
-					serving.RouteLabelKey: r.Name,
+				PrivateLoadBalancer: &netv1alpha1.LoadBalancerStatus{
+					Ingress: []netv1alpha1.LoadBalancerIngressStatus{{MeshOnly: true}},
 				},
 			},
-			expectedSpec: corev1.ServiceSpec{
-				Type: corev1.ServiceTypeClusterIP,
-				Ports: []corev1.ServicePort{{
-					Name: "http",
-					Port: 80,
-				}},
+		},
+		expectedMeta: metav1.ObjectMeta{
+			Name:      "my-target-name-test-route",
+			Namespace: r.Namespace,
+			OwnerReferences: []metav1.OwnerReference{
+				*kmeta.NewControllerRef(r),
+			},
+			Labels: map[string]string{
+				serving.RouteLabelKey: r.Name,
 			},
 		},
-	}
+		expectedSpec: corev1.ServiceSpec{
+			Type: corev1.ServiceTypeClusterIP,
+			Ports: []corev1.ServicePort{{
+				Name: "http",
+				Port: 80,
+			}},
+		},
+	}}
 
-	for name, scenario := range scenarios {
-		t.Run(name, func(t *testing.T) {
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
 			cfg := testConfig()
 			ctx := config.ToContext(context.Background(), cfg)
-			service, err := MakeK8sService(ctx, scenario.route, scenario.targetName, scenario.ingress, false)
+			service, err := MakeK8sService(ctx, tc.route, tc.targetName, tc.ingress, false)
 			// Validate
-			if scenario.shouldFail && err == nil {
-				t.Errorf("Test %q failed: returned success but expected error", name)
+			if tc.shouldFail && err == nil {
+				t.Fatal("MakeK8sService returned success but expected error")
 			}
-			if !scenario.shouldFail {
+			if !tc.shouldFail {
 				if err != nil {
-					t.Errorf("Test %q failed: returned error: %v", name, err)
+					t.Fatal("MakeK8sService:", err)
 				}
 
-				if !cmp.Equal(scenario.expectedMeta, service.ObjectMeta) {
-					t.Errorf("Unexpected Metadata (-want +got): %s", cmp.Diff(scenario.expectedMeta, service.ObjectMeta))
+				if !cmp.Equal(tc.expectedMeta, service.ObjectMeta) {
+					t.Error("Unexpected Metadata (-want +got):", cmp.Diff(tc.expectedMeta, service.ObjectMeta))
 				}
-				if !cmp.Equal(scenario.expectedSpec, service.Spec) {
-					t.Errorf("Unexpected ServiceSpec (-want +got): %s", cmp.Diff(scenario.expectedSpec, service.Spec))
+				if !cmp.Equal(tc.expectedSpec, service.Spec) {
+					t.Error("Unexpected ServiceSpec (-want +got):", cmp.Diff(tc.expectedSpec, service.Spec))
 				}
 			}
 		})
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: