Repository: knative/serving
Pull request #8353 (2020-06-17 15:01:50)

Add and set InitialScale field in DeciderSpec
/lint

Part 4 of #7682

<!-- Please include the 'why' behind your changes if no issue exists -->
## Proposed Changes
* Add InitialScale field to DeciderSpec, so that we are able to access the initial scale in the Autoscaler constructor to determine if we want to start in panic mode.

**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
NONE
```
/cc @julz @vagababov @markusthoemmes @dprotaso @mattmoor 


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

This pull-request has been approved by: *<a href="https://github.com/knative/serving/pull/8353#" title="Author self-approved">taragu</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **markusthoemmes**
You can assign the PR to them by writing `/assign @markusthoemmes` 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:

- **[pkg/autoscaler/OWNERS](https://github.com/knative/serving/blob/master/pkg/autoscaler/OWNERS)**
- **[pkg/reconciler/autoscaling/OWNERS](https://github.com/knative/serving/blob/master/pkg/reconciler/autoscaling/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":["markusthoemmes"]} -->
knative-prow-robot: @taragu: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-upgrade-tests | cc2d5b0c881f3fac25e11671675195f876f892c4 | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_serving/8353/pull-knative-serving-upgrade-tests/1273277112471523331) | `/test pull-knative-serving-upgrade-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_serving/8353). [Your PR dashboard](https://gubernator.knative.dev/pr/taragu).

<details>

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

Test name | Triggers | Retries
--- | --- | ---
pull-knative-serving-upgrade-tests | [pull-knative-serving-upgrade-tests](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_serving/8353/pull-knative-serving-upgrade-tests/1273277112471523331) | 1/3

Automatically retrying due to test flakiness...
/test pull-knative-serving-upgrade-tests

Patch:
diff --git a/go.mod b/go.mod
index 66b30ce04..5f2c9397a 100644
--- a/go.mod
+++ b/go.mod
@@ -42,9 +42,9 @@ require (
 	k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29
 	k8s.io/metrics v0.17.6
 	knative.dev/caching v0.0.0-20200606210318-787aec80f71c
-	knative.dev/networking v0.0.0-20200611155523-bd13675bcf70
-	knative.dev/pkg v0.0.0-20200615224524-dbd32a77f41c
-	knative.dev/test-infra v0.0.0-20200612191923-a4dd434e085b
+	knative.dev/networking v0.0.0-20200617160325-c5b1bddf865c
+	knative.dev/pkg v0.0.0-20200617194224-40c7e164ccd7
+	knative.dev/test-infra v0.0.0-20200617184924-0fc4e2aeef74
 )
 
 // pin the older grpc - see: https://github.com/grpc/grpc-go/issues/3180
diff --git a/go.sum b/go.sum
index 1aecab7b9..7ef9c2112 100644
--- a/go.sum
+++ b/go.sum
@@ -1339,8 +1339,8 @@ knative.dev/caching v0.0.0-20200606210318-787aec80f71c h1:4w9QOkxY1VNH/82LIefx8u
 knative.dev/caching v0.0.0-20200606210318-787aec80f71c/go.mod h1:rWD+0zSqcE7L//jJyt866xtXsOi9hCdB0FoD4w2Rygg=
 knative.dev/eventing-contrib v0.6.1-0.20190723221543-5ce18048c08b/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
 knative.dev/eventing-contrib v0.11.2/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
-knative.dev/networking v0.0.0-20200611155523-bd13675bcf70 h1:xS3YAzmfFwmNXFXj6rM2n3FbBHziWvN3FMQmgUKv7bU=
-knative.dev/networking v0.0.0-20200611155523-bd13675bcf70/go.mod h1:PU1dM8mNwh+k3xDBXR54h2fafRgp8pIU5W43dYV7LkA=
+knative.dev/networking v0.0.0-20200617160325-c5b1bddf865c h1:Uv3REjPWLs/mohATHb7G9FZFbH4lkiHGX7IVDz+J+zE=
+knative.dev/networking v0.0.0-20200617160325-c5b1bddf865c/go.mod h1:eypU2utW0QjphexxIWI39NrzDxqrGpMWcZXkx8gM1xM=
 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-20200207155214-fef852970f43/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
@@ -1354,10 +1354,11 @@ knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7/go.mod h1:QgNZTxnwpB/oSpNcfnL
 knative.dev/pkg v0.0.0-20200520073958-94316e20e860/go.mod h1:QgNZTxnwpB/oSpNcfnLVlw+WpEwwyKAvJlvR3hgeltA=
 knative.dev/pkg v0.0.0-20200603222317-b79e4a24ca50 h1:sbJMCCtOENPgfE5dUSwBUxZtJzhfqORhExyWFX7L0Fk=
 knative.dev/pkg v0.0.0-20200603222317-b79e4a24ca50/go.mod h1:8IfPj/lpuKHHg82xZCl2wuFZ3BM96To72sN1W8T9wjQ=
-knative.dev/pkg v0.0.0-20200609184032-fef70cc2616b/go.mod h1:rA+FklsrVahwF4a+D63NyHJlzDoAFH81K4J5CYuE3bA=
 knative.dev/pkg v0.0.0-20200611204322-2ddcfef739a2/go.mod h1:rA+FklsrVahwF4a+D63NyHJlzDoAFH81K4J5CYuE3bA=
-knative.dev/pkg v0.0.0-20200615224524-dbd32a77f41c h1:sFGEpJ7MJSaUs9nSOdBHVVxGsOvuFrl4iEHoT4uRAfM=
-knative.dev/pkg v0.0.0-20200615224524-dbd32a77f41c/go.mod h1:rA+FklsrVahwF4a+D63NyHJlzDoAFH81K4J5CYuE3bA=
+knative.dev/pkg v0.0.0-20200616232624-ffb929374a39 h1:wcbxKTDGymyIMI3uUkli5KqvmfBnHLJpTI77QREjC0A=
+knative.dev/pkg v0.0.0-20200616232624-ffb929374a39/go.mod h1:NtvASnZHh7yo6VBfGk7dXdpAK2vJyrDc/+Syk4os8xk=
+knative.dev/pkg v0.0.0-20200617194224-40c7e164ccd7 h1:okYYk80lDA++IV4SqZb53xZX5gTxdZYzkAMihvF7Wss=
+knative.dev/pkg v0.0.0-20200617194224-40c7e164ccd7/go.mod h1:4ty6MSlNjZk5qBaGb3Gt4gopjMD4gRknfTABblcFpQ8=
 knative.dev/sample-controller v0.0.0-20200510050845-bf7c19498b7e/go.mod h1:D2ZDLrR9Dq9LiiVN7TatzI7WMcEPgk1MHbbhgBKE6W8=
 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=
@@ -1368,9 +1369,11 @@ knative.dev/test-infra v0.0.0-20200519015156-82551620b0a9/go.mod h1:A5b2OAXTOeHT
 knative.dev/test-infra v0.0.0-20200519161858-554a95a37986/go.mod h1:LeNa1Wvn47efeQUkpkn3XG7Fx9Ga+rhAP13SZyjaEGg=
 knative.dev/test-infra v0.0.0-20200522180958-6a0a9b9d893a/go.mod h1:n9eQkzmSNj8BiqNFl1lzoz68D09uMeJfyOjc132Gbik=
 knative.dev/test-infra v0.0.0-20200606045118-14ebc4a42974/go.mod h1://I6IZIF0QDgs5wotU243ZZ5cTpm6/GthayjUenBBc0=
-knative.dev/test-infra v0.0.0-20200610141822-fd009ecf10fe/go.mod h1:vyiP+4ospN/PJgwVzHkjnhlNP4uBPnvwkgb9dPCFSj4=
-knative.dev/test-infra v0.0.0-20200612191923-a4dd434e085b h1:QF1luh24SFfO3llKMl3i1drC6hBBqXCO1D8QV13gkNY=
-knative.dev/test-infra v0.0.0-20200612191923-a4dd434e085b/go.mod h1:+BfrTJpc++rH30gX/C0QY6NT2eYVzycll52uw6CrQnc=
+knative.dev/test-infra v0.0.0-20200615231324-3a016f44102c/go.mod h1:+BfrTJpc++rH30gX/C0QY6NT2eYVzycll52uw6CrQnc=
+knative.dev/test-infra v0.0.0-20200617011927-7220df1de9de h1:6249W/lXfJw84H4loClOfjN4NdYiFt9ZJ4deAeprPQI=
+knative.dev/test-infra v0.0.0-20200617011927-7220df1de9de/go.mod h1:+BfrTJpc++rH30gX/C0QY6NT2eYVzycll52uw6CrQnc=
+knative.dev/test-infra v0.0.0-20200617184924-0fc4e2aeef74 h1:+dA0pgMzoPxUk4hJqMMLvUnBDQw/E/FcLYG7AxteFCo=
+knative.dev/test-infra v0.0.0-20200617184924-0fc4e2aeef74/go.mod h1:+BfrTJpc++rH30gX/C0QY6NT2eYVzycll52uw6CrQnc=
 modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
diff --git a/pkg/activator/handler/metrics.go b/pkg/activator/handler/metrics.go
index 73fc3de7d..812c25ed7 100644
--- a/pkg/activator/handler/metrics.go
+++ b/pkg/activator/handler/metrics.go
@@ -17,6 +17,7 @@ limitations under the License.
 package handler
 
 import (
+	pkgmetrics "knative.dev/pkg/metrics"
 	"knative.dev/serving/pkg/metrics"
 
 	"go.opencensus.io/stats"
@@ -50,7 +51,7 @@ func register() {
 	// Create views to see our measurements. This can return an error if
 	// a previously-registered view has the same name with a different value.
 	// View name defaults to the measure name if unspecified.
-	if err := view.Register(
+	if err := pkgmetrics.RegisterResourceView(
 		&view.View{
 			Description: "Concurrent requests that are routed to Activator",
 			Measure:     requestConcurrencyM,
diff --git a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go
index 386db59a5..9cec371cd 100644
--- a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go
+++ b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go
@@ -60,16 +60,12 @@ func (pa *PodAutoscaler) Metric() string {
 	return defaultMetric(pa.Class())
 }
 
-func (pa *PodAutoscaler) annotationInt32(key string) int32 {
+func (pa *PodAutoscaler) annotationInt32(key string) (int32, bool) {
 	if s, ok := pa.Annotations[key]; ok {
-		// no error check: relying on validation
-		i, _ := strconv.ParseInt(s, 10, 32)
-		if i < 0 {
-			return 0
-		}
-		return int32(i)
+		i, err := strconv.ParseInt(s, 10, 32)
+		return int32(i), err == nil
 	}
-	return 0
+	return 0, false
 }
 
 func (pa *PodAutoscaler) annotationFloat64(key string) (float64, bool) {
@@ -86,9 +82,9 @@ func (pa *PodAutoscaler) annotationFloat64(key string) (float64, bool) {
 // Note: min will be ignored if the PA is not reachable
 func (pa *PodAutoscaler) ScaleBounds() (min, max int32) {
 	if pa.Spec.Reachability != ReachabilityUnreachable {
-		min = pa.annotationInt32(autoscaling.MinScaleAnnotationKey)
+		min, _ = pa.annotationInt32(autoscaling.MinScaleAnnotationKey)
 	}
-	max = pa.annotationInt32(autoscaling.MaxScaleAnnotationKey)
+	max, _ = pa.annotationInt32(autoscaling.MaxScaleAnnotationKey)
 
 	return
 }
@@ -146,6 +142,12 @@ func (pa *PodAutoscaler) PanicThresholdPercentage() (percentage float64, ok bool
 	return pa.annotationFloat64(autoscaling.PanicThresholdPercentageAnnotationKey)
 }
 
+// InitialScale returns the initial scale on the revision if present, or false if not present.
+func (pa *PodAutoscaler) InitialScale() (int32, bool) {
+	// The value is validated in the webhook.
+	return pa.annotationInt32(autoscaling.InitialScaleAnnotationKey)
+}
+
 // IsReady returns true if the Status condition PodAutoscalerConditionReady
 // is true and the latest spec has been observed.
 func (pa *PodAutoscaler) IsReady() bool {
diff --git a/pkg/autoscaler/metrics/stats_scraper.go b/pkg/autoscaler/metrics/stats_scraper.go
index dc29764f4..45cbaa269 100644
--- a/pkg/autoscaler/metrics/stats_scraper.go
+++ b/pkg/autoscaler/metrics/stats_scraper.go
@@ -102,22 +102,32 @@ type scrapeClient interface {
 	Scrape(url string) (Stat, error)
 }
 
-// cacheDisabledClient is a http client with cache disabled. It is shared by
-// every goruntime for a revision scraper.
-var cacheDisabledClient = &http.Client{
+// noKeepaliveClient is a http client with HTTP Keep-Alive disabled.
+// This client is used in the mesh case since we want to get a new connection -
+// and therefore, hopefully, host - on every scrape of the service.
+var noKeepaliveClient = &http.Client{
 	Transport: &http.Transport{
-		// Do not use the cached connection
 		DisableKeepAlives: true,
 	},
 	Timeout: httpClientTimeout,
 }
 
+// client is a normal http client with HTTP Keep-Alive enabled.
+// This client is used in the direct pod scraping (no mesh) case where we want
+// to take advantage of HTTP Keep-Alive to avoid connection creation overhead
+// between scrapes of the same pod.
+var client = &http.Client{
+	Timeout: httpClientTimeout,
+}
+
 // serviceScraper scrapes Revision metrics via a K8S service by sampling. Which
 // pod to be picked up to serve the request is decided by K8S. Please see
 // https://kubernetes.io/docs/concepts/services-networking/network-policies/
 // for details.
 type serviceScraper struct {
-	sClient  scrapeClient
+	directClient scrapeClient
+	meshClient   scrapeClient
+
 	counter  resources.EndpointsCounter
 	url      string
 	statsCtx context.Context
@@ -131,18 +141,22 @@ type serviceScraper struct {
 // the given Metric is responsible for.
 func NewStatsScraper(metric *av1alpha1.Metric, counter resources.EndpointsCounter,
 	podAccessor resources.PodAccessor, logger *zap.SugaredLogger) (StatsScraper, error) {
-	sClient, err := newHTTPScrapeClient(cacheDisabledClient)
+	directClient, err := newHTTPScrapeClient(client)
 	if err != nil {
 		return nil, err
 	}
-	return newServiceScraperWithClient(metric, counter, podAccessor, sClient, logger)
+	meshClient, err := newHTTPScrapeClient(noKeepaliveClient)
+	if err != nil {
+		return nil, err
+	}
+	return newServiceScraperWithClient(metric, counter, podAccessor, directClient, meshClient, logger)
 }
 
 func newServiceScraperWithClient(
 	metric *av1alpha1.Metric,
 	counter resources.EndpointsCounter,
 	podAccessor resources.PodAccessor,
-	sClient scrapeClient,
+	directClient, meshClient scrapeClient,
 	logger *zap.SugaredLogger) (*serviceScraper, error) {
 	if metric == nil {
 		return nil, errors.New("metric must not be nil")
@@ -150,9 +164,6 @@ func newServiceScraperWithClient(
 	if counter == nil {
 		return nil, errors.New("counter must not be nil")
 	}
-	if sClient == nil {
-		return nil, errors.New("scrape client must not be nil")
-	}
 	revName := metric.Labels[serving.RevisionLabelKey]
 	if revName == "" {
 		return nil, errors.New("no Revision label found for Metric " + metric.Name)
@@ -166,7 +177,8 @@ func newServiceScraperWithClient(
 	}
 
 	return &serviceScraper{
-		sClient:         sClient,
+		directClient:    directClient,
+		meshClient:      meshClient,
 		counter:         counter,
 		url:             urlFromTarget(metric.Spec.ScrapeTarget, metric.ObjectMeta.Namespace),
 		podAccessor:     podAccessor,
@@ -253,7 +265,7 @@ func (s *serviceScraper) scrapePods(readyPods int) (Stat, error) {
 
 				// Scrape!
 				target := "http://" + pods[myIdx] + ":" + portAndPath
-				stat, err := s.sClient.Scrape(target)
+				stat, err := s.directClient.Scrape(target)
 				if err == nil {
 					results <- stat
 					return nil
@@ -388,7 +400,7 @@ func (s *serviceScraper) scrapeService(window time.Duration, readyPods int) (Sta
 // tryScrape runs a single scrape and returns stat if this is a pod that has not been
 // seen before. An error otherwise or if scraping failed.
 func (s *serviceScraper) tryScrape(scrapedPods *sync.Map) (Stat, error) {
-	stat, err := s.sClient.Scrape(s.url)
+	stat, err := s.meshClient.Scrape(s.url)
 	if err != nil {
 		return emptyStat, err
 	}
diff --git a/pkg/autoscaler/scaling/metrics.go b/pkg/autoscaler/scaling/metrics.go
index 3cfa93bfc..f918cf710 100644
--- a/pkg/autoscaler/scaling/metrics.go
+++ b/pkg/autoscaler/scaling/metrics.go
@@ -17,6 +17,7 @@ limitations under the License.
 package scaling
 
 import (
+	pkgmetrics "knative.dev/pkg/metrics"
 	"knative.dev/serving/pkg/metrics"
 
 	"go.opencensus.io/stats"
@@ -70,7 +71,7 @@ func register() {
 	// Create views to see our measurements. This can return an error if
 	// a previously-registered view has the same name with a different value.
 	// View name defaults to the measure name if unspecified.
-	if err := view.Register(
+	if err := pkgmetrics.RegisterResourceView(
 		&view.View{
 			Description: "Number of pods autoscaler wants to allocate",
 			Measure:     desiredPodCountM,
diff --git a/pkg/autoscaler/scaling/multiscaler.go b/pkg/autoscaler/scaling/multiscaler.go
index a7962fe5f..83acb7379 100644
--- a/pkg/autoscaler/scaling/multiscaler.go
+++ b/pkg/autoscaler/scaling/multiscaler.go
@@ -70,6 +70,10 @@ type DeciderSpec struct {
 	StableWindow time.Duration
 	// The name of the k8s service for pod information.
 	ServiceName string
+	// InitialScale is the calculated initial scale of the revision, taking both
+	// revision initial scale and cluster initial scale into account. Revision initial
+	// scale overrides cluster initial scale.
+	InitialScale int32
 }
 
 // DeciderStatus is the current scale recommendation.
diff --git a/pkg/queue/request_metric.go b/pkg/queue/request_metric.go
index 3978b5c7d..dc1a802bb 100644
--- a/pkg/queue/request_metric.go
+++ b/pkg/queue/request_metric.go
@@ -76,7 +76,7 @@ func NewRequestMetricsHandler(next http.Handler,
 	ns, service, config, rev, pod string) (http.Handler, error) {
 	keys := append(metrics.CommonRevisionKeys, metrics.PodTagKey,
 		metrics.ContainerTagKey, metrics.ResponseCodeKey, metrics.ResponseCodeClassKey)
-	if err := view.Register(
+	if err := pkgmetrics.RegisterResourceView(
 		&view.View{
 			Description: "The number of requests that are routed to queue-proxy",
 			Measure:     requestCountM,
@@ -136,7 +136,7 @@ func NewAppRequestMetricsHandler(next http.Handler, b *Breaker,
 	ns, service, config, rev, pod string) (http.Handler, error) {
 	keys := append(metrics.CommonRevisionKeys, metrics.PodTagKey,
 		metrics.ContainerTagKey, metrics.ResponseCodeKey, metrics.ResponseCodeClassKey)
-	if err := view.Register(&view.View{
+	if err := pkgmetrics.RegisterResourceView(&view.View{
 		Description: "The number of requests that are routed to user-container",
 		Measure:     appRequestCountM,
 		Aggregation: view.Count(),
diff --git a/pkg/reconciler/autoscaling/hpa/hpa.go b/pkg/reconciler/autoscaling/hpa/hpa.go
index daca718d1..ee89b83fc 100644
--- a/pkg/reconciler/autoscaling/hpa/hpa.go
+++ b/pkg/reconciler/autoscaling/hpa/hpa.go
@@ -50,14 +50,6 @@ var _ pareconciler.Interface = (*Reconciler)(nil)
 
 func (c *Reconciler) ReconcileKind(ctx context.Context, pa *pav1alpha1.PodAutoscaler) pkgreconciler.Event {
 	logger := logging.FromContext(ctx)
-
-	// We may be reading a version of the object that was stored at an older version
-	// and may not have had all of the assumed defaults specified.  This won't result
-	// in this getting written back to the API Server, but lets downstream logic make
-	// assumptions about defaulting.
-	pa.SetDefaults(ctx)
-
-	pa.Status.InitializeConditions()
 	logger.Debug("PA exists")
 
 	// HPA-class PAs don't yet support scale-to-zero
diff --git a/pkg/reconciler/autoscaling/kpa/kpa.go b/pkg/reconciler/autoscaling/kpa/kpa.go
index 3dbf6b153..ee372a351 100644
--- a/pkg/reconciler/autoscaling/kpa/kpa.go
+++ b/pkg/reconciler/autoscaling/kpa/kpa.go
@@ -71,14 +71,6 @@ var _ pareconciler.Interface = (*Reconciler)(nil)
 func (c *Reconciler) ReconcileKind(ctx context.Context, pa *pav1alpha1.PodAutoscaler) pkgreconciler.Event {
 	logger := logging.FromContext(ctx)
 
-	// We may be reading a version of the object that was stored at an older version
-	// and may not have had all of the assumed defaults specified.  This won't result
-	// in this getting written back to the API Server, but lets downstream logic make
-	// assumptions about defaulting.
-	pa.SetDefaults(ctx)
-
-	pa.Status.InitializeConditions()
-
 	// We need the SKS object in order to optimize scale to zero
 	// performance. It is OK if SKS is nil at this point.
 	sksName := anames.SKS(pa.Name)
diff --git a/pkg/reconciler/autoscaling/kpa/metrics.go b/pkg/reconciler/autoscaling/kpa/metrics.go
index 956e53abf..b84087d1b 100644
--- a/pkg/reconciler/autoscaling/kpa/metrics.go
+++ b/pkg/reconciler/autoscaling/kpa/metrics.go
@@ -17,6 +17,7 @@ limitations under the License.
 package kpa
 
 import (
+	pkgmetrics "knative.dev/pkg/metrics"
 	"knative.dev/serving/pkg/metrics"
 
 	"go.opencensus.io/stats"
@@ -54,7 +55,7 @@ func register() {
 	// Create views to see our measurements. This can return an error if
 	// a previously-registered view has the same name with a different value.
 	// View name defaults to the measure name if unspecified.
-	if err := view.Register(
+	if err := pkgmetrics.RegisterResourceView(
 		&view.View{
 			Description: "Number of pods autoscaler requested from Kubernetes",
 			Measure:     requestedPodCountM,
diff --git a/pkg/reconciler/autoscaling/kpa/resources/decider.go b/pkg/reconciler/autoscaling/kpa/resources/decider.go
index 87cefae0a..9f8566eae 100644
--- a/pkg/reconciler/autoscaling/kpa/resources/decider.go
+++ b/pkg/reconciler/autoscaling/kpa/resources/decider.go
@@ -73,6 +73,19 @@ func MakeDecider(ctx context.Context, pa *v1alpha1.PodAutoscaler, config *autosc
 			PanicThreshold:      panicThreshold,
 			StableWindow:        resources.StableWindow(pa, config),
 			ServiceName:         svc,
+			InitialScale:        GetInitialScale(config, pa),
 		},
 	}
 }
+
+// GetInitialScale returns the calculated initial scale based on the autoscaler
+// ConfigMap and PA initial scale annotation value.
+// TODO(taragu): This function is exported and will be reused in other packages.
+func GetInitialScale(asConfig *autoscalerconfig.Config, pa *v1alpha1.PodAutoscaler) int32 {
+	initialScale := asConfig.InitialScale
+	revisionInitialScale, ok := pa.InitialScale()
+	if !ok || (revisionInitialScale == 0 && !asConfig.AllowZeroInitialScale) {
+		return initialScale
+	}
+	return revisionInitialScale
+}
diff --git a/pkg/reconciler/configuration/configuration.go b/pkg/reconciler/configuration/configuration.go
index f3ddc205b..4a0d8674f 100644
--- a/pkg/reconciler/configuration/configuration.go
+++ b/pkg/reconciler/configuration/configuration.go
@@ -54,13 +54,6 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, config *v1.Configuration
 	logger := logging.FromContext(ctx)
 	recorder := controller.GetEventRecorder(ctx)
 
-	// We may be reading a version of the object that was stored at an older version
-	// and may not have had all of the assumed defaults specified.  This won't result
-	// in this getting written back to the API Server, but lets downstream logic make
-	// assumptions about defaulting.
-	config.SetDefaults(ctx)
-	config.Status.InitializeConditions()
-
 	// 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/metric/metric.go b/pkg/reconciler/metric/metric.go
index 000abc3fe..a12e4278f 100644
--- a/pkg/reconciler/metric/metric.go
+++ b/pkg/reconciler/metric/metric.go
@@ -35,9 +35,6 @@ type reconciler struct {
 var _ metricreconciler.Interface = (*reconciler)(nil)
 
 func (r *reconciler) ReconcileKind(ctx context.Context, metric *v1alpha1.Metric) pkgreconciler.Event {
-	metric.SetDefaults(ctx)
-	metric.Status.InitializeConditions()
-
 	if err := r.collector.CreateOrUpdate(metric); err != nil {
 		switch err {
 		case metrics.ErrFailedGetEndpoints:
diff --git a/pkg/reconciler/revision/revision.go b/pkg/reconciler/revision/revision.go
index 19df27cc3..db2018e85 100644
--- a/pkg/reconciler/revision/revision.go
+++ b/pkg/reconciler/revision/revision.go
@@ -128,13 +128,6 @@ func (c *Reconciler) reconcileDigest(ctx context.Context, rev *v1.Revision) erro
 
 func (c *Reconciler) ReconcileKind(ctx context.Context, rev *v1.Revision) pkgreconciler.Event {
 	readyBeforeReconcile := rev.IsReady()
-
-	// We may be reading a version of the object that was stored at an older version
-	// and may not have had all of the assumed defaults specified.  This won't result
-	// in this getting written back to the API Server, but lets downstream logic make
-	// assumptions about defaulting.
-	rev.SetDefaults(ctx)
-	rev.Status.InitializeConditions()
 	c.updateRevisionLoggingURL(ctx, rev)
 
 	phases := []struct {
@@ -160,7 +153,7 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, rev *v1.Revision) pkgrec
 		}
 	}
 
-	readyAfterReconcile := rev.IsReady()
+	readyAfterReconcile := rev.Status.GetCondition(v1.RevisionConditionReady).IsTrue()
 	if !readyBeforeReconcile && readyAfterReconcile {
 		logging.FromContext(ctx).Info("Revision became ready")
 		controller.GetEventRecorder(ctx).Event(
diff --git a/pkg/reconciler/route/route.go b/pkg/reconciler/route/route.go
index 60f18a9a6..09fe04aaa 100644
--- a/pkg/reconciler/route/route.go
+++ b/pkg/reconciler/route/route.go
@@ -104,14 +104,6 @@ func (c *Reconciler) getServices(route *v1.Route) ([]*corev1.Service, error) {
 
 func (c *Reconciler) ReconcileKind(ctx context.Context, r *v1.Route) pkgreconciler.Event {
 	logger := logging.FromContext(ctx)
-
-	// We may be reading a version of the object that was stored at an older version
-	// and may not have had all of the assumed defaults specified.  This won't result
-	// in this getting written back to the API Server, but lets downstream logic make
-	// assumptions about defaulting.
-	r.SetDefaults(ctx)
-	r.Status.InitializeConditions()
-
 	logger.Debugf("Reconciling route: %#v", r)
 
 	// Configure traffic based on the RouteSpec.
diff --git a/pkg/reconciler/serverlessservice/serverlessservice.go b/pkg/reconciler/serverlessservice/serverlessservice.go
index ceb1f79c7..ee20f5e45 100644
--- a/pkg/reconciler/serverlessservice/serverlessservice.go
+++ b/pkg/reconciler/serverlessservice/serverlessservice.go
@@ -69,9 +69,6 @@ func (r *reconciler) ReconcileKind(ctx context.Context, sks *netv1alpha1.Serverl
 		return nil
 	}
 
-	sks.SetDefaults(ctx)
-	sks.Status.InitializeConditions()
-
 	for i, fn := range []func(context.Context, *netv1alpha1.ServerlessService) error{
 		r.reconcilePrivateService, // First make sure our data source is setup.
 		r.reconcilePublicService,
diff --git a/pkg/reconciler/service/service.go b/pkg/reconciler/service/service.go
index 666b2dc7f..12b03e0a0 100644
--- a/pkg/reconciler/service/service.go
+++ b/pkg/reconciler/service/service.go
@@ -63,13 +63,6 @@ var _ ksvcreconciler.Interface = (*Reconciler)(nil)
 func (c *Reconciler) ReconcileKind(ctx context.Context, service *v1.Service) pkgreconciler.Event {
 	logger := logging.FromContext(ctx)
 
-	// We may be reading a version of the object that was stored at an older version
-	// and may not have had all of the assumed defaults specified.  This won't result
-	// in this getting written back to the API Server, but lets downstream logic make
-	// assumptions about defaulting.
-	service.SetDefaults(ctx)
-	service.Status.InitializeConditions()
-
 	config, err := c.config(ctx, logger, service)
 	if err != nil {
 		return err
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 b11c313c4..8ce78c9f4 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: "v20200616-e96c1a1"
+    serving.knative.dev/release: "v20200618-65f6d04"
     serving.knative.dev/controller: "true"
     networking.knative.dev/certificate-provider: cert-manager
 rules:
@@ -49,7 +49,7 @@ kind: ValidatingWebhookConfiguration
 metadata:
   name: config.webhook.net-certmanager.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200616-e96c1a1"
+    serving.knative.dev/release: "v20200618-65f6d04"
 webhooks:
 - admissionReviewVersions:
   - v1beta1
@@ -86,7 +86,7 @@ metadata:
   name: net-certmanager-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200616-e96c1a1"
+    serving.knative.dev/release: "v20200618-65f6d04"
 
 ---
 # Copyright 2020 The Knative Authors
@@ -109,7 +109,7 @@ metadata:
   name: config-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200616-e96c1a1"
+    serving.knative.dev/release: "v20200618-65f6d04"
     networking.knative.dev/certificate-provider: cert-manager
 data:
   _example: |
@@ -156,7 +156,7 @@ metadata:
   name: networking-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200616-e96c1a1"
+    serving.knative.dev/release: "v20200618-65f6d04"
     networking.knative.dev/certificate-provider: cert-manager
 spec:
   selector:
@@ -168,14 +168,14 @@ spec:
         cluster-autoscaler.kubernetes.io/safe-to-evict: "true"
       labels:
         app: networking-certmanager
-        serving.knative.dev/release: "v20200616-e96c1a1"
+        serving.knative.dev/release: "v20200618-65f6d04"
     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:ec59261bf095f2bab7c86a30fe43e5928aa366181a89304db0f4f36c9126a63c
+        image: gcr.io/knative-nightly/knative.dev/net-certmanager/cmd/controller@sha256:4b4717d67d0ccbc13eb59b2eaa0b1c096ecf7bd1d8ef6b7cfc1f9471e873329b
         resources:
           requests:
             cpu: 30m
@@ -208,7 +208,7 @@ kind: Service
 metadata:
   labels:
     app: networking-certmanager
-    serving.knative.dev/release: "v20200616-e96c1a1"
+    serving.knative.dev/release: "v20200618-65f6d04"
     networking.knative.dev/certificate-provider: cert-manager
   name: networking-certmanager
   namespace: knative-serving
@@ -245,7 +245,7 @@ metadata:
   name: net-certmanager-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200616-e96c1a1"
+    serving.knative.dev/release: "v20200618-65f6d04"
 spec:
   selector:
     matchLabels:
@@ -258,14 +258,14 @@ spec:
       labels:
         app: net-certmanager-webhook
         role: net-certmanager-webhook
-        serving.knative.dev/release: "v20200616-e96c1a1"
+        serving.knative.dev/release: "v20200618-65f6d04"
     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:4fbdcaa4ccd801cc053f5c3cfa57524e5bb32fd28eb472f77c4526c9c3a12185
+        image: gcr.io/knative-nightly/knative.dev/net-certmanager/cmd/webhook@sha256:5c66ddf218d40e58060808371c397b3fca2fc3713d065a3d95ae528f9d4817ac
         resources:
           requests:
             cpu: 20m
@@ -319,7 +319,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: net-certmanager-webhook
-    serving.knative.dev/release: "v20200616-e96c1a1"
+    serving.knative.dev/release: "v20200618-65f6d04"
 spec:
   ports:
   - # Define metrics and profiling for them to be accessible within service meshes.
diff --git a/third_party/net-istio.yaml b/third_party/net-istio.yaml
index 75b1754b1..f0e585189 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: "v20200616-82c13c1"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     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: "v20200616-82c13c1"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     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: "v20200616-82c13c1"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     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: "v20200616-82c13c1"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     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: "v20200616-82c13c1"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     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: "v20200616-82c13c1"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 
 ---
@@ -216,7 +216,7 @@ metadata:
   name: config-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200616-82c13c1"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 data:
   _example: |
@@ -286,7 +286,7 @@ metadata:
   name: networking-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200616-82c13c1"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     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: "v20200616-82c13c1"
+        serving.knative.dev/release: "v20200618-a1e2b03"
     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:858674730d543213c0ae49198fd63b6183dfd2b3c4d44f8a33fc31b57c4a6b41
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/controller@sha256:05c5fd63bdac0f015789fd4798bd682d6c663a7e0689358308f798ed5b3ca362
         resources:
           requests:
             cpu: 30m
@@ -361,7 +361,7 @@ metadata:
   name: istio-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200616-82c13c1"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -375,14 +375,14 @@ spec:
       labels:
         app: istio-webhook
         role: istio-webhook
-        serving.knative.dev/release: "v20200616-82c13c1"
+        serving.knative.dev/release: "v20200618-a1e2b03"
     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:6ef7b86f7f60f0b1a2ce3096e2ea654d1eb263d1c6530f7bf618f2ac3b66ec64
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/webhook@sha256:2a255fd368dbb4cb01533a0bf9147e148177d18360e1e59cf2cc63f7353f5f1d
         resources:
           requests:
             cpu: 20m
@@ -436,7 +436,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: istio-webhook
-    serving.knative.dev/release: "v20200616-82c13c1"
+    serving.knative.dev/release: "v20200618-a1e2b03"
     networking.knative.dev/ingress-provider: istio
 spec:
   ports:
diff --git a/vendor/knative.dev/pkg/hack/tools.go b/vendor/knative.dev/pkg/hack/tools.go
index f37c08577..22763c5a8 100644
--- a/vendor/knative.dev/pkg/hack/tools.go
+++ b/vendor/knative.dev/pkg/hack/tools.go
@@ -26,9 +26,7 @@ import (
 	_ "k8s.io/code-generator/cmd/defaulter-gen"
 	_ "k8s.io/code-generator/cmd/informer-gen"
 	_ "k8s.io/code-generator/cmd/lister-gen"
-	_ "k8s.io/kube-openapi/cmd/openapi-gen"
 
 	_ "knative.dev/pkg/codegen/cmd/injection-gen"
-
 	_ "knative.dev/test-infra/scripts"
 )
diff --git a/vendor/knative.dev/pkg/metrics/config.go b/vendor/knative.dev/pkg/metrics/config.go
index b9c5aa29a..d44a22f62 100644
--- a/vendor/knative.dev/pkg/metrics/config.go
+++ b/vendor/knative.dev/pkg/metrics/config.go
@@ -37,39 +37,44 @@ import (
 type metricsBackend string
 
 const (
+	// BackendDestinationKey points to the config map entry key for metrics backend destination.
+	BackendDestinationKey = "metrics.backend-destination"
+	// DomainEnv points to the metrics domain env var.
+	DomainEnv = "METRICS_DOMAIN"
+
 	// The following keys are used to configure metrics reporting.
 	// See https://github.com/knative/serving/blob/master/config/config-observability.yaml
 	// for details.
-	AllowStackdriverCustomMetricsKey    = "metrics.allow-stackdriver-custom-metrics"
-	BackendDestinationKey               = "metrics.backend-destination"
-	ReportingPeriodKey                  = "metrics.reporting-period-seconds"
-	StackdriverCustomMetricSubDomainKey = "metrics.stackdriver-custom-metrics-subdomain"
-	// Stackdriver client configuration keys
-	StackdriverProjectIDKey   = "metrics.stackdriver-project-id"
-	StackdriverGCPLocationKey = "metrics.stackdriver-gcp-location"
-	StackdriverClusterNameKey = "metrics.stackdriver-cluster-name"
-	StackdriverUseSecretKey   = "metrics.stackdriver-use-secret"
+	allowStackdriverCustomMetricsKey = "metrics.allow-stackdriver-custom-metrics"
+	collectorAddressKey              = "metrics.opencensus-address"
+	collectorSecureKey               = "metrics.opencensus-require-tls"
+	reportingPeriodKey               = "metrics.reporting-period-seconds"
 
-	DomainEnv = "METRICS_DOMAIN"
-
-	// Stackdriver is used for Stackdriver backend
-	Stackdriver metricsBackend = "stackdriver"
-	// Prometheus is used for Prometheus backend
-	Prometheus metricsBackend = "prometheus"
-	// OpenCensus is used to export to the OpenCensus Agent / Collector,
-	// which can send to many other services.
-	OpenCensus metricsBackend = "opencensus"
-	// None is used to export, well, nothing.
-	None metricsBackend = "none"
+	// Stackdriver client configuration keys
+	stackdriverClusterNameKey           = "metrics.stackdriver-cluster-name"
+	stackdriverCustomMetricSubDomainKey = "metrics.stackdriver-custom-metrics-subdomain"
+	stackdriverGCPLocationKey           = "metrics.stackdriver-gcp-location"
+	stackdriverProjectIDKey             = "metrics.stackdriver-project-id"
+	stackdriverUseSecretKey             = "metrics.stackdriver-use-secret"
 
 	defaultBackendEnvName = "DEFAULT_METRICS_BACKEND"
-
-	CollectorAddressKey = "metrics.opencensus-address"
-	CollectorSecureKey  = "metrics.opencensus-require-tls"
-
 	defaultPrometheusPort = 9090
 	maxPrometheusPort     = 65535
 	minPrometheusPort     = 1024
+	prometheusPortEnvName = "METRICS_PROMETHEUS_PORT"
+)
+
+// Metrics backend "enum".
+const (
+	// stackdriver is used for Stackdriver backend
+	stackdriver metricsBackend = "stackdriver"
+	// prometheus is used for Prometheus backend
+	prometheus metricsBackend = "prometheus"
+	// openCensus is used to export to the OpenCensus Agent / Collector,
+	// which can send to many other services.
+	openCensus metricsBackend = "opencensus"
+	// none is used to export, well, nothing.
+	none metricsBackend = "none"
 )
 
 type metricsConfig struct {
@@ -141,10 +146,10 @@ type StackdriverClientConfig struct {
 // NewStackdriverClientConfigFromMap creates a stackdriverClientConfig from the given map
 func NewStackdriverClientConfigFromMap(config map[string]string) *StackdriverClientConfig {
 	return &StackdriverClientConfig{
-		ProjectID:   config[StackdriverProjectIDKey],
-		GCPLocation: config[StackdriverGCPLocationKey],
-		ClusterName: config[StackdriverClusterNameKey],
-		UseSecret:   strings.EqualFold(config[StackdriverUseSecretKey], "true"),
+		ProjectID:   config[stackdriverProjectIDKey],
+		GCPLocation: config[stackdriverGCPLocationKey],
+		ClusterName: config[stackdriverClusterNameKey],
+		UseSecret:   strings.EqualFold(config[stackdriverUseSecretKey], "true"),
 	}
 }
 
@@ -183,7 +188,7 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
 	backend := os.Getenv(defaultBackendEnvName)
 	if backend == "" {
 		// Use Prometheus if DEFAULT_METRICS_BACKEND does not exist or is empty
-		backend = string(Prometheus)
+		backend = string(prometheus)
 	}
 	// Override backend if it is set in the config map.
 	if backendFromConfig, ok := m[BackendDestinationKey]; ok {
@@ -191,18 +196,18 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
 	}
 	lb := metricsBackend(strings.ToLower(backend))
 	switch lb {
-	case Stackdriver, Prometheus, OpenCensus:
+	case stackdriver, prometheus, openCensus:
 		mc.backendDestination = lb
 	default:
 		return nil, fmt.Errorf("unsupported metrics backend value %q", backend)
 	}
 
-	if mc.backendDestination == OpenCensus {
-		mc.collectorAddress = ops.ConfigMap[CollectorAddressKey]
-		if isSecure := ops.ConfigMap[CollectorSecureKey]; isSecure != "" {
+	if mc.backendDestination == openCensus {
+		mc.collectorAddress = ops.ConfigMap[collectorAddressKey]
+		if isSecure := ops.ConfigMap[collectorSecureKey]; isSecure != "" {
 			var err error
 			if mc.requireSecure, err = strconv.ParseBool(isSecure); err != nil {
-				return nil, fmt.Errorf("invalid %s value %q", CollectorSecureKey, isSecure)
+				return nil, fmt.Errorf("invalid %s value %q", collectorSecureKey, isSecure)
 			}
 
 			if mc.requireSecure {
@@ -214,21 +219,28 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
 		}
 	}
 
-	if mc.backendDestination == Prometheus {
+	if mc.backendDestination == prometheus {
 		pp := ops.PrometheusPort
 		if pp == 0 {
-			pp = defaultPrometheusPort
+			var err error
+			pp, err = prometheusPort()
+			if err != nil {
+				return nil, fmt.Errorf("failed to determine Prometheus port: %w", err)
+			}
 		}
+
 		if pp < minPrometheusPort || pp > maxPrometheusPort {
-			return nil, fmt.Errorf("invalid port %v, should between %v and %v", pp, minPrometheusPort, maxPrometheusPort)
+			return nil, fmt.Errorf("invalid port %d, should be between %d and %d",
+				pp, minPrometheusPort, maxPrometheusPort)
 		}
+
 		mc.prometheusPort = pp
 	}
 
 	// If stackdriverClientConfig is not provided for stackdriver backend destination, OpenCensus will try to
 	// use the application default credentials. If that is not available, Opencensus would fail to create the
 	// metrics exporter.
-	if mc.backendDestination == Stackdriver {
+	if mc.backendDestination == stackdriver {
 		scc := NewStackdriverClientConfigFromMap(m)
 		mc.stackdriverClientConfig = *scc
 		mc.isStackdriverBackend = true
@@ -236,15 +248,15 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
 		var err error
 		mc.stackdriverMetricTypePrefix = path.Join(mc.domain, mc.component)
 
-		customMetricsSubDomain := m[StackdriverCustomMetricSubDomainKey]
+		customMetricsSubDomain := m[stackdriverCustomMetricSubDomainKey]
 		if customMetricsSubDomain == "" {
 			customMetricsSubDomain = defaultCustomMetricSubDomain
 		}
 		mc.stackdriverCustomMetricTypePrefix = path.Join(customMetricTypePrefix, customMetricsSubDomain, mc.component)
-		if ascmStr := m[AllowStackdriverCustomMetricsKey]; ascmStr != "" {
+		if ascmStr := m[allowStackdriverCustomMetricsKey]; ascmStr != "" {
 			allowCustomMetrics, err = strconv.ParseBool(ascmStr)
 			if err != nil {
-				return nil, fmt.Errorf("invalid %s value %q", AllowStackdriverCustomMetricsKey, ascmStr)
+				return nil, fmt.Errorf("invalid %s value %q", allowStackdriverCustomMetricsKey, ascmStr)
 			}
 		}
 
@@ -289,15 +301,15 @@ func createMetricsConfig(ops ExporterOptions, logger *zap.SugaredLogger) (*metri
 	// For Prometheus, we will use a lower value since the exporter doesn't
 	// push anything but just responds to pull requests, and shorter durations
 	// do not really hurt the performance and we rely on the scraping configuration.
-	if repStr, ok := m[ReportingPeriodKey]; ok && repStr != "" {
+	if repStr, ok := m[reportingPeriodKey]; ok && repStr != "" {
 		repInt, err := strconv.Atoi(repStr)
 		if err != nil {
-			return nil, fmt.Errorf("invalid %s value %q", ReportingPeriodKey, repStr)
+			return nil, fmt.Errorf("invalid %s value %q", reportingPeriodKey, repStr)
 		}
 		mc.reportingPeriod = time.Duration(repInt) * time.Second
-	} else if mc.backendDestination == Stackdriver {
+	} else if mc.backendDestination == stackdriver {
 		mc.reportingPeriod = 60 * time.Second
-	} else if mc.backendDestination == Prometheus {
+	} else if mc.backendDestination == prometheus {
 		mc.reportingPeriod = 5 * time.Second
 	}
 
@@ -327,6 +339,25 @@ import (
 )`, DomainEnv, DomainEnv))
 }
 
+// prometheusPort returns the TCP port number configured via the environment
+// for the Prometheus metrics exporter if it's set, a default value otherwise.
+// No validation is performed on the port value, other than ensuring that value
+// is a valid port number (16-bit unsigned integer).
+func prometheusPort() (int, error) {
+	ppStr := os.Getenv(prometheusPortEnvName)
+	if ppStr == "" {
+		return defaultPrometheusPort, nil
+	}
+
+	pp, err := strconv.ParseUint(ppStr, 10, 16)
+	if err != nil {
+		return -1, fmt.Errorf("the environment variable %q could not be parsed as a port number: %w",
+			prometheusPortEnvName, err)
+	}
+
+	return int(pp), nil
+}
+
 // JsonToMetricsOptions converts a json string of a
 // ExporterOptions. Returns a non-nil ExporterOptions always.
 func JsonToMetricsOptions(jsonOpts string) (*ExporterOptions, error) {
diff --git a/vendor/knative.dev/pkg/metrics/exporter.go b/vendor/knative.dev/pkg/metrics/exporter.go
index b8f4fd12f..4c2479382 100644
--- a/vendor/knative.dev/pkg/metrics/exporter.go
+++ b/vendor/knative.dev/pkg/metrics/exporter.go
@@ -85,6 +85,26 @@ func UpdateExporterFromConfigMap(component string, logger *zap.SugaredLogger) fu
 	return ConfigMapWatcher(component, nil, logger)
 }
 
+// RegisterResourceView is similar to view.Register(), except that it will
+// register the view across all Resources tracked by the system, rather than
+// simply the default view.
+// this is a placeholder for real implementation in https://github.com/knative/pkg/pull/1392.
+// That PR will introduce a breaking change, as we need to convert some view.Register to RegisterResourceView in
+// all callers, in serving, eventing, and eventing-contrib.
+// Since that PR is huge, a better approach is to introduce the breaking change up front, by creating this
+// passthrough method, and fixing all the callers before merging that PR.
+func RegisterResourceView(views ...*view.View) error {
+	return view.Register(views...)
+}
+
+// UnregisterResourceView is similar to view.unRegister(), except that it will
+// unregister the view across all Resources tracked by the system, rather than
+// simply the default view.
+// this is a placeholder for real implementation in https://github.com/knative/pkg/pull/1392.
+func UnregisterResourceView(views ...*view.View) {
+	view.Unregister(views...)
+}
+
 // ConfigMapWatcher returns a helper func which updates the exporter configuration based on
 // values in the supplied ConfigMap. This method captures a corev1.SecretLister which is used
 // to configure mTLS with the opencensus agent.
@@ -176,11 +196,11 @@ func isNewExporterRequired(newConfig *metricsConfig) bool {
 
 	// If the OpenCensus address has changed, restart the exporter.
 	// TODO(evankanderson): Should we just always restart the opencensus agent?
-	if newConfig.backendDestination == OpenCensus {
+	if newConfig.backendDestination == openCensus {
 		return newConfig.collectorAddress != cc.collectorAddress || newConfig.requireSecure != cc.requireSecure
 	}
 
-	return newConfig.backendDestination == Stackdriver && newConfig.stackdriverClientConfig != cc.stackdriverClientConfig
+	return newConfig.backendDestination == stackdriver && newConfig.stackdriverClientConfig != cc.stackdriverClientConfig
 }
 
 // newMetricsExporter gets a metrics exporter based on the config.
@@ -198,13 +218,13 @@ func newMetricsExporter(config *metricsConfig, logger *zap.SugaredLogger) (view.
 	var err error
 	var e view.Exporter
 	switch config.backendDestination {
-	case OpenCensus:
+	case openCensus:
 		e, err = newOpenCensusExporter(config, logger)
-	case Stackdriver:
+	case stackdriver:
 		e, err = newStackdriverExporter(config, logger)
-	case Prometheus:
+	case prometheus:
 		e, err = newPrometheusExporter(config, logger)
-	case None:
+	case none:
 		e, err = nil, nil
 	default:
 		err = fmt.Errorf("unsupported metrics backend %v", config.backendDestination)
diff --git a/vendor/knative.dev/pkg/metrics/prometheus_exporter.go b/vendor/knative.dev/pkg/metrics/prometheus_exporter.go
index 8c3f462ac..c8a7b73d9 100644
--- a/vendor/knative.dev/pkg/metrics/prometheus_exporter.go
+++ b/vendor/knative.dev/pkg/metrics/prometheus_exporter.go
@@ -21,7 +21,7 @@ import (
 	"net/http"
 	"sync"
 
-	"contrib.go.opencensus.io/exporter/prometheus"
+	prom "contrib.go.opencensus.io/exporter/prometheus"
 	"go.opencensus.io/stats/view"
 	"go.uber.org/zap"
 )
@@ -32,7 +32,7 @@ var (
 )
 
 func newPrometheusExporter(config *metricsConfig, logger *zap.SugaredLogger) (view.Exporter, error) {
-	e, err := prometheus.NewExporter(prometheus.Options{Namespace: config.component})
+	e, err := prom.NewExporter(prom.Options{Namespace: config.component})
 	if err != nil {
 		logger.Errorw("Failed to create the Prometheus exporter.", zap.Error(err))
 		return nil, err
@@ -61,7 +61,7 @@ func resetCurPromSrv() {
 	}
 }
 
-func startNewPromSrv(e *prometheus.Exporter, port int) *http.Server {
+func startNewPromSrv(e *prom.Exporter, port int) *http.Server {
 	sm := http.NewServeMux()
 	sm.Handle("/metrics", e)
 	curPromSrvMux.Lock()
diff --git a/vendor/knative.dev/pkg/metrics/stackdriver_exporter.go b/vendor/knative.dev/pkg/metrics/stackdriver_exporter.go
index e4c2d08dc..5974e6bcf 100644
--- a/vendor/knative.dev/pkg/metrics/stackdriver_exporter.go
+++ b/vendor/knative.dev/pkg/metrics/stackdriver_exporter.go
@@ -21,7 +21,7 @@ import (
 	"path"
 	"sync"
 
-	"contrib.go.opencensus.io/exporter/stackdriver"
+	sd "contrib.go.opencensus.io/exporter/stackdriver"
 	"contrib.go.opencensus.io/exporter/stackdriver/monitoredresource"
 	"go.opencensus.io/metric/metricdata"
 	"go.opencensus.io/stats/view"
@@ -60,7 +60,7 @@ var (
 	// In product usage, this is always set to function newOpencensusSDExporter.
 	// In unit tests this is set to a fake one to avoid calling actual Google API
 	// service.
-	newStackdriverExporterFunc func(stackdriver.Options) (view.Exporter, error)
+	newStackdriverExporterFunc func(sd.Options) (view.Exporter, error)
 
 	// kubeclient is the in-cluster Kubernetes kubeclient, which is lazy-initialized on first use.
 	kubeclient *kubernetes.Clientset
@@ -100,8 +100,8 @@ func init() {
 	kubeclientInitErr = nil
 }
 
-func newOpencensusSDExporter(o stackdriver.Options) (view.Exporter, error) {
-	e, err := stackdriver.NewExporter(o)
+func newOpencensusSDExporter(o sd.Options) (view.Exporter, error) {
+	e, err := sd.NewExporter(o)
 	if err != nil {
 		return nil, err
 	}
@@ -123,7 +123,7 @@ func newStackdriverExporter(config *metricsConfig, logger *zap.SugaredLogger) (v
 		logger.Warnw("Issue configuring Stackdriver exporter client options, no additional client options will be used: ", zap.Error(err))
 	}
 	// Automatically fall back on Google application default credentials
-	e, err := newStackdriverExporterFunc(stackdriver.Options{
+	e, err := newStackdriverExporterFunc(sd.Options{
 		ProjectID:               gm.project,
 		Location:                gm.location,
 		MonitoringClientOptions: co,
@@ -131,7 +131,7 @@ func newStackdriverExporter(config *metricsConfig, logger *zap.SugaredLogger) (v
 		GetMetricPrefix:         mpf,
 		ResourceByDescriptor:    getResourceByDescriptorFunc(config.stackdriverMetricTypePrefix, gm),
 		ReportingInterval:       config.reportingPeriod,
-		DefaultMonitoringLabels: &stackdriver.Labels{},
+		DefaultMonitoringLabels: &sd.Labels{},
 	})
 	if err != nil {
 		logger.Errorw("Failed to create the Stackdriver exporter: ", zap.Error(err))
diff --git a/vendor/knative.dev/pkg/network/transports.go b/vendor/knative.dev/pkg/network/transports.go
index 745e6a5a8..dcb07a175 100644
--- a/vendor/knative.dev/pkg/network/transports.go
+++ b/vendor/knative.dev/pkg/network/transports.go
@@ -96,15 +96,15 @@ func newHTTPTransport(connTimeout time.Duration, disableKeepAlives bool) http.Ro
 	return &http.Transport{
 		// Those match net/http/transport.go
 		Proxy:                 http.ProxyFromEnvironment,
-		MaxIdleConns:          1000,
-		MaxIdleConnsPerHost:   100,
-		IdleConnTimeout:       5 * time.Second,
+		IdleConnTimeout:       90 * time.Second,
 		TLSHandshakeTimeout:   10 * time.Second,
 		ExpectContinueTimeout: 1 * time.Second,
 		DisableKeepAlives:     disableKeepAlives,
 
-		// This is bespoke.
-		DialContext: DialWithBackOff,
+		// Those are bespoke.
+		DialContext:         DialWithBackOff,
+		MaxIdleConns:        1000,
+		MaxIdleConnsPerHost: 100,
 	}
 }
 
diff --git a/vendor/knative.dev/pkg/reconciler/reconcile_common.go b/vendor/knative.dev/pkg/reconciler/reconcile_common.go
index 6a32c06f7..c9f8b5cb8 100644
--- a/vendor/knative.dev/pkg/reconciler/reconcile_common.go
+++ b/vendor/knative.dev/pkg/reconciler/reconcile_common.go
@@ -19,6 +19,7 @@ package reconciler
 import (
 	"context"
 
+	"knative.dev/pkg/apis"
 	duckv1 "knative.dev/pkg/apis/duck/v1"
 	"knative.dev/pkg/logging"
 )
@@ -29,12 +30,20 @@ const failedGenerationBump = "NewObservedGenFailure"
 func PreProcessReconcile(ctx context.Context, resource duckv1.KRShaped) {
 	newStatus := resource.GetStatus()
 
-	if newStatus.ObservedGeneration != resource.GetGeneration() {
-		condSet := resource.GetConditionSet()
-		manager := condSet.Manage(newStatus)
-		// Ensure conditions are initialized before we modify.
-		manager.InitializeConditions()
+	// We may be reading a version of the object that was stored at an older version
+	// and may not have had all of the assumed defaults specified.  This won't result
+	// in this getting written back to the API Server, but lets downstream logic make
+	// assumptions about defaulting.
+	if d, ok := resource.(apis.Defaultable); ok {
+		d.SetDefaults(ctx)
+	}
+
+	// Ensure conditions are initialized before we modify.
+	condSet := resource.GetConditionSet()
+	manager := condSet.Manage(newStatus)
+	manager.InitializeConditions()
 
+	if newStatus.ObservedGeneration != resource.GetGeneration() {
 		// Reset Ready/Successful to unknown. The reconciler is expected to overwrite this.
 		manager.MarkUnknown(condSet.GetTopLevelConditionType(), failedGenerationBump, "unsuccessfully observed a new generation")
 	}
@@ -43,12 +52,12 @@ func PreProcessReconcile(ctx context.Context, resource duckv1.KRShaped) {
 // PostProcessReconcile contains logic to apply after reconciliation of a resource.
 func PostProcessReconcile(ctx context.Context, resource duckv1.KRShaped) {
 	logger := logging.FromContext(ctx)
-	newStatus := resource.GetStatus()
-	mgr := resource.GetConditionSet().Manage(newStatus)
+	status := resource.GetStatus()
+	mgr := resource.GetConditionSet().Manage(status)
 
 	// Bump observed generation to denote that we have processed this
 	// generation regardless of success or failure.
-	newStatus.ObservedGeneration = resource.GetGeneration()
+	status.ObservedGeneration = resource.GetGeneration()
 
 	if rc := mgr.GetTopLevelCondition(); rc == nil {
 		logger.Warn("A reconciliation included no top-level condition")
diff --git a/vendor/knative.dev/pkg/webhook/resourcesemantics/defaulting/defaulting.go b/vendor/knative.dev/pkg/webhook/resourcesemantics/defaulting/defaulting.go
index 75bfa9fd6..d84e6ed9c 100644
--- a/vendor/knative.dev/pkg/webhook/resourcesemantics/defaulting/defaulting.go
+++ b/vendor/knative.dev/pkg/webhook/resourcesemantics/defaulting/defaulting.go
@@ -30,6 +30,7 @@ import (
 	jsonpatch "gomodules.xyz/jsonpatch/v2"
 	admissionv1beta1 "k8s.io/api/admission/v1beta1"
 	admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/client-go/kubernetes"
 	admissionlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
@@ -172,6 +173,12 @@ func (ac *reconciler) reconcileMutatingWebhook(ctx context.Context, caCert []byt
 			continue
 		}
 		webhook.Webhooks[i].Rules = rules
+		webhook.Webhooks[i].NamespaceSelector = &metav1.LabelSelector{
+			MatchExpressions: []metav1.LabelSelectorRequirement{{
+				Key:      "webhooks.knative.dev/exclude",
+				Operator: metav1.LabelSelectorOpDoesNotExist,
+			}},
+		}
 		webhook.Webhooks[i].ClientConfig.CABundle = caCert
 		if webhook.Webhooks[i].ClientConfig.Service == nil {
 			return fmt.Errorf("missing service reference for webhook: %s", wh.Name)
diff --git a/vendor/knative.dev/pkg/webhook/resourcesemantics/validation/reconcile_config.go b/vendor/knative.dev/pkg/webhook/resourcesemantics/validation/reconcile_config.go
index e1b1b0e1e..c17db6281 100644
--- a/vendor/knative.dev/pkg/webhook/resourcesemantics/validation/reconcile_config.go
+++ b/vendor/knative.dev/pkg/webhook/resourcesemantics/validation/reconcile_config.go
@@ -25,6 +25,7 @@ import (
 	"github.com/markbates/inflect"
 	"go.uber.org/zap"
 	admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/client-go/kubernetes"
 	admissionlisters "k8s.io/client-go/listers/admissionregistration/v1beta1"
@@ -135,6 +136,12 @@ func (ac *reconciler) reconcileValidatingWebhook(ctx context.Context, caCert []b
 			continue
 		}
 		webhook.Webhooks[i].Rules = rules
+		webhook.Webhooks[i].NamespaceSelector = &metav1.LabelSelector{
+			MatchExpressions: []metav1.LabelSelectorRequirement{{
+				Key:      "webhooks.knative.dev/exclude",
+				Operator: metav1.LabelSelectorOpDoesNotExist,
+			}},
+		}
 		webhook.Webhooks[i].ClientConfig.CABundle = caCert
 		if webhook.Webhooks[i].ClientConfig.Service == nil {
 			return fmt.Errorf("missing service reference for webhook: %s", wh.Name)
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 3dd3630b2..2a6b6c272 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1200,7 +1200,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/networking v0.0.0-20200611155523-bd13675bcf70
+# knative.dev/networking v0.0.0-20200617160325-c5b1bddf865c
 ## explicit
 knative.dev/networking/pkg/apis/config
 knative.dev/networking/pkg/apis/networking
@@ -1227,7 +1227,7 @@ knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/server
 knative.dev/networking/pkg/client/injection/reconciler/networking/v1alpha1/serverlessservice
 knative.dev/networking/pkg/client/istio/listers/networking/v1alpha3
 knative.dev/networking/pkg/client/listers/networking/v1alpha1
-# knative.dev/pkg v0.0.0-20200615224524-dbd32a77f41c
+# knative.dev/pkg v0.0.0-20200617194224-40c7e164ccd7
 ## explicit
 knative.dev/pkg/apiextensions/storageversion
 knative.dev/pkg/apiextensions/storageversion/cmd/migrate
@@ -1344,7 +1344,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-20200612191923-a4dd434e085b
+# knative.dev/test-infra v0.0.0-20200617184924-0fc4e2aeef74
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/structured-merge-diff/v2 v2.0.1


Test patch:
diff --git a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle_test.go b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle_test.go
index 353c46d9c..5ab08c904 100644
--- a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle_test.go
+++ b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle_test.go
@@ -667,12 +667,6 @@ func TestScaleBounds(t *testing.T) {
 		max:     "sandwich",
 		wantMin: 0,
 		wantMax: 0,
-	}, {
-		name:    "too small",
-		min:     "-1",
-		max:     "-1",
-		wantMin: 0,
-		wantMax: 0,
 	}}
 
 	for _, tc := range cases {
@@ -1143,3 +1137,37 @@ func TestScaleToZeroPodRetention(t *testing.T) {
 		})
 	}
 }
+
+func TestInitialScale(t *testing.T) {
+	cases := []struct {
+		name   string
+		pa     *PodAutoscaler
+		want   int32
+		wantOK bool
+	}{{
+		name: "nil",
+		pa:   pa(nil),
+	}, {
+		name: "not present",
+		pa:   pa(map[string]string{}),
+	}, {
+		name: "present",
+		pa: pa(map[string]string{
+			autoscaling.InitialScaleAnnotationKey: "2",
+		}),
+		want:   2,
+		wantOK: true,
+	}}
+
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			got, gotOK := tc.pa.InitialScale()
+			if got != tc.want {
+				t.Errorf("InitialScale = %v, want: %v", got, tc.want)
+			}
+			if gotOK != tc.wantOK {
+				t.Errorf("OK = %v, want: %v", gotOK, tc.wantOK)
+			}
+		})
+	}
+}
diff --git a/pkg/autoscaler/metrics/stats_scraper_test.go b/pkg/autoscaler/metrics/stats_scraper_test.go
index fc439a2a5..f33382912 100644
--- a/pkg/autoscaler/metrics/stats_scraper_test.go
+++ b/pkg/autoscaler/metrics/stats_scraper_test.go
@@ -152,12 +152,6 @@ func TestNewServiceScraperWithClientErrorCases(t *testing.T) {
 		counter:     counter,
 		accessor:    podAccessor,
 		expectedErr: "no Revision label found for Metric test-revision",
-	}, {
-		name:        "Empty scrape client",
-		metric:      metric,
-		counter:     counter,
-		accessor:    podAccessor,
-		expectedErr: "scrape client must not be nil",
 	}, {
 		name:        "Empty counter",
 		metric:      metric,
@@ -170,7 +164,7 @@ func TestNewServiceScraperWithClientErrorCases(t *testing.T) {
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
 			if _, err := newServiceScraperWithClient(test.metric, test.counter,
-				test.accessor, test.client, logger); err != nil {
+				test.accessor, test.client, test.client, logger); err != nil {
 				got := err.Error()
 				want := test.expectedErr
 				if got != want {
@@ -189,7 +183,7 @@ func TestPodDirectScrapeSuccess(t *testing.T) {
 	fake.KubeInformer = kubeinformers.NewSharedInformerFactory(fake.KubeClient, 0)
 
 	client := newTestScrapeClient(testStats, []error{nil})
-	scraper, err := serviceScraperForTest(t, client, true)
+	scraper, err := serviceScraperForTest(t, client, nil /* mesh not used */, true)
 	if err != nil {
 		t.Fatal("serviceScraperForTest:", err)
 	}
@@ -216,7 +210,7 @@ func TestPodDirectScrapeSomeFailButSuccess(t *testing.T) {
 
 	// For 5 pods, we need 4 successes.
 	client := newTestScrapeClient(testStats, []error{nil, nil, errors.New("okay"), nil, nil})
-	scraper, err := serviceScraperForTest(t, client, true)
+	scraper, err := serviceScraperForTest(t, client, nil /* mesh not used */, true)
 	if err != nil {
 		t.Fatal("serviceScraperForTest:", err)
 	}
@@ -248,13 +242,15 @@ func TestPodDirectScrapeNoneSucceed(t *testing.T) {
 	fake.KubeInformer = kubeinformers.NewSharedInformerFactory(fake.KubeClient, 0)
 
 	testStats := testStatsWithTime(4, youngPodCutOffDuration.Seconds() /*youngest*/)
-	client := newTestScrapeClient(testStats, []error{
+	direct := newTestScrapeClient(testStats, []error{
 		// Pods fail.
 		errors.New("okay"), errors.New("okay"), errors.New("okay"), errors.New("okay"),
+	})
+	mesh := newTestScrapeClient(testStats, []error{
 		// Service succeeds.
 		nil, nil, nil, nil,
 	})
-	scraper, err := serviceScraperForTest(t, client, true)
+	scraper, err := serviceScraperForTest(t, direct, mesh, true)
 	if err != nil {
 		t.Fatal("serviceScraperForTest:", err)
 	}
@@ -286,7 +282,7 @@ func TestPodDirectScrapePodsExhausted(t *testing.T) {
 	fake.KubeInformer = kubeinformers.NewSharedInformerFactory(fake.KubeClient, 0)
 
 	client := newTestScrapeClient(testStats, []error{nil, nil, errors.New("okay"), nil})
-	scraper, err := serviceScraperForTest(t, client, true)
+	scraper, err := serviceScraperForTest(t, client, nil /* mesh not used */, true)
 	if err != nil {
 		t.Fatal("serviceScraperForTest:", err)
 	}
@@ -305,7 +301,7 @@ func TestPodDirectScrapePodsExhausted(t *testing.T) {
 
 func TestScrapeReportStatWhenAllCallsSucceed(t *testing.T) {
 	client := newTestScrapeClient(testStats, []error{nil})
-	scraper, err := serviceScraperForTest(t, client, false)
+	scraper, err := serviceScraperForTest(t, client, client, false)
 	if err != nil {
 		t.Fatal("serviceScraperForTest:", err)
 	}
@@ -336,8 +332,9 @@ func TestScrapeAllPodsYoungPods(t *testing.T) {
 	// acceptable.
 	testStats := testStatsWithTime(numP, 0. /*youngest*/)
 
-	client := newTestScrapeClient(testStats, []error{nil})
-	scraper, err := serviceScraperForTest(t, client, false)
+	direct := newTestScrapeClient(testStats, []error{errNoPodsScraped}) // fall back to service scrape
+	mesh := newTestScrapeClient(testStats, []error{nil})
+	scraper, err := serviceScraperForTest(t, direct, mesh, false)
 	if err != nil {
 		t.Fatalf("serviceScraperForTest=%v, want no error", err)
 	}
@@ -368,8 +365,9 @@ func TestScrapeAllPodsOldPods(t *testing.T) {
 	// All pods are at least cutoff time old, so first 5 stats will be picked.
 	testStats := testStatsWithTime(numP, youngPodCutOffDuration.Seconds() /*youngest*/)
 
-	client := newTestScrapeClient(testStats, []error{nil})
-	scraper, err := serviceScraperForTest(t, client, false)
+	direct := newTestScrapeClient(testStats, []error{errNoPodsScraped}) // fall back to service scrape
+	mesh := newTestScrapeClient(testStats, []error{nil})
+	scraper, err := serviceScraperForTest(t, direct, mesh, false)
 	if err != nil {
 		t.Fatalf("serviceScraperForTest=%v, want no error", err)
 	}
@@ -401,8 +399,9 @@ func TestScrapeSomePodsOldPods(t *testing.T) {
 	// So pods 3-10 qualify (for 11 total sample is 7).
 	testStats := testStatsWithTime(numP, youngPodCutOffDuration.Seconds()/2 /*youngest*/)
 
-	client := newTestScrapeClient(testStats, []error{nil})
-	scraper, err := serviceScraperForTest(t, client, false)
+	direct := newTestScrapeClient(testStats, []error{errNoPodsScraped}) // fall back to service scrape
+	mesh := newTestScrapeClient(testStats, []error{nil})
+	scraper, err := serviceScraperForTest(t, direct, mesh, false)
 	if err != nil {
 		t.Fatalf("serviceScraperForTest=%v, want no error", err)
 	}
@@ -430,7 +429,7 @@ func TestScrapeSomePodsOldPods(t *testing.T) {
 
 func TestScrapeReportErrorCannotFindEnoughPods(t *testing.T) {
 	client := newTestScrapeClient(testStats[2:], []error{nil})
-	scraper, err := serviceScraperForTest(t, client, false)
+	scraper, err := serviceScraperForTest(t, client, client, false)
 	if err != nil {
 		t.Fatalf("serviceScraperForTest=%v, want no error", err)
 	}
@@ -448,9 +447,9 @@ func TestScrapeReportErrorIfAnyFails(t *testing.T) {
 	errTest := errors.New("test")
 
 	// 1 success and 10 failures so one scrape fails permanently through retries.
-	client := newTestScrapeClient(testStats, []error{nil,
-		errTest, errTest, errTest, errTest, errTest, errTest, errTest, errTest, errTest, errTest})
-	scraper, err := serviceScraperForTest(t, client, false)
+	client := newTestScrapeClient(testStats, []error{nil, errTest, errTest,
+		errTest, errTest, errTest, errTest, errTest, errTest, errTest, errTest})
+	scraper, err := serviceScraperForTest(t, client, client, false)
 	if err != nil {
 		t.Fatalf("serviceScraperForTest=%v, want no error", err)
 	}
@@ -466,7 +465,7 @@ func TestScrapeReportErrorIfAnyFails(t *testing.T) {
 
 func TestScrapeDoNotScrapeIfNoPodsFound(t *testing.T) {
 	client := newTestScrapeClient(testStats, nil)
-	scraper, err := serviceScraperForTest(t, client, false)
+	scraper, err := serviceScraperForTest(t, client, client, false)
 	if err != nil {
 		t.Fatalf("serviceScraperForTest=%v, want no error", err)
 	}
@@ -483,7 +482,7 @@ func TestScrapeDoNotScrapeIfNoPodsFound(t *testing.T) {
 	}
 }
 
-func serviceScraperForTest(t *testing.T, sClient scrapeClient, podsAddressable bool) (*serviceScraper, error) {
+func serviceScraperForTest(t *testing.T, directClient, meshClient scrapeClient, podsAddressable bool) (*serviceScraper, error) {
 	metric := testMetric()
 	counter := resources.NewScopedEndpointsCounter(
 		fake.KubeInformer.Core().V1().Endpoints().Lister(),
@@ -492,7 +491,7 @@ func serviceScraperForTest(t *testing.T, sClient scrapeClient, podsAddressable b
 		fake.KubeInformer.Core().V1().Pods().Lister(),
 		fake.TestNamespace, fake.TestRevision)
 	logger := logtesting.TestLogger(t)
-	ss, err := newServiceScraperWithClient(metric, counter, accessor, sClient, logger)
+	ss, err := newServiceScraperWithClient(metric, counter, accessor, directClient, meshClient, logger)
 	if ss != nil {
 		ss.podsAddressable = podsAddressable
 	}
diff --git a/pkg/metrics/tags_test.go b/pkg/metrics/tags_test.go
index 1f01ac133..2fd1ce59b 100644
--- a/pkg/metrics/tags_test.go
+++ b/pkg/metrics/tags_test.go
@@ -36,7 +36,7 @@ var testM = stats.Int64(
 	stats.UnitDimensionless)
 
 func register(t *testing.T) func() {
-	if err := view.Register(
+	if err := pkgmetrics.RegisterResourceView(
 		&view.View{
 			Description: "Number of pods autoscaler wants to allocate",
 			Measure:     testM,
diff --git a/pkg/reconciler/autoscaling/kpa/kpa_test.go b/pkg/reconciler/autoscaling/kpa/kpa_test.go
index ec32928c2..dbeda6a4d 100644
--- a/pkg/reconciler/autoscaling/kpa/kpa_test.go
+++ b/pkg/reconciler/autoscaling/kpa/kpa_test.go
@@ -196,6 +196,7 @@ func markInactive(pa *asv1a1.PodAutoscaler) {
 func kpa(ns, n string, opts ...PodAutoscalerOption) *asv1a1.PodAutoscaler {
 	rev := newTestRevision(ns, n)
 	kpa := revisionresources.MakePA(rev)
+	kpa.Generation = 1
 	kpa.Annotations["autoscaling.knative.dev/class"] = "kpa.autoscaling.knative.dev"
 	kpa.Annotations["autoscaling.knative.dev/metric"] = "concurrency"
 	for _, opt := range opts {
@@ -244,18 +245,21 @@ func TestReconcile(t *testing.T) {
 		return kpa(
 			testNamespace, testRevision, markInactive, withScales(g, unknownScale), WithReachabilityReachable,
 			withMinScale(defaultScale), WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
+			WithObservedGeneration(1),
 		)
 	}
 	activatingKPAMinScale := func(g int32) *asv1a1.PodAutoscaler {
 		return kpa(
 			testNamespace, testRevision, markActivating, withScales(g, defaultScale), WithReachabilityReachable,
 			withMinScale(defaultScale), WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
+			WithObservedGeneration(1),
 		)
 	}
 	activeKPAMinScale := func(g, w int32) *asv1a1.PodAutoscaler {
 		return kpa(
 			testNamespace, testRevision, markActive, withScales(g, w), WithReachabilityReachable,
 			withMinScale(defaultScale), WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
+			WithObservedGeneration(1),
 		)
 	}
 
@@ -290,7 +294,7 @@ func TestReconcile(t *testing.T) {
 		Key:  key,
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithSKSReady),
 			metric(testNamespace, testRevision),
 			defaultDeployment, defaultEndpoints,
@@ -316,10 +320,10 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markUnknown, WithPAMetricsService(privateSvc), withScales(1, defaultScale),
-				WithPAStatusService(testRevision)),
+				WithPAStatusService(testRevision), WithObservedGeneration(1), WithObservedGenerationFailure()),
 		}, {
 			Object: kpa(testNamespace, testRevision, markUnknown, WithPAMetricsService(privateSvc), withScales(1, defaultScale),
-				WithPAStatusService(testRevision)),
+				WithPAStatusService(testRevision), WithObservedGeneration(1), WithObservedGenerationFailure()),
 		}},
 		WantErr: true,
 		WantEvents: []string{
@@ -331,7 +335,7 @@ func TestReconcile(t *testing.T) {
 		Key:  key,
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithSKSReady),
 			defaultDeployment, defaultEndpoints,
 		},
@@ -351,7 +355,7 @@ func TestReconcile(t *testing.T) {
 		Key:  key,
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithSKSReady),
 			defaultDeployment, defaultEndpoints,
 			metricWithDiffSvc(testNamespace, testRevision),
@@ -379,7 +383,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActive, withScales(1, defaultScale),
-				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc), WithObservedGeneration(1)),
 		}},
 		WantCreates: []runtime.Object{
 			metric(testNamespace, testRevision),
@@ -389,7 +393,7 @@ func TestReconcile(t *testing.T) {
 		Key:  key,
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			defaultSKS,
 			metric(testNamespace, testRevision),
 			deploy(testNamespace, testRevision),
@@ -411,7 +415,7 @@ func TestReconcile(t *testing.T) {
 		WantErr: true,
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			defaultSKS,
 			metric(testNamespace, testRevision),
 			deploy(testNamespace, testRevision),
@@ -433,7 +437,7 @@ func TestReconcile(t *testing.T) {
 		Key:  key,
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			defaultSKS,
 			metric(testNamespace, testRevision),
 			defaultDeployment,
@@ -462,8 +466,7 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActivating, withScales(0, defaultScale),
-				WithPAMetricsService(privateSvc),
-				WithPAStatusService(testRevision)),
+				WithPAMetricsService(privateSvc), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "sks is still not ready",
@@ -478,7 +481,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActivating, withScales(0, defaultScale),
-				WithPAMetricsService(privateSvc), WithPAStatusService(testRevision)),
+				WithPAMetricsService(privateSvc), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "sks becomes ready",
@@ -491,7 +494,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActive, WithPAStatusService(testRevision),
-				WithPAMetricsService(privateSvc), withScales(1, defaultScale)),
+				WithPAMetricsService(privateSvc), withScales(1, defaultScale), WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "kpa does not become ready without minScale endpoints when reachable",
@@ -505,7 +508,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActivating, withMinScale(2), WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision), WithReachabilityReachable),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithReachabilityReachable,
+				WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "kpa does not become ready without minScale endpoints when reachability is unknown",
@@ -519,7 +523,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActivating, withMinScale(2), WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision), WithReachabilityUnknown),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithReachabilityUnknown,
+				WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "kpa becomes ready without minScale endpoints when unreachable",
@@ -533,7 +538,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActive, withMinScale(2), WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision), WithReachabilityUnreachable),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithReachabilityUnreachable,
+				WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "kpa becomes ready with minScale endpoints when reachable",
@@ -548,7 +554,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActive, withMinScale(2), WithPAMetricsService(privateSvc),
-				withScales(2, defaultScale), WithPAStatusService(testRevision), WithReachabilityReachable),
+				withScales(2, defaultScale), WithPAStatusService(testRevision), WithReachabilityReachable,
+				WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "kpa becomes ready with minScale endpoints when reachability is unknown",
@@ -563,7 +570,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markActive, withMinScale(2), WithPAMetricsService(privateSvc),
-				withScales(2, defaultScale), WithPAStatusService(testRevision), WithReachabilityUnknown),
+				withScales(2, defaultScale), WithPAStatusService(testRevision), WithReachabilityUnknown,
+				WithObservedGeneration(1)),
 		}},
 	}, {
 		Name: "sks does not exist",
@@ -574,7 +582,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			// SKS does not exist, so we're just creating and have no status.
-			Object: kpa(testNamespace, testRevision, markActivating, WithPAMetricsService(privateSvc), withScales(0, unknownScale)),
+			Object: kpa(testNamespace, testRevision, markActivating, WithPAMetricsService(privateSvc), withScales(0, unknownScale),
+				WithObservedGeneration(1)),
 		}},
 		WantCreates: []runtime.Object{
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithNumActivators(0)),
@@ -591,7 +600,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			// SKS just got updated and we don't have up to date status.
 			Object: kpa(testNamespace, testRevision, markActivating,
-				withScales(0, defaultScale), WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				withScales(0, defaultScale), WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
+				WithObservedGeneration(1)),
 		}},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: sks(testNamespace, testRevision, WithPubService, WithPrivateService,
@@ -601,7 +611,7 @@ func TestReconcile(t *testing.T) {
 		Name: "sks cannot be created",
 		Key:  key,
 		Objects: []runtime.Object{
-			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc), withScales(1, defaultScale)),
+			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc), withScales(1, defaultScale), WithObservedGeneration(1)),
 			metric(testNamespace, testRevision),
 			defaultDeployment,
 		},
@@ -620,7 +630,7 @@ func TestReconcile(t *testing.T) {
 		Name: "sks cannot be updated",
 		Key:  key,
 		Objects: []runtime.Object{
-			kpa(testNamespace, testRevision, withScales(1, defaultScale), WithPAMetricsService(privateSvc), markActive),
+			kpa(testNamespace, testRevision, withScales(1, defaultScale), WithPAMetricsService(privateSvc), markActive, WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef("bar")),
 			metric(testNamespace, testRevision),
 			defaultDeployment,
@@ -648,7 +658,7 @@ func TestReconcile(t *testing.T) {
 		WantErr: true,
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, withScales(1, defaultScale),
-				WithPAMetricsService(privateSvc), markResourceNotOwned("ServerlessService", testRevision)),
+				WithPAMetricsService(privateSvc), markResourceNotOwned("ServerlessService", testRevision), WithObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", "error reconciling SKS: PA: test-revision does not own SKS: test-revision"),
@@ -665,7 +675,7 @@ func TestReconcile(t *testing.T) {
 		WantErr: true,
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, withScales(1, defaultScale),
-				WithPAMetricsService(privateSvc), markResourceNotOwned("Metric", testRevision)),
+				WithPAMetricsService(privateSvc), markResourceNotOwned("Metric", testRevision), WithObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", `error reconciling Metric: PA: test-revision does not own Metric: test-revision`),
@@ -678,7 +688,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, withScales(0, 0),
 				WithNoTraffic("NoTraffic", "The target is not receiving traffic."),
-				markOld, WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				markOld, WithPAStatusService(testRevision), WithPAMetricsService(privateSvc), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithProxyMode, WithSKSReady),
 			metric(testNamespace, testRevision),
 			deploy(testNamespace, testRevision, func(d *appsv1.Deployment) {
@@ -695,7 +705,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, withScales(0, 0),
 				WithNoTraffic("NoTraffic", "The target is not receiving traffic."),
-				markOld, WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				markOld, WithPAStatusService(testRevision), WithPAMetricsService(privateSvc), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithProxyMode, WithSKSReady),
 			metric(testNamespace, testRevision),
 			deploy(testNamespace, testRevision),
@@ -725,7 +735,8 @@ func TestReconcile(t *testing.T) {
 			Object: kpa(testNamespace, testRevision, withScales(1, 0),
 				WithPAMetricsService(privateSvc),
 				WithNoTraffic("NoTraffic", "The target is not receiving traffic."),
-				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
+				WithObservedGeneration(1)),
 		}},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: sks(testNamespace, testRevision, WithSKSReady,
@@ -738,7 +749,7 @@ func TestReconcile(t *testing.T) {
 			decider(testNamespace, testRevision, 0 /* desiredScale */, 0 /* ebc */, scaling.MinActivators)),
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, withScales(1, 1),
-				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc), WithObservedGeneration(1)),
 			defaultSKS,
 			metric(testNamespace, testRevision),
 			deploy(testNamespace, testRevision), defaultEndpoints,
@@ -759,7 +770,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, WithPAMetricsService(privateSvc),
 				WithNoTraffic("TimedOut", "The target could not be activated."), withScales(1, 0),
-				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc)),
+				WithPAStatusService(testRevision), WithPAMetricsService(privateSvc),
+				WithObservedGeneration(1)),
 		}},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: sks(testNamespace, testRevision, WithSKSReady,
@@ -911,7 +923,7 @@ func TestReconcile(t *testing.T) {
 				0 /* ebc */, scaling.MinActivators)),
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markInactive, WithPAMetricsService(privateSvc),
-				withScales(0, -1), WithPAStatusService(testRevision)),
+				withScales(0, -1), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithProxyMode, WithPubService, WithPrivateService),
 			metric(testNamespace, testRevision),
 			defaultDeployment, defaultEndpoints,
@@ -924,7 +936,7 @@ func TestReconcile(t *testing.T) {
 				-42 /* ebc */, scaling.MinActivators)),
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithProxyMode, WithSKSReady),
 			metric(testNamespace, testRevision),
 			defaultDeployment, defaultEndpoints,
@@ -937,7 +949,7 @@ func TestReconcile(t *testing.T) {
 				-42 /* ebc */, 1982 /*numActivators*/)),
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName),
 				WithProxyMode, WithSKSReady, WithNumActivators(4)),
 			metric(testNamespace, testRevision),
@@ -955,7 +967,7 @@ func TestReconcile(t *testing.T) {
 				-18 /* ebc */, scaling.MinActivators+1)),
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithSKSReady,
 				WithNumActivators(2)),
 			metric(testNamespace, testRevision),
@@ -973,7 +985,7 @@ func TestReconcile(t *testing.T) {
 				1 /* ebc */, scaling.MinActivators)),
 		Objects: []runtime.Object{
 			kpa(testNamespace, testRevision, markActive, WithPAMetricsService(privateSvc),
-				withScales(1, defaultScale), WithPAStatusService(testRevision)),
+				withScales(1, defaultScale), WithPAStatusService(testRevision), WithObservedGeneration(1)),
 			sks(testNamespace, testRevision, WithDeployRef(deployName), WithSKSReady, WithProxyMode,
 				WithNumActivators(3)),
 			metric(testNamespace, testRevision),
diff --git a/pkg/reconciler/autoscaling/kpa/resources/decider_test.go b/pkg/reconciler/autoscaling/kpa/resources/decider_test.go
index 3fb330608..c442cf5eb 100644
--- a/pkg/reconciler/autoscaling/kpa/resources/decider_test.go
+++ b/pkg/reconciler/autoscaling/kpa/resources/decider_test.go
@@ -130,6 +130,16 @@ func TestMakeDecider(t *testing.T) {
 		name: "with metric annotation",
 		pa:   pa(WithMetricAnnotation("rps")),
 		want: decider(withTarget(100.0), withPanicThreshold(2.0), withTotal(100), withMetric("rps"), withMetricAnnotation("rps")),
+	}, {
+		name: "with initial scale",
+		pa: pa(func(pa *v1alpha1.PodAutoscaler) {
+			pa.ObjectMeta.Annotations[autoscaling.InitialScaleAnnotationKey] = "2"
+		}),
+		want: decider(withTarget(100.0), withPanicThreshold(2.0), withTotal(100),
+			func(d *scaling.Decider) {
+				d.Spec.InitialScale = 2
+				d.ObjectMeta.Annotations[autoscaling.InitialScaleAnnotationKey] = "2"
+			}),
 	}}
 
 	for _, tc := range cases {
@@ -146,6 +156,58 @@ func TestMakeDecider(t *testing.T) {
 	}
 }
 
+func TestGetInitialScale(t *testing.T) {
+	tests := []struct {
+		name          string
+		paMutation    func(*v1alpha1.PodAutoscaler)
+		configMutator func(*autoscalerconfig.Config)
+		want          int
+	}{{
+		name: "revision initial scale not set",
+		want: 1,
+	}, {
+		name: "revision initial scale overrides cluster initial scale",
+		paMutation: func(pa *v1alpha1.PodAutoscaler) {
+			pa.ObjectMeta.Annotations[autoscaling.InitialScaleAnnotationKey] = "2"
+		},
+		want: 2,
+	}, {
+		name: "cluster allows initial scale zero",
+		paMutation: func(pa *v1alpha1.PodAutoscaler) {
+			pa.ObjectMeta.Annotations[autoscaling.InitialScaleAnnotationKey] = "0"
+		},
+		configMutator: func(c *autoscalerconfig.Config) {
+			c.AllowZeroInitialScale = true
+		},
+		want: 0,
+	}, {
+		name: "cluster does not allows initial scale zero",
+		paMutation: func(pa *v1alpha1.PodAutoscaler) {
+			pa.ObjectMeta.Annotations[autoscaling.InitialScaleAnnotationKey] = "0"
+		},
+		configMutator: func(c *autoscalerconfig.Config) {
+			c.AllowZeroInitialScale = false
+		},
+		want: 1,
+	}}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			autoscalerConfig, _ := autoscalerconfig.NewConfigFromMap(map[string]string{})
+			if test.configMutator != nil {
+				test.configMutator(autoscalerConfig)
+			}
+			pa := pa()
+			if test.paMutation != nil {
+				test.paMutation(pa)
+			}
+			got := int(GetInitialScale(autoscalerConfig, pa))
+			if want := test.want; got != want {
+				t.Errorf("got = %v, want: %v", got, want)
+			}
+		})
+	}
+}
+
 func pa(options ...PodAutoscalerOption) *v1alpha1.PodAutoscaler {
 	p := &v1alpha1.PodAutoscaler{
 		ObjectMeta: metav1.ObjectMeta{
@@ -200,6 +262,7 @@ func decider(options ...deciderOption) *scaling.Decider {
 			PanicThreshold:      200,
 			ActivatorCapacity:   811,
 			StableWindow:        config.StableWindow,
+			InitialScale:        1,
 		},
 	}
 	for _, fn := range options {
@@ -288,4 +351,6 @@ var config = &autoscalerconfig.Config{
 	PanicThresholdPercentage:           200,
 	PanicWindowPercentage:              10,
 	ScaleToZeroGracePeriod:             30 * time.Second,
+	InitialScale:                       1,
+	AllowZeroInitialScale:              false,
 }
diff --git a/pkg/reconciler/configuration/configuration_test.go b/pkg/reconciler/configuration/configuration_test.go
index 7bd8575a0..11130fefb 100644
--- a/pkg/reconciler/configuration/configuration_test.go
+++ b/pkg/reconciler/configuration/configuration_test.go
@@ -94,12 +94,12 @@ func TestReconcile(t *testing.T) {
 			Object: cfg("no-revisions-yet", "foo", 1234,
 				// The following properties are set when we first reconcile a
 				// Configuration and a Revision is created.
-				WithLatestCreated("no-revisions-yet-00001"), WithObservedGen),
+				WithLatestCreated("no-revisions-yet-00001"), WithConfigObservedGen),
 		}, {
 			Object: cfg("no-revisions-yet", "foo", 1234,
 				// The following properties are set when we first reconcile a
 				// Configuration and a Revision is created.
-				WithLatestCreated("no-revisions-yet-00001"), WithObservedGen),
+				WithLatestCreated("no-revisions-yet-00001"), WithConfigObservedGen),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Revision %q", "no-revisions-yet-00001"),
@@ -124,7 +124,7 @@ func TestReconcile(t *testing.T) {
 			},
 				// The following properties are set when we first reconcile a
 				// Configuration and a Revision is created.
-				WithLatestCreated("byo-name-create-foo"), WithObservedGen),
+				WithLatestCreated("byo-name-create-foo"), WithConfigObservedGen),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Revision %q", "byo-name-create-foo"),
@@ -138,7 +138,7 @@ func TestReconcile(t *testing.T) {
 			},
 				// The following properties are set when we first reconcile a
 				// Configuration and a Revision is created.
-				WithLatestCreated("byo-name-exists-foo"), WithObservedGen),
+				WithLatestCreated("byo-name-exists-foo"), WithConfigObservedGen),
 			rev("byo-name-exists", "foo", 1234, WithCreationTimestamp(now), func(rev *v1.Revision) {
 				rev.Name = "byo-name-exists-foo"
 				rev.GenerateName = ""
@@ -160,7 +160,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: cfg("byo-name-git-revert", "foo", 1234, func(cfg *v1.Configuration) {
 				cfg.Spec.GetTemplate().Name = "byo-name-git-revert-foo"
-			}, WithLatestCreated("byo-name-git-revert-foo"), WithObservedGen),
+			}, WithLatestCreated("byo-name-git-revert-foo"), WithConfigObservedGen),
 		}},
 		Key: "foo/byo-name-git-revert",
 	}, {
@@ -181,7 +181,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: cfg("byo-name-wrong-gen-wrong-spec", "foo", 1234, func(cfg *v1.Configuration) {
 				cfg.Spec.GetTemplate().Name = "byo-name-wrong-gen-wrong-spec-foo"
-			}, MarkRevisionCreationFailed(`revisions.serving.knative.dev "byo-name-wrong-gen-wrong-spec-foo" already exists`), WithObservedGen),
+			}, MarkRevisionCreationFailed(`revisions.serving.knative.dev "byo-name-wrong-gen-wrong-spec-foo" already exists`), WithConfigObservedGen),
 		}},
 		Key: "foo/byo-name-wrong-gen-wrong-spec",
 	}, {
@@ -199,7 +199,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: cfg("byo-rev-not-owned", "foo", 1234, func(cfg *v1.Configuration) {
 				cfg.Spec.GetTemplate().Name = "byo-rev-not-owned-foo"
-			}, MarkRevisionCreationFailed(`revisions.serving.knative.dev "byo-rev-not-owned-foo" already exists`), WithObservedGen),
+			}, MarkRevisionCreationFailed(`revisions.serving.knative.dev "byo-rev-not-owned-foo" already exists`), WithConfigObservedGen),
 		}},
 		Key: "foo/byo-rev-not-owned",
 	}, {
@@ -215,7 +215,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: cfg("validation-failure", "foo", 1234, WithConfigContainerConcurrency(-1),
 				// Expect Revision creation to fail with the following error.
-				MarkRevisionCreationFailed("expected 0 <= -1 <= 1000: spec.containerConcurrency"), WithObservedGen),
+				MarkRevisionCreationFailed("expected 0 <= -1 <= 1000: spec.containerConcurrency"), WithConfigObservedGen),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "CreationFailed", "Failed to create Revision: expected 0 <= -1 <= 1000: spec.containerConcurrency"),
@@ -235,18 +235,18 @@ func TestReconcile(t *testing.T) {
 			Object: cfg("matching-revision-not-done", "foo", 5432,
 				// If the Revision already exists, we still update these fields.
 				// This could happen if the prior status update failed for some reason.
-				WithLatestCreated("matching-revision-not-done-00001"), WithObservedGen),
+				WithLatestCreated("matching-revision-not-done-00001"), WithConfigObservedGen),
 		}},
 		Key: "foo/matching-revision-not-done",
 	}, {
 		Name: "reconcile revision matching generation (ready: true)",
 		Objects: []runtime.Object{
-			cfg("matching-revision-done", "foo", 5555, WithLatestCreated("matching-revision-done-00001"), WithObservedGen),
+			cfg("matching-revision-done", "foo", 5555, WithLatestCreated("matching-revision-done-00001"), WithConfigObservedGen),
 			rev("matching-revision-done", "foo", 5555,
 				WithCreationTimestamp(now), MarkRevisionReady, WithRevName("matching-revision-done-00001")),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
-			Object: cfg("matching-revision-done", "foo", 5555, WithObservedGen,
+			Object: cfg("matching-revision-done", "foo", 5555, WithConfigObservedGen,
 				// When we see the LatestCreatedRevision become Ready, then we
 				// update the latest ready revision.
 				WithLatestCreated("matching-revision-done-00001"),
@@ -262,7 +262,7 @@ func TestReconcile(t *testing.T) {
 		Name: "reconcile revision matching generation (ready: true, idempotent)",
 		Objects: []runtime.Object{
 			cfg("matching-revision-done-idempotent", "foo", 5566,
-				WithObservedGen, WithLatestCreated("matching-revision"), WithLatestReady("matching-revision")),
+				WithConfigObservedGen, WithLatestCreated("matching-revision"), WithLatestReady("matching-revision")),
 			rev("matching-revision-done-idempotent", "foo", 5566,
 				WithCreationTimestamp(now), MarkRevisionReady, WithRevName("matching-revision")),
 		},
@@ -270,13 +270,13 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "reconcile revision matching generation (ready: false)",
 		Objects: []runtime.Object{
-			cfg("matching-revision-failed", "foo", 5555, WithLatestCreated("matching-revision"), WithObservedGen),
+			cfg("matching-revision-failed", "foo", 5555, WithLatestCreated("matching-revision"), WithConfigObservedGen),
 			rev("matching-revision-failed", "foo", 5555,
 				WithCreationTimestamp(now), MarkContainerMissing, WithRevName("matching-revision")),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: cfg("matching-revision-failed", "foo", 5555,
-				WithLatestCreated("matching-revision"), WithObservedGen,
+				WithLatestCreated("matching-revision"), WithConfigObservedGen,
 				// When the LatestCreatedRevision reports back a failure,
 				// then we surface that failure.
 				MarkLatestCreatedFailed("It's the end of the world as we know it")),
@@ -289,7 +289,7 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "reconcile revision matching generation (ready: bad)",
 		Objects: []runtime.Object{
-			cfg("bad-condition", "foo", 5555, WithLatestCreated("bad-condition"), WithObservedGen),
+			cfg("bad-condition", "foo", 5555, WithLatestCreated("bad-condition"), WithConfigObservedGen),
 			rev("bad-condition", "foo", 5555,
 				WithRevName("bad-condition"),
 				WithRevStatus(v1.RevisionStatus{
@@ -324,7 +324,7 @@ func TestReconcile(t *testing.T) {
 			Object: cfg("create-revision-failure", "foo", 99998,
 				// When we fail to create a Revision is should be surfaced in
 				// the Configuration status.
-				MarkRevisionCreationFailed("inducing failure for create revisions"), WithObservedGen),
+				MarkRevisionCreationFailed("inducing failure for create revisions"), WithConfigObservedGen),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "CreationFailed", "Failed to create Revision: inducing failure for create revisions"),
@@ -349,7 +349,7 @@ func TestReconcile(t *testing.T) {
 				// These would be the status updates after a first
 				// reconcile, which we use to trigger the update
 				// where we've induced a failure.
-				WithLatestCreated("update-config-failure-00001"), WithObservedGen),
+				WithLatestCreated("update-config-failure-00001"), WithConfigObservedGen),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Revision %q", "update-config-failure-00001"),
@@ -362,7 +362,7 @@ func TestReconcile(t *testing.T) {
 			cfg("revision-recovers", "foo", 1337,
 				WithLatestCreated("revision-recovers-00001"),
 				WithLatestReady("revision-recovers-00001"),
-				WithObservedGen,
+				WithConfigObservedGen,
 				MarkLatestCreatedFailed("Weebles wobble, but they don't fall down")),
 			rev("revision-recovers", "foo", 1337,
 				WithCreationTimestamp(now),
@@ -374,7 +374,7 @@ func TestReconcile(t *testing.T) {
 			Object: cfg("revision-recovers", "foo", 1337,
 				WithLatestCreated("revision-recovers-00001"),
 				WithLatestReady("revision-recovers-00001"),
-				WithObservedGen,
+				WithConfigObservedGen,
 				// When a LatestReadyRevision recovers from failure,
 				// then we should go back to Ready.
 			),
@@ -390,10 +390,10 @@ func TestReconcile(t *testing.T) {
 			// when no fix is present
 			cfg("double-trouble", "foo", 1,
 				WithLatestCreated("double-trouble-00001"),
-				WithLatestReady("double-trouble-00001"), WithObservedGen),
+				WithLatestReady("double-trouble-00001"), WithConfigObservedGen),
 			cfg("first-trouble", "foo", 1,
 				WithLatestCreated("first-trouble-00001"),
-				WithLatestReady("first-trouble-00001"), WithObservedGen),
+				WithLatestReady("first-trouble-00001"), WithConfigObservedGen),
 
 			rev("first-trouble", "foo", 1,
 				WithRevName("first-trouble-00001"),
@@ -408,7 +408,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			cfg("threerevs", "foo", 3,
 				WithLatestCreated("threerevs-00002"),
-				WithLatestReady("threerevs-00001"), WithObservedGen, func(cfg *v1.Configuration) {
+				WithLatestReady("threerevs-00001"), WithConfigObservedGen, func(cfg *v1.Configuration) {
 					cfg.Spec.GetTemplate().Name = "threerevs-00003"
 				},
 			),
@@ -426,7 +426,7 @@ func TestReconcile(t *testing.T) {
 			Object: cfg("threerevs", "foo", 3,
 				WithLatestCreated("threerevs-00003"),
 				WithLatestReady("threerevs-00002"),
-				WithObservedGen, func(cfg *v1.Configuration) {
+				WithConfigObservedGen, func(cfg *v1.Configuration) {
 					cfg.Spec.GetTemplate().Name = "threerevs-00003"
 				},
 			),
@@ -440,7 +440,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			cfg("revnotready", "foo", 3,
 				WithLatestCreated("revnotready-00002"),
-				WithLatestReady("revnotready-00001"), WithObservedGen, func(cfg *v1.Configuration) {
+				WithLatestReady("revnotready-00001"), WithConfigObservedGen, func(cfg *v1.Configuration) {
 					cfg.Spec.GetTemplate().Name = "revnotready-00003"
 				},
 			),
@@ -459,7 +459,7 @@ func TestReconcile(t *testing.T) {
 				// The config should NOT be ready, because LCR != LRR
 				WithLatestCreated("revnotready-00003"),
 				WithLatestReady("revnotready-00002"),
-				WithObservedGen, func(cfg *v1.Configuration) {
+				WithConfigObservedGen, func(cfg *v1.Configuration) {
 					cfg.Spec.GetTemplate().Name = "revnotready-00003"
 				},
 			),
@@ -473,7 +473,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			cfg("lrrnotexist", "foo", 2,
 				WithLatestCreated("lrrnotexist-00002"),
-				WithLatestReady("lrrnotexist-00001"), WithObservedGen, func(cfg *v1.Configuration) {
+				WithLatestReady("lrrnotexist-00001"), WithConfigObservedGen, func(cfg *v1.Configuration) {
 					cfg.Spec.GetTemplate().Name = "lrrnotexist-00002"
 				},
 			),
@@ -488,7 +488,7 @@ func TestReconcile(t *testing.T) {
 			Object: cfg("lrrnotexist", "foo", 2,
 				WithLatestCreated("lrrnotexist-00002"),
 				WithLatestReady("lrrnotexist-00002"),
-				WithObservedGen, func(cfg *v1.Configuration) {
+				WithConfigObservedGen, func(cfg *v1.Configuration) {
 					cfg.Spec.GetTemplate().Name = "lrrnotexist-00002"
 				},
 			),
diff --git a/pkg/reconciler/gc/gc_test.go b/pkg/reconciler/gc/gc_test.go
index 709333f02..4a39b94fb 100644
--- a/pkg/reconciler/gc/gc_test.go
+++ b/pkg/reconciler/gc/gc_test.go
@@ -71,7 +71,7 @@ func TestGCReconcile(t *testing.T) {
 			cfg("keep-two", "foo", 5556,
 				WithLatestCreated("5556"),
 				WithLatestReady("5556"),
-				WithObservedGen),
+				WithConfigObservedGen),
 			rev("keep-two", "foo", 5554, MarkRevisionReady,
 				WithRevName("5554"),
 				WithCreationTimestamp(oldest),
@@ -104,7 +104,7 @@ func TestGCReconcile(t *testing.T) {
 			cfg("keep-no-last-pinned", "foo", 5556,
 				WithLatestCreated("5556"),
 				WithLatestReady("5556"),
-				WithObservedGen),
+				WithConfigObservedGen),
 			// No lastPinned so we will keep this.
 			rev("keep-no-last-pinned", "foo", 5554, MarkRevisionReady,
 				WithRevName("5554"),
@@ -125,7 +125,7 @@ func TestGCReconcile(t *testing.T) {
 			cfg("keep-recent-last-pinned", "foo", 5556,
 				WithLatestCreated("5556"),
 				WithLatestReady("5556"),
-				WithObservedGen),
+				WithConfigObservedGen),
 			rev("keep-recent-last-pinned", "foo", 5554, MarkRevisionReady,
 				WithRevName("5554"),
 				WithCreationTimestamp(oldest),
@@ -151,7 +151,7 @@ func TestGCReconcile(t *testing.T) {
 				// This comes after 'WithLatestReady' so the
 				// Configuration's 'Ready' Status is 'Unknown'
 				WithLatestCreated("5556"),
-				WithObservedGen),
+				WithConfigObservedGen),
 			rev("keep-two", "foo", 5554, MarkRevisionReady,
 				WithRevName("5554"),
 				WithCreationTimestamp(oldest),
@@ -173,7 +173,7 @@ func TestGCReconcile(t *testing.T) {
 				// Don't set the latest ready revision here
 				// since those by default are always retained
 				WithLatestCreated("keep-all"),
-				WithObservedGen),
+				WithConfigObservedGen),
 			rev("keep-all", "foo", 5554,
 				WithRevName("keep-all"),
 				WithCreationTimestamp(oldest),
diff --git a/pkg/reconciler/labeler/labeler_test.go b/pkg/reconciler/labeler/labeler_test.go
index d22b3b965..2166e78d6 100644
--- a/pkg/reconciler/labeler/labeler_test.go
+++ b/pkg/reconciler/labeler/labeler_test.go
@@ -333,7 +333,7 @@ func revTraffic(name string, latest bool) v1.TrafficTarget {
 
 func routeWithTraffic(namespace, name string, spec, status v1.TrafficTarget, opts ...RouteOption) *v1.Route {
 	return Route(namespace, name,
-		append([]RouteOption{WithSpecTraffic(spec), WithStatusTraffic(status)}, opts...)...)
+		append([]RouteOption{WithSpecTraffic(spec), WithStatusTraffic(status), WithInitRouteConditions}, opts...)...)
 }
 
 func simpleRunLatest(namespace, name, config string, opts ...RouteOption) *v1.Route {
diff --git a/pkg/reconciler/revision/table_test.go b/pkg/reconciler/revision/table_test.go
index e67402d7c..cdcd20cef 100644
--- a/pkg/reconciler/revision/table_test.go
+++ b/pkg/reconciler/revision/table_test.go
@@ -85,7 +85,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "first-reconcile",
 				// The first reconciliation Populates the following status properties.
 				WithLogURL, AllUnknownConditions, MarkDeploying("Deploying"),
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		Key: "foo/first-reconcile",
 	}, {
@@ -109,7 +109,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "update-status-failure",
 				// Despite failure, the following status properties are set.
 				WithLogURL, AllUnknownConditions, MarkDeploying("Deploying"),
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "UpdateFailed", "Failed to update status for %q: %v",
@@ -137,7 +137,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "create-pa-failure",
 				// Despite failure, the following status properties are set.
 				WithLogURL, WithInitRevConditions,
-				MarkDeploying("Deploying"), withDefaultContainerStatuses()),
+				MarkDeploying("Deploying"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", `failed to create PA "create-pa-failure": inducing failure for create podautoscalers`),
@@ -163,7 +163,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "create-user-deploy-failure",
 				// Despite failure, the following status properties are set.
 				WithLogURL, WithInitRevConditions,
-				MarkDeploying("Deploying"), withDefaultContainerStatuses()),
+				MarkDeploying("Deploying"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError",
@@ -178,7 +178,7 @@ func TestReconcile(t *testing.T) {
 		// are necessary.
 		Objects: []runtime.Object{
 			rev("foo", "stable-reconcile", WithLogURL, AllUnknownConditions,
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 			pa("foo", "stable-reconcile", WithReachability(asv1a1.ReachabilityUnknown)),
 
 			deploy(t, "foo", "stable-reconcile"),
@@ -192,7 +192,7 @@ func TestReconcile(t *testing.T) {
 		// with our desired spec.
 		Objects: []runtime.Object{
 			rev("foo", "fix-containers",
-				WithLogURL, AllUnknownConditions, withDefaultContainerStatuses()),
+				WithLogURL, AllUnknownConditions, withDefaultContainerStatuses(), withObservedGeneration(1)),
 			pa("foo", "fix-containers", WithReachability(asv1a1.ReachabilityUnknown)),
 			changeContainers(deploy(t, "foo", "fix-containers")),
 			image("foo", "fix-containers"),
@@ -211,7 +211,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			rev("foo", "failure-update-deploy",
 				withK8sServiceName("whateves"), WithLogURL, AllUnknownConditions,
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 			pa("foo", "failure-update-deploy"),
 			changeContainers(deploy(t, "foo", "failure-update-deploy")),
 			image("foo", "failure-update-deploy"),
@@ -233,7 +233,7 @@ func TestReconcile(t *testing.T) {
 			rev("foo", "stable-deactivation",
 				WithLogURL, MarkRevisionReady,
 				MarkInactive("NoTraffic", "This thing is inactive."),
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 			pa("foo", "stable-deactivation",
 				WithNoTraffic("NoTraffic", "This thing is inactive.")),
 			deploy(t, "foo", "stable-deactivation"),
@@ -254,7 +254,7 @@ func TestReconcile(t *testing.T) {
 				WithLogURL,
 				// When the endpoint and pa are ready, then we will see the
 				// Revision become ready.
-				MarkRevisionReady, withDefaultContainerStatuses()),
+				MarkRevisionReady, withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "RevisionReady", "Revision becomes ready upon all resources being ready"),
@@ -266,7 +266,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			rev("foo", "pa-not-ready",
 				withK8sServiceName("somebody-told-me"), WithLogURL,
-				MarkRevisionReady),
+				MarkRevisionReady, withObservedGeneration(1)),
 			pa("foo", "pa-not-ready",
 				WithPAStatusService("its-not-confidential"),
 				WithBufferedTraffic("Something", "This is something longer")),
@@ -280,6 +280,7 @@ func TestReconcile(t *testing.T) {
 				// When we reconcile a ready state and our pa is in an activating
 				// state, we should see the following mutation.
 				MarkActivating("Something", "This is something longer"),
+				withObservedGeneration(1),
 			),
 		}},
 		Key: "foo/pa-not-ready",
@@ -288,7 +289,8 @@ func TestReconcile(t *testing.T) {
 		// Test propagating the inactivity signal from the pa to the Revision.
 		Objects: []runtime.Object{
 			rev("foo", "pa-inactive",
-				withK8sServiceName("something-in-the-way"), WithLogURL, MarkRevisionReady),
+				withK8sServiceName("something-in-the-way"), WithLogURL,
+				MarkRevisionReady, withObservedGeneration(1)),
 			pa("foo", "pa-inactive",
 				WithNoTraffic("NoTraffic", "This thing is inactive.")),
 			readyDeploy(deploy(t, "foo", "pa-inactive")),
@@ -299,7 +301,7 @@ func TestReconcile(t *testing.T) {
 				WithLogURL, MarkRevisionReady, withDefaultContainerStatuses(),
 				// When we reconcile an "all ready" revision when the PA
 				// is inactive, we should see the following change.
-				MarkInactive("NoTraffic", "This thing is inactive.")),
+				MarkInactive("NoTraffic", "This thing is inactive."), withObservedGeneration(1)),
 		}},
 		Key: "foo/pa-inactive",
 	}, {
@@ -308,7 +310,8 @@ func TestReconcile(t *testing.T) {
 		// But propagate the service name.
 		Objects: []runtime.Object{
 			rev("foo", "pa-inactive",
-				withK8sServiceName("here-comes-the-sun"), WithLogURL, MarkRevisionReady),
+				withK8sServiceName("here-comes-the-sun"), WithLogURL,
+				MarkRevisionReady, withObservedGeneration(1)),
 			pa("foo", "pa-inactive",
 				WithNoTraffic("NoTraffic", "This thing is inactive."),
 				WithPAStatusService("pa-inactive-svc")),
@@ -322,7 +325,7 @@ func TestReconcile(t *testing.T) {
 				// When we reconcile an "all ready" revision when the PA
 				// is inactive, we should see the following change.
 				MarkInactive("NoTraffic", "This thing is inactive."),
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		Key: "foo/pa-inactive",
 	}, {
@@ -344,12 +347,15 @@ func TestReconcile(t *testing.T) {
 				// When our reconciliation has to change the service
 				// we should see the following mutations to status.
 				withK8sServiceName("fix-mutated-pa"), WithLogURL, MarkRevisionReady,
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: pa("foo", "fix-mutated-pa", WithTraffic,
 				WithPAStatusService("fix-mutated-pa")),
 		}},
+		WantEvents: []string{
+			Eventf(corev1.EventTypeNormal, "RevisionReady", "Revision becomes ready upon all resources being ready"),
+		},
 		Key: "foo/fix-mutated-pa",
 	}, {
 		Name: "mutated pa gets error during the fix",
@@ -357,7 +363,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			rev("foo", "fix-mutated-pa-fail",
 				withK8sServiceName("some-old-stuff"),
-				WithLogURL, AllUnknownConditions, withDefaultContainerStatuses()),
+				WithLogURL, AllUnknownConditions, withDefaultContainerStatuses(), withObservedGeneration(1)),
 			pa("foo", "fix-mutated-pa-fail", WithProtocolType(networking.ProtocolH2C), WithReachability(asv1a1.ReachabilityUnknown)),
 			deploy(t, "foo", "fix-mutated-pa-fail"),
 			image("foo", "fix-mutated-pa-fail"),
@@ -392,7 +398,8 @@ func TestReconcile(t *testing.T) {
 				WithLogURL, AllUnknownConditions,
 				// When the revision is reconciled after a Deployment has
 				// timed out, we should see it marked with the PDE state.
-				MarkProgressDeadlineExceeded("I timed out!"), withDefaultContainerStatuses()),
+				MarkProgressDeadlineExceeded("I timed out!"), withDefaultContainerStatuses(),
+				withObservedGeneration(1)),
 		}},
 		Key: "foo/deploy-timeout",
 	}, {
@@ -414,7 +421,7 @@ func TestReconcile(t *testing.T) {
 				// When the revision is reconciled after a Deployment has
 				// timed out, we should see it marked with the FailedCreate state.
 				MarkResourcesUnavailable("FailedCreate", "I replica failed!"),
-				withDefaultContainerStatuses()),
+				withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		Key: "foo/deploy-replica-failure",
 	}, {
@@ -431,7 +438,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: rev("foo", "pull-backoff",
 				WithLogURL, AllUnknownConditions,
-				MarkResourcesUnavailable("ImagePullBackoff", "can't pull it"), withDefaultContainerStatuses()),
+				MarkResourcesUnavailable("ImagePullBackoff", "can't pull it"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: pa("foo", "pull-backoff", WithReachability(asv1a1.ReachabilityUnreachable)),
@@ -454,7 +461,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: rev("foo", "pod-error",
 				WithLogURL, AllUnknownConditions, MarkContainerExiting(5,
-					v1.RevisionContainerExitingMessage("I failed man!")), withDefaultContainerStatuses()),
+					v1.RevisionContainerExitingMessage("I failed man!")), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		Key: "foo/pod-error",
 	}, {
@@ -473,7 +480,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: rev("foo", "pod-schedule-error",
 				WithLogURL, AllUnknownConditions, MarkResourcesUnavailable("Insufficient energy",
-					"Unschedulable"), withDefaultContainerStatuses()),
+					"Unschedulable"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		Key: "foo/pod-schedule-error",
 	}, {
@@ -493,7 +500,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "steady-ready", withK8sServiceName("steadier-even"), WithLogURL,
 				// All resources are ready to go, we should see the revision being
 				// marked ready
-				MarkRevisionReady, withDefaultContainerStatuses()),
+				MarkRevisionReady, withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "RevisionReady", "Revision becomes ready upon all resources being ready"),
@@ -513,7 +520,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "missing-owners", withK8sServiceName("lesser-revision"), WithLogURL,
 				MarkRevisionReady,
 				// When we're missing the OwnerRef for PodAutoscaler we see this update.
-				MarkResourceNotOwned("PodAutoscaler", "missing-owners"), withDefaultContainerStatuses()),
+				MarkResourceNotOwned("PodAutoscaler", "missing-owners"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", `revision: "missing-owners" does not own PodAutoscaler: "missing-owners"`),
@@ -533,7 +540,7 @@ func TestReconcile(t *testing.T) {
 			Object: rev("foo", "missing-owners", withK8sServiceName("youre-gonna-lose"), WithLogURL,
 				MarkRevisionReady,
 				// When we're missing the OwnerRef for Deployment we see this update.
-				MarkResourceNotOwned("Deployment", "missing-owners-deployment"), withDefaultContainerStatuses()),
+				MarkResourceNotOwned("Deployment", "missing-owners-deployment"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", `revision: "missing-owners" does not own Deployment: "missing-owners-deployment"`),
@@ -553,7 +560,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: rev("foo", "image-pull-secrets",
 				WithImagePullSecrets("foo-secret"),
-				WithLogURL, AllUnknownConditions, MarkDeploying("Deploying"), withDefaultContainerStatuses()),
+				WithLogURL, AllUnknownConditions, MarkDeploying("Deploying"), withDefaultContainerStatuses(), withObservedGeneration(1)),
 		}},
 		Key: "foo/image-pull-secrets",
 	}}
@@ -635,9 +642,10 @@ func changeContainers(deploy *appsv1.Deployment) *appsv1.Deployment {
 func rev(namespace, name string, ro ...RevisionOption) *v1.Revision {
 	r := &v1.Revision{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      name,
-			Namespace: namespace,
-			UID:       "test-uid",
+			Name:       name,
+			Namespace:  namespace,
+			UID:        "test-uid",
+			Generation: 1,
 		},
 		Spec: v1.RevisionSpec{
 			PodSpec: corev1.PodSpec{
@@ -662,6 +670,12 @@ func withK8sServiceName(sn string) RevisionOption {
 	}
 }
 
+func withObservedGeneration(gen int64) RevisionOption {
+	return func(r *v1.Revision) {
+		r.Status.ObservedGeneration = gen
+	}
+}
+
 func withDefaultContainerStatuses() RevisionOption {
 	return func(r *v1.Revision) {
 		r.Status.ContainerStatuses = []v1.ContainerStatuses{{
diff --git a/pkg/reconciler/route/table_test.go b/pkg/reconciler/route/table_test.go
index ec9b9a9b7..1c98a11d3 100644
--- a/pkg/reconciler/route/table_test.go
+++ b/pkg/reconciler/route/table_test.go
@@ -76,8 +76,8 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "configuration not yet ready",
 		Objects: []runtime.Object{
-			Route("default", "first-reconcile", WithConfigTarget("not-ready")),
-			cfg("default", "not-ready", WithGeneration(1), WithLatestCreated("not-ready-00001")),
+			Route("default", "first-reconcile", WithConfigTarget("not-ready"), WithRouteGeneration(1)),
+			cfg("default", "not-ready", WithConfigGeneration(1), WithLatestCreated("not-ready-00001")),
 			rev("default", "not-ready", 1, WithInitRevConditions, WithRevName("not-ready-00001")),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
@@ -90,9 +90,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "configuration permanently failed",
 		Objects: []runtime.Object{
-			Route("default", "first-reconcile", WithConfigTarget("permanently-failed")),
+			Route("default", "first-reconcile", WithConfigTarget("permanently-failed"), WithRouteGeneration(1)),
 			cfg("default", "permanently-failed",
-				WithGeneration(1), WithLatestCreated("permanently-failed-00001"), MarkLatestCreatedFailed("blah")),
+				WithConfigGeneration(1), WithLatestCreated("permanently-failed-00001"), MarkLatestCreatedFailed("blah")),
 			rev("default", "permanently-failed", 1,
 				WithRevName("permanently-failed-00001"),
 				WithInitRevConditions, MarkContainerMissing),
@@ -109,8 +109,8 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("update", "routes"),
 		},
 		Objects: []runtime.Object{
-			Route("default", "first-reconcile", WithConfigTarget("not-ready")),
-			cfg("default", "not-ready", WithGeneration(1), WithLatestCreated("not-ready-00001")),
+			Route("default", "first-reconcile", WithConfigTarget("not-ready"), WithRouteGeneration(1)),
+			cfg("default", "not-ready", WithConfigGeneration(1), WithLatestCreated("not-ready-00001")),
 			rev("default", "not-ready", 1, WithInitRevConditions, WithRevName("not-ready-00001")),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
@@ -125,15 +125,15 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "simple route becomes ready, ingress unknown",
 		Objects: []runtime.Object{
-			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
+			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 		},
 		WantCreates: []runtime.Object{
 			simpleIngress(
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithURL,
-					WithRouteUID("12-34")),
+					WithRouteUID("12-34"), WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -156,7 +156,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
-				WithRouteUID("12-34"),
+				WithRouteUID("12-34"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when all traffic has been assigned.
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
@@ -174,9 +174,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "simple route, ingress failed",
 		Objects: []runtime.Object{
-			Route("default", "ingress-failed", WithConfigTarget("config"), WithRouteUID("12-34")),
+			Route("default", "ingress-failed", WithConfigTarget("config"), WithRouteUID("12-34"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			simpleIngress(
 				Route("default", "ingress-failed", WithConfigTarget("config"), WithURL,
@@ -203,7 +203,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "ingress-failed", WithConfigTarget("config"),
-				WithRouteUID("12-34"),
+				WithRouteUID("12-34"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when all traffic has been assigned.
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithInitRouteConditions,
 				MarkTrafficAssigned,
@@ -222,9 +222,10 @@ func TestReconcile(t *testing.T) {
 		Name: "custom ingress route becomes ready, ingress unknown",
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready",
-				WithConfigTarget("config"), WithRouteUID("12-34"), WithIngressClass("custom-ingress-class")),
+				WithConfigTarget("config"), WithRouteUID("12-34"), WithIngressClass("custom-ingress-class"),
+				WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("bk")),
 		},
 		WantCreates: []runtime.Object{
@@ -255,6 +256,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
 				WithRouteUID("12-34"), WithIngressClass("custom-ingress-class"),
+				WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when all traffic has been assigned.
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
@@ -274,9 +276,9 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithLocalDomain,
 				WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"}),
-				WithRouteUID("65-23")),
+				WithRouteUID("65-23"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("tb")),
 		},
 		WantCreates: []runtime.Object{
@@ -305,13 +307,13 @@ func TestReconcile(t *testing.T) {
 				getContext(),
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithLocalDomain,
 					WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"}),
-					WithRouteUID("65-23")),
+					WithRouteUID("65-23"), WithRouteGeneration(1)),
 				"",
 			),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
-				WithRouteUID("65-23"),
+				WithRouteUID("65-23"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when all traffic has been assigned.
 				WithLocalDomain, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"}),
@@ -330,9 +332,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "simple route becomes ready",
 		Objects: []runtime.Object{
-			Route("default", "becomes-ready", WithConfigTarget("config")),
+			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
 			simpleReadyIngress(
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithURL),
@@ -360,7 +362,7 @@ func TestReconcile(t *testing.T) {
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
 				// Populated by reconciliation when the route becomes ready.
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
@@ -379,9 +381,9 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("create", "services"),
 		},
 		Objects: []runtime.Object{
-			Route("default", "create-svc-failure", WithConfigTarget("config"), WithRouteFinalizer),
+			Route("default", "create-svc-failure", WithConfigTarget("config"), WithRouteFinalizer, WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
 		},
 		WantCreates: []runtime.Object{
@@ -409,9 +411,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "failure creating ingress",
 		Objects: []runtime.Object{
-			Route("default", "ingress-create-failure", WithConfigTarget("config"), WithRouteFinalizer),
+			Route("default", "ingress-create-failure", WithConfigTarget("config"), WithRouteFinalizer, WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("astrid")),
 		},
 		// We induce a failure creating the Ingress.
@@ -447,7 +449,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "ingress-create-failure", WithConfigTarget("config"),
-				WithRouteFinalizer,
+				WithRouteFinalizer, WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when we fail to create the ingress.
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, WithStatusTraffic(
@@ -468,7 +470,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "steady-state", WithConfigTarget("config"),
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady,
+				MarkTrafficAssigned, MarkIngressReady, WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
@@ -476,7 +478,7 @@ func TestReconcile(t *testing.T) {
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "steady-state"),
 			),
@@ -504,14 +506,14 @@ func TestReconcile(t *testing.T) {
 		WantErr: true,
 		Objects: []runtime.Object{
 			Route("default", "unhappy-owner", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName: "config-00001",
 						Percent:      ptr.Int64(100),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "unhappy-owner"),
 			),
@@ -521,8 +523,8 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "unhappy-owner", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
@@ -542,7 +544,7 @@ func TestReconcile(t *testing.T) {
 		Name: "different labels, different domain - steady state",
 		Objects: []runtime.Object{
 			Route("default", "different-domain", WithConfigTarget("config"),
-				WithAnotherDomain, WithAddress,
+				WithAnotherDomain, WithAddress, WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithRouteConditionsAutoTLSDisabled, MarkTrafficAssigned, MarkIngressReady,
 				WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
@@ -551,7 +553,7 @@ func TestReconcile(t *testing.T) {
 						LatestRevision: ptr.Bool(true),
 					}), WithRouteLabel(map[string]string{"app": "prod"})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "different-domain"),
 			),
@@ -579,7 +581,7 @@ func TestReconcile(t *testing.T) {
 			{
 				Object: simpleReadyIngress(
 					Route("default", "different-domain", WithConfigTarget("config"),
-						WithAnotherDomain),
+						WithAnotherDomain, WithRouteGeneration(1)),
 					&traffic.Config{
 						Targets: map[string]traffic.RevisionTargets{
 							traffic.DefaultTarget: {{
@@ -605,15 +607,15 @@ func TestReconcile(t *testing.T) {
 		Name: "new latest created revision",
 		Objects: []runtime.Object{
 			Route("default", "new-latest-created", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(2), WithLatestReady("config-00001"), WithLatestCreated("config-00002"),
+				WithConfigGeneration(2), WithLatestReady("config-00001"), WithLatestCreated("config-00002"),
 				WithConfigLabel("serving.knative.dev/route", "new-latest-created"),
 			),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("daisy")),
@@ -643,14 +645,14 @@ func TestReconcile(t *testing.T) {
 		Name: "new latest ready revision",
 		Objects: []runtime.Object{
 			Route("default", "new-latest-ready", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName: "config-00001",
 						Percent:      ptr.Int64(100),
 					})),
 			cfg("default", "config",
-				WithGeneration(2), WithLatestCreated("config-00002"), WithLatestReady("config-00002"),
+				WithConfigGeneration(2), WithLatestCreated("config-00002"), WithLatestReady("config-00002"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "new-latest-ready"),
 			),
@@ -678,7 +680,7 @@ func TestReconcile(t *testing.T) {
 		// A new LatestReadyRevisionName on the Configuration should result in the new Revision being rolled out.
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: simpleReadyIngress(
-				Route("default", "new-latest-ready", WithConfigTarget("config"), WithURL),
+				Route("default", "new-latest-ready", WithConfigTarget("config"), WithURL, WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -696,8 +698,8 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "new-latest-ready", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00002",
 						Percent:        ptr.Int64(100),
@@ -708,11 +710,11 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "public becomes cluster local",
 		Objects: []runtime.Object{
-			Route("default", "becomes-local", WithConfigTarget("config"),
+			Route("default", "becomes-local", WithConfigTarget("config"), WithRouteGeneration(1),
 				WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"}),
 				WithRouteUID("65-23")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("tb")),
 			simpleIngress(
 				Route("default", "becomes-local", WithConfigTarget("config"), WithRouteUID("65-23"), WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"})),
@@ -736,7 +738,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: simpleIngress(
-				Route("default", "becomes-local", WithConfigTarget("config"),
+				Route("default", "becomes-local", WithConfigTarget("config"), WithRouteGeneration(1),
 					WithRouteUID("65-23"),
 					WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"})),
 				&traffic.Config{
@@ -759,7 +761,7 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-local", WithConfigTarget("config"),
-				WithRouteUID("65-23"),
+				WithRouteUID("65-23"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				MarkTrafficAssigned, MarkIngressNotConfigured,
 				WithLocalDomain, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"}),
@@ -775,9 +777,9 @@ func TestReconcile(t *testing.T) {
 		Name: "cluster local becomes public",
 		Objects: []runtime.Object{
 			Route("default", "becomes-public", WithConfigTarget("config"),
-				WithRouteUID("65-23")),
+				WithRouteUID("65-23"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("tb")),
 			simpleIngress(
 				Route("default", "becomes-public", WithConfigTarget("config"), WithRouteUID("65-23"),
@@ -805,7 +807,8 @@ func TestReconcile(t *testing.T) {
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: simpleIngress(
 				Route("default", "becomes-public", WithConfigTarget("config"),
-					WithRouteUID("65-23"), WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"})),
+					WithRouteUID("65-23"), WithRouteGeneration(1), WithRouteObservedGeneration,
+					WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"})),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -823,7 +826,7 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-public", WithConfigTarget("config"),
-				WithRouteUID("65-23"),
+				WithRouteUID("65-23"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				MarkTrafficAssigned, MarkIngressNotConfigured,
 				WithAddress, WithRouteConditionsAutoTLSDisabled, WithURL,
 				WithStatusTraffic(
@@ -843,14 +846,14 @@ func TestReconcile(t *testing.T) {
 		},
 		Objects: []runtime.Object{
 			Route("default", "update-ci-failure", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName: "config-00001",
 						Percent:      ptr.Int64(100),
 					})),
 			cfg("default", "config",
-				WithGeneration(2), WithLatestCreated("config-00002"), WithLatestReady("config-00002"),
+				WithConfigGeneration(2), WithLatestCreated("config-00002"), WithLatestReady("config-00002"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "update-ci-failure"),
 			),
@@ -877,7 +880,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: simpleReadyIngress(
-				Route("default", "update-ci-failure", WithConfigTarget("config"), WithURL),
+				Route("default", "update-ci-failure", WithConfigTarget("config"), WithURL, WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -895,8 +898,8 @@ func TestReconcile(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "update-ci-failure", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00002",
 						Percent:        ptr.Int64(100),
@@ -911,15 +914,15 @@ func TestReconcile(t *testing.T) {
 		Name: "reconcile service mutation",
 		Objects: []runtime.Object{
 			Route("default", "svc-mutation", WithConfigTarget("config"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "svc-mutation"),
 			),
@@ -955,15 +958,15 @@ func TestReconcile(t *testing.T) {
 		},
 		Objects: []runtime.Object{
 			Route("default", "svc-mutation", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "svc-mutation"),
 			),
@@ -1000,15 +1003,15 @@ func TestReconcile(t *testing.T) {
 		Name: "drop cluster ip",
 		Objects: []runtime.Object{
 			Route("default", "cluster-ip", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "cluster-ip"),
 			),
@@ -1040,15 +1043,15 @@ func TestReconcile(t *testing.T) {
 		Name: "fix external name",
 		Objects: []runtime.Object{
 			Route("default", "external-name", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "external-name"),
 			),
@@ -1079,15 +1082,15 @@ func TestReconcile(t *testing.T) {
 		Name: "reconcile ingress mutation",
 		Objects: []runtime.Object{
 			Route("default", "ingress-mutation", WithConfigTarget("config"), WithRouteFinalizer,
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "ingress-mutation"),
 			),
@@ -1112,7 +1115,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: simpleReadyIngress(
-				Route("default", "ingress-mutation", WithConfigTarget("config"), WithURL),
+				Route("default", "ingress-mutation", WithConfigTarget("config"), WithURL, WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -1134,20 +1137,20 @@ 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, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName: "oldconfig-00001",
 						Percent:      ptr.Int64(100),
 					})),
 			// Both configs exist, but only "oldconfig" is labelled.
 			cfg("default", "oldconfig",
-				WithGeneration(1), WithLatestCreated("oldconfig-00001"), WithLatestReady("oldconfig-00001"),
+				WithConfigGeneration(1), WithLatestCreated("oldconfig-00001"), WithLatestReady("oldconfig-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "change-configs"),
 			),
 			cfg("default", "newconfig",
-				WithGeneration(1), WithLatestCreated("newconfig-00001"), WithLatestReady("newconfig-00001")),
+				WithConfigGeneration(1), WithLatestCreated("newconfig-00001"), WithLatestReady("newconfig-00001")),
 			rev("default", "oldconfig", 1, MarkRevisionReady, WithRevName("oldconfig-00001"), WithServiceName("greenwood")),
 			rev("default", "newconfig", 1, MarkRevisionReady, WithRevName("newconfig-00001"), WithServiceName("broadview")),
 			simpleReadyIngress(
@@ -1171,7 +1174,7 @@ func TestReconcile(t *testing.T) {
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			// Updated to point to "newconfig" things.
 			Object: simpleReadyIngress(
-				Route("default", "change-configs", WithConfigTarget("newconfig"), WithURL),
+				Route("default", "change-configs", WithConfigTarget("newconfig"), WithURL, WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -1190,8 +1193,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			// Status updated to "newconfig"
 			Object: Route("default", "change-configs", WithConfigTarget("newconfig"),
-				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled, WithRouteGeneration(1),
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithRouteFinalizer, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "newconfig-00001",
 						Percent:        ptr.Int64(100),
@@ -1202,10 +1205,11 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "configuration missing",
 		Objects: []runtime.Object{
-			Route("default", "config-missing", WithConfigTarget("not-found")),
+			Route("default", "config-missing", WithConfigTarget("not-found"), WithRouteGeneration(1)),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "config-missing", WithConfigTarget("not-found"), WithURL,
+				WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithInitRouteConditions, MarkMissingTrafficTarget("Configuration", "not-found")),
 		}},
 		PostConditions: []func(*testing.T, *TableRow){
@@ -1215,9 +1219,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "revision missing (direct)",
 		Objects: []runtime.Object{
-			Route("default", "missing-revision-direct", WithRevTarget("not-found")),
+			Route("default", "missing-revision-direct", WithRevTarget("not-found"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "missing-revision-direct", WithRevTarget("not-found"), WithURL,
@@ -1230,9 +1234,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "revision missing (indirect)",
 		Objects: []runtime.Object{
-			Route("default", "missing-revision-indirect", WithConfigTarget("config")),
+			Route("default", "missing-revision-indirect", WithConfigTarget("config"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "missing-revision-indirect", WithConfigTarget("config"), WithURL,
@@ -1245,9 +1249,10 @@ func TestReconcile(t *testing.T) {
 			Route("default", "pinned-becomes-ready",
 				// Use the Revision name from the config
 				WithRevTarget("config-00001"), WithRouteFinalizer,
+				WithRouteGeneration(1),
 			),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
 			simpleK8sService(Route("default", "pinned-becomes-ready", WithConfigTarget("config"))),
 			simpleReadyIngress(
@@ -1270,9 +1275,9 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "pinned-becomes-ready",
 				// Use the Revision name from the config
-				WithRevTarget("config-00001"), WithRouteFinalizer,
+				WithRevTarget("config-00001"), WithRouteFinalizer, WithRouteGeneration(1),
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
@@ -1283,7 +1288,7 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "traffic split becomes ready",
 		Objects: []runtime.Object{
-			Route("default", "named-traffic-split", WithSpecTraffic(
+			Route("default", "named-traffic-split", WithRouteGeneration(1), WithSpecTraffic(
 				v1.TrafficTarget{
 					ConfigurationName: "blue",
 					Percent:           ptr.Int64(50),
@@ -1292,15 +1297,15 @@ func TestReconcile(t *testing.T) {
 					Percent:           ptr.Int64(50),
 				}), WithRouteUID("34-78"), WithRouteFinalizer),
 			cfg("default", "blue",
-				WithGeneration(1), WithLatestCreated("blue-00001"), WithLatestReady("blue-00001")),
+				WithConfigGeneration(1), WithLatestCreated("blue-00001"), WithLatestReady("blue-00001")),
 			cfg("default", "green",
-				WithGeneration(1), WithLatestCreated("green-00001"), WithLatestReady("green-00001")),
+				WithConfigGeneration(1), WithLatestCreated("green-00001"), WithLatestReady("green-00001")),
 			rev("default", "blue", 1, MarkRevisionReady, WithRevName("blue-00001"), WithServiceName("blue-ridge")),
 			rev("default", "green", 1, MarkRevisionReady, WithRevName("green-00001"), WithServiceName("green-lake")),
 		},
 		WantCreates: []runtime.Object{
 			simpleIngress(
-				Route("default", "named-traffic-split", WithURL, WithSpecTraffic(
+				Route("default", "named-traffic-split", WithURL, WithRouteGeneration(1), WithSpecTraffic(
 					v1.TrafficTarget{
 						ConfigurationName: "blue",
 						Percent:           ptr.Int64(50),
@@ -1332,20 +1337,22 @@ func TestReconcile(t *testing.T) {
 			),
 			simplePlaceholderK8sService(
 				getContext(),
-				Route("default", "named-traffic-split", WithSpecTraffic(
-					v1.TrafficTarget{
-						ConfigurationName: "blue",
-						Percent:           ptr.Int64(50),
-					},
-					v1.TrafficTarget{
-						ConfigurationName: "green",
-						Percent:           ptr.Int64(50),
-					}), WithRouteUID("34-78"), WithRouteFinalizer),
+				Route("default", "named-traffic-split", WithRouteGeneration(1),
+					WithSpecTraffic(
+						v1.TrafficTarget{
+							ConfigurationName: "blue",
+							Percent:           ptr.Int64(50),
+						},
+						v1.TrafficTarget{
+							ConfigurationName: "green",
+							Percent:           ptr.Int64(50),
+						}), WithRouteUID("34-78"), WithRouteFinalizer),
 				"",
 			),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "named-traffic-split", WithRouteFinalizer,
+				WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithSpecTraffic(v1.TrafficTarget{
 					ConfigurationName: "blue",
 					Percent:           ptr.Int64(50),
@@ -1373,23 +1380,8 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "same revision targets",
 		Objects: []runtime.Object{
-			Route("default", "same-revision-targets", WithSpecTraffic(
-				v1.TrafficTarget{
-					Tag:               "gray",
-					ConfigurationName: "gray",
-					Percent:           ptr.Int64(50),
-				}, v1.TrafficTarget{
-					Tag:          "also-gray",
-					RevisionName: "gray-00001",
-					Percent:      ptr.Int64(50),
-				}), WithRouteUID("1-2"), WithRouteFinalizer),
-			cfg("default", "gray",
-				WithGeneration(1), WithLatestCreated("gray-00001"), WithLatestReady("gray-00001")),
-			rev("default", "gray", 1, MarkRevisionReady, WithRevName("gray-00001"), WithServiceName("shades")),
-		},
-		WantCreates: []runtime.Object{
-			simpleIngress(
-				Route("default", "same-revision-targets", WithURL, WithSpecTraffic(
+			Route("default", "same-revision-targets", WithRouteGeneration(1),
+				WithSpecTraffic(
 					v1.TrafficTarget{
 						Tag:               "gray",
 						ConfigurationName: "gray",
@@ -1398,7 +1390,24 @@ func TestReconcile(t *testing.T) {
 						Tag:          "also-gray",
 						RevisionName: "gray-00001",
 						Percent:      ptr.Int64(50),
-					}), WithRouteUID("1-2")),
+					}), WithRouteUID("1-2"), WithRouteFinalizer),
+			cfg("default", "gray",
+				WithConfigGeneration(1), WithLatestCreated("gray-00001"), WithLatestReady("gray-00001")),
+			rev("default", "gray", 1, MarkRevisionReady, WithRevName("gray-00001"), WithServiceName("shades")),
+		},
+		WantCreates: []runtime.Object{
+			simpleIngress(
+				Route("default", "same-revision-targets", WithURL, WithRouteGeneration(1),
+					WithSpecTraffic(
+						v1.TrafficTarget{
+							Tag:               "gray",
+							ConfigurationName: "gray",
+							Percent:           ptr.Int64(50),
+						}, v1.TrafficTarget{
+							Tag:          "also-gray",
+							RevisionName: "gray-00001",
+							Percent:      ptr.Int64(50),
+						}), WithRouteUID("1-2")),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -1433,17 +1442,19 @@ func TestReconcile(t *testing.T) {
 			),
 			simplePlaceholderK8sService(
 				getContext(),
-				Route("default", "same-revision-targets", WithSpecTraffic(
-					v1.TrafficTarget{
-						Tag:               "gray",
-						ConfigurationName: "gray",
-						Percent:           ptr.Int64(50),
-					},
-					v1.TrafficTarget{
-						Tag:          "also-gray",
-						RevisionName: "gray-00001",
-						Percent:      ptr.Int64(50),
-					}), WithRouteUID("1-2"), WithRouteFinalizer),
+				Route("default", "same-revision-targets",
+					WithRouteGeneration(1), WithRouteObservedGeneration,
+					WithSpecTraffic(
+						v1.TrafficTarget{
+							Tag:               "gray",
+							ConfigurationName: "gray",
+							Percent:           ptr.Int64(50),
+						},
+						v1.TrafficTarget{
+							Tag:          "also-gray",
+							RevisionName: "gray-00001",
+							Percent:      ptr.Int64(50),
+						}), WithRouteUID("1-2"), WithRouteFinalizer),
 				"",
 			),
 			simplePlaceholderK8sService(
@@ -1463,22 +1474,23 @@ func TestReconcile(t *testing.T) {
 			),
 			simplePlaceholderK8sService(
 				getContext(),
-				Route("default", "same-revision-targets", WithSpecTraffic(
-					v1.TrafficTarget{
-						Tag:               "gray",
-						ConfigurationName: "gray",
-						Percent:           ptr.Int64(50),
-					},
-					v1.TrafficTarget{
-						Tag:          "also-gray",
-						RevisionName: "gray-00001",
-						Percent:      ptr.Int64(50),
-					}), WithRouteUID("1-2"), WithRouteFinalizer),
+				Route("default", "same-revision-targets", WithRouteGeneration(1),
+					WithSpecTraffic(
+						v1.TrafficTarget{
+							Tag:               "gray",
+							ConfigurationName: "gray",
+							Percent:           ptr.Int64(50),
+						},
+						v1.TrafficTarget{
+							Tag:          "also-gray",
+							RevisionName: "gray-00001",
+							Percent:      ptr.Int64(50),
+						}), WithRouteUID("1-2"), WithRouteFinalizer),
 				"gray",
 			),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
-			Object: Route("default", "same-revision-targets",
+			Object: Route("default", "same-revision-targets", WithRouteGeneration(1), WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithSpecTraffic(
 					v1.TrafficTarget{
 						Tag:               "gray",
@@ -1526,7 +1538,8 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "switch-configs", WithConfigTarget("green"),
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteGeneration(1), WithRouteObservedGeneration,
+				WithStatusTraffic(
 					v1.TrafficTarget{
 						Tag:          "blue",
 						RevisionName: "blue-00001",
@@ -1534,12 +1547,12 @@ func TestReconcile(t *testing.T) {
 						URL:          url("http://blue.example.com"),
 					}), WithRouteFinalizer),
 			cfg("default", "blue",
-				WithGeneration(1), WithLatestCreated("blue-00001"), WithLatestReady("blue-00001"),
+				WithConfigGeneration(1), WithLatestCreated("blue-00001"), WithLatestReady("blue-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "switch-configs"),
 			),
 			cfg("default", "green",
-				WithGeneration(1), WithLatestCreated("green-00001"), WithLatestReady("green-00001")),
+				WithConfigGeneration(1), WithLatestCreated("green-00001"), WithLatestReady("green-00001")),
 			rev("default", "blue", 1, MarkRevisionReady, WithRevName("blue-00001"), WithServiceName("alki-beach")),
 			rev("default", "green", 1, MarkRevisionReady, WithRevName("green-00001"), WithServiceName("rainier-beach")),
 			simpleReadyIngress(
@@ -1581,7 +1594,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "switch-configs", WithConfigTarget("green"),
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteObservedGeneration, WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "green-00001",
 						Percent:        ptr.Int64(100),
@@ -1596,6 +1609,8 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "split", WithURL, WithAddress,
 				WithInitRouteConditions, MarkTrafficAssigned, MarkIngressReady,
+				WithRouteObservedGeneration,
+				WithRouteGeneration(1),
 				WithSpecTraffic(
 					v1.TrafficTarget{
 						ConfigurationName: "blue",
@@ -1611,9 +1626,9 @@ func TestReconcile(t *testing.T) {
 						Percent:      ptr.Int64(100),
 					},
 				)),
-			cfg("default", "blue", WithGeneration(1),
+			cfg("default", "blue", WithConfigGeneration(1),
 				WithLatestCreated("blue-00001"), WithLatestReady("blue-00001")),
-			cfg("default", "green", WithGeneration(1),
+			cfg("default", "green", WithConfigGeneration(1),
 				WithLatestCreated("green-00001")),
 			rev("default", "blue", 1, MarkRevisionReady, WithRevName("blue-00001")),
 			rev("default", "green", 1, WithRevName("green-00001")),
@@ -1623,6 +1638,7 @@ func TestReconcile(t *testing.T) {
 			Object: Route("default", "split", WithURL, WithAddress,
 				WithInitRouteConditions, MarkTrafficAssigned, MarkIngressReady,
 				MarkConfigurationNotReady("green"),
+				WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithSpecTraffic(
 					v1.TrafficTarget{
 						ConfigurationName: "blue",
@@ -1644,14 +1660,16 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "stale-lastpinned", WithConfigTarget("config"),
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer,
+				WithRouteGeneration(1), WithRouteObservedGeneration,
+				WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "stale-lastpinned"),
 			),
@@ -1684,14 +1702,15 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "old-naming", WithConfigTarget("config"), WithRouteFinalizer,
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
-				MarkTrafficAssigned, MarkIngressReady, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteGeneration(1), WithRouteObservedGeneration,
+				WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1),
+				WithConfigGeneration(1),
 				WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "old-naming"),
@@ -1721,14 +1740,16 @@ func TestReconcile(t *testing.T) {
 			Route("default", "my-route", WithConfigTarget("config"),
 				WithURL, WithAddress, WithRouteConditionsAutoTLSDisabled,
 				MarkTrafficAssigned, MarkIngressReady,
-				WithRouteFinalizer, WithStatusTraffic(
+				WithRouteGeneration(1), WithRouteObservedGeneration,
+				WithRouteFinalizer,
+				WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "steady-state"),
 			),
@@ -1773,15 +1794,16 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "my-route", WithConfigTarget("config"),
 				WithURL, WithAddress, WithInitRouteConditions,
-				MarkTrafficAssigned, MarkIngressReady,
-				WithRouteFinalizer, WithStatusTraffic(
+				MarkTrafficAssigned, MarkIngressReady, WithRouteFinalizer,
+				WithRouteGeneration(1), WithRouteObservedGeneration,
+				WithStatusTraffic(
 					v1.TrafficTarget{
 						RevisionName:   "config-00001",
 						Percent:        ptr.Int64(100),
 						LatestRevision: ptr.Bool(true),
 					})),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001"),
 				// The Route controller attaches our label to this Configuration.
 				WithConfigLabel("serving.knative.dev/route", "steady-state"),
 			),
@@ -1837,7 +1859,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 			wildcardCert("default", "example.com"),
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 		},
 		WantCreates: []runtime.Object{
@@ -1871,7 +1893,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
-				WithRouteUID("12-34"),
+				WithRouteUID("12-34"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when all traffic has been assigned.
 				WithAddress, WithInitRouteConditions,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
@@ -1891,7 +1913,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 		},
 		WantCreates: []runtime.Object{
@@ -1944,7 +1966,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			certificateWithStatus(resources.MakeCertificates(Route("default", "becomes-ready", WithConfigTarget("config"), WithURL, WithRouteUID("12-34")),
 				map[string]string{"becomes-ready.default.example.com": ""}, network.CertManagerCertificateClassName)[0], readyCertStatus()),
@@ -2002,7 +2024,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			// MakeCertificates will create a certificate with DNS name "*.test-ns.example.com" which is not the host name
 			// needed by the input Route.
@@ -2083,7 +2105,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			&netv1alpha1.Certificate{
 				ObjectMeta: metav1.ObjectMeta{
@@ -2176,9 +2198,9 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		Name:    "check that Route updates status and produces event log when valid name but not owned certificate",
 		WantErr: true,
 		Objects: []runtime.Object{
-			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
+			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			&netv1alpha1.Certificate{
 				ObjectMeta: metav1.ObjectMeta{
@@ -2201,11 +2223,12 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		},
 		WantDeleteCollections: []clientgotesting.DeleteCollectionActionImpl{},
 		WantCreates: []runtime.Object{
-			simplePlaceholderK8sService(getContext(), Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")), ""),
+			simplePlaceholderK8sService(getContext(), Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34"), WithRouteGeneration(1)), ""),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
 				WithRouteUID("12-34"),
+				WithRouteGeneration(1), WithRouteObservedGeneration,
 				WithAddress, WithInitRouteConditions, WithURL,
 				MarkTrafficAssigned, WithStatusTraffic(
 					v1.TrafficTarget{
@@ -2222,9 +2245,9 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 	}, {
 		Name: "check that Route is correctly updated when Certificate is not ready",
 		Objects: []runtime.Object{
-			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
+			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34"), WithRouteGeneration(1)),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			// MakeCertificates will create a certificate with DNS name "*.test-ns.example.com" which is not the host name
 			// needed by the input Route.
@@ -2250,7 +2273,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		WantCreates: []runtime.Object{
 			ingressWithTLS(
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithURL,
-					WithRouteUID("12-34")),
+					WithRouteUID("12-34"), WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
 						traffic.DefaultTarget: {{
@@ -2278,7 +2301,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-ready", WithConfigTarget("config"),
-				WithRouteUID("12-34"),
+				WithRouteUID("12-34"), WithRouteGeneration(1), WithRouteObservedGeneration,
 				// Populated by reconciliation when all traffic has been assigned.
 				WithAddress, WithRouteConditionsHTTPDowngrade,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithStatusTraffic(
@@ -2300,11 +2323,11 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		// This test is a same with "public becomes cluster local" above, but confirm it does not create certs with autoTLS for cluster-local.
 		Name: "public becomes cluster local w/ autoTLS",
 		Objects: []runtime.Object{
-			Route("default", "becomes-local", WithConfigTarget("config"),
+			Route("default", "becomes-local", WithConfigTarget("config"), WithRouteGeneration(1),
 				WithRouteLabel(map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal}),
 				WithRouteUID("65-23")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("tb")),
 			simpleIngress(
 				Route("default", "becomes-local", WithConfigTarget("config"), WithRouteUID("65-23"), WithRouteLabel(map[string]string{"serving.knative.dev/visibility": "cluster-local"})),
@@ -2329,7 +2352,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: simpleIngress(
 				Route("default", "becomes-local", WithConfigTarget("config"),
-					WithRouteUID("65-23"),
+					WithRouteUID("65-23"), WithRouteGeneration(1), WithRouteObservedGeneration,
 					WithRouteLabel(map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal})),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -2352,6 +2375,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-local", WithConfigTarget("config"),
 				WithRouteUID("65-23"),
+				WithRouteGeneration(1), WithRouteObservedGeneration,
 				MarkTrafficAssigned, MarkIngressNotConfigured, WithRouteConditionsTLSNotEnabledForClusterLocalMessage,
 				WithLocalDomain, WithAddress, WithInitRouteConditions,
 				WithRouteLabel(map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal}),
@@ -2409,7 +2433,7 @@ func TestReconcile_EnableAutoTLS_HTTPDisabled(t *testing.T) {
 		Objects: []runtime.Object{
 			Route("default", "becomes-ready", WithConfigTarget("config"), WithRouteUID("12-34")),
 			cfg("default", "config",
-				WithGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
+				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithServiceName("mcd")),
 			// MakeCertificates will create a certificate with DNS name "*.test-ns.example.com" which is not the host name
 			// needed by the input Route.
diff --git a/pkg/reconciler/service/service_test.go b/pkg/reconciler/service/service_test.go
index cd8d3bab8..7490135ab 100644
--- a/pkg/reconciler/service/service_test.go
+++ b/pkg/reconciler/service/service_test.go
@@ -70,10 +70,10 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "inline - byo rev name used in traffic serialize, with retry",
 		Objects: []runtime.Object{
-			DefaultService("byo-rev", "foo", WithNamedRevision),
+			DefaultService("byo-rev", "foo", WithNamedRevision, WithServiceGeneration(1)),
 			config("byo-rev", "foo",
 				WithNamedRevision,
-				WithGeneration(2)),
+				WithConfigGeneration(2)),
 		},
 		// Route should not be created until config progresses
 		WantCreates: []runtime.Object{},
@@ -81,11 +81,13 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("byo-rev", "foo", WithNamedRevision,
 				// Route conditions should be at init state while Config should be OutOfDate
-				WithInitSvcConditions, WithOutOfDateConfig),
+				WithInitSvcConditions, WithOutOfDateConfig,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}, {
 			Object: DefaultService("byo-rev", "foo", WithNamedRevision,
 				// Route conditions should be at init state while Config should be OutOfDate
-				WithInitSvcConditions, WithOutOfDateConfig),
+				WithInitSvcConditions, WithOutOfDateConfig,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WithReactors: []clientgotesting.ReactionFunc{
 			func(action clientgotesting.Action) (handled bool, ret runtime.Object, err error) {
@@ -99,10 +101,9 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "inline - byo rev name - existing revision - same spec",
 		Objects: []runtime.Object{
-			DefaultService("byo-rev", "foo", WithNamedRevision,
-				WithServiceGeneration(2), WithServiceObservedGeneration),
+			DefaultService("byo-rev", "foo", WithNamedRevision, WithServiceGeneration(2)),
 			config("byo-rev", "foo", WithNamedRevision,
-				WithGeneration(2), WithObservedGen),
+				WithConfigGeneration(2), WithConfigObservedGen),
 			route("byo-rev", "foo", WithNamedRevision,
 				WithRouteGeneration(2), WithRouteObservedGeneration),
 			&v1.Revision{
@@ -125,33 +126,33 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("byo-rev", "foo", WithNamedRevision,
 				// The first reconciliation will initialize the status conditions.
-				WithInitSvcConditions),
+				WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(2), WithServiceObservedGeneration),
 		}},
 	}, {
 		Name: "inline - byo rev name - existing older revision with same spec",
 		Objects: []runtime.Object{
-			DefaultService("byo-rev", "foo", WithNamedRevision,
-				WithServiceGeneration(2), WithServiceObservedGeneration),
+			DefaultService("byo-rev", "foo", WithNamedRevision, WithServiceGeneration(2)),
 			config("byo-rev", "foo", WithNamedRevision,
-				WithGeneration(2), WithObservedGen),
+				WithConfigGeneration(2), WithConfigObservedGen),
 			route("byo-rev", "foo", WithNamedRevision,
 				WithRouteGeneration(2), WithRouteObservedGeneration),
 			rev("byo-rev", "foo", WithNamedRevision,
 				// Older Revision
-				WithGeneration(1), WithObservedGen),
+				WithConfigGeneration(1), WithConfigObservedGen),
 		},
 		Key: "foo/byo-rev",
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("byo-rev", "foo",
-				WithNamedRevision, WithInitSvcConditions),
+				WithNamedRevision, WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(2), WithServiceObservedGeneration),
 		}},
 	}, {
 		Name: "inline - byo rev name - existing older revision with different spec",
 		Objects: []runtime.Object{
-			DefaultService("byo-rev", "foo", WithNamedRevision,
-				WithServiceGeneration(2), WithServiceObservedGeneration),
+			DefaultService("byo-rev", "foo", WithNamedRevision, WithServiceGeneration(2)),
 			config("byo-rev", "foo", WithNamedRevision,
-				WithGeneration(2), WithObservedGen),
+				WithConfigGeneration(2), WithConfigObservedGen),
 			route("byo-rev", "foo", WithNamedRevision,
 				WithRouteGeneration(2), WithRouteObservedGeneration),
 
@@ -174,12 +175,14 @@ func TestReconcile(t *testing.T) {
 		Key: "foo/byo-rev",
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("byo-rev", "foo", WithNamedRevision,
-				WithInitSvcConditions, MarkRevisionNameTaken),
+				WithInitSvcConditions, MarkRevisionNameTaken,
+				WithServiceGeneration(2), WithServiceObservedGeneration,
+			),
 		}},
 	}, {
 		Name: "inline - byo rev name used in traffic",
 		Objects: []runtime.Object{
-			DefaultService("byo-rev", "foo", WithNamedRevision),
+			DefaultService("byo-rev", "foo", WithNamedRevision, WithServiceGeneration(1)),
 		},
 		Key: "foo/byo-rev",
 		WantCreates: []runtime.Object{
@@ -189,7 +192,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("byo-rev", "foo", WithNamedRevision,
 				// The first reconciliation will initialize the status conditions.
-				WithInitSvcConditions),
+				WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Configuration %q", "byo-rev"),
@@ -198,7 +202,7 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "create route and configuration",
 		Objects: []runtime.Object{
-			DefaultService("run-latest", "foo", WithRunLatestRollout),
+			DefaultService("run-latest", "foo", WithRunLatestRollout, WithServiceGeneration(1)),
 		},
 		Key: "foo/run-latest",
 		WantCreates: []runtime.Object{
@@ -208,7 +212,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("run-latest", "foo", WithRunLatestRollout,
 				// The first reconciliation will initialize the status conditions.
-				WithInitSvcConditions),
+				WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Configuration %q", "run-latest"),
@@ -217,7 +222,8 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "steady state",
 		Objects: []runtime.Object{
-			DefaultService("no-updates", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("no-updates", "foo", WithRunLatestRollout, WithInitSvcConditions,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 			route("no-updates", "foo", WithRunLatestRollout),
 			config("no-updates", "foo", WithRunLatestRollout),
 		},
@@ -271,7 +277,8 @@ func TestReconcile(t *testing.T) {
 	}, {
 		Name: "update route and configuration",
 		Objects: []runtime.Object{
-			DefaultService("update-route-and-config", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("update-route-and-config", "foo", WithRunLatestRollout,
+				WithInitSvcConditions),
 			// Mutate the Config/Route to have a different body than we want.
 			config("update-route-and-config", "foo", WithRunLatestRollout,
 				// This is just an unexpected mutation of the config spec vs. the service spec.
@@ -289,7 +296,7 @@ func TestReconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			DefaultService("update-route-and-config", "foo", WithRunLatestRollout, func(svc *v1.Service) {
 				svc.Spec.GetTemplate().Name = "update-route-and-config-blah"
-			}, WithInitSvcConditions),
+			}, WithInitSvcConditions, WithServiceGeneration(1)),
 			// Mutate the Config/Route to have a different body than we want.
 			config("update-route-and-config", "foo", WithRunLatestRollout,
 				// Change the concurrency to ensure it is corrected.
@@ -313,15 +320,17 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("update-route-and-config", "foo", WithRunLatestRollout, func(svc *v1.Service) {
 				svc.Spec.GetTemplate().Name = "update-route-and-config-blah"
-			}, WithInitSvcConditions, func(svc *v1.Service) {
-				svc.Status.MarkRevisionNameTaken("update-route-and-config-blah")
-			}),
+			}, WithInitSvcConditions, WithServiceGeneration(1), WithServiceObservedGeneration,
+				func(svc *v1.Service) {
+					svc.Status.MarkRevisionNameTaken("update-route-and-config-blah")
+				}),
 		}},
 	}, {
 		Name: "update route and configuration labels",
 		Objects: []runtime.Object{
 			// Mutate the Service to add some more labels
-			DefaultService("update-route-and-config-labels", "foo", WithRunLatestRollout, WithInitSvcConditions, WithServiceLabel("new-label", "new-value")),
+			DefaultService("update-route-and-config-labels", "foo", WithRunLatestRollout,
+				WithInitSvcConditions, WithServiceLabel("new-label", "new-value")),
 			config("update-route-and-config-labels", "foo", WithRunLatestRollout),
 			route("update-route-and-config-labels", "foo", WithRunLatestRollout),
 		},
@@ -382,7 +391,7 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("create", "routes"),
 		},
 		Objects: []runtime.Object{
-			DefaultService("create-route-failure", "foo", WithRunLatestRollout),
+			DefaultService("create-route-failure", "foo", WithRunLatestRollout, WithServiceGeneration(1)),
 		},
 		Key: "foo/create-route-failure",
 		WantCreates: []runtime.Object{
@@ -392,7 +401,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("create-route-failure", "foo", WithRunLatestRollout,
 				// First reconcile initializes conditions.
-				WithInitSvcConditions),
+				WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Configuration %q", "create-route-failure"),
@@ -408,7 +418,7 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("create", "configurations"),
 		},
 		Objects: []runtime.Object{
-			DefaultService("create-config-failure", "foo", WithRunLatestRollout),
+			DefaultService("create-config-failure", "foo", WithRunLatestRollout, WithServiceGeneration(1)),
 		},
 		Key: "foo/create-config-failure",
 		WantCreates: []runtime.Object{
@@ -418,7 +428,8 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("create-config-failure", "foo", WithRunLatestRollout,
 				// First reconcile initializes conditions.
-				WithInitSvcConditions),
+				WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "CreationFailed", "Failed to create Configuration %q: %v",
@@ -433,7 +444,8 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("update", "routes"),
 		},
 		Objects: []runtime.Object{
-			DefaultService("update-route-failure", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("update-route-failure", "foo", WithRunLatestRollout,
+				WithInitSvcConditions),
 			// Mutate the Route to have an unexpected body to trigger an update.
 			route("update-route-failure", "foo", WithRunLatestRollout, MutateRoute),
 			config("update-route-failure", "foo", WithRunLatestRollout),
@@ -453,7 +465,8 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("update", "configurations"),
 		},
 		Objects: []runtime.Object{
-			DefaultService("update-config-failure", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("update-config-failure", "foo", WithRunLatestRollout,
+				WithInitSvcConditions, WithServiceGeneration(1), WithServiceObservedGeneration),
 			route("update-config-failure", "foo", WithRunLatestRollout),
 			// Mutate the Config to have an unexpected body to trigger an update.
 			config("update-config-failure", "foo", WithRunLatestRollout,
@@ -475,7 +488,7 @@ func TestReconcile(t *testing.T) {
 			InduceFailure("update", "services"),
 		},
 		Objects: []runtime.Object{
-			DefaultService("run-latest", "foo", WithRunLatestRollout),
+			DefaultService("run-latest", "foo", WithRunLatestRollout, WithServiceGeneration(1)),
 		},
 		Key: "foo/run-latest",
 		WantCreates: []runtime.Object{
@@ -486,7 +499,8 @@ func TestReconcile(t *testing.T) {
 			Object: DefaultService("run-latest", "foo", WithRunLatestRollout,
 				// We attempt to update the Service to initialize its
 				// conditions, which is where we induce the failure.
-				WithInitSvcConditions),
+				WithInitSvcConditions, WithServiceObservedGenFailure,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "Created", "Created Configuration %q", "run-latest"),
@@ -498,7 +512,7 @@ func TestReconcile(t *testing.T) {
 		Name: "route and config ready, propagate ready",
 		// When both route and config are ready, the service should become ready.
 		Objects: []runtime.Object{
-			DefaultService("all-ready", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("all-ready", "foo", WithRunLatestRollout, WithInitSvcConditions, WithServiceGeneration(1)),
 			route("all-ready", "foo", WithRunLatestRollout, RouteReady,
 				WithURL, WithAddress, WithInitRouteConditions,
 				WithStatusTraffic(
@@ -507,7 +521,7 @@ func TestReconcile(t *testing.T) {
 						Percent:      ptr.Int64(100),
 					}), MarkTrafficAssigned, MarkIngressReady),
 			config("all-ready", "foo", WithRunLatestRollout,
-				WithGeneration(1), WithObservedGen,
+				WithConfigGeneration(1), WithConfigObservedGen,
 				// These turn a Configuration to Ready=true
 				WithLatestCreated("all-ready-00001"), WithLatestReady("all-ready-00001")),
 		},
@@ -527,7 +541,7 @@ func TestReconcile(t *testing.T) {
 		// When both route and config are ready, the service should become ready.
 		Objects: []runtime.Object{
 			DefaultService("all-ready", "foo", WithRunLatestRollout, WithInitSvcConditions,
-				WithReadyConfig("all-ready-00001")),
+				WithReadyConfig("all-ready-00001"), WithServiceGeneration(1)),
 			route("all-ready", "foo", WithRunLatestRollout, RouteReady,
 				WithURL, WithAddress, WithInitRouteConditions,
 				WithStatusTraffic(v1.TrafficTarget{
@@ -535,7 +549,7 @@ func TestReconcile(t *testing.T) {
 					Percent:      ptr.Int64(100),
 				}), MarkTrafficAssigned, MarkIngressReady),
 			config("all-ready", "foo", WithRunLatestRollout,
-				WithGeneration(1), WithObservedGen, WithGeneration(2),
+				WithConfigGeneration(1), WithConfigObservedGen, WithConfigGeneration(2),
 				// These turn a Configuration to Ready=true
 				WithLatestCreated("all-ready-00001"), WithLatestReady("all-ready-00001")),
 		},
@@ -556,7 +570,7 @@ func TestReconcile(t *testing.T) {
 		// When both route and config are ready, but the route points to the previous revision
 		// the service should not be ready.
 		Objects: []runtime.Object{
-			DefaultService("config-only-ready", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("config-only-ready", "foo", WithRunLatestRollout, WithInitSvcConditions, WithServiceGeneration(1)),
 			route("config-only-ready", "foo", WithRunLatestRollout, RouteReady,
 				WithURL, WithAddress, WithInitRouteConditions,
 				WithStatusTraffic(v1.TrafficTarget{
@@ -564,7 +578,7 @@ func TestReconcile(t *testing.T) {
 					Percent:      ptr.Int64(100),
 				}), MarkTrafficAssigned, MarkIngressReady),
 			config("config-only-ready", "foo", WithRunLatestRollout,
-				WithGeneration(2 /*will generate revision -00002*/), WithObservedGen,
+				WithConfigGeneration(2 /*will generate revision -00002*/), WithConfigObservedGen,
 				// These turn a Configuration to Ready=true
 				WithLatestCreated("config-only-ready-00002"), WithLatestReady("config-only-ready-00002")),
 		},
@@ -584,16 +598,16 @@ func TestReconcile(t *testing.T) {
 		// Gen 2: config update fails;
 		//    => service is still OK serving Gen 1.
 		Objects: []runtime.Object{
-			DefaultService("config-fails", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("config-fails", "foo", WithRunLatestRollout, WithInitSvcConditions, WithServiceGeneration(1)),
 			route("config-fails", "foo", WithRunLatestRollout, RouteReady,
 				WithURL, WithAddress, WithInitRouteConditions,
 				WithStatusTraffic(v1.TrafficTarget{
 					RevisionName: "config-fails-00001",
 					Percent:      ptr.Int64(100),
 				}), MarkTrafficAssigned, MarkIngressReady),
-			config("config-fails", "foo", WithRunLatestRollout, WithGeneration(2),
+			config("config-fails", "foo", WithRunLatestRollout, WithConfigGeneration(2),
 				WithLatestReady("config-fails-00001"), WithLatestCreated("config-fails-00002"),
-				MarkLatestCreatedFailed("blah"), WithObservedGen),
+				MarkLatestCreatedFailed("blah"), WithConfigObservedGen),
 		},
 		Key: "foo/config-fails",
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
@@ -610,9 +624,9 @@ func TestReconcile(t *testing.T) {
 		Name: "configuration failure is propagated",
 		// When config fails, the service should fail.
 		Objects: []runtime.Object{
-			DefaultService("config-fails", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("config-fails", "foo", WithRunLatestRollout, WithInitSvcConditions, WithServiceGeneration(1)),
 			route("config-fails", "foo", WithRunLatestRollout, RouteReady),
-			config("config-fails", "foo", WithRunLatestRollout, WithGeneration(1), WithObservedGen,
+			config("config-fails", "foo", WithRunLatestRollout, WithConfigGeneration(1), WithConfigObservedGen,
 				WithLatestCreated("config-fails-00001"), MarkLatestCreatedFailed("blah")),
 		},
 		Key: "foo/config-fails",
@@ -625,10 +639,10 @@ func TestReconcile(t *testing.T) {
 		Name: "route failure is propagated",
 		// When route fails, the service should fail.
 		Objects: []runtime.Object{
-			DefaultService("route-fails", "foo", WithRunLatestRollout, WithInitSvcConditions),
+			DefaultService("route-fails", "foo", WithRunLatestRollout, WithInitSvcConditions, WithServiceGeneration(1)),
 			route("route-fails", "foo", WithRunLatestRollout,
 				RouteFailed("Propagate me, please", "")),
-			config("route-fails", "foo", WithRunLatestRollout, WithGeneration(1), WithObservedGen,
+			config("route-fails", "foo", WithRunLatestRollout, WithConfigGeneration(1), WithConfigObservedGen,
 				// These turn a Configuration to Ready=true
 				WithLatestCreated("route-fails-00001"), WithLatestReady("route-fails-00001")),
 		},
@@ -644,14 +658,15 @@ func TestReconcile(t *testing.T) {
 		Name:    "existing configuration without an owner causes a failure",
 		WantErr: true,
 		Objects: []runtime.Object{
-			DefaultService("run-latest", "foo", WithRunLatestRollout),
+			DefaultService("run-latest", "foo", WithRunLatestRollout, WithServiceGeneration(1)),
 			config("run-latest", "foo", WithRunLatestRollout, WithConfigOwnersRemoved),
 		},
 		Key: "foo/run-latest",
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("run-latest", "foo", WithRunLatestRollout,
 				// The first reconciliation will initialize the status conditions.
-				WithInitSvcConditions, MarkConfigurationNotOwned),
+				WithInitSvcConditions, MarkConfigurationNotOwned,
+				WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", `service: "run-latest" does not own configuration: "run-latest"`),
@@ -660,7 +675,7 @@ func TestReconcile(t *testing.T) {
 		Name:    "existing route without an owner causes a failure",
 		WantErr: true,
 		Objects: []runtime.Object{
-			DefaultService("run-latest", "foo", WithRunLatestRollout),
+			DefaultService("run-latest", "foo", WithRunLatestRollout, WithServiceGeneration(1)),
 			config("run-latest", "foo", WithRunLatestRollout),
 			route("run-latest", "foo", WithRunLatestRollout, WithRouteOwnersRemoved),
 		},
@@ -668,7 +683,7 @@ func TestReconcile(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: DefaultService("run-latest", "foo", WithRunLatestRollout,
 				// The first reconciliation will initialize the status conditions.
-				WithInitSvcConditions, MarkRouteNotOwned),
+				WithInitSvcConditions, MarkRouteNotOwned, WithServiceGeneration(1), WithServiceObservedGeneration),
 		}},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError", `service: "run-latest" does not own route: "run-latest"`),
@@ -688,7 +703,7 @@ func TestReconcile(t *testing.T) {
 					RevisionName: "new-owner-00001",
 					Percent:      ptr.Int64(100),
 				}), MarkTrafficAssigned, MarkIngressReady),
-			config("new-owner", "foo", WithRunLatestRollout, WithGeneration(1), WithObservedGen,
+			config("new-owner", "foo", WithRunLatestRollout, WithConfigGeneration(1), WithConfigObservedGen,
 				// These turn a Configuration to Ready=true
 				WithLatestCreated("new-owner-00001"), WithLatestReady("new-owner-00001")),
 		},
diff --git a/pkg/testing/functional.go b/pkg/testing/functional.go
index 237ba90e5..4a9103f6a 100644
--- a/pkg/testing/functional.go
+++ b/pkg/testing/functional.go
@@ -187,6 +187,24 @@ func WithMetricAnnotation(metric string) PodAutoscalerOption {
 	return withAnnotationValue(autoscaling.MetricAnnotationKey, metric)
 }
 
+// WithObservedGeneration returns a PodAutoScalerOption which sets
+// the Status.ObservedGeneration field to the given generation.
+func WithObservedGeneration(gen int64) PodAutoscalerOption {
+	return func(pa *asv1a1.PodAutoscaler) {
+		pa.Status.ObservedGeneration = gen
+	}
+}
+
+// WithObservedGenerationFailure marks the top level condition as unknown when the reconciler
+// does not set any condition during reconciliation of a new generation.
+func WithObservedGenerationFailure() PodAutoscalerOption {
+	return func(pa *asv1a1.PodAutoscaler) {
+		condSet := pa.GetConditionSet()
+		condSet.Manage(&pa.Status).MarkUnknown(condSet.GetTopLevelConditionType(),
+			"NewObservedGenFailure", "unsuccessfully observed a new generation")
+	}
+}
+
 // WithMetricOwnersRemoved clears the owner references of this PodAutoscaler.
 func WithMetricOwnersRemoved(m *asv1a1.Metric) {
 	m.OwnerReferences = nil
diff --git a/pkg/testing/v1/configuration.go b/pkg/testing/v1/configuration.go
index ac74dbfa6..602deb0e0 100644
--- a/pkg/testing/v1/configuration.go
+++ b/pkg/testing/v1/configuration.go
@@ -56,15 +56,15 @@ func WithConfigContainerConcurrency(cc int64) ConfigOption {
 	}
 }
 
-// WithGeneration sets the generation of the Configuration.
-func WithGeneration(gen int64) ConfigOption {
+// WithConfigGeneration sets the generation of the Configuration.
+func WithConfigGeneration(gen int64) ConfigOption {
 	return func(cfg *v1.Configuration) {
 		cfg.Generation = gen
 	}
 }
 
-// WithObservedGen sets the observed generation of the Configuration.
-func WithObservedGen(cfg *v1.Configuration) {
+// WithConfigObservedGen sets the observed generation of the Configuration.
+func WithConfigObservedGen(cfg *v1.Configuration) {
 	cfg.Status.ObservedGeneration = cfg.Generation
 }
 
diff --git a/pkg/testing/v1/route.go b/pkg/testing/v1/route.go
index 5f3cf3b0f..26ecaf4e8 100644
--- a/pkg/testing/v1/route.go
+++ b/pkg/testing/v1/route.go
@@ -50,8 +50,8 @@ func WithRouteUID(uid types.UID) RouteOption {
 
 // WithRouteGeneration sets the route's generation
 func WithRouteGeneration(generation int64) RouteOption {
-	return func(svc *v1.Route) {
-		svc.Status.ObservedGeneration = generation
+	return func(r *v1.Route) {
+		r.Status.ObservedGeneration = generation
 	}
 }
 
diff --git a/pkg/testing/v1/service.go b/pkg/testing/v1/service.go
index d6d1b9e46..27ccdde70 100644
--- a/pkg/testing/v1/service.go
+++ b/pkg/testing/v1/service.go
@@ -72,6 +72,14 @@ func WithInitSvcConditions(s *v1.Service) {
 	s.Status.InitializeConditions()
 }
 
+// WithServiceObservedGenFailure marks the top level condition as unknown when the reconciler
+// does not set any condition during reconciliation of a new generation.
+func WithServiceObservedGenFailure(s *v1.Service) {
+	condSet := s.GetConditionSet()
+	condSet.Manage(&s.Status).MarkUnknown(condSet.GetTopLevelConditionType(),
+		"NewObservedGenFailure", "unsuccessfully observed a new generation")
+}
+
 // WithConfigSpec confgures the Service to use the given config spec
 func WithConfigSpec(config v1.ConfigurationSpec) ServiceOption {
 	return func(svc *v1.Service) {
diff --git a/test/performance/benchmarks/.ko.yaml b/test/performance/benchmarks/.ko.yaml
new file mode 100644
index 000000000..5e5fb741d
--- /dev/null
+++ b/test/performance/benchmarks/.ko.yaml
@@ -0,0 +1,4 @@
+# Use :nonroot base image for all containers
+defaultBaseImage: gcr.io/distroless/static:nonroot
+baseImageOverrides:
+  knative.dev/serving/vendor/github.com/tsenart/vegeta: ubuntu:latest
diff --git a/test/performance/benchmarks/dataplane-probe/continuous/.ko.yaml b/test/performance/benchmarks/dataplane-probe/continuous/.ko.yaml
deleted file mode 120000
index fc96611ba..000000000
--- a/test/performance/benchmarks/dataplane-probe/continuous/.ko.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../../.ko.yaml
\ No newline at end of file
diff --git a/test/performance/benchmarks/deployment-probe/continuous/.ko.yaml b/test/performance/benchmarks/deployment-probe/continuous/.ko.yaml
deleted file mode 120000
index fc96611ba..000000000
--- a/test/performance/benchmarks/deployment-probe/continuous/.ko.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../../.ko.yaml
\ No newline at end of file
diff --git a/test/performance/benchmarks/load-test/continuous/.ko.yaml b/test/performance/benchmarks/load-test/continuous/.ko.yaml
deleted file mode 120000
index fc96611ba..000000000
--- a/test/performance/benchmarks/load-test/continuous/.ko.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../../.ko.yaml
\ No newline at end of file
diff --git a/test/performance/benchmarks/rollout-probe/continuous/.ko.yaml b/test/performance/benchmarks/rollout-probe/continuous/.ko.yaml
deleted file mode 120000
index fc96611ba..000000000
--- a/test/performance/benchmarks/rollout-probe/continuous/.ko.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../../.ko.yaml
\ No newline at end of file
diff --git a/test/performance/benchmarks/rollout-probe/continuous/main.go b/test/performance/benchmarks/rollout-probe/continuous/main.go
index 84b839fa0..186275d2f 100644
--- a/test/performance/benchmarks/rollout-probe/continuous/main.go
+++ b/test/performance/benchmarks/rollout-probe/continuous/main.go
@@ -23,8 +23,12 @@ import (
 	"time"
 
 	vegeta "github.com/tsenart/vegeta/lib"
+
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/labels"
 
+	servingclient "knative.dev/serving/pkg/client/injection/client"
+
 	"knative.dev/pkg/signals"
 	"knative.dev/pkg/test/mako"
 	"knative.dev/serving/pkg/apis/serving"
@@ -37,9 +41,7 @@ var (
 	duration = flag.Duration("duration", 5*time.Minute, "The duration of the probe")
 )
 
-const (
-	namespace = "default"
-)
+const namespace = "default"
 
 func main() {
 	flag.Parse()
@@ -106,7 +108,9 @@ func main() {
 	log.Printf("Selector: %v", selector)
 	deploymentStatus := metrics.FetchDeploymentsStatus(ctx, namespace, selector, time.Second)
 	// Start the attack!
-	results := attacker.Attack(targeter, rate, *duration, "load-test")
+	results := attacker.Attack(targeter, rate, *duration, "rollout-test")
+	// After a minute, update the Ksvc.
+	updateSvc := time.After(time.Minute)
 LOOP:
 	for {
 		select {
@@ -115,6 +119,20 @@ LOOP:
 			// clean thing up.
 			break LOOP
 
+		case <-updateSvc:
+			log.Println("Updating the service: ", *target)
+			sc := servingclient.Get(ctx)
+			svc, err := sc.ServingV1().Services(namespace).Get(*target, metav1.GetOptions{})
+			if err != nil {
+				log.Fatalf("Error getting ksvc %s: %v", *target, err)
+			}
+			// Make sure we start with a single instance.
+			svc.Spec.Template.Annotations["autoscaling.knative.dev/minScale"] = "1"
+			_, err = sc.ServingV1().Services(namespace).Update(svc)
+			if err != nil {
+				log.Fatalf("Error updating ksvc %s: %v", *target, err)
+			}
+			log.Println("Successfully updated the service.")
 		case res, ok := <-results:
 			if !ok {
 				// Once we have read all of the request results, break out of
diff --git a/test/performance/benchmarks/rollout-probe/continuous/rollout-probe-setup.yaml b/test/performance/benchmarks/rollout-probe/continuous/rollout-probe-setup.yaml
index 19987e609..f081a2a28 100644
--- a/test/performance/benchmarks/rollout-probe/continuous/rollout-probe-setup.yaml
+++ b/test/performance/benchmarks/rollout-probe/continuous/rollout-probe-setup.yaml
@@ -37,7 +37,8 @@ spec:
   template:
     metadata:
       annotations:
-        autoscaling.knative.dev/minScale: "1"
+        autoscaling.knative.dev/minScale: "100"
+        autoscaling.knative.dev/maxScale: "150"
         # Always hook the activator in.
         autoscaling.knative.dev/targetBurstCapacity: "-1"
     spec:
@@ -77,7 +78,8 @@ spec:
   template:
     metadata:
       annotations:
-        autoscaling.knative.dev/minScale: "1"
+        autoscaling.knative.dev/minScale: "100"
+        autoscaling.knative.dev/maxScale: "150"
         # Only hook the activator in when scaled to zero.
         autoscaling.knative.dev/targetBurstCapacity: "0"
     spec:
diff --git a/test/performance/benchmarks/scale-from-zero/continuous/.ko.yaml b/test/performance/benchmarks/scale-from-zero/continuous/.ko.yaml
deleted file mode 120000
index fc96611ba..000000000
--- a/test/performance/benchmarks/scale-from-zero/continuous/.ko.yaml
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../../.ko.yaml
\ No newline at end of file
diff --git a/third_party/contour-latest/contour.yaml b/third_party/contour-latest/contour.yaml
index a0b2a9790..7b236f873 100644
--- a/third_party/contour-latest/contour.yaml
+++ b/third_party/contour-latest/contour.yaml
@@ -1432,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:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         command:
         - contour
@@ -1672,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:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         name: contour
         ports:
@@ -1764,7 +1764,7 @@ spec:
         args:
         - envoy
         - shutdown-manager
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         lifecycle:
           preStop:
@@ -1840,7 +1840,7 @@ spec:
         - --envoy-key-file=/certs/tls.key
         command:
         - contour
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         name: envoy-initconfig
         volumeMounts:
@@ -3305,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:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         command:
         - contour
@@ -3545,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:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         name: contour
         ports:
@@ -3637,7 +3637,7 @@ spec:
         args:
         - envoy
         - shutdown-manager
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         lifecycle:
           preStop:
@@ -3713,7 +3713,7 @@ spec:
         - --envoy-key-file=/certs/tls.key
         command:
         - contour
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7e8baabfdce34f22939ea89c666520dc95391cbf24efbd6850de040bb53a3e77
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:2078247f021fa1939f391d21c2625a192a5f3cd08465d2ceb9f4c6535f4fdabf
         imagePullPolicy: Always
         name: envoy-initconfig
         volumeMounts:
diff --git a/third_party/contour-latest/net-contour.yaml b/third_party/contour-latest/net-contour.yaml
index 66506770d..c70075650 100644
--- a/third_party/contour-latest/net-contour.yaml
+++ b/third_party/contour-latest/net-contour.yaml
@@ -35,7 +35,7 @@ metadata:
   namespace: knative-serving
   labels:
     networking.knative.dev/ingress-provider: contour
-    serving.knative.dev/release: "v20200616-e825070b"
+    serving.knative.dev/release: "v20200618-e5e8dbd1"
 data:
   _example: |
     ################################
@@ -94,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:c0bc3f3b6b8fe90f2feba120b37d1a4cba6e00c0fc91f8bd05a28db4376440b2
+        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:117f50d9ef1867275c1ab66082c02ca930bd481fd6a4105fbad7f350f1275c53
         resources:
           requests:
             cpu: 40m
diff --git a/vendor/knative.dev/pkg/metrics/testing.go b/vendor/knative.dev/pkg/metrics/testing.go
index d33668ead..eb048f9cb 100644
--- a/vendor/knative.dev/pkg/metrics/testing.go
+++ b/vendor/knative.dev/pkg/metrics/testing.go
@@ -19,7 +19,7 @@ package metrics
 // InitForTesting initialize the necessary global variables for unit tests.
 func InitForTesting() {
 	setCurMetricsConfig(&metricsConfig{
-		backendDestination: Prometheus,
+		backendDestination: prometheus,
 		component:          "test",
 		domain:             "test",
 	})
diff --git a/vendor/knative.dev/test-infra/scripts/e2e-tests.sh b/vendor/knative.dev/test-infra/scripts/e2e-tests.sh
index 01b2d0e40..354819385 100644
--- a/vendor/knative.dev/test-infra/scripts/e2e-tests.sh
+++ b/vendor/knative.dev/test-infra/scripts/e2e-tests.sh
@@ -108,6 +108,8 @@ function dump_cluster_state() {
       echo ">>> Details" >> ${output}
       if [[ "${crd}" == "secrets" ]]; then
         echo "Secrets are ignored for security reasons" >> ${output}
+      elif [[ "${crd}" == "events" ]]; then
+        echo "events are ignored as making a lot of noise" >> ${output}
       else
         kubectl get ${crd} --all-namespaces -o yaml >> ${output}
       fi
diff --git a/vendor/knative.dev/test-infra/scripts/library.sh b/vendor/knative.dev/test-infra/scripts/library.sh
index de059f536..1ed038116 100644
--- a/vendor/knative.dev/test-infra/scripts/library.sh
+++ b/vendor/knative.dev/test-infra/scripts/library.sh
@@ -389,25 +389,8 @@ function mktemp_with_extension() {
 #             $2 - check name as an identifier (e.g., GoBuild)
 #             $3 - failure message (can contain newlines), optional (means success)
 function create_junit_xml() {
-  local xml="$(mktemp_with_extension ${ARTIFACTS}/junit_XXXXXXXX xml)"
-  local failure=""
-  if [[ "$3" != "" ]]; then
-    # Transform newlines into HTML code.
-    # Also escape `<` and `>` as here: https://github.com/golang/go/blob/50bd1c4d4eb4fac8ddeb5f063c099daccfb71b26/src/encoding/json/encode.go#L48,
-    # this is temporary solution for fixing https://github.com/knative/test-infra/issues/1204,
-    # which should be obsolete once Test-infra 2.0 is in place
-    local msg="$(echo -n "$3" | sed 's/$/\&#xA;/g' | sed 's/</\\u003c/' | sed 's/>/\\u003e/' | sed 's/&/\\u0026/' | tr -d '\n')"
-    failure="<failure message=\"Failed\" type=\"\">${msg}</failure>"
-  fi
-  cat << EOF > "${xml}"
-<testsuites>
-	<testsuite tests="1" failures="1" time="0.000" name="$1">
-		<testcase classname="" name="$2" time="0.0">
-			${failure}
-		</testcase>
-	</testsuite>
-</testsuites>
-EOF
+  local xml="$(mktemp_with_extension "${ARTIFACTS}"/junit_XXXXXXXX xml)"
+  run_kntest junit --suite="$1" --name="$2" --err-msg="$3" --dest="${xml}" || return 1
 }
 
 # Runs a go test and generate a junit summary.
@@ -542,6 +525,15 @@ function run_go_tool() {
   ${tool} "$@"
 }
 
+# Run kntest tool, error out and ask users to install it if it's not currently installed.
+# Parameters: $1..$n - parameters passed to the tool.
+function run_kntest() {
+  if [[ -z "$(which kntest)" ]]; then
+    echo "--- FAIL: kntest not installed, please clone test-infra repo and run \`go install ./kntest/cmd/kntest\` to install it"; return 1;
+  fi
+  kntest "$@"
+}
+
 # Run go-licenses to update licenses.
 # Parameters: $1 - output file, relative to repo root dir.
 #             $2 - directory to inspect.