Repository: knative/serving
Pull request #10815 (2021-02-19 11:56:03)

Fix race condition with Prober logger in upgrade tests
The actual issue is that the test context expires between individual
stages run by the upgrade framework. This fix passes an external logger
that survives the stages.

Fixes https://github.com/knative/pkg/issues/2026

<!-- Please include the 'why' behind your changes if no issue exists -->
## Proposed Changes

*
*
*

**Release Note**

<!-- Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". -->

```release-note

```


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

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **chizhg** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @chizhg` 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:

- **[test/OWNERS](https://github.com/knative/serving/blob/master/test/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":["chizhg"]} -->
knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/serving/pull/10815#pullrequestreview-594120905" title="Approved">markusthoemmes</a>*

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

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

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

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

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":[]} -->
cardil: /hold
codecov[bot]: # [Codecov](https://codecov.io/gh/knative/serving/pull/10815?src=pr&el=h1) Report
> Merging [#10815](https://codecov.io/gh/knative/serving/pull/10815?src=pr&el=desc) (598e57e) into [master](https://codecov.io/gh/knative/serving/commit/2516dae3d46ee652840bd7351c9b3a10556a7e05?el=desc) (2516dae) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/knative/serving/pull/10815/graphs/tree.svg?width=650&height=150&src=pr&token=MPpDfAj4Ui)](https://codecov.io/gh/knative/serving/pull/10815?src=pr&el=tree)

```diff
@@           Coverage Diff           @@
##           master   #10815   +/-   ##
=======================================
  Coverage   88.01%   88.01%           
=======================================
  Files         188      188           
  Lines        9043     9043           
=======================================
  Hits         7959     7959           
  Misses        830      830           
  Partials      254      254           
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/knative/serving/pull/10815?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/knative/serving/pull/10815?src=pr&el=footer). Last update [2516dae...598e57e](https://codecov.io/gh/knative/serving/pull/10815?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

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

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-istio-stable-no-mesh | 598e57edf5033544c655dbd0e1156df19b074998 | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/10815/pull-knative-serving-istio-stable-no-mesh/1362732697444683776) | `/test pull-knative-serving-istio-stable-no-mesh`

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

<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 -->
mgencur: /retest

Patch:
diff --git a/cmd/activator/main.go b/cmd/activator/main.go
index 68e3ae46b..98d09740e 100644
--- a/cmd/activator/main.go
+++ b/cmd/activator/main.go
@@ -34,7 +34,6 @@ import (
 	kubeclient "knative.dev/pkg/client/injection/kube/client"
 	"knative.dev/pkg/injection"
 	"knative.dev/serving/pkg/activator"
-	revisioninformer "knative.dev/serving/pkg/client/injection/informers/serving/v1/revision"
 
 	"k8s.io/apimachinery/pkg/util/wait"
 
@@ -181,7 +180,7 @@ func main() {
 	ah = tracing.HTTPSpanMiddleware(ah)
 	ah = configStore.HTTPMiddleware(ah)
 	reqLogHandler, err := pkghttp.NewRequestLogHandler(ah, logging.NewSyncFileWriter(os.Stdout), "",
-		requestLogTemplateInputGetter(revisioninformer.Get(ctx).Lister()), false /*enableProbeRequestLog*/)
+		requestLogTemplateInputGetter, false /*enableProbeRequestLog*/)
 	if err != nil {
 		logger.Fatalw("Unable to create request log handler", zap.Error(err))
 	}
diff --git a/cmd/activator/request_log.go b/cmd/activator/request_log.go
index 3878f80d1..c800ed7c6 100644
--- a/cmd/activator/request_log.go
+++ b/cmd/activator/request_log.go
@@ -22,9 +22,8 @@ import (
 	"go.uber.org/zap"
 	corev1 "k8s.io/api/core/v1"
 	"knative.dev/pkg/metrics"
-	"knative.dev/serving/pkg/activator"
+	"knative.dev/serving/pkg/activator/handler"
 	"knative.dev/serving/pkg/apis/serving"
-	servinglisters "knative.dev/serving/pkg/client/listers/serving/v1"
 	pkghttp "knative.dev/serving/pkg/http"
 )
 
@@ -48,25 +47,25 @@ func updateRequestLogFromConfigMap(logger *zap.SugaredLogger, h *pkghttp.Request
 	}
 }
 
-func requestLogTemplateInputGetter(revisionLister servinglisters.RevisionLister) pkghttp.RequestLogTemplateInputGetter {
-	return func(req *http.Request, resp *pkghttp.RequestLogResponse) *pkghttp.RequestLogTemplateInput {
-		namespace := req.Header.Get(activator.RevisionHeaderNamespace)
-		name := req.Header.Get(activator.RevisionHeaderName)
-		revInfo := &pkghttp.RequestLogRevision{
-			Namespace: namespace,
-			Name:      name,
-		}
+// requestLogTemplateInputGetter gets the template input from the request.
+// It assumes the Revision has been set on the context such that
+// handler.FromRevision() returns a non-nil revision.
+func requestLogTemplateInputGetter(req *http.Request, resp *pkghttp.RequestLogResponse) *pkghttp.RequestLogTemplateInput {
+	revision := handler.RevisionFrom(req.Context())
 
-		revision, err := revisionLister.Revisions(namespace).Get(name)
-		if err == nil && revision.Labels != nil {
-			revInfo.Configuration = revision.Labels[serving.ConfigurationLabelKey]
-			revInfo.Service = revision.Labels[serving.ServiceLabelKey]
-		}
+	revInfo := &pkghttp.RequestLogRevision{
+		Namespace: revision.Namespace,
+		Name:      revision.Name,
+	}
 
-		return &pkghttp.RequestLogTemplateInput{
-			Request:  req,
-			Response: resp,
-			Revision: revInfo,
-		}
+	if revision.Labels != nil {
+		revInfo.Configuration = revision.Labels[serving.ConfigurationLabelKey]
+		revInfo.Service = revision.Labels[serving.ServiceLabelKey]
+	}
+
+	return &pkghttp.RequestLogTemplateInput{
+		Request:  req,
+		Response: resp,
+		Revision: revInfo,
 	}
 }
diff --git a/cmd/queue/main.go b/cmd/queue/main.go
index 582b75552..2e1f6e338 100644
--- a/cmd/queue/main.go
+++ b/cmd/queue/main.go
@@ -165,7 +165,7 @@ func main() {
 	}()
 
 	// Setup probe to run for checking user-application healthiness.
-	probe := buildProbe(logger, env.ServingReadinessProbe)
+	probe := buildProbe(ctx, logger, env.ServingReadinessProbe)
 	healthState := health.NewState()
 
 	mainServer := buildServer(ctx, env, healthState, probe, stats, logger)
@@ -220,7 +220,7 @@ func main() {
 	}()
 
 	// Blocks until we actually receive a TERM signal or one of the servers
-	// exit unexpectedly. We fold both signals together because we only want
+	// exits unexpectedly. We fold both signals together because we only want
 	// to act on the first of those to reach here.
 	select {
 	case err := <-errCh:
@@ -254,12 +254,12 @@ func main() {
 	}
 }
 
-func buildProbe(logger *zap.SugaredLogger, probeJSON string) *readiness.Probe {
+func buildProbe(ctx context.Context, logger *zap.SugaredLogger, probeJSON string) *readiness.Probe {
 	coreProbe, err := readiness.DecodeProbe(probeJSON)
 	if err != nil {
 		logger.Fatalw("Queue container failed to parse readiness probe", zap.Error(err))
 	}
-	return readiness.NewProbe(coreProbe)
+	return readiness.NewProbe(ctx, coreProbe)
 }
 
 func buildServer(ctx context.Context, env config, healthState *health.State, rp *readiness.Probe, stats *network.RequestStats,
@@ -291,7 +291,7 @@ func buildServer(ctx context.Context, env config, healthState *health.State, rp
 	}
 	composedHandler = queue.ProxyHandler(breaker, stats, tracingEnabled, composedHandler)
 	composedHandler = queue.ForwardedShimHandler(composedHandler)
-	composedHandler = handler.NewTimeToFirstByteTimeoutHandler(composedHandler, "request timeout", handler.StaticTimeoutFunc(timeout))
+	composedHandler = handler.NewTimeToFirstByteTimeoutHandler(composedHandler, "request timeout", timeout)
 
 	if metricsSupported {
 		composedHandler = requestMetricsHandler(logger, composedHandler, env)
diff --git a/config/core/configmaps/features.yaml b/config/core/configmaps/features.yaml
index ef0a7336a..6f6aeb97f 100644
--- a/config/core/configmaps/features.yaml
+++ b/config/core/configmaps/features.yaml
@@ -20,9 +20,9 @@ metadata:
   labels:
     serving.knative.dev/release: devel
   annotations:
-    knative.dev/example-checksum: "4437b773"
+    knative.dev/example-checksum: "2cf73688"
 data:
-  _example: |
+  _example: |-
     ################################
     #                              #
     #    EXAMPLE CONFIGURATION     #
@@ -127,3 +127,8 @@ data:
     # 2. Disabled: disabling tag header based routing
     # See: https://knative.dev/docs/serving/feature-flags/#tag-header-based-routing
     tag-header-based-routing: "disabled"
+
+    # Controls whether http2 auto-detection should be enabled or not.
+    # 1. Enabled: http2 connection will be attempted via upgrade.
+    # 2. Disabled: http2 connection will only be attempted when port name is set to "h2c".
+    autodetect-http2: "disabled"
diff --git a/go.mod b/go.mod
index 60b6ea7ac..09191cc4b 100644
--- a/go.mod
+++ b/go.mod
@@ -35,6 +35,7 @@ require (
 	go.uber.org/zap v1.16.0
 	golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
 	golang.org/x/mod v0.4.1 // indirect
+	golang.org/x/net v0.0.0-20210119194325-5f4716e94777
 	golang.org/x/oauth2 v0.0.0-20210126194326-f9ce19ea3013
 	golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
 	golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
@@ -51,9 +52,9 @@ require (
 	k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027 // indirect
 	k8s.io/kube-openapi v0.0.0-20210113233702-8566a335510f
 	k8s.io/utils v0.0.0-20210111153108-fddb29f9d009 // indirect
-	knative.dev/caching v0.0.0-20210215030244-1212288570f0
+	knative.dev/caching v0.0.0-20210216192653-d1dfa6451cd1
 	knative.dev/hack v0.0.0-20210203173706-8368e1f6eacf
-	knative.dev/networking v0.0.0-20210215030235-088986a1c2a3
-	knative.dev/pkg v0.0.0-20210215165523-84c98f3c3e7a
+	knative.dev/networking v0.0.0-20210216193011-d5e05ad323f0
+	knative.dev/pkg v0.0.0-20210226182947-9039dc189ced
 	sigs.k8s.io/yaml v1.2.0
 )
diff --git a/go.sum b/go.sum
index f424a6b21..c0ab41be3 100644
--- a/go.sum
+++ b/go.sum
@@ -1282,16 +1282,16 @@ k8s.io/utils v0.0.0-20200729134348-d5654de09c73 h1:uJmqzgNWG7XyClnU/mLPBWwfKKF1K
 k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 k8s.io/utils v0.0.0-20210111153108-fddb29f9d009 h1:0T5IaWHO3sJTEmCP6mUlBvMukxPKUQWqiI/YuiBNMiQ=
 k8s.io/utils v0.0.0-20210111153108-fddb29f9d009/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-knative.dev/caching v0.0.0-20210215030244-1212288570f0 h1:gOZqiYCuEP0CzNFfeaqQmee7dPEk/mKIwl5Ss1lYxZQ=
-knative.dev/caching v0.0.0-20210215030244-1212288570f0/go.mod h1:rAPalJe9Lx3jHffJpackk5WjZYl3j2QvXUgw0GPllxQ=
+knative.dev/caching v0.0.0-20210216192653-d1dfa6451cd1 h1:0b3wT5a4/3djue61/5i3o1/H9TcL7Q7+YMM2MqZzOMw=
+knative.dev/caching v0.0.0-20210216192653-d1dfa6451cd1/go.mod h1:1UeUQ6zQ3vHtZRpfuAW/f5oXb11EBqICfJqIzOeOrqU=
 knative.dev/hack v0.0.0-20210203173706-8368e1f6eacf h1:u4cY4jr2LYvhoz/1HBWEPsMiLkm0HMdDTfmmw1RE8zE=
 knative.dev/hack v0.0.0-20210203173706-8368e1f6eacf/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
-knative.dev/networking v0.0.0-20210215030235-088986a1c2a3 h1:viMEi8ob8FdeiIsiv0ww6eKgamS+YkTP3+u95qF4n/s=
-knative.dev/networking v0.0.0-20210215030235-088986a1c2a3/go.mod h1:pmAMQjMqQUxpK0UyjE71KljMs6rwDMVIAlvrZsU3I6Y=
-knative.dev/pkg v0.0.0-20210212203835-448ae657fb5f h1:pz+UDOTsCIClHXGM/SgCzkctLxgi3+P0C26x6Ujlqho=
-knative.dev/pkg v0.0.0-20210212203835-448ae657fb5f/go.mod h1:TJSdebQOWX5N2bszohOYVi0H1QtXbtlYLuMghAFBMhY=
+knative.dev/networking v0.0.0-20210216193011-d5e05ad323f0 h1:3TwevWoWJ/xlU1Bjzb3vtRRl1NfJWIJTkZb+1eNdW8k=
+knative.dev/networking v0.0.0-20210216193011-d5e05ad323f0/go.mod h1:Crdn87hxdFd3Jj6PIyrjzGnr8OGHX35k5xo9jlOrjjA=
 knative.dev/pkg v0.0.0-20210215165523-84c98f3c3e7a h1:Jb8iKzfqhqwYoK9xHulmFdoscUBPLlXmBQMb2grTa78=
 knative.dev/pkg v0.0.0-20210215165523-84c98f3c3e7a/go.mod h1:TJSdebQOWX5N2bszohOYVi0H1QtXbtlYLuMghAFBMhY=
+knative.dev/pkg v0.0.0-20210226182947-9039dc189ced h1:OCtTbXsJhiR35qCDxFj9Xc8sU3i87ePbc7YlHCt0al4=
+knative.dev/pkg v0.0.0-20210226182947-9039dc189ced/go.mod h1:TJSdebQOWX5N2bszohOYVi0H1QtXbtlYLuMghAFBMhY=
 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/concurrency_reporter.go b/pkg/activator/handler/concurrency_reporter.go
index d8da02d77..3fe7a3dc5 100644
--- a/pkg/activator/handler/concurrency_reporter.go
+++ b/pkg/activator/handler/concurrency_reporter.go
@@ -242,7 +242,7 @@ func (cr *ConcurrencyReporter) run(stopCh <-chan struct{}, reportCh <-chan time.
 // machinery.
 func (cr *ConcurrencyReporter) Handler(next http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
-		revisionKey := revIDFrom(r.Context())
+		revisionKey := RevIDFrom(r.Context())
 
 		stat := cr.handleRequestIn(network.ReqEvent{Key: revisionKey, Type: network.ReqIn, Time: time.Now()})
 		defer func() {
diff --git a/pkg/activator/handler/context.go b/pkg/activator/handler/context.go
index 835a44b6a..6cee2d3b8 100644
--- a/pkg/activator/handler/context.go
+++ b/pkg/activator/handler/context.go
@@ -33,22 +33,22 @@ type (
 	revIDKey    struct{}
 )
 
-// withRevision attaches the Revision object to the context.
-func withRevision(ctx context.Context, rev *v1.Revision) context.Context {
+// WithRevision attaches the Revision object to the context.
+func WithRevision(ctx context.Context, rev *v1.Revision) context.Context {
 	return context.WithValue(ctx, revisionKey{}, rev)
 }
 
-// revisionFrom retrieves the Revision object from the context.
-func revisionFrom(ctx context.Context) *v1.Revision {
+// RevisionFrom retrieves the Revision object from the context.
+func RevisionFrom(ctx context.Context) *v1.Revision {
 	return ctx.Value(revisionKey{}).(*v1.Revision)
 }
 
-// withRevID attaches the the revisionID to the context.
-func withRevID(ctx context.Context, revID types.NamespacedName) context.Context {
+// WithRevID attaches the the revisionID to the context.
+func WithRevID(ctx context.Context, revID types.NamespacedName) context.Context {
 	return context.WithValue(ctx, revIDKey{}, revID)
 }
 
-// revIDFrom retrieves the the revisionID from the context.
-func revIDFrom(ctx context.Context) types.NamespacedName {
+// RevIDFrom retrieves the the revisionID from the context.
+func RevIDFrom(ctx context.Context) types.NamespacedName {
 	return ctx.Value(revIDKey{}).(types.NamespacedName)
 }
diff --git a/pkg/activator/handler/context_handler.go b/pkg/activator/handler/context_handler.go
index c255dbed6..e49676927 100644
--- a/pkg/activator/handler/context_handler.go
+++ b/pkg/activator/handler/context_handler.go
@@ -76,8 +76,8 @@ func (h *contextHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 
 	ctx := r.Context()
 	ctx = logging.WithLogger(ctx, logger)
-	ctx = withRevision(ctx, revision)
-	ctx = withRevID(ctx, revID)
+	ctx = WithRevision(ctx, revision)
+	ctx = WithRevID(ctx, revID)
 
 	h.nextHandler.ServeHTTP(w, r.WithContext(ctx))
 }
diff --git a/pkg/activator/handler/handler.go b/pkg/activator/handler/handler.go
index a291675b1..5cf085767 100644
--- a/pkg/activator/handler/handler.go
+++ b/pkg/activator/handler/handler.go
@@ -74,7 +74,7 @@ func (a *activationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		tryContext, trySpan = trace.StartSpan(r.Context(), "throttler_try")
 	}
 
-	if err := a.throttler.Try(tryContext, revIDFrom(r.Context()), func(dest string) error {
+	if err := a.throttler.Try(tryContext, RevIDFrom(r.Context()), func(dest string) error {
 		trySpan.End()
 
 		proxyCtx, proxySpan := r.Context(), (*trace.Span)(nil)
diff --git a/pkg/activator/handler/metric_handler.go b/pkg/activator/handler/metric_handler.go
index c2f4938cc..8c3b058f3 100644
--- a/pkg/activator/handler/metric_handler.go
+++ b/pkg/activator/handler/metric_handler.go
@@ -42,7 +42,7 @@ type MetricHandler struct {
 }
 
 func (h *MetricHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	rev := revisionFrom(r.Context())
+	rev := RevisionFrom(r.Context())
 	reporterCtx, _ := metrics.PodRevisionContext(h.podName, activator.Name,
 		rev.Namespace, rev.Labels[serving.ServiceLabelKey], rev.Labels[serving.ConfigurationLabelKey], rev.Name)
 
diff --git a/pkg/apis/autoscaling/annotation_validation.go b/pkg/apis/autoscaling/annotation_validation.go
index d4230a165..805047e24 100644
--- a/pkg/apis/autoscaling/annotation_validation.go
+++ b/pkg/apis/autoscaling/annotation_validation.go
@@ -58,6 +58,7 @@ func ValidateAnnotations(ctx context.Context, config *autoscalerconfig.Config, a
 		Also(validateLastPodRetention(anns)).
 		Also(validateScaleDownDelay(anns)).
 		Also(validateMetric(anns)).
+		Also(validateAlgorithm(anns)).
 		Also(validateInitialScale(config, anns))
 }
 
@@ -70,6 +71,22 @@ func validateClass(annotations map[string]string) *apis.FieldError {
 	return nil
 }
 
+func validateAlgorithm(annotations map[string]string) *apis.FieldError {
+	// Not a KPA? Don't validate, custom autoscalers might have custom values.
+	if c := annotations[ClassAnnotationKey]; c != KPA {
+		return nil
+	}
+	if a := annotations[MetricAggregationAlgorithmKey]; a != "" {
+		switch a {
+		case MetricAggregationAlgorithmLinear, MetricAggregationAlgorithmWeightedExponential:
+			return nil
+		default:
+			return apis.ErrInvalidValue(a, MetricAggregationAlgorithmKey)
+		}
+	}
+	return nil
+}
+
 func validateFloats(annotations map[string]string) (errs *apis.FieldError) {
 	if v, ok := annotations[PanicWindowPercentageAnnotationKey]; ok {
 		if fv, err := strconv.ParseFloat(v, 64); err != nil {
diff --git a/pkg/apis/autoscaling/register.go b/pkg/apis/autoscaling/register.go
index 27920743d..8fe83fb75 100644
--- a/pkg/apis/autoscaling/register.go
+++ b/pkg/apis/autoscaling/register.go
@@ -83,6 +83,28 @@ const (
 	// scale-to-zero-pod-retention-period global setting.
 	ScaleToZeroPodRetentionPeriodKey = GroupName + "/scaleToZeroPodRetentionPeriod"
 
+	// MetricAggregationAlgorithmKey is the annotation that can be used for selection
+	// of the algorithm to use for averaging metric data in the Autoscaler.
+	// Since autoscalers are a pluggable concept, this field is only validated
+	// for Revisions that are owned by Knative Pod Autoscaler.
+	// The algorithm will apply to both panic and stagble windows.
+	// NB: this is an Alpha feature and can be removed or modified
+	//     at any point.
+	// Possible values for KPA are:
+	// - empty/missing or "linear" — linear average over the whole
+	//   metric window (default);
+	// - weightedExponential — weighted average with exponential decay.
+	//   KPA will compute the decay multiplier automatically based on the window size
+	//   and it is at least 0.2. This algorithm might not utilize all the values
+	//   in the window, due to their coefficients being infinitesimal.
+	MetricAggregationAlgorithmKey = GroupName + "/metricAggregationAlgorithm"
+	// MetricAggregationAlgorithmLinear is the linear aggregation algorithm with all weights
+	// equal to 1.
+	MetricAggregationAlgorithmLinear = "linear"
+	// MetricAggregationAlgorithmWeightedExponential is the weighted aggregation algorithm
+	// with exponentially decaying weights.
+	MetricAggregationAlgorithmWeightedExponential = "weightedExponential"
+
 	// WindowAnnotationKey is the annotation to specify the time
 	// interval over which to calculate the average metric.  Larger
 	// values result in more smoothing. For example,
diff --git a/pkg/apis/autoscaling/v1alpha1/metric_validation.go b/pkg/apis/autoscaling/v1alpha1/metric_validation.go
index fd1adb049..f2f40f3d9 100644
--- a/pkg/apis/autoscaling/v1alpha1/metric_validation.go
+++ b/pkg/apis/autoscaling/v1alpha1/metric_validation.go
@@ -26,7 +26,7 @@ import (
 
 // Validate validates the entire Metric.
 func (m *Metric) Validate(ctx context.Context) *apis.FieldError {
-	return serving.ValidateObjectMetadata(ctx, m.GetObjectMeta()).ViaField("metadata").
+	return serving.ValidateObjectMetadata(ctx, m.GetObjectMeta(), true).ViaField("metadata").
 		Also(m.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec"))
 }
 
diff --git a/pkg/apis/autoscaling/v1alpha1/pa_validation.go b/pkg/apis/autoscaling/v1alpha1/pa_validation.go
index 1a17d8ab7..cb9db30c9 100644
--- a/pkg/apis/autoscaling/v1alpha1/pa_validation.go
+++ b/pkg/apis/autoscaling/v1alpha1/pa_validation.go
@@ -26,7 +26,7 @@ import (
 
 // Validate implements apis.Validatable interface.
 func (pa *PodAutoscaler) Validate(ctx context.Context) *apis.FieldError {
-	return serving.ValidateObjectMetadata(ctx, pa.GetObjectMeta()).ViaField("metadata").
+	return serving.ValidateObjectMetadata(ctx, pa.GetObjectMeta(), true).ViaField("metadata").
 		Also(pa.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec"))
 }
 
diff --git a/pkg/apis/config/features.go b/pkg/apis/config/features.go
index f916df878..2cf7fbbba 100644
--- a/pkg/apis/config/features.go
+++ b/pkg/apis/config/features.go
@@ -51,6 +51,7 @@ func defaultFeaturesConfig() *Features {
 		PodSpecSecurityContext:  Disabled,
 		PodSpecTolerations:      Disabled,
 		TagHeaderBasedRouting:   Disabled,
+		AutoDetectHTTP2:         Disabled,
 	}
 }
 
@@ -68,7 +69,8 @@ func NewFeaturesConfigFromMap(data map[string]string) (*Features, error) {
 		asFlag("kubernetes.podspec-runtimeclassname", &nc.PodSpecRuntimeClassName),
 		asFlag("kubernetes.podspec-securitycontext", &nc.PodSpecSecurityContext),
 		asFlag("kubernetes.podspec-tolerations", &nc.PodSpecTolerations),
-		asFlag("tag-header-based-routing", &nc.TagHeaderBasedRouting)); err != nil {
+		asFlag("tag-header-based-routing", &nc.TagHeaderBasedRouting),
+		asFlag("autodetect-http2", &nc.AutoDetectHTTP2)); err != nil {
 		return nil, err
 	}
 	return nc, nil
@@ -91,6 +93,7 @@ type Features struct {
 	PodSpecSecurityContext  Flag
 	PodSpecTolerations      Flag
 	TagHeaderBasedRouting   Flag
+	AutoDetectHTTP2         Flag
 }
 
 // asFlag parses the value at key as a Flag into the target, if it exists.
diff --git a/pkg/apis/serving/metadata_validation.go b/pkg/apis/serving/metadata_validation.go
index 81c6e39d9..3b08ee66c 100644
--- a/pkg/apis/serving/metadata_validation.go
+++ b/pkg/apis/serving/metadata_validation.go
@@ -24,33 +24,27 @@ import (
 
 	"k8s.io/apimachinery/pkg/api/equality"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/util/sets"
 
 	"knative.dev/pkg/apis"
 	"knative.dev/serving/pkg/apis/autoscaling"
 	"knative.dev/serving/pkg/apis/config"
 )
 
-var (
-	allowedAnnotations = sets.NewString(
-		CreatorAnnotation,
-		ForceUpgradeAnnotationKey,
-		RevisionLastPinnedAnnotationKey,
-		RevisionPreservedAnnotationKey,
-		RolloutDurationKey,
-		RoutesAnnotationKey,
-		RoutingStateModifiedAnnotationKey,
-		UpdaterAnnotation,
-	)
-)
-
-// ValidateObjectMetadata validates that `metadata` stanza of the
+// ValidateObjectMetadata validates that the `metadata` stanza of the
 // resources is correct.
-func ValidateObjectMetadata(ctx context.Context, meta metav1.Object) *apis.FieldError {
-	return apis.ValidateObjectMetadata(meta).
-		Also(autoscaling.ValidateAnnotations(ctx, config.FromContextOrDefaults(ctx).Autoscaler, meta.GetAnnotations()).
-			Also(validateKnativeAnnotations(meta.GetAnnotations())).
-			ViaField("annotations"))
+// If `allowAutoscalingAnnotations` is true autoscaling annotations, if
+// present, are validated. If `allowAutoscalingAnnotations` is false
+// autoscaling annotations are validated not to be present.
+func ValidateObjectMetadata(ctx context.Context, meta metav1.Object, allowAutoscalingAnnotations bool) *apis.FieldError {
+	errs := apis.ValidateObjectMetadata(meta)
+
+	if allowAutoscalingAnnotations {
+		errs = errs.Also(autoscaling.ValidateAnnotations(ctx, config.FromContextOrDefaults(ctx).Autoscaler, meta.GetAnnotations()).ViaField("annotations"))
+	} else {
+		errs = errs.Also(ValidateHasNoAutoscalingAnnotation(meta.GetAnnotations()).ViaField("annotations"))
+	}
+
+	return errs
 }
 
 // ValidateRolloutDurationAnnotation validates the rollout duration annotation.
@@ -81,15 +75,6 @@ func ValidateRolloutDurationAnnotation(annos map[string]string) (errs *apis.Fiel
 	return errs
 }
 
-func validateKnativeAnnotations(annotations map[string]string) (errs *apis.FieldError) {
-	for key := range annotations {
-		if !allowedAnnotations.Has(key) && strings.HasPrefix(key, GroupNamePrefix) {
-			errs = errs.Also(apis.ErrInvalidKeyName(key, apis.CurrentField))
-		}
-	}
-	return errs
-}
-
 // ValidateHasNoAutoscalingAnnotation validates that the respective entity does not have
 // annotations from the autoscaling group. It's to be used to validate Service and
 // Configuration.
diff --git a/pkg/apis/serving/register.go b/pkg/apis/serving/register.go
index ea8d6471e..c0e1dd61b 100644
--- a/pkg/apis/serving/register.go
+++ b/pkg/apis/serving/register.go
@@ -91,18 +91,12 @@ const (
 
 	// DomainMappingLabelKey is the label key attached to Ingress resources to indicate
 	// which DomainMapping triggered their creation.
-	DomainMappingLabelKey = GroupName + "/domainmapping"
+	DomainMappingLabelKey = GroupName + "/domainMapping"
 
 	// ConfigurationGenerationLabelKey is the label key attached to a Revision indicating the
 	// metadata generation of the Configuration that created this revision
 	ConfigurationGenerationLabelKey = GroupName + "/configurationGeneration"
 
-	// ForceUpgradeAnnotationKey is the annotation which was added to resources
-	// upgraded from v1alpha1.
-	// This annotation is no longer used since v1alpha1 was removed, but
-	// must continue to be allowed since it may be present on existing resources.
-	ForceUpgradeAnnotationKey = GroupName + "/forceUpgrade"
-
 	// CreatorAnnotation is the annotation key to describe the user that
 	// created the resource.
 	CreatorAnnotation = GroupName + "/creator"
diff --git a/pkg/apis/serving/v1/configuration_validation.go b/pkg/apis/serving/v1/configuration_validation.go
index 01c0cefad..bc253be6e 100644
--- a/pkg/apis/serving/v1/configuration_validation.go
+++ b/pkg/apis/serving/v1/configuration_validation.go
@@ -18,7 +18,6 @@ package v1
 
 import (
 	"context"
-	"strings"
 
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"knative.dev/pkg/apis"
@@ -32,9 +31,8 @@ func (c *Configuration) Validate(ctx context.Context) (errs *apis.FieldError) {
 	// have changed (i.e. due to config-defaults changes), we elide the metadata and
 	// spec validation.
 	if !apis.IsInStatusUpdate(ctx) {
-		errs = errs.Also(serving.ValidateObjectMetadata(ctx, c.GetObjectMeta()))
+		errs = errs.Also(serving.ValidateObjectMetadata(ctx, c.GetObjectMeta(), false))
 		errs = errs.Also(c.validateLabels().ViaField("labels"))
-		errs = errs.Also(serving.ValidateHasNoAutoscalingAnnotation(c.GetAnnotations()).ViaField("annotations"))
 		errs = errs.ViaField("metadata")
 
 		ctx = apis.WithinParent(ctx, c.ObjectMeta)
@@ -63,19 +61,8 @@ func (cs *ConfigurationSpec) Validate(ctx context.Context) *apis.FieldError {
 
 // validateLabels function validates configuration labels
 func (c *Configuration) validateLabels() (errs *apis.FieldError) {
-	for key, val := range c.GetLabels() {
-		switch key {
-		case serving.RouteLabelKey,
-			serving.ConfigurationUIDLabelKey,
-			serving.ServiceUIDLabelKey:
-			// Known valid labels - so just skip them
-		case serving.ServiceLabelKey:
-			errs = errs.Also(verifyLabelOwnerRef(val, serving.ServiceLabelKey, "Service", c.GetOwnerReferences()))
-		default:
-			if strings.HasPrefix(key, serving.GroupNamePrefix) {
-				errs = errs.Also(apis.ErrInvalidKeyName(key, apis.CurrentField))
-			}
-		}
+	if val, ok := c.Labels[serving.ServiceLabelKey]; ok {
+		errs = errs.Also(verifyLabelOwnerRef(val, serving.ServiceLabelKey, "Service", c.GetOwnerReferences()))
 	}
 	return errs
 }
diff --git a/pkg/apis/serving/v1/revision_types.go b/pkg/apis/serving/v1/revision_types.go
index 32ac6180a..63ea4f6c3 100644
--- a/pkg/apis/serving/v1/revision_types.go
+++ b/pkg/apis/serving/v1/revision_types.go
@@ -82,9 +82,9 @@ type RevisionSpec struct {
 	// +optional
 	ContainerConcurrency *int64 `json:"containerConcurrency,omitempty"`
 
-	// TimeoutSeconds holds the max duration the instance is allowed for
-	// responding to a request.  If unspecified, a system default will
-	// be provided.
+	// TimeoutSeconds is the maximum duration in seconds that the request routing
+	// layer will wait for a request delivered to a container to begin replying
+	// (send network traffic). If unspecified, a system default will be provided.
 	// +optional
 	TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
 }
diff --git a/pkg/apis/serving/v1/revision_validation.go b/pkg/apis/serving/v1/revision_validation.go
index 02f50a196..c5371167e 100644
--- a/pkg/apis/serving/v1/revision_validation.go
+++ b/pkg/apis/serving/v1/revision_validation.go
@@ -32,7 +32,7 @@ import (
 
 // Validate ensures Revision is properly configured.
 func (r *Revision) Validate(ctx context.Context) *apis.FieldError {
-	errs := serving.ValidateObjectMetadata(ctx, r.GetObjectMeta()).Also(
+	errs := serving.ValidateObjectMetadata(ctx, r.GetObjectMeta(), true).Also(
 		r.ValidateLabels().ViaField("labels")).ViaField("metadata")
 	errs = errs.Also(r.Status.Validate(apis.WithinStatus(ctx)).ViaField("status"))
 
@@ -123,22 +123,8 @@ func (rs *RevisionStatus) Validate(_ context.Context) *apis.FieldError {
 
 // ValidateLabels function validates service labels
 func (r *Revision) ValidateLabels() (errs *apis.FieldError) {
-	for key, val := range r.GetLabels() {
-		switch key {
-		case serving.RoutingStateLabelKey,
-			serving.RouteLabelKey,
-			serving.ServiceLabelKey,
-			serving.ConfigurationUIDLabelKey,
-			serving.ServiceUIDLabelKey,
-			serving.ConfigurationGenerationLabelKey:
-			// Known valid labels.
-		case serving.ConfigurationLabelKey:
-			errs = errs.Also(verifyLabelOwnerRef(val, serving.ConfigurationLabelKey, "Configuration", r.GetOwnerReferences()))
-		default:
-			if strings.HasPrefix(key, serving.GroupNamePrefix) {
-				errs = errs.Also(apis.ErrInvalidKeyName(key, ""))
-			}
-		}
+	if val, ok := r.Labels[serving.ConfigurationLabelKey]; ok {
+		errs = errs.Also(verifyLabelOwnerRef(val, serving.ConfigurationLabelKey, "Configuration", r.GetOwnerReferences()))
 	}
 	return errs
 }
diff --git a/pkg/apis/serving/v1/route_validation.go b/pkg/apis/serving/v1/route_validation.go
index cbe667f7f..f0ba15213 100644
--- a/pkg/apis/serving/v1/route_validation.go
+++ b/pkg/apis/serving/v1/route_validation.go
@@ -19,7 +19,6 @@ package v1
 import (
 	"context"
 	"fmt"
-	"strings"
 
 	"k8s.io/apimachinery/pkg/util/validation"
 	network "knative.dev/networking/pkg"
@@ -29,7 +28,7 @@ import (
 
 // Validate makes sure that Route is properly configured.
 func (r *Route) Validate(ctx context.Context) *apis.FieldError {
-	errs := serving.ValidateObjectMetadata(ctx, r.GetObjectMeta()).Also(
+	errs := serving.ValidateObjectMetadata(ctx, r.GetObjectMeta(), false).Also(
 		r.validateLabels().ViaField("labels"))
 	errs = errs.Also(serving.ValidateRolloutDurationAnnotation(
 		r.GetAnnotations()).ViaField("annotations"))
@@ -204,17 +203,13 @@ func validateClusterVisibilityLabel(label string) *apis.FieldError {
 
 // validateLabels function validates route labels.
 func (r *Route) validateLabels() (errs *apis.FieldError) {
-	for key, val := range r.GetLabels() {
-		switch key {
-		case network.VisibilityLabelKey:
-			errs = errs.Also(validateClusterVisibilityLabel(val))
-		case serving.ServiceLabelKey:
-			errs = errs.Also(verifyLabelOwnerRef(val, serving.ServiceLabelKey, "Service", r.GetOwnerReferences()))
-		default:
-			if strings.HasPrefix(key, serving.GroupNamePrefix) {
-				errs = errs.Also(apis.ErrInvalidKeyName(key, apis.CurrentField))
-			}
-		}
+	if val, ok := r.Labels[network.VisibilityLabelKey]; ok {
+		errs = errs.Also(validateClusterVisibilityLabel(val))
+	}
+
+	if val, ok := r.Labels[serving.ServiceLabelKey]; ok {
+		errs = errs.Also(verifyLabelOwnerRef(val, serving.ServiceLabelKey, "Service", r.GetOwnerReferences()))
 	}
+
 	return errs
 }
diff --git a/pkg/apis/serving/v1/service_validation.go b/pkg/apis/serving/v1/service_validation.go
index 7178bd7fe..33b54aadb 100644
--- a/pkg/apis/serving/v1/service_validation.go
+++ b/pkg/apis/serving/v1/service_validation.go
@@ -18,7 +18,6 @@ package v1
 
 import (
 	"context"
-	"strings"
 
 	network "knative.dev/networking/pkg"
 	"knative.dev/pkg/apis"
@@ -32,12 +31,10 @@ func (s *Service) Validate(ctx context.Context) (errs *apis.FieldError) {
 	// have changed (i.e. due to config-defaults changes), we elide the metadata and
 	// spec validation.
 	if !apis.IsInStatusUpdate(ctx) {
-		errs = errs.Also(serving.ValidateObjectMetadata(ctx, s.GetObjectMeta()))
+		errs = errs.Also(serving.ValidateObjectMetadata(ctx, s.GetObjectMeta(), false))
 		errs = errs.Also(s.validateLabels().ViaField("labels"))
 		errs = errs.Also(serving.ValidateRolloutDurationAnnotation(
 			s.GetAnnotations()).ViaField("annotations"))
-		errs = errs.Also(serving.ValidateHasNoAutoscalingAnnotation(
-			s.GetAnnotations()).ViaField("annotations"))
 		errs = errs.ViaField("metadata")
 
 		ctx = apis.WithinParent(ctx, s.ObjectMeta)
@@ -67,13 +64,8 @@ func (ss *ServiceSpec) Validate(ctx context.Context) *apis.FieldError {
 
 // validateLabels function validates service labels
 func (s *Service) validateLabels() (errs *apis.FieldError) {
-	for key, val := range s.GetLabels() {
-		switch {
-		case key == network.VisibilityLabelKey:
-			errs = errs.Also(validateClusterVisibilityLabel(val))
-		case strings.HasPrefix(key, serving.GroupNamePrefix):
-			errs = errs.Also(apis.ErrInvalidKeyName(key, apis.CurrentField))
-		}
+	if val, ok := s.Labels[network.VisibilityLabelKey]; ok {
+		errs = errs.Also(validateClusterVisibilityLabel(val))
 	}
 	return errs
 }
diff --git a/pkg/autoscaler/aggregation/bucketing.go b/pkg/autoscaler/aggregation/bucketing.go
index 48bb84461..3714b5a2d 100644
--- a/pkg/autoscaler/aggregation/bucketing.go
+++ b/pkg/autoscaler/aggregation/bucketing.go
@@ -24,61 +24,106 @@ import (
 	"github.com/davecgh/go-spew/spew"
 )
 
-// TimedFloat64Buckets keeps buckets that have been collected at a certain time.
-type TimedFloat64Buckets struct {
-	bucketsMutex sync.RWMutex
-	// buckets is a ring buffer indexed by timeToIndex() % len(buckets).
-	// Each element represents a certain granularity of time, and the total
-	// represented duration adds up to a window length of time.
-	buckets []float64
-
-	// firstWrite holds the time when the first write has been made.
-	// This time is reset to `now` when the very first write happens,
-	// or when a first write happens after `window` time of inactivity.
-	// The difference between `now` and `firstWrite` is used to compute
-	// the number of eligible buckets for computation of average values.
-	firstWrite time.Time
-
-	// lastWrite stores the time when the last write was made.
-	// This is used to detect when we have gaps in the data (i.e. more than a
-	// granularity has expired since the last write) so that we can zero those
-	// entries in the buckets array. It is also used when calculating the
-	// WindowAverage to know how much of the buckets array represents valid data.
-	lastWrite time.Time
-
-	// granularity is the duration represented by each bucket in the buckets ring buffer.
-	granularity time.Duration
-	// window is the total time represented by the buckets ring buffer.
-	window time.Duration
-	// The total sum of all buckets within the window. This total includes
-	// invalid buckets, e.g. buckets written to before firstTime or after
-	// lastTime are included in this total.
-	windowTotal float64
-}
+type (
+	// TimedFloat64Buckets keeps buckets that have been collected at a certain time.
+	TimedFloat64Buckets struct {
+		bucketsMutex sync.RWMutex
+		// buckets is a ring buffer indexed by timeToIndex() % len(buckets).
+		// Each element represents a certain granularity of time, and the total
+		// represented duration adds up to a window length of time.
+		buckets []float64
+
+		// firstWrite holds the time when the first write has been made.
+		// This time is reset to `now` when the very first write happens,
+		// or when a first write happens after `window` time of inactivity.
+		// The difference between `now` and `firstWrite` is used to compute
+		// the number of eligible buckets for computation of average values.
+		firstWrite time.Time
+
+		// lastWrite stores the time when the last write was made.
+		// This is used to detect when we have gaps in the data (i.e. more than a
+		// granularity has expired since the last write) so that we can zero those
+		// entries in the buckets array. It is also used when calculating the
+		// WindowAverage to know how much of the buckets array represents valid data.
+		lastWrite time.Time
+
+		// granularity is the duration represented by each bucket in the buckets ring buffer.
+		granularity time.Duration
+		// window is the total time represented by the buckets ring buffer.
+		window time.Duration
+		// The total sum of all buckets within the window. This total includes
+		// invalid buckets, e.g. buckets written to before firstTime or after
+		// lastTime are included in this total.
+		windowTotal float64
+	}
+
+	// WeightedFloat64Buckets is the implementation of buckets, that
+	// uses weighted average algorithm.
+	WeightedFloat64Buckets struct {
+		*TimedFloat64Buckets
+
+		// smoothingCoeff contains the speed with which the importance
+		// of items in the past decays. The larger the faster weights decay.
+		// It is autocomputed from window size and weightPrecision constant
+		// and is bounded by minExponent below.
+		smoothingCoeff float64
+	}
+)
 
 // String implements the Stringer interface.
 func (t *TimedFloat64Buckets) String() string {
 	return spew.Sdump(t.buckets)
 }
 
+// computeSmoothingCoeff computes the decay given number of buckets.
+// The function uses precision and min exponent value constants.
+func computeSmoothingCoeff(nb float64) float64 {
+	return math.Max(
+		// Given number of buckets, infer the desired multiplier
+		// so that at least weightPrecision sum of buckets is met.
+		1-math.Pow(1-weightPrecision, 1/nb),
+		// If it's smaller than minExponent — then use minExponent,
+		// otherwise with extremely large windows we basically end up
+		// very close to the simple average.
+		minExponent,
+	)
+}
+
 // NewTimedFloat64Buckets generates a new TimedFloat64Buckets with the given
 // granularity.
 func NewTimedFloat64Buckets(window, granularity time.Duration) *TimedFloat64Buckets {
 	// Number of buckets is `window` divided by `granularity`, rounded up.
 	// e.g. 60s / 2s = 30.
-	nb := int(math.Ceil(float64(window) / float64(granularity)))
+	nb := math.Ceil(float64(window) / float64(granularity))
 	return &TimedFloat64Buckets{
-		buckets:     make([]float64, nb),
+		buckets:     make([]float64, int(nb)),
 		granularity: granularity,
 		window:      window,
 	}
 }
 
+// NewWeightedFloat64Buckets generates a new WeightedFloat64Buckets with the given
+// granularity.
+func NewWeightedFloat64Buckets(window, granularity time.Duration) *WeightedFloat64Buckets {
+	// Number of buckets is `window` divided by `granularity`, rounded up.
+	// e.g. 60s / 2s = 30.
+	nb := math.Ceil(float64(window) / float64(granularity))
+	return &WeightedFloat64Buckets{
+		TimedFloat64Buckets: NewTimedFloat64Buckets(window, granularity),
+		smoothingCoeff:      computeSmoothingCoeff(nb),
+	}
+}
+
 // IsEmpty returns true if no data has been recorded for the `window` period.
 func (t *TimedFloat64Buckets) IsEmpty(now time.Time) bool {
 	now = now.Truncate(t.granularity)
 	t.bucketsMutex.RLock()
 	defer t.bucketsMutex.RUnlock()
+	return t.isEmptyLocked(now)
+}
+
+// isEmptyLocked expects `now` to be truncated and at least Read Lock held.
+func (t *TimedFloat64Buckets) isEmptyLocked(now time.Time) bool {
 	return now.Sub(t.lastWrite) > t.window
 }
 
@@ -87,6 +132,56 @@ func roundToNDigits(n int, f float64) float64 {
 	return math.Floor(f*p) / p
 }
 
+const (
+	// minExponent is the minimal decay multiplier for the weighted average computations.
+	minExponent = 0.2
+	// The sum of weights for weighted average should be at least this much.
+	weightPrecision = 0.9999
+	precision       = 6
+)
+
+// WindowAverage returns the exponential weighted average. This means
+// that more recent items have much greater impact on the average than
+// the older ones.
+// TODO(vagababov): optimize for O(1) computation, if possible.
+// E.g. with data  [10, 10, 5, 5] (newest last), then
+// the `WindowAverage` would return (10+10+5+5)/4 = 7.5
+// This with exponent of 0.6 would return 5*0.6+5*0.6*0.4+10*0.6*0.4^2+10*0.6*0.4^3 = 5.544
+// If we reverse the data to [5, 5, 10, 10] the simple average would remain the same,
+// but this one would change to 9.072.
+func (t *WeightedFloat64Buckets) WindowAverage(now time.Time) float64 {
+	now = now.Truncate(t.granularity)
+	t.bucketsMutex.RLock()
+	defer t.bucketsMutex.RUnlock()
+	if t.isEmptyLocked(now) {
+		return 0
+	}
+
+	totalB := len(t.buckets)
+	numB := len(t.buckets)
+
+	multiplier := t.smoothingCoeff
+	// We start with 0es. But we know that we have _some_ data because
+	// IsEmpty returned false.
+	if now.After(t.lastWrite) {
+		numZ := now.Sub(t.lastWrite) / t.granularity
+		// Skip to this multiplier directly: m*(1-m)^(nz-1).
+		multiplier = multiplier * math.Pow(1-t.smoothingCoeff, float64(numZ))
+		// Reduce effective number of buckets.
+		numB -= int(numZ)
+	}
+	startIdx := t.timeToIndex(t.lastWrite) + totalB // To ensure always positive % operation.
+	ret := 0.
+	for i := 0; i < numB; i++ {
+		effectiveIdx := (startIdx - i) % totalB
+		v := t.buckets[effectiveIdx] * multiplier
+		ret += v
+		multiplier *= (1 - t.smoothingCoeff)
+		// TODO(vagababov): bail out if sm > weightPrecision?
+	}
+	return ret
+}
+
 // WindowAverage returns the average bucket value over the window.
 //
 // If the first write was less than the window length ago, an average is
@@ -104,7 +199,6 @@ func roundToNDigits(n int, f float64) float64 {
 // window length, the missing data is assumed to be 0 and the average is over
 // the whole window length inclusive of the missing data.
 func (t *TimedFloat64Buckets) WindowAverage(now time.Time) float64 {
-	const precision = 6
 	now = now.Truncate(t.granularity)
 	t.bucketsMutex.RLock()
 	defer t.bucketsMutex.RUnlock()
@@ -209,6 +303,12 @@ func min(a, b int) int {
 	return b
 }
 
+// ResizeWindow implements window resizing for the weighted averaging buckets object.
+func (t *WeightedFloat64Buckets) ResizeWindow(w time.Duration) {
+	t.TimedFloat64Buckets.ResizeWindow(w)
+	t.smoothingCoeff = computeSmoothingCoeff(math.Ceil(float64(w) / float64(t.granularity)))
+}
+
 // ResizeWindow resizes the window. This is an O(N) operation,
 // and is not supposed to be executed very often.
 func (t *TimedFloat64Buckets) ResizeWindow(w time.Duration) {
diff --git a/pkg/autoscaler/metrics/collector.go b/pkg/autoscaler/metrics/collector.go
index 01fb6e6e6..d960b4ee7 100644
--- a/pkg/autoscaler/metrics/collector.go
+++ b/pkg/autoscaler/metrics/collector.go
@@ -25,6 +25,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/clock"
 	"knative.dev/pkg/logging/logkey"
+	"knative.dev/serving/pkg/apis/autoscaling"
 	autoscalingv1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
 	"knative.dev/serving/pkg/autoscaler/aggregation"
 	"knative.dev/serving/pkg/autoscaler/config"
@@ -216,25 +217,35 @@ func (c *MetricCollector) StableAndPanicRPS(key types.NamespacedName, now time.T
 		nil
 }
 
-// collection represents the collection of metrics for one specific entity.
-type collection struct {
-	// mux guards access to all of the collection's state.
-	mux sync.RWMutex
+type (
+	// windowAverager is the client side abstraction for various bucket types.
+	windowAverager interface {
+		Record(time.Time, float64)
+		ResizeWindow(time.Duration)
+		WindowAverage(time.Time) float64
+		IsEmpty(time.Time) bool
+	}
 
-	metric *autoscalingv1alpha1.Metric
+	// collection represents the collection of metrics for one specific entity.
+	collection struct {
+		// mux guards access to all of the collection's state.
+		mux sync.RWMutex
 
-	// Fields relevant to metric collection in general.
-	concurrencyBuckets      *aggregation.TimedFloat64Buckets
-	concurrencyPanicBuckets *aggregation.TimedFloat64Buckets
-	rpsBuckets              *aggregation.TimedFloat64Buckets
-	rpsPanicBuckets         *aggregation.TimedFloat64Buckets
+		metric *autoscalingv1alpha1.Metric
 
-	// Fields relevant for metric scraping specifically.
-	scraper StatsScraper
-	lastErr error
-	grp     sync.WaitGroup
-	stopCh  chan struct{}
-}
+		// Fields relevant to metric collection in general.
+		concurrencyBuckets      windowAverager
+		concurrencyPanicBuckets windowAverager
+		rpsBuckets              windowAverager
+		rpsPanicBuckets         windowAverager
+
+		// Fields relevant for metric scraping specifically.
+		scraper StatsScraper
+		lastErr error
+		grp     sync.WaitGroup
+		stopCh  chan struct{}
+	}
+)
 
 func (c *collection) updateScraper(ss StatsScraper) {
 	c.mux.Lock()
@@ -252,15 +263,27 @@ func (c *collection) getScraper() StatsScraper {
 // collect stats every scrapeTickInterval.
 func newCollection(metric *autoscalingv1alpha1.Metric, scraper StatsScraper, clock clock.Clock,
 	callback func(types.NamespacedName), logger *zap.SugaredLogger) *collection {
+	// Pick the constructor to use to build the buckets.
+	// NB: this relies on the fact that aggregation algorithm is set on annotation of revision
+	// and as such is immutable.
+	bucketCtor := func(w time.Duration, g time.Duration) windowAverager {
+		return aggregation.NewTimedFloat64Buckets(w, g)
+	}
+	if metric.Annotations[autoscaling.MetricAggregationAlgorithmKey] == autoscaling.MetricAggregationAlgorithmWeightedExponential {
+		bucketCtor = func(w time.Duration, g time.Duration) windowAverager {
+			return aggregation.NewWeightedFloat64Buckets(w, g)
+		}
+	}
+
 	c := &collection{
 		metric: metric,
-		concurrencyBuckets: aggregation.NewTimedFloat64Buckets(
+		concurrencyBuckets: bucketCtor(
 			metric.Spec.StableWindow, config.BucketSize),
-		concurrencyPanicBuckets: aggregation.NewTimedFloat64Buckets(
+		concurrencyPanicBuckets: bucketCtor(
 			metric.Spec.PanicWindow, config.BucketSize),
-		rpsBuckets: aggregation.NewTimedFloat64Buckets(
+		rpsBuckets: bucketCtor(
 			metric.Spec.StableWindow, config.BucketSize),
-		rpsPanicBuckets: aggregation.NewTimedFloat64Buckets(
+		rpsPanicBuckets: bucketCtor(
 			metric.Spec.PanicWindow, config.BucketSize),
 		scraper: scraper,
 
diff --git a/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/controller.go b/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/controller.go
index f53cefb5c..4d245ffc8 100644
--- a/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/controller.go
+++ b/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/controller.go
@@ -113,6 +113,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/reconciler.go b/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/reconciler.go
index c7aa9011d..023022b6b 100644
--- a/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/reconciler.go
+++ b/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/reconciler.go
@@ -169,6 +169,9 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	return rec
diff --git a/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/controller.go b/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/controller.go
index 5f017639f..a9638885a 100644
--- a/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/controller.go
+++ b/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/controller.go
@@ -117,6 +117,9 @@ func NewImpl(ctx context.Context, r Interface, classValue string, optionsFns ...
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/reconciler.go b/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/reconciler.go
index 7b479b613..fc003a0a9 100644
--- a/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/reconciler.go
+++ b/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/reconciler.go
@@ -173,6 +173,9 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	return rec
diff --git a/pkg/client/injection/reconciler/serving/v1/configuration/controller.go b/pkg/client/injection/reconciler/serving/v1/configuration/controller.go
index 55778dc38..6e8c51e28 100644
--- a/pkg/client/injection/reconciler/serving/v1/configuration/controller.go
+++ b/pkg/client/injection/reconciler/serving/v1/configuration/controller.go
@@ -113,6 +113,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go b/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go
index 78f51378f..de0e39011 100644
--- a/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go
@@ -169,6 +169,9 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	return rec
diff --git a/pkg/client/injection/reconciler/serving/v1/revision/controller.go b/pkg/client/injection/reconciler/serving/v1/revision/controller.go
index 3e53a7847..5e7b0f685 100644
--- a/pkg/client/injection/reconciler/serving/v1/revision/controller.go
+++ b/pkg/client/injection/reconciler/serving/v1/revision/controller.go
@@ -113,6 +113,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go b/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
index bda25f0cc..443894ad8 100644
--- a/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
@@ -169,6 +169,9 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	return rec
diff --git a/pkg/client/injection/reconciler/serving/v1/route/controller.go b/pkg/client/injection/reconciler/serving/v1/route/controller.go
index 16304365c..c11a41ea1 100644
--- a/pkg/client/injection/reconciler/serving/v1/route/controller.go
+++ b/pkg/client/injection/reconciler/serving/v1/route/controller.go
@@ -113,6 +113,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/serving/v1/route/reconciler.go b/pkg/client/injection/reconciler/serving/v1/route/reconciler.go
index 3fc5760bf..44a4e27db 100644
--- a/pkg/client/injection/reconciler/serving/v1/route/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/route/reconciler.go
@@ -169,6 +169,9 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	return rec
diff --git a/pkg/client/injection/reconciler/serving/v1/service/controller.go b/pkg/client/injection/reconciler/serving/v1/service/controller.go
index 7eb4305eb..f5df77a9a 100644
--- a/pkg/client/injection/reconciler/serving/v1/service/controller.go
+++ b/pkg/client/injection/reconciler/serving/v1/service/controller.go
@@ -113,6 +113,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/serving/v1/service/reconciler.go b/pkg/client/injection/reconciler/serving/v1/service/reconciler.go
index 1ef099270..aa7705ded 100644
--- a/pkg/client/injection/reconciler/serving/v1/service/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/service/reconciler.go
@@ -169,6 +169,9 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	return rec
diff --git a/pkg/client/injection/reconciler/serving/v1alpha1/domainmapping/controller.go b/pkg/client/injection/reconciler/serving/v1alpha1/domainmapping/controller.go
index 344ef8baf..d56716d17 100644
--- a/pkg/client/injection/reconciler/serving/v1alpha1/domainmapping/controller.go
+++ b/pkg/client/injection/reconciler/serving/v1alpha1/domainmapping/controller.go
@@ -113,6 +113,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/serving/v1alpha1/domainmapping/reconciler.go b/pkg/client/injection/reconciler/serving/v1alpha1/domainmapping/reconciler.go
index fdc0542c4..5be33ab79 100644
--- a/pkg/client/injection/reconciler/serving/v1alpha1/domainmapping/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1alpha1/domainmapping/reconciler.go
@@ -169,6 +169,9 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versio
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	return rec
diff --git a/pkg/http/handler/timeout.go b/pkg/http/handler/timeout.go
index d8a667f2b..0e49c89df 100644
--- a/pkg/http/handler/timeout.go
+++ b/pkg/http/handler/timeout.go
@@ -28,25 +28,14 @@ import (
 	"knative.dev/pkg/websocket"
 )
 
-// TimeoutFunc returns the timeout duration to be used by the timeout handler.
-type TimeoutFunc func(req *http.Request) time.Duration
-
-// StaticTimeoutFunc returns a TimeoutFunc that always returns the same duration.
-func StaticTimeoutFunc(timeout time.Duration) TimeoutFunc {
-	return func(req *http.Request) time.Duration {
-		return timeout
-	}
-}
-
 type timeToFirstByteTimeoutHandler struct {
-	handler     http.Handler
-	timeoutFunc TimeoutFunc
-	body        string
+	handler http.Handler
+	timeout time.Duration
+	body    string
 }
 
 // NewTimeToFirstByteTimeoutHandler returns a Handler that runs `h` with the
-// given time limit from the timeout function in which the first byte of
-// the response must be written.
+// given timeout in which the first byte of the response must be written.
 //
 // The new Handler calls h.ServeHTTP to handle each request, but if a
 // call runs for longer than its time limit, the handler responds with
@@ -60,11 +49,11 @@ type timeToFirstByteTimeoutHandler struct {
 // https://golang.org/pkg/net/http/#Handler.
 //
 // The implementation is largely inspired by http.TimeoutHandler.
-func NewTimeToFirstByteTimeoutHandler(h http.Handler, msg string, timeoutFunc TimeoutFunc) http.Handler {
+func NewTimeToFirstByteTimeoutHandler(h http.Handler, msg string, timeout time.Duration) http.Handler {
 	return &timeToFirstByteTimeoutHandler{
-		handler:     h,
-		body:        msg,
-		timeoutFunc: timeoutFunc,
+		handler: h,
+		body:    msg,
+		timeout: timeout,
 	}
 }
 
@@ -86,7 +75,7 @@ func (h *timeToFirstByteTimeoutHandler) ServeHTTP(w http.ResponseWriter, r *http
 		h.handler.ServeHTTP(tw, r.WithContext(ctx))
 	}()
 
-	timeout := time.NewTimer(h.timeoutFunc(r))
+	timeout := time.NewTimer(h.timeout)
 	defer timeout.Stop()
 	for {
 		select {
@@ -164,8 +153,8 @@ func (tw *timeoutWriter) WriteHeader(code int) {
 	tw.w.WriteHeader(code)
 }
 
-// timeoutAndError writes an error to the response write if
-// nothing has been written on the writer before. Returns whether
+// timeoutAndWriteError writes an error to the responsewriter if
+// nothing has been written to the writer before. Returns whether
 // an error was written or not.
 //
 // If this writes an error, all subsequent calls to Write will
diff --git a/pkg/http/proxy.go b/pkg/http/proxy.go
index ff96d23a2..06085d782 100644
--- a/pkg/http/proxy.go
+++ b/pkg/http/proxy.go
@@ -19,6 +19,8 @@ package http
 import (
 	"net/http"
 	"net/http/httputil"
+
+	"knative.dev/pkg/network"
 )
 
 // NewHeaderPruningReverseProxy returns a httputil.ReverseProxy that proxies
@@ -30,9 +32,9 @@ func NewHeaderPruningReverseProxy(targetHost string, headersToRemove []string) *
 			req.URL.Host = targetHost
 
 			// Copied from httputil.NewSingleHostReverseProxy.
-			if _, ok := req.Header["User-Agent"]; !ok {
+			if _, ok := req.Header[network.UserAgentKey]; !ok {
 				// explicitly disable User-Agent so it's not set to default value
-				req.Header.Set("User-Agent", "")
+				req.Header.Set(network.UserAgentKey, "")
 			}
 
 			for _, h := range headersToRemove {
diff --git a/pkg/queue/health/probe.go b/pkg/queue/health/probe.go
index 3e93abb68..4a379051c 100644
--- a/pkg/queue/health/probe.go
+++ b/pkg/queue/health/probe.go
@@ -17,6 +17,7 @@ limitations under the License.
 package health
 
 import (
+	"context"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -27,14 +28,17 @@ import (
 
 	corev1 "k8s.io/api/core/v1"
 	network "knative.dev/networking/pkg"
+	pkgnet "knative.dev/pkg/network"
+	apicfg "knative.dev/serving/pkg/apis/config"
 )
 
 // HTTPProbeConfigOptions holds the HTTP probe config options
 type HTTPProbeConfigOptions struct {
 	Timeout time.Duration
 	*corev1.HTTPGetAction
-	KubeMajor string
-	KubeMinor string
+	KubeMajor     string
+	KubeMinor     string
+	MaxProtoMajor int
 }
 
 // TCPProbeConfigOptions holds the TCP probe config options
@@ -55,26 +59,117 @@ func TCPProbe(config TCPProbeConfigOptions) error {
 	return nil
 }
 
+// Returns a transport that uses HTTP/2 if it's known to be supported, and otherwise
+// spoofs the request & response versions to HTTP/1.1.
+func autoDowngradingTransport(opt HTTPProbeConfigOptions) http.RoundTripper {
+	t := pkgnet.NewProberTransport()
+	return pkgnet.RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
+		// If the user-container can handle HTTP2, we pass through the request as-is.
+		// We have to set r.ProtoMajor to 2, since auto transport relies solely on it
+		// to decide whether to use h2c or http1.1.
+		if opt.MaxProtoMajor == 2 {
+			r.ProtoMajor = 2
+			return t.RoundTrip(r)
+		}
+
+		// Otherwise, save the request HTTP version and downgrade it
+		// to HTTP1 before sending.
+		version := r.ProtoMajor
+		r.ProtoMajor = 1
+		resp, err := t.RoundTrip(r)
+
+		// Restore the request & response HTTP version before sending back.
+		r.ProtoMajor = version
+		if resp != nil {
+			resp.ProtoMajor = version
+		}
+		return resp, err
+	})
+}
+
 var transport = func() *http.Transport {
 	t := http.DefaultTransport.(*http.Transport).Clone()
-	t.DisableKeepAlives = true
 	//nolint:gosec // We explicitly don't need to check certs here.
 	t.TLSClientConfig.InsecureSkipVerify = true
 	return t
 }()
 
-// HTTPProbe checks that HTTP connection can be established to the address.
-func HTTPProbe(config HTTPProbeConfigOptions) error {
+func getURL(config HTTPProbeConfigOptions) url.URL {
+	return url.URL{
+		Scheme: string(config.Scheme),
+		Host:   net.JoinHostPort(config.Host, config.Port.String()),
+		Path:   config.Path,
+	}
+}
+
+// http2UpgradeProbe checks that an HTTP with HTTP2 upgrade request
+// connection can be understood by the address.
+// Returns: the highest known proto version supported (0 if not ready or error)
+func http2UpgradeProbe(config HTTPProbeConfigOptions) (int, error) {
 	httpClient := &http.Client{
 		Transport: transport,
 		Timeout:   config.Timeout,
 	}
+	url := getURL(config)
+	req, err := http.NewRequest(http.MethodOptions, url.String(), nil)
+	if err != nil {
+		return 0, fmt.Errorf("error constructing probe request %w", err)
+	}
 
-	url := url.URL{
-		Scheme: string(config.Scheme),
-		Host:   net.JoinHostPort(config.Host, config.Port.String()),
-		Path:   config.Path,
+	// An upgrade will need to have at least these 3 headers.
+	// This is documented at https://tools.ietf.org/html/rfc7540#section-3.2
+	req.Header.Add("Connection", "Upgrade, HTTP2-Settings")
+	req.Header.Add("Upgrade", "h2c")
+	req.Header.Add("HTTP2-Settings", "")
+
+	req.Header.Add(network.UserAgentKey, network.KubeProbeUAPrefix+config.KubeMajor+"/"+config.KubeMinor)
+
+	res, err := httpClient.Do(req)
+	if err != nil {
+		return 0, err
+	}
+	defer res.Body.Close()
+
+	maxProto := 0
+
+	if IsHTTPProbeUpgradingToH2C(res) {
+		maxProto = 2
+	} else if IsHTTPProbeReady(res) {
+		maxProto = 1
+	} else {
+		return maxProto, fmt.Errorf("HTTP probe did not respond Ready, got status code: %d", res.StatusCode)
+	}
+
+	return maxProto, nil
+}
+
+// HTTPProbe checks that HTTP connection can be established to the address.
+func HTTPProbe(ctx context.Context, config HTTPProbeConfigOptions) error {
+	cfg := apicfg.FromContextOrDefaults(ctx)
+	autoDetect := cfg.Features.AutoDetectHTTP2
+	if autoDetect == apicfg.Disabled {
+		config.MaxProtoMajor = 1
+	}
+	// If we don't know if the connection supports HTTP2, we will try it.
+	// Once we get a non-error response, we won't try again.
+	// If maxProto is 0, container is not ready, so we don't know whether http2 is supported.
+	// If maxProto is 1, we know we're ready, but we also can't upgrade, so just return.
+	// If maxProto is 2, we know we can upgrade to http2
+	if autoDetect == apicfg.Enabled && config.MaxProtoMajor == 0 {
+		maxProto, err := http2UpgradeProbe(config)
+		if err != nil {
+			return fmt.Errorf("failed to run HTTP2 upgrade probe with error: %w", err)
+		}
+		config.MaxProtoMajor = maxProto
+		if config.MaxProtoMajor == 1 {
+			return nil
+		}
+	}
+	httpClient := &http.Client{
+		Transport: autoDowngradingTransport(config),
+		Timeout:   config.Timeout,
 	}
+	url := getURL(config)
 	req, err := http.NewRequest(http.MethodGet, url.String(), nil)
 	if err != nil {
 		return fmt.Errorf("error constructing probe request %w", err)
@@ -105,6 +200,11 @@ func HTTPProbe(config HTTPProbeConfigOptions) error {
 	return nil
 }
 
+// IsHTTPProbeUpgradingToH2C checks whether the server indicates it's switching to h2c protocol.
+func IsHTTPProbeUpgradingToH2C(res *http.Response) bool {
+	return res.StatusCode == http.StatusSwitchingProtocols && res.Header.Get("Upgrade") == "h2c"
+}
+
 // IsHTTPProbeReady checks whether we received a successful Response
 func IsHTTPProbeReady(res *http.Response) bool {
 	// response status code between 200-399 indicates success
diff --git a/pkg/queue/readiness/probe.go b/pkg/queue/readiness/probe.go
index 4bbd56d58..beff529c7 100644
--- a/pkg/queue/readiness/probe.go
+++ b/pkg/queue/readiness/probe.go
@@ -17,6 +17,7 @@ limitations under the License.
 package readiness
 
 import (
+	"context"
 	"fmt"
 	"io"
 	"os"
@@ -40,8 +41,9 @@ const (
 type Probe struct {
 	*corev1.Probe
 	count       int32
-	pollTimeout time.Duration // To make tests not run for 10 seconds.
-	out         io.Writer     // To make tests not log errors in good cases.
+	pollTimeout time.Duration   // To make tests not run for 10 seconds.
+	out         io.Writer       // To make tests not log errors in good cases.
+	ctx         context.Context // To enable feature flags support.
 
 	// Barrier sync to ensure only one probe is happening at the same time.
 	// When a probe is active `gv` will be non-nil.
@@ -77,11 +79,12 @@ func (gv *gateValue) read() bool {
 }
 
 // NewProbe returns a pointer a new Probe
-func NewProbe(v1p *corev1.Probe) *Probe {
+func NewProbe(ctx context.Context, v1p *corev1.Probe) *Probe {
 	return &Probe{
 		Probe:       v1p,
 		pollTimeout: PollTimeout,
 		out:         os.Stderr,
+		ctx:         ctx,
 	}
 }
 
@@ -199,6 +202,6 @@ func (p *Probe) httpProbe() error {
 
 	return p.doProbe(func(to time.Duration) error {
 		config.Timeout = to
-		return health.HTTPProbe(config)
+		return health.HTTPProbe(p.ctx, config)
 	})
 }
diff --git a/pkg/reconciler/autoscaling/hpa/resources/hpa.go b/pkg/reconciler/autoscaling/hpa/resources/hpa.go
index fb4833e2d..536191988 100644
--- a/pkg/reconciler/autoscaling/hpa/resources/hpa.go
+++ b/pkg/reconciler/autoscaling/hpa/resources/hpa.go
@@ -56,8 +56,7 @@ func MakeHPA(pa *v1alpha1.PodAutoscaler, config *autoscalerconfig.Config) *autos
 		hpa.Spec.MinReplicas = &min
 	}
 
-	switch pa.Metric() {
-	case autoscaling.CPU:
+	if pa.Metric() == autoscaling.CPU {
 		if target, ok := pa.Target(); ok {
 			hpa.Spec.Metrics = []autoscalingv2beta1.MetricSpec{{
 				Type: autoscalingv2beta1.ResourceMetricSourceType,
diff --git a/pkg/reconciler/revision/resources/queue.go b/pkg/reconciler/revision/resources/queue.go
index 008234072..8ee966e5f 100644
--- a/pkg/reconciler/revision/resources/queue.go
+++ b/pkg/reconciler/revision/resources/queue.go
@@ -247,8 +247,7 @@ func makeQueueContainer(rev *v1.Revision, cfg *config.Config) (*corev1.Container
 	if cfg.Observability.EnableProfiling {
 		ports = append(ports, profilingPort)
 	}
-	// We need to configure only one serving port for the Queue proxy, since
-	// we know the protocol that is being used by this application.
+	// TODO(knative/serving/#4283): Eventually only one port should be needed.
 	servingPort := queueHTTPPort
 	if rev.GetProtocol() == pkgnet.ProtocolH2C {
 		servingPort = queueHTTP2Port
diff --git a/pkg/reconciler/route/controller.go b/pkg/reconciler/route/controller.go
index 721015cc7..fdcf50202 100644
--- a/pkg/reconciler/route/controller.go
+++ b/pkg/reconciler/route/controller.go
@@ -47,12 +47,12 @@ func NewController(
 	ctx context.Context,
 	cmw configmap.Watcher,
 ) *controller.Impl {
-	return newControllerWithClock(ctx, cmw, clock.RealClock{})
+	return newController(ctx, cmw, clock.RealClock{})
 }
 
 type reconcilerOption func(*Reconciler)
 
-func newControllerWithClock(
+func newController(
 	ctx context.Context,
 	cmw configmap.Watcher,
 	clock clock.Clock,
diff --git a/pkg/reconciler/route/reconcile_resources.go b/pkg/reconciler/route/reconcile_resources.go
index 552511bd5..cbd182132 100644
--- a/pkg/reconciler/route/reconcile_resources.go
+++ b/pkg/reconciler/route/reconcile_resources.go
@@ -120,11 +120,10 @@ func (c *Reconciler) reconcilePlaceholderServices(ctx context.Context, route *v1
 	logger := logging.FromContext(ctx)
 	recorder := controller.GetEventRecorder(ctx)
 
-	existingServices, err := c.getServices(route)
+	existingServiceNames, err := c.getPlaceholderServiceNames(route)
 	if err != nil {
 		return nil, fmt.Errorf("failed to fetch existing services: %w", err)
 	}
-	existingServiceNames := resources.GetNames(existingServices)
 
 	ns := route.Namespace
 	names := make(sets.String, len(targets))
@@ -134,6 +133,7 @@ func (c *Reconciler) reconcilePlaceholderServices(ctx context.Context, route *v1
 
 	services := make([]*corev1.Service, 0, names.Len())
 	createdServiceNames := make(sets.String, names.Len())
+	// NB: we don't need to sort here, but it makes table tests work...
 	for _, name := range names.List() {
 		desiredService, err := resources.MakeK8sPlaceholderService(ctx, route, name)
 		if err != nil {
@@ -267,7 +267,7 @@ func (c *Reconciler) reconcileRollout(
 	if nextStepTime > 0 {
 		nextStepTime -= now
 		c.enqueueAfter(r, time.Duration(nextStepTime))
-		logger.Debug("Re-enqueuing after", zap.Duration("duration", time.Duration(nextStepTime)))
+		logger.Debug("Re-enqueuing after", zap.Duration("nextStepTime", time.Duration(nextStepTime)))
 	}
 
 	// Comparing and diffing isn't cheap so do it only if we're going
diff --git a/pkg/reconciler/route/resources/service.go b/pkg/reconciler/route/resources/service.go
index 86e8adc0a..8d53a8d18 100644
--- a/pkg/reconciler/route/resources/service.go
+++ b/pkg/reconciler/route/resources/service.go
@@ -22,9 +22,7 @@ import (
 
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/util/intstr"
-	"k8s.io/apimachinery/pkg/util/sets"
 
 	network "knative.dev/networking/pkg"
 	"knative.dev/networking/pkg/apis/networking"
@@ -37,22 +35,6 @@ import (
 
 var errLoadBalancerNotFound = errors.New("failed to fetch loadbalancer domain/IP from ingress status")
 
-// GetNames returns a set of service names.
-func GetNames(services []*corev1.Service) sets.String {
-	names := make(sets.String, len(services))
-
-	for i := range services {
-		names.Insert(services[i].Name)
-	}
-
-	return names
-}
-
-// SelectorFromRoute creates a label selector given a specific route.
-func SelectorFromRoute(route *v1.Route) labels.Selector {
-	return labels.SelectorFromSet(labels.Set{serving.RouteLabelKey: route.Name})
-}
-
 // MakeK8sPlaceholderService creates a placeholder Service to prevent naming collisions. It's owned by the
 // provided v1.Route.
 func MakeK8sPlaceholderService(ctx context.Context, route *v1.Route, targetName string) (*corev1.Service, error) {
@@ -65,11 +47,7 @@ func MakeK8sPlaceholderService(ctx context.Context, route *v1.Route, targetName
 		return nil, err
 	}
 
-	service, err := makeK8sService(ctx, route, targetName)
-	if err != nil {
-		return nil, err
-	}
-	service.Spec = corev1.ServiceSpec{
+	return makeK8sService(ctx, route, &corev1.ServiceSpec{
 		Type:            corev1.ServiceTypeExternalName,
 		ExternalName:    fullName,
 		SessionAffinity: corev1.ServiceAffinityNone,
@@ -78,28 +56,23 @@ func MakeK8sPlaceholderService(ctx context.Context, route *v1.Route, targetName
 			Port:       int32(80),
 			TargetPort: intstr.FromInt(80),
 		}},
-	}
-
-	return service, nil
+	}, targetName)
 }
 
 // MakeK8sService creates a Service that redirect to the loadbalancer specified
 // in Ingress status. It's owned by the provided v1.Route.
-func MakeK8sService(ctx context.Context, route *v1.Route, targetName string, ingress *netv1alpha1.Ingress, isPrivate bool, clusterIP string) (*corev1.Service, error) {
+func MakeK8sService(ctx context.Context, route *v1.Route,
+	targetName string, ingress *netv1alpha1.Ingress, isPrivate bool, clusterIP string) (*corev1.Service, error) {
 	svcSpec, err := makeServiceSpec(ingress, isPrivate, clusterIP)
 	if err != nil {
 		return nil, err
 	}
 
-	service, err := makeK8sService(ctx, route, targetName)
-	if err != nil {
-		return nil, err
-	}
-	service.Spec = *svcSpec
-	return service, nil
+	return makeK8sService(ctx, route, svcSpec, targetName)
 }
 
-func makeK8sService(ctx context.Context, route *v1.Route, targetName string) (*corev1.Service, error) {
+func makeK8sService(ctx context.Context, route *v1.Route,
+	spec *corev1.ServiceSpec, targetName string) (*corev1.Service, error) {
 	hostname, err := domains.HostnameFromTemplate(ctx, route.Name, targetName)
 	if err != nil {
 		return nil, err
@@ -124,6 +97,7 @@ func makeK8sService(ctx context.Context, route *v1.Route, targetName string) (*c
 			}), svcLabels),
 			Annotations: route.GetAnnotations(),
 		},
+		Spec: *spec,
 	}, nil
 }
 
diff --git a/pkg/reconciler/route/route.go b/pkg/reconciler/route/route.go
index 02279d6ed..b0aefd33e 100644
--- a/pkg/reconciler/route/route.go
+++ b/pkg/reconciler/route/route.go
@@ -24,7 +24,6 @@ import (
 	"strings"
 	"time"
 
-	corev1 "k8s.io/api/core/v1"
 	kubelabels "k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/util/clock"
@@ -42,6 +41,7 @@ import (
 	"knative.dev/pkg/logging"
 	pkgreconciler "knative.dev/pkg/reconciler"
 	"knative.dev/pkg/tracker"
+	"knative.dev/serving/pkg/apis/serving"
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
 	clientset "knative.dev/serving/pkg/client/clientset/versioned"
 	routereconciler "knative.dev/serving/pkg/client/injection/reconciler/serving/v1/route"
@@ -92,18 +92,21 @@ func certClass(ctx context.Context, r *v1.Route) string {
 	return config.FromContext(ctx).Network.DefaultCertificateClass
 }
 
-func (c *Reconciler) getServices(route *v1.Route) ([]*corev1.Service, error) {
-	currentServices, err := c.serviceLister.Services(route.Namespace).List(resources.SelectorFromRoute(route))
+// getPlaceholderServiceNames returns the placeholder services names, or an error.
+func (c *Reconciler) getPlaceholderServiceNames(route *v1.Route) (sets.String, error) {
+	currentServices, err := c.serviceLister.Services(route.Namespace).List(
+		kubelabels.SelectorFromSet(kubelabels.Set{serving.RouteLabelKey: route.Name}),
+	)
 	if err != nil {
 		return nil, err
 	}
 
-	serviceCopy := make([]*corev1.Service, len(currentServices))
-	for i, svc := range currentServices {
-		serviceCopy[i] = svc.DeepCopy()
+	names := make(sets.String, len(currentServices))
+	for _, svc := range currentServices {
+		names.Insert(svc.Name)
 	}
 
-	return serviceCopy, nil
+	return names, nil
 }
 
 // ReconcileKind implements Interface.ReconcileKind.
@@ -155,11 +158,7 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, r *v1.Route) pkgreconcil
 	roInProgress := !effectiveRO.Done()
 	if ingress.GetObjectMeta().GetGeneration() != ingress.Status.ObservedGeneration {
 		r.Status.MarkIngressNotConfigured()
-	} else if roInProgress {
-		logger.Info("Rollout is in progress")
-		// Rollout in progress, so mark the status as such.
-		r.Status.MarkIngressRolloutInProgress()
-	} else {
+	} else if !roInProgress {
 		r.Status.PropagateIngressStatus(ingress.Status)
 	}
 
@@ -171,8 +170,11 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, r *v1.Route) pkgreconcil
 	// We do it here, rather than in the similar check above,
 	// since we might be inside a rollout and Ingress
 	// is not yet ready and that takes priority, so the `roInProgress` branch
-	// wil not be triggered.
+	// will not be triggered.
 	if roInProgress {
+		logger.Info("Rollout is in progress")
+		// Rollout in progress, so mark the status as such.
+		r.Status.MarkIngressRolloutInProgress()
 		// Update the route.Status.Traffic to contain correct traffic
 		// distribution based on rollout status.
 		r.Status.Traffic, err = traffic.GetRevisionTrafficTargets(ctx, r, effectiveRO)
diff --git a/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/controller.go b/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/controller.go
index 228cc1694..ebed7022f 100644
--- a/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/controller.go
+++ b/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/controller.go
@@ -111,6 +111,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/reconciler.go b/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/reconciler.go
index 65f36a966..8dac191d5 100644
--- a/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/reconciler.go
+++ b/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/reconciler.go
@@ -168,6 +168,9 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client kubern
 		if opts.SkipStatusUpdates {
 			rec.skipStatusUpdates = true
 		}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	return rec
diff --git a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_controller.go b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_controller.go
index 06d436648..78ff96730 100644
--- a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_controller.go
+++ b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_controller.go
@@ -271,6 +271,9 @@ func NewImpl(ctx {{.contextContext|raw}}, r Interface{{if .hasClass}}, classValu
 			rec.skipStatusUpdates = true
 		}
 		{{- end}}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler.go b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler.go
index 10149fadd..cc6757da8 100644
--- a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler.go
+++ b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler.go
@@ -363,6 +363,9 @@ func NewReconciler(ctx {{.contextContext|raw}}, logger *{{.zapSugaredLogger|raw}
 			rec.skipStatusUpdates = true
 		}
 		{{- end}}
+		if opts.DemoteFunc != nil {
+			rec.DemoteFunc = opts.DemoteFunc
+		}
 	}
 
 	return rec
diff --git a/vendor/knative.dev/pkg/controller/controller.go b/vendor/knative.dev/pkg/controller/controller.go
index 27fe75e40..2a28497fa 100644
--- a/vendor/knative.dev/pkg/controller/controller.go
+++ b/vendor/knative.dev/pkg/controller/controller.go
@@ -152,6 +152,13 @@ func FilterControllerGK(gk schema.GroupKind) func(obj interface{}) bool {
 	}
 }
 
+// FilterController makes it simple to create FilterFunc's for use with
+// cache.FilteringResourceEventHandler that filter based on the
+// controlling resource.
+func FilterController(r kmeta.OwnerRefable) func(obj interface{}) bool {
+	return FilterControllerGK(r.GetGroupVersionKind().GroupKind())
+}
+
 // FilterWithName makes it simple to create FilterFunc's for use with
 // cache.FilteringResourceEventHandler that filter based on a name.
 func FilterWithName(name string) func(obj interface{}) bool {
diff --git a/vendor/knative.dev/pkg/controller/options.go b/vendor/knative.dev/pkg/controller/options.go
index b40b5a359..8d61835e6 100644
--- a/vendor/knative.dev/pkg/controller/options.go
+++ b/vendor/knative.dev/pkg/controller/options.go
@@ -35,6 +35,9 @@ type Options struct {
 	// SkipStatusUpdates configures this reconciler to either do automated status
 	// updates (default) or skip them if this is set to true.
 	SkipStatusUpdates bool
+
+	// DemoteFunc configures the demote function this reconciler uses
+	DemoteFunc func(b reconciler.Bucket)
 }
 
 // OptionsFn is a callback method signature that accepts an Impl and returns
diff --git a/vendor/knative.dev/pkg/metrics/exporter.go b/vendor/knative.dev/pkg/metrics/exporter.go
index b98a18de1..3fc4966fd 100644
--- a/vendor/knative.dev/pkg/metrics/exporter.go
+++ b/vendor/knative.dev/pkg/metrics/exporter.go
@@ -21,7 +21,6 @@ import (
 	"errors"
 	"fmt"
 	"strings"
-	"sync"
 
 	"go.opencensus.io/resource"
 	"go.opencensus.io/stats/view"
@@ -32,9 +31,14 @@ import (
 var (
 	curMetricsExporter view.Exporter
 	curMetricsConfig   *metricsConfig
-	metricsMux         sync.RWMutex
+	mWorker            *metricsWorker
 )
 
+func init() {
+	mWorker = newMetricsWorker()
+	go mWorker.start()
+}
+
 // SecretFetcher is a function (extracted from SecretNamespaceLister) for fetching
 // a specific Secret. This avoids requiring global or namespace list in controllers.
 type SecretFetcher func(string) (*corev1.Secret, error)
@@ -154,28 +158,14 @@ func UpdateExporter(ctx context.Context, ops ExporterOptions, logger *zap.Sugare
 
 	// Updating the metrics config and the metrics exporters needs to be atomic to
 	// avoid using an outdated metrics config with new exporters.
-	metricsMux.Lock()
-	defer metricsMux.Unlock()
-
-	if isNewExporterRequired(newConfig) {
-		logger.Info("Flushing the existing exporter before setting up the new exporter.")
-		flushGivenExporter(curMetricsExporter)
-		e, f, err := newMetricsExporter(newConfig, logger)
-		if err != nil {
-			logger.Errorw("Failed to update a new metrics exporter based on metric config", zap.Error(err), "config", newConfig)
-			return err
-		}
-		existingConfig := curMetricsConfig
-		curMetricsExporter = e
-		if err := setFactory(f); err != nil {
-			logger.Errorw("Failed to update metrics factory when loading metric config", zap.Error(err), "config", newConfig)
-			return err
-		}
-		logger.Infof("Successfully updated the metrics exporter; old config: %v; new config %v", existingConfig, newConfig)
+	updateCmd := &updateMetricsConfigWithExporter{
+		ctx:       ctx,
+		newConfig: newConfig,
+		done:      make(chan error),
 	}
-
-	setCurMetricsConfigUnlocked(newConfig)
-	return nil
+	mWorker.c <- updateCmd
+	err = <-updateCmd.done
+	return err
 }
 
 // isNewExporterRequired compares the non-nil newConfig against curMetricsConfig. When backend changes,
@@ -228,27 +218,35 @@ func newMetricsExporter(config *metricsConfig, logger *zap.SugaredLogger) (view.
 }
 
 func getCurMetricsExporter() view.Exporter {
-	metricsMux.RLock()
-	defer metricsMux.RUnlock()
-	return curMetricsExporter
+	readCmd := &readExporter{done: make(chan *view.Exporter)}
+	mWorker.c <- readCmd
+	e := <-readCmd.done
+	return *e
 }
 
 func setCurMetricsExporter(e view.Exporter) {
-	metricsMux.Lock()
-	defer metricsMux.Unlock()
-	curMetricsExporter = e
+	setCmd := &setExporter{
+		newExporter: &e,
+		done:        make(chan struct{}),
+	}
+	mWorker.c <- setCmd
+	<-setCmd.done
 }
 
 func getCurMetricsConfig() *metricsConfig {
-	metricsMux.RLock()
-	defer metricsMux.RUnlock()
-	return curMetricsConfig
+	readCmd := &readMetricsConfig{done: make(chan *metricsConfig)}
+	mWorker.c <- readCmd
+	cfg := <-readCmd.done
+	return cfg
 }
 
 func setCurMetricsConfig(c *metricsConfig) {
-	metricsMux.Lock()
-	defer metricsMux.Unlock()
-	setCurMetricsConfigUnlocked(c)
+	setCmd := &setMetricsConfig{
+		newConfig: c,
+		done:      make(chan struct{}),
+	}
+	mWorker.c <- setCmd
+	<-setCmd.done
 }
 
 func setCurMetricsConfigUnlocked(c *metricsConfig) {
diff --git a/vendor/knative.dev/pkg/metrics/metrics_worker.go b/vendor/knative.dev/pkg/metrics/metrics_worker.go
new file mode 100644
index 000000000..0b7753acb
--- /dev/null
+++ b/vendor/knative.dev/pkg/metrics/metrics_worker.go
@@ -0,0 +1,111 @@
+/*
+Copyright 2021 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package metrics
+
+import (
+	"context"
+
+	"go.opencensus.io/stats/view"
+	"go.uber.org/zap"
+	"knative.dev/pkg/logging"
+)
+
+type metricsWorker struct {
+	c chan command
+}
+
+func newMetricsWorker() *metricsWorker {
+	return &metricsWorker{c: make(chan command)}
+}
+
+type command interface {
+	handleCommand(w *metricsWorker)
+}
+
+type readExporter struct {
+	done chan *view.Exporter
+}
+
+type setExporter struct {
+	newExporter *view.Exporter
+	done        chan struct{}
+}
+
+type readMetricsConfig struct {
+	done chan *metricsConfig
+}
+
+type updateMetricsConfigWithExporter struct {
+	ctx       context.Context
+	newConfig *metricsConfig
+	done      chan error
+}
+
+type setMetricsConfig struct {
+	newConfig *metricsConfig
+	done      chan struct{}
+}
+
+func (cmd *readMetricsConfig) handleCommand(w *metricsWorker) {
+	cmd.done <- curMetricsConfig
+}
+
+func (cmd *setMetricsConfig) handleCommand(w *metricsWorker) {
+	setCurMetricsConfigUnlocked(cmd.newConfig)
+	cmd.done <- struct{}{}
+}
+
+func (cmd *updateMetricsConfigWithExporter) handleCommand(w *metricsWorker) {
+	ctx := cmd.ctx
+	logger := logging.FromContext(ctx)
+	if isNewExporterRequired(cmd.newConfig) {
+		logger.Info("Flushing the existing exporter before setting up the new exporter.")
+		flushGivenExporter(curMetricsExporter)
+		e, f, err := newMetricsExporter(cmd.newConfig, logger)
+		if err != nil {
+			logger.Errorw("Failed to update a new metrics exporter based on metric config", zap.Error(err), "config", cmd.newConfig)
+			cmd.done <- err
+			return
+		}
+		existingConfig := curMetricsConfig
+		curMetricsExporter = e
+		if err := setFactory(f); err != nil {
+			logger.Errorw("Failed to update metrics factory when loading metric config", zap.Error(err), "config", cmd.newConfig)
+			cmd.done <- err
+			return
+		}
+		logger.Infof("Successfully updated the metrics exporter; old config: %v; new config %v", existingConfig, cmd.newConfig)
+	}
+	setCurMetricsConfigUnlocked(cmd.newConfig)
+	cmd.done <- nil
+}
+
+func (w *metricsWorker) start() {
+	for {
+		cmd := <-w.c
+		cmd.handleCommand(w)
+	}
+}
+
+func (cmd *setExporter) handleCommand(w *metricsWorker) {
+	curMetricsExporter = *cmd.newExporter
+	cmd.done <- struct{}{}
+}
+
+func (cmd *readExporter) handleCommand(w *metricsWorker) {
+	cmd.done <- &curMetricsExporter
+}
diff --git a/vendor/knative.dev/pkg/metrics/resource_view.go b/vendor/knative.dev/pkg/metrics/resource_view.go
index 7135af70a..236fd588b 100644
--- a/vendor/knative.dev/pkg/metrics/resource_view.go
+++ b/vendor/knative.dev/pkg/metrics/resource_view.go
@@ -82,10 +82,16 @@ func cleanup() {
 	expiryCutoff := allMeters.clock.Now().Add(-1 * maxMeterExporterAge)
 	allMeters.lock.Lock()
 	defer allMeters.lock.Unlock()
+	resourceViews.lock.Lock()
+	defer resourceViews.lock.Unlock()
 	for key, meter := range allMeters.meters {
 		if key != "" && meter.t.Before(expiryCutoff) {
 			flushGivenExporter(meter.e)
+			// Make a copy of views to avoid data races
+			viewsCopy := copyViews(resourceViews.views)
+			meter.m.Unregister(viewsCopy...)
 			delete(allMeters.meters, key)
+			meter.m.Stop()
 		}
 	}
 }
@@ -139,7 +145,7 @@ func RegisterResourceView(views ...*view.View) error {
 	return nil
 }
 
-// UnregisterResourceView is similar to view.Unregiste(), except that it will
+// UnregisterResourceView is similar to view.Unregister(), except that it will
 // unregister the view across all Resources tracked byt he system, rather than
 // simply the default view.
 func UnregisterResourceView(views ...*view.View) {
diff --git a/vendor/knative.dev/pkg/network/handlers/drain.go b/vendor/knative.dev/pkg/network/handlers/drain.go
index 0ed37ba6d..b8dc6ff1b 100644
--- a/vendor/knative.dev/pkg/network/handlers/drain.go
+++ b/vendor/knative.dev/pkg/network/handlers/drain.go
@@ -17,6 +17,7 @@ limitations under the License.
 package handlers
 
 import (
+	"fmt"
 	"net/http"
 	"sync"
 	"time"
@@ -90,11 +91,11 @@ func (d *Drainer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		}
 		return
 	}
-	if network.IsKProbe(r) {
+	if isKProbe(r) {
 		if d.draining() {
 			http.Error(w, "shutting down", http.StatusServiceUnavailable)
 		} else {
-			network.ServeKProbe(w, r)
+			serveKProbe(w, r)
 		}
 		return
 	}
@@ -146,3 +147,21 @@ func (d *Drainer) draining() bool {
 	defer d.RUnlock()
 	return d.timer != nil
 }
+
+// isKProbe returns true if the request is a knatvie probe.
+func isKProbe(r *http.Request) bool {
+	return r.Header.Get(network.ProbeHeaderName) == network.ProbeHeaderValue
+}
+
+// serveKProbe serve KProbe requests.
+func serveKProbe(w http.ResponseWriter, r *http.Request) {
+	hh := r.Header.Get(network.HashHeaderName)
+	if hh == "" {
+		http.Error(w,
+			fmt.Sprintf("a probe request must contain a non-empty %q header", network.HashHeaderName),
+			http.StatusBadRequest)
+		return
+	}
+	w.Header().Set(network.HashHeaderName, hh)
+	w.WriteHeader(http.StatusOK)
+}
diff --git a/vendor/knative.dev/pkg/network/network.go b/vendor/knative.dev/pkg/network/network.go
index b2ac63ce7..cf5feaa8f 100644
--- a/vendor/knative.dev/pkg/network/network.go
+++ b/vendor/knative.dev/pkg/network/network.go
@@ -17,7 +17,6 @@ limitations under the License.
 package network
 
 import (
-	"fmt"
 	"net/http"
 	"strings"
 	"time"
@@ -52,33 +51,19 @@ const (
 	// uses to find out which version of the networking config is deployed.
 	HashHeaderName = "K-Network-Hash"
 
+	// KubeProbeUAPrefix is the prefix for the User-Agent header.
 	// Since K8s 1.8, prober requests have
 	//   User-Agent = "kube-probe/{major-version}.{minor-version}".
 	KubeProbeUAPrefix = "kube-probe/"
 
+	// KubeletProbeHeaderName is the header name to augment the probes, because
 	// Istio with mTLS rewrites probes, but their probes pass a different
-	// user-agent.  So we augment the probes with this header.
+	// user-agent.
 	KubeletProbeHeaderName = "K-Kubelet-Probe"
 )
 
 // IsKubeletProbe returns true if the request is a Kubernetes probe.
 func IsKubeletProbe(r *http.Request) bool {
-	return strings.HasPrefix(r.Header.Get("User-Agent"), KubeProbeUAPrefix) ||
+	return strings.HasPrefix(r.Header.Get(UserAgentKey), KubeProbeUAPrefix) ||
 		r.Header.Get(KubeletProbeHeaderName) != ""
 }
-
-// IsKProbe returns true if the request is a knatvie probe.
-func IsKProbe(r *http.Request) bool {
-	return r.Header.Get(ProbeHeaderName) == ProbeHeaderValue
-}
-
-// ServeKProbe serve KProbe requests.
-func ServeKProbe(w http.ResponseWriter, r *http.Request) {
-	hh := r.Header.Get(HashHeaderName)
-	if hh == "" {
-		http.Error(w, fmt.Sprintf("a probe request must contain a non-empty %q header", HashHeaderName), http.StatusBadRequest)
-		return
-	}
-	w.Header().Set(HashHeaderName, hh)
-	w.WriteHeader(http.StatusOK)
-}
diff --git a/vendor/knative.dev/pkg/system/clock.go b/vendor/knative.dev/pkg/system/clock.go
deleted file mode 100644
index 7d99d9b5c..000000000
--- a/vendor/knative.dev/pkg/system/clock.go
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-Copyright 2019 The Knative Authors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package system
-
-import (
-	"time"
-)
-
-// Mockable interface for time based testing
-type Clock interface {
-	Now() time.Time
-}
-
-type RealClock struct{}
-
-func (RealClock) Now() time.Time {
-	return time.Now()
-}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index d242e4b19..2b1a01ca8 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -386,6 +386,7 @@ golang.org/x/lint/golint
 golang.org/x/mod/module
 golang.org/x/mod/semver
 # golang.org/x/net v0.0.0-20210119194325-5f4716e94777
+## explicit
 golang.org/x/net/context
 golang.org/x/net/context/ctxhttp
 golang.org/x/net/http/httpguts
@@ -996,7 +997,7 @@ k8s.io/utils/buffer
 k8s.io/utils/integer
 k8s.io/utils/pointer
 k8s.io/utils/trace
-# knative.dev/caching v0.0.0-20210215030244-1212288570f0
+# knative.dev/caching v0.0.0-20210216192653-d1dfa6451cd1
 ## explicit
 knative.dev/caching/config
 knative.dev/caching/pkg/apis/caching
@@ -1021,7 +1022,7 @@ knative.dev/caching/pkg/client/listers/caching/v1alpha1
 ## explicit
 knative.dev/hack
 knative.dev/hack/shell
-# knative.dev/networking v0.0.0-20210215030235-088986a1c2a3
+# knative.dev/networking v0.0.0-20210216193011-d5e05ad323f0
 ## explicit
 knative.dev/networking/config
 knative.dev/networking/pkg
@@ -1056,7 +1057,7 @@ knative.dev/networking/test/conformance/ingress
 knative.dev/networking/test/defaultsystem
 knative.dev/networking/test/test_images/grpc-ping/proto
 knative.dev/networking/test/types
-# knative.dev/pkg v0.0.0-20210215165523-84c98f3c3e7a
+# knative.dev/pkg v0.0.0-20210226182947-9039dc189ced
 ## explicit
 knative.dev/pkg/apiextensions/storageversion
 knative.dev/pkg/apiextensions/storageversion/cmd/migrate


Test patch:
diff --git a/.github/workflows/kind-e2e.yaml b/.github/workflows/kind-e2e.yaml
index 691550fff..aa4bea147 100644
--- a/.github/workflows/kind-e2e.yaml
+++ b/.github/workflows/kind-e2e.yaml
@@ -18,9 +18,10 @@ jobs:
       fail-fast: false # Keep running if one leg fails.
       matrix:
         k8s-version:
-        - v1.17.11
-        - v1.18.8
-        - v1.19.1
+        - v1.17.17
+        - v1.18.15
+        - v1.19.7
+        - v1.20.2
 
         test-suite:
         - ./test/conformance/runtime
@@ -32,19 +33,24 @@ jobs:
           # Map between K8s and KinD versions.
           # This is attempting to make it a bit clearer what's being tested.
           # See: https://github.com/kubernetes-sigs/kind/releases/tag/v0.9.0
-        - k8s-version: v1.17.11
-          kind-version: v0.9.0
-          kind-image-sha: sha256:5240a7a2c34bf241afb54ac05669f8a46661912eab05705d660971eeb12f6555
+        - k8s-version: v1.17.17
+          kind-version: v0.10.0
+          kind-image-sha: sha256:7b6369d27eee99c7a85c48ffd60e11412dc3f373658bc59b7f4d530b7056823e
           kingress: istio
           cluster-suffix: c${{ github.run_id }}.local
-        - k8s-version: v1.18.8
-          kind-version: v0.9.0
-          kind-image-sha: sha256:f4bcc97a0ad6e7abaf3f643d890add7efe6ee4ab90baeb374b4f41a4c95567eb
+        - k8s-version: v1.18.15
+          kind-version: v0.10.0
+          kind-image-sha: sha256:5c1b980c4d0e0e8e7eb9f36f7df525d079a96169c8a8f20d8bd108c0d0889cc4
           kingress: kourier
           cluster-suffix: c${{ github.run_id }}.local
-        - k8s-version: v1.19.1
-          kind-version: v0.9.0
-          kind-image-sha: sha256:98cf5288864662e37115e362b23e4369c8c4a408f99cbc06e58ac30ddc721600
+        - k8s-version: v1.19.7
+          kind-version: v0.10.0
+          kind-image-sha: sha256:a70639454e97a4b733f9d9b67e12c01f6b0297449d5b9cbbef87473458e26dca
+          kingress: contour
+          cluster-suffix: c${{ github.run_id }}.local
+        - k8s-version: v1.20.2
+          kind-version: v0.10.0
+          kind-image-sha: sha256:8f7ea6e7642c0da54f04a7ee10431549c0257315b3a634f6ef2fecaaedb19bab
           kingress: contour
           cluster-suffix: c${{ github.run_id }}.local
 
diff --git a/cmd/activator/request_log_test.go b/cmd/activator/request_log_test.go
index aa9762e40..e3dd0ecc5 100644
--- a/cmd/activator/request_log_test.go
+++ b/cmd/activator/request_log_test.go
@@ -23,20 +23,15 @@ import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
-
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/uuid"
-
 	ltesting "knative.dev/pkg/logging/testing"
 	"knative.dev/pkg/metrics"
-	rtesting "knative.dev/pkg/reconciler/testing"
 	"knative.dev/serving/pkg/activator"
+	activatorhandler "knative.dev/serving/pkg/activator/handler"
 	"knative.dev/serving/pkg/apis/serving"
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
-	fakeservingclient "knative.dev/serving/pkg/client/injection/client/fake"
-	fakerevisioninformer "knative.dev/serving/pkg/client/injection/informers/serving/v1/revision/fake"
-	servinglisters "knative.dev/serving/pkg/client/listers/serving/v1"
 	pkghttp "knative.dev/serving/pkg/http"
 )
 
@@ -53,7 +48,7 @@ func TestUpdateRequestLogFromConfigMap(t *testing.T) {
 	})
 	buf := bytes.NewBufferString("")
 	handler, err := pkghttp.NewRequestLogHandler(baseHandler, buf, "",
-		requestLogTemplateInputGetter(revisionLister(t, true)), false /*enableProbeRequestLog*/)
+		requestLogTemplateInputGetter, false /*enableProbeRequestLog*/)
 	if err != nil {
 		t.Fatal("want: no error, got:", err)
 	}
@@ -131,11 +126,8 @@ func TestUpdateRequestLogFromConfigMap(t *testing.T) {
 			resp := httptest.NewRecorder()
 			rs := string(uuid.NewUUID())
 			req := httptest.NewRequest(http.MethodPost, test.url, bytes.NewBufferString(rs))
-			req.Header = map[string][]string{
-				activator.RevisionHeaderName:      {testRevisionName},
-				activator.RevisionHeaderNamespace: {testNamespaceName},
-			}
-			handler.ServeHTTP(resp, req)
+			ctx := activatorhandler.WithRevision(req.Context(), revision(true))
+			handler.ServeHTTP(resp, req.WithContext(ctx))
 
 			if got := buf.String(); got != test.want {
 				t.Errorf("got '%v', want '%v'", got, test.want)
@@ -147,13 +139,13 @@ func TestUpdateRequestLogFromConfigMap(t *testing.T) {
 func TestRequestLogTemplateInputGetter(t *testing.T) {
 	tests := []struct {
 		name     string
-		getter   pkghttp.RequestLogTemplateInputGetter
+		revision *v1.Revision
 		request  *http.Request
 		response *pkghttp.RequestLogResponse
 		want     pkghttp.RequestLogRevision
 	}{{
-		name:   "success",
-		getter: requestLogTemplateInputGetter(revisionLister(t, true)),
+		name:     "success",
+		revision: revision(true),
 		request: &http.Request{Header: map[string][]string{
 			activator.RevisionHeaderName:      {testRevisionName},
 			activator.RevisionHeaderNamespace: {testNamespaceName},
@@ -166,20 +158,8 @@ func TestRequestLogTemplateInputGetter(t *testing.T) {
 			Service:       testServiceName,
 		},
 	}, {
-		name:   "revision not found",
-		getter: requestLogTemplateInputGetter(revisionLister(t, true)),
-		request: &http.Request{Header: map[string][]string{
-			activator.RevisionHeaderName:      {"foo"},
-			activator.RevisionHeaderNamespace: {"bar"},
-		}},
-		response: &pkghttp.RequestLogResponse{Code: http.StatusAlreadyReported},
-		want: pkghttp.RequestLogRevision{
-			Name:      "foo",
-			Namespace: "bar",
-		},
-	}, {
-		name:   "labels not found",
-		getter: requestLogTemplateInputGetter(revisionLister(t, false)),
+		name:     "labels not found",
+		revision: revision(false),
 		request: &http.Request{Header: map[string][]string{
 			activator.RevisionHeaderName:      {testRevisionName},
 			activator.RevisionHeaderNamespace: {testNamespaceName},
@@ -193,12 +173,15 @@ func TestRequestLogTemplateInputGetter(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			got := test.getter(test.request, test.response)
+			ctx := activatorhandler.WithRevision(test.request.Context(), test.revision)
+			request := test.request.WithContext(ctx)
+
+			got := requestLogTemplateInputGetter(request, test.response)
 			if !cmp.Equal(*got.Revision, test.want) {
 				t.Errorf("Got = %v, want: %v, diff:\n%s", got.Revision, test.want, cmp.Diff(got.Revision, test.want))
 			}
-			if got.Request != test.request {
-				t.Errorf("Got = %v, want: %v", got.Request, test.request)
+			if got.Request != request {
+				t.Errorf("Got = %v, want: %v", got.Request, request)
 			}
 			if got.Response != test.response {
 				t.Errorf("Got = %v, want: %v", got.Response, test.response)
@@ -207,13 +190,14 @@ func TestRequestLogTemplateInputGetter(t *testing.T) {
 	}
 }
 
-func revisionLister(t *testing.T, addLabels bool) servinglisters.RevisionLister {
+func revision(addLabels bool) *v1.Revision {
 	rev := &v1.Revision{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      testRevisionName,
 			Namespace: testNamespaceName,
 		},
 	}
+
 	if addLabels {
 		rev.Labels = map[string]string{
 			serving.ConfigurationLabelKey: testConfigName,
@@ -221,9 +205,5 @@ func revisionLister(t *testing.T, addLabels bool) servinglisters.RevisionLister
 		}
 	}
 
-	ctx, _ := rtesting.SetupFakeContext(t)
-	fakeservingclient.Get(ctx).ServingV1().Revisions(testNamespaceName).Create(ctx, rev, metav1.CreateOptions{})
-	ri := fakerevisioninformer.Get(ctx)
-	ri.Informer().GetIndexer().Add(rev)
-	return ri.Lister()
+	return rev
 }
diff --git a/pkg/activator/handler/concurrency_reporter_test.go b/pkg/activator/handler/concurrency_reporter_test.go
index e93f284c9..1db6d3f9f 100644
--- a/pkg/activator/handler/concurrency_reporter_test.go
+++ b/pkg/activator/handler/concurrency_reporter_test.go
@@ -437,7 +437,7 @@ func TestConcurrencyReporterHandler(t *testing.T) {
 
 	resp := httptest.NewRecorder()
 	req := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
-	rCtx := withRevID(context.Background(), rev1)
+	rCtx := WithRevID(context.Background(), rev1)
 
 	// Send a few requests.
 	handler.ServeHTTP(resp, req.WithContext(rCtx))
@@ -644,7 +644,7 @@ func BenchmarkConcurrencyReporterHandler(b *testing.B) {
 			Name:      testRevName + strconv.Itoa(i),
 		}
 		req := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
-		reqs = append(reqs, req.WithContext(withRevID(context.Background(), key)))
+		reqs = append(reqs, req.WithContext(WithRevID(context.Background(), key)))
 
 		// Create revisions in the fake clients to trigger report logic.
 		rev := revision(key.Namespace, key.Name)
diff --git a/pkg/activator/handler/context_handler_test.go b/pkg/activator/handler/context_handler_test.go
index 06c688ddb..c4cee0092 100644
--- a/pkg/activator/handler/context_handler_test.go
+++ b/pkg/activator/handler/context_handler_test.go
@@ -38,12 +38,12 @@ func TestContextHandler(t *testing.T) {
 	revisionInformer(ctx, revision)
 
 	baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		if got := revisionFrom(r.Context()); got != revision {
+		if got := RevisionFrom(r.Context()); got != revision {
 			t.Errorf("revisionFrom() = %v, want %v", got, revision)
 		}
 
-		if got := revIDFrom(r.Context()); got != revID {
-			t.Errorf("revIDFrom() = %v, want %v", got, revID)
+		if got := RevIDFrom(r.Context()); got != revID {
+			t.Errorf("RevIDFrom() = %v, want %v", got, revID)
 		}
 	})
 
diff --git a/pkg/activator/handler/handler_test.go b/pkg/activator/handler/handler_test.go
index 273442ba5..a706b4b4e 100644
--- a/pkg/activator/handler/handler_test.go
+++ b/pkg/activator/handler/handler_test.go
@@ -134,7 +134,7 @@ func TestActivationHandler(t *testing.T) {
 			// Set up config store to populate context.
 			configStore := setupConfigStore(t, logging.FromContext(ctx))
 			ctx = configStore.ToContext(ctx)
-			ctx = withRevID(ctx, types.NamespacedName{Namespace: testNamespace, Name: testRevName})
+			ctx = WithRevID(ctx, types.NamespacedName{Namespace: testNamespace, Name: testRevName})
 
 			handler.ServeHTTP(resp, req.WithContext(ctx))
 
@@ -172,7 +172,7 @@ func TestActivationHandlerProxyHeader(t *testing.T) {
 	// Set up config store to populate context.
 	configStore := setupConfigStore(t, logging.FromContext(ctx))
 	ctx = configStore.ToContext(req.Context())
-	ctx = withRevID(ctx, types.NamespacedName{Namespace: testNamespace, Name: testRevName})
+	ctx = WithRevID(ctx, types.NamespacedName{Namespace: testNamespace, Name: testRevName})
 
 	handler.ServeHTTP(writer, req.WithContext(ctx))
 
@@ -267,7 +267,7 @@ func sendRequest(namespace, revName string, handler http.Handler, store *activat
 	resp := httptest.NewRecorder()
 	req := httptest.NewRequest(http.MethodPost, "http://example.com/", nil)
 	ctx := store.ToContext(req.Context())
-	ctx = withRevID(ctx, types.NamespacedName{Namespace: namespace, Name: revName})
+	ctx = WithRevID(ctx, types.NamespacedName{Namespace: namespace, Name: revName})
 	handler.ServeHTTP(resp, req.WithContext(ctx))
 	return resp
 }
@@ -318,7 +318,7 @@ func BenchmarkHandler(b *testing.B) {
 			req.Host = "test-host"
 
 			reqCtx := configStore.ToContext(context.Background())
-			reqCtx = withRevID(reqCtx, types.NamespacedName{Namespace: testNamespace, Name: testRevName})
+			reqCtx = WithRevID(reqCtx, types.NamespacedName{Namespace: testNamespace, Name: testRevName})
 			return req.WithContext(reqCtx)
 		}
 
diff --git a/pkg/activator/handler/healthz_handler_test.go b/pkg/activator/handler/healthz_handler_test.go
index 239923d28..74891206e 100644
--- a/pkg/activator/handler/healthz_handler_test.go
+++ b/pkg/activator/handler/healthz_handler_test.go
@@ -24,6 +24,7 @@ import (
 	"testing"
 
 	ktesting "knative.dev/pkg/logging/testing"
+	"knative.dev/pkg/network"
 )
 
 func TestHealthHandler(t *testing.T) {
@@ -36,19 +37,17 @@ func TestHealthHandler(t *testing.T) {
 		check          func() error
 	}{{
 		name:           "forward non-kubelet request",
-		headers:        mapToHeader(map[string]string{"User-Agent": "chromium/734.6.5"}),
+		headers:        http.Header{network.UserAgentKey: []string{"chromium/734.6.5"}},
 		passed:         true,
 		expectedStatus: http.StatusOK,
 	}, {
 		name:           "kubelet probe success",
-		headers:        mapToHeader(map[string]string{"User-Agent": "kube-probe/something"}),
-		passed:         false,
+		headers:        http.Header{network.UserAgentKey: []string{"kube-probe/something"}},
 		expectedStatus: http.StatusOK,
 		check:          func() error { return nil },
 	}, {
 		name:           "kubelet probe failure",
-		headers:        mapToHeader(map[string]string{"User-Agent": "kube-probe/something"}),
-		passed:         false,
+		headers:        http.Header{network.UserAgentKey: []string{"kube-probe/something"}},
 		expectedStatus: http.StatusInternalServerError,
 		check:          func() error { return errors.New("not ready") },
 	}}
@@ -77,7 +76,7 @@ func TestHealthHandler(t *testing.T) {
 			}
 
 			if resp.Code != e.expectedStatus {
-				t.Errorf("Unexpected response status. Want %d, got %d", e.expectedStatus, resp.Code)
+				t.Errorf("HTTP Status = %d, want: %d", resp.Code, e.expectedStatus)
 			}
 		})
 	}
@@ -90,15 +89,15 @@ func BenchmarkHealthHandler(b *testing.B) {
 		check   func() error
 	}{{
 		label:   "forward non-kubelet request",
-		headers: mapToHeader(map[string]string{"User-Agent": "chromium/734.6.5"}),
+		headers: http.Header{network.UserAgentKey: []string{"chromium/734.6.5"}},
 		check:   func() error { return nil },
 	}, {
 		label:   "kubelet probe success",
-		headers: mapToHeader(map[string]string{"User-Agent": "kube-probe/something"}),
+		headers: http.Header{network.UserAgentKey: []string{"kube-probe/something"}},
 		check:   func() error { return nil },
 	}, {
 		label:   "kubelet probe failure",
-		headers: mapToHeader(map[string]string{"User-Agent": "kube-probe/something"}),
+		headers: http.Header{network.UserAgentKey: []string{"kube-probe/something"}},
 		check:   func() error { return errors.New("not ready") },
 	}}
 
diff --git a/pkg/activator/handler/metric_handler_test.go b/pkg/activator/handler/metric_handler_test.go
index 154790684..0c6a4421c 100644
--- a/pkg/activator/handler/metric_handler_test.go
+++ b/pkg/activator/handler/metric_handler_test.go
@@ -114,8 +114,8 @@ func TestRequestMetricHandler(t *testing.T) {
 				metricstest.AssertMetricExists(t, responseTimeInMsecM.Name())
 			}()
 
-			reqCtx := withRevision(context.Background(), rev)
-			reqCtx = withRevID(reqCtx, types.NamespacedName{Namespace: testNamespace, Name: testRevName})
+			reqCtx := WithRevision(context.Background(), rev)
+			reqCtx = WithRevID(reqCtx, types.NamespacedName{Namespace: testNamespace, Name: testRevName})
 			handler.ServeHTTP(resp, req.WithContext(reqCtx))
 		})
 	}
@@ -128,7 +128,7 @@ func reset() {
 
 func BenchmarkMetricHandler(b *testing.B) {
 	baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
-	reqCtx := withRevision(context.Background(), revision(testNamespace, testRevName))
+	reqCtx := WithRevision(context.Background(), revision(testNamespace, testRevName))
 
 	handler := NewMetricHandler("benchPod", baseHandler)
 
diff --git a/pkg/activator/handler/probe_handler_test.go b/pkg/activator/handler/probe_handler_test.go
index 630a188c3..3251f4b34 100644
--- a/pkg/activator/handler/probe_handler_test.go
+++ b/pkg/activator/handler/probe_handler_test.go
@@ -42,14 +42,12 @@ func TestProbeHandler(t *testing.T) {
 		method:         http.MethodPost,
 	}, {
 		label:          "filter a POST request containing probe header, even if probe is for a different target",
-		headers:        mapToHeader(map[string]string{network.ProbeHeaderName: queue.Name}),
-		passed:         false,
+		headers:        http.Header{network.ProbeHeaderName: []string{queue.Name}},
 		expectedStatus: http.StatusBadRequest,
 		method:         http.MethodPost,
 	}, {
 		label:          "filter a POST request containing probe header",
-		headers:        mapToHeader(map[string]string{network.ProbeHeaderName: activator.Name}),
-		passed:         false,
+		headers:        http.Header{network.ProbeHeaderName: []string{activator.Name}},
 		expectedStatus: http.StatusOK,
 		method:         http.MethodPost,
 	}, {
@@ -60,19 +58,18 @@ func TestProbeHandler(t *testing.T) {
 		method:         http.MethodGet,
 	}, {
 		label:          "filter a GET request containing probe header, with wrong target system",
-		headers:        mapToHeader(map[string]string{network.ProbeHeaderName: "not-empty"}),
-		passed:         false,
+		headers:        http.Header{network.ProbeHeaderName: []string{"not-empty"}},
 		expectedStatus: http.StatusBadRequest,
 		method:         http.MethodGet,
 	}, {
 		label:          "filter a GET request containing probe header",
-		headers:        mapToHeader(map[string]string{network.ProbeHeaderName: activator.Name}),
+		headers:        http.Header{network.ProbeHeaderName: []string{activator.Name}},
 		passed:         false,
 		expectedStatus: http.StatusOK,
 		method:         http.MethodGet,
 	}, {
 		label:          "forward a request containing empty retry header",
-		headers:        mapToHeader(map[string]string{network.ProbeHeaderName: ""}),
+		headers:        http.Header{network.ProbeHeaderName: []string{""}},
 		passed:         true,
 		expectedStatus: http.StatusOK,
 		method:         http.MethodPost,
@@ -108,24 +105,16 @@ func TestProbeHandler(t *testing.T) {
 	}
 }
 
-func mapToHeader(m map[string]string) http.Header {
-	h := http.Header{}
-	for k, v := range m {
-		h.Add(k, v)
-	}
-	return h
-}
-
 func BenchmarkProbeHandler(b *testing.B) {
 	tests := []struct {
 		label   string
 		headers http.Header
 	}{{
 		label:   "valid header name",
-		headers: mapToHeader(map[string]string{network.ProbeHeaderName: activator.Name}),
+		headers: http.Header{network.ProbeHeaderName: []string{activator.Name}},
 	}, {
 		label:   "invalid header name",
-		headers: mapToHeader(map[string]string{network.ProbeHeaderName: "not-empty"}),
+		headers: http.Header{network.ProbeHeaderName: []string{"some-other-cool-value"}},
 	}, {
 		label:   "empty header name",
 		headers: http.Header{},
diff --git a/pkg/apis/autoscaling/annotation_validation_test.go b/pkg/apis/autoscaling/annotation_validation_test.go
index 9619e4e43..360d8d6b2 100644
--- a/pkg/apis/autoscaling/annotation_validation_test.go
+++ b/pkg/apis/autoscaling/annotation_validation_test.go
@@ -151,6 +151,31 @@ func TestValidateAnnotations(t *testing.T) {
 		},
 		isInCreate:  true,
 		annotations: map[string]string{MaxScaleAnnotationKey: "9"},
+	}, {
+		name: "valid algorithm on KPA",
+		annotations: map[string]string{
+			MetricAggregationAlgorithmKey: MetricAggregationAlgorithmLinear,
+			ClassAnnotationKey:            KPA,
+		},
+	}, {
+		name: "empty algorithm",
+		annotations: map[string]string{
+			MetricAggregationAlgorithmKey: "",
+			ClassAnnotationKey:            KPA,
+		},
+	}, {
+		name: "random algorithm on KPA",
+		annotations: map[string]string{
+			MetricAggregationAlgorithmKey: "random-selection",
+			ClassAnnotationKey:            KPA,
+		},
+		expectErr: "invalid value: random-selection: " + MetricAggregationAlgorithmKey,
+	}, {
+		name: "random algorithm on non KPA",
+		annotations: map[string]string{
+			MetricAggregationAlgorithmKey: "random-selection",
+			ClassAnnotationKey:            "of-keys",
+		},
 	}, {
 		name:        "panic window percentage bad",
 		annotations: map[string]string{PanicWindowPercentageAnnotationKey: "-1"},
diff --git a/pkg/apis/serving/metadata_validation_test.go b/pkg/apis/serving/metadata_validation_test.go
index 2a6b10554..a1c0b75df 100644
--- a/pkg/apis/serving/metadata_validation_test.go
+++ b/pkg/apis/serving/metadata_validation_test.go
@@ -35,10 +35,11 @@ import (
 
 func TestValidateObjectMetadata(t *testing.T) {
 	cases := []struct {
-		name       string
-		objectMeta metav1.Object
-		ctx        context.Context
-		expectErr  *apis.FieldError
+		name             string
+		objectMeta       metav1.Object
+		allowAutoscaling bool
+		ctx              context.Context
+		expectErr        *apis.FieldError
 	}{{
 		name: "invalid name - dots",
 		objectMeta: &metav1.ObjectMeta{
@@ -101,14 +102,6 @@ func TestValidateObjectMetadata(t *testing.T) {
 			Message: "name or generateName is required",
 			Paths:   []string{"name"},
 		},
-	}, {
-		name: "valid forceUpgrade annotation label",
-		objectMeta: &metav1.ObjectMeta{
-			GenerateName: "some-name",
-			Annotations: map[string]string{
-				"serving.knative.dev/forceUpgrade": "true",
-			},
-		},
 	}, {
 		name: "valid creator annotation label",
 		objectMeta: &metav1.ObjectMeta{
@@ -142,14 +135,13 @@ func TestValidateObjectMetadata(t *testing.T) {
 			},
 		},
 	}, {
-		name: "invalid knative prefix annotation",
+		name: "valid knative prefix annotation",
 		objectMeta: &metav1.ObjectMeta{
 			GenerateName: "some-name",
 			Annotations: map[string]string{
 				"serving.knative.dev/testAnnotation": "value",
 			},
 		},
-		expectErr: apis.ErrInvalidKeyName("serving.knative.dev/testAnnotation", "annotations"),
 	}, {
 		name: "valid non-knative prefix annotation label",
 		objectMeta: &metav1.ObjectMeta{
@@ -159,8 +151,9 @@ func TestValidateObjectMetadata(t *testing.T) {
 			},
 		},
 	}, {
-		name: "revision initial scale not parseable",
-		ctx:  config.ToContext(context.Background(), &config.Config{Autoscaler: &autoscalerconfig.Config{AllowZeroInitialScale: true}}),
+		name:             "revision initial scale not parseable",
+		ctx:              config.ToContext(context.Background(), &config.Config{Autoscaler: &autoscalerconfig.Config{AllowZeroInitialScale: true}}),
+		allowAutoscaling: true,
 		objectMeta: &metav1.ObjectMeta{
 			GenerateName: "some-name",
 			Annotations: map[string]string{
@@ -169,8 +162,9 @@ func TestValidateObjectMetadata(t *testing.T) {
 		},
 		expectErr: apis.ErrInvalidValue("invalid", "annotations."+autoscaling.InitialScaleAnnotationKey),
 	}, {
-		name: "negative revision initial scale",
-		ctx:  config.ToContext(context.Background(), &config.Config{Autoscaler: &autoscalerconfig.Config{AllowZeroInitialScale: true}}),
+		name:             "negative revision initial scale",
+		ctx:              config.ToContext(context.Background(), &config.Config{Autoscaler: &autoscalerconfig.Config{AllowZeroInitialScale: true}}),
+		allowAutoscaling: true,
 		objectMeta: &metav1.ObjectMeta{
 			GenerateName: "some-name",
 			Annotations: map[string]string{
@@ -179,8 +173,9 @@ func TestValidateObjectMetadata(t *testing.T) {
 		},
 		expectErr: apis.ErrInvalidValue("-2", "annotations."+autoscaling.InitialScaleAnnotationKey),
 	}, {
-		name: "cluster allows zero revision initial scale",
-		ctx:  config.ToContext(context.Background(), &config.Config{Autoscaler: &autoscalerconfig.Config{AllowZeroInitialScale: true}}),
+		name:             "cluster allows zero revision initial scale",
+		ctx:              config.ToContext(context.Background(), &config.Config{Autoscaler: &autoscalerconfig.Config{AllowZeroInitialScale: true}}),
+		allowAutoscaling: true,
 		objectMeta: &metav1.ObjectMeta{
 			GenerateName: "some-name",
 			Annotations: map[string]string{
@@ -188,7 +183,8 @@ func TestValidateObjectMetadata(t *testing.T) {
 			},
 		},
 	}, {
-		name: "cluster does not allows zero revision initial scale",
+		name:             "cluster does not allow zero revision initial scale",
+		allowAutoscaling: true,
 		objectMeta: &metav1.ObjectMeta{
 			GenerateName: "some-name",
 			Annotations: map[string]string{
@@ -196,6 +192,16 @@ func TestValidateObjectMetadata(t *testing.T) {
 			},
 		},
 		expectErr: apis.ErrInvalidValue("0", "annotations."+autoscaling.InitialScaleAnnotationKey),
+	}, {
+		name:             "autoscaling annotations on a resource that doesn't allow them",
+		allowAutoscaling: false,
+		objectMeta: &metav1.ObjectMeta{
+			GenerateName: "some-name",
+			Annotations: map[string]string{
+				autoscaling.InitialScaleAnnotationKey: "0",
+			},
+		},
+		expectErr: apis.ErrInvalidKeyName(autoscaling.InitialScaleAnnotationKey, "annotations", `autoscaling annotations must be put under "spec.template.metadata.annotations" to work`),
 	}}
 
 	for _, c := range cases {
@@ -203,7 +209,7 @@ func TestValidateObjectMetadata(t *testing.T) {
 			if c.ctx == nil {
 				c.ctx = config.ToContext(context.Background(), &config.Config{Autoscaler: &autoscalerconfig.Config{AllowZeroInitialScale: false}})
 			}
-			err := ValidateObjectMetadata(c.ctx, c.objectMeta)
+			err := ValidateObjectMetadata(c.ctx, c.objectMeta, c.allowAutoscaling)
 			if got, want := err.Error(), c.expectErr.Error(); got != want {
 				t.Errorf("\nGot:  %q\nwant: %q", got, want)
 			}
diff --git a/pkg/apis/serving/v1/configuration_validation_test.go b/pkg/apis/serving/v1/configuration_validation_test.go
index 337bca88c..59c17e9f2 100644
--- a/pkg/apis/serving/v1/configuration_validation_test.go
+++ b/pkg/apis/serving/v1/configuration_validation_test.go
@@ -385,7 +385,9 @@ func TestConfigurationLabelValidation(t *testing.T) {
 		},
 		want: apis.ErrMissingField("metadata.labels.serving.knative.dev/service"),
 	}, {
-		name: "invalid knative label",
+		// We want to be able to introduce new labels with the serving prefix in the future
+		// and not break downgrading.
+		name: "allow unknown uses of knative.dev/serving prefix",
 		c: &Configuration{
 			ObjectMeta: metav1.ObjectMeta{
 				Name: "byo-name",
@@ -395,8 +397,9 @@ func TestConfigurationLabelValidation(t *testing.T) {
 			},
 			Spec: validConfigSpec,
 		},
-		want: apis.ErrInvalidKeyName("serving.knative.dev/testlabel", "metadata.labels"),
+		want: nil,
 	}}
+
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 			got := test.c.Validate(context.Background())
diff --git a/pkg/apis/serving/v1/revision_validation_test.go b/pkg/apis/serving/v1/revision_validation_test.go
index 78e7c7bf1..9ffe1ff41 100644
--- a/pkg/apis/serving/v1/revision_validation_test.go
+++ b/pkg/apis/serving/v1/revision_validation_test.go
@@ -221,7 +221,9 @@ func TestRevisionLabelAnnotationValidation(t *testing.T) {
 		},
 		want: apis.ErrMissingField("metadata.labels.serving.knative.dev/configuration"),
 	}, {
-		name: "invalid knative label",
+		// We want to be able to introduce new labels with the serving prefix in the future
+		// and not break downgrading.
+		name: "allow unknown uses of knative.dev/serving prefix",
 		r: &Revision{
 			ObjectMeta: metav1.ObjectMeta{
 				Name: "byo-name",
@@ -231,7 +233,7 @@ func TestRevisionLabelAnnotationValidation(t *testing.T) {
 			},
 			Spec: validRevisionSpec,
 		},
-		want: apis.ErrInvalidKeyName("serving.knative.dev/testlabel", "metadata.labels"),
+		want: nil,
 	}}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
diff --git a/pkg/apis/serving/v1/route_validation_test.go b/pkg/apis/serving/v1/route_validation_test.go
index 3aac8e96a..76678adfa 100644
--- a/pkg/apis/serving/v1/route_validation_test.go
+++ b/pkg/apis/serving/v1/route_validation_test.go
@@ -585,7 +585,9 @@ func TestRouteLabelValidation(t *testing.T) {
 			Spec: validRouteSpec,
 		},
 	}, {
-		name: "invalid knative label",
+		// We want to be able to introduce new labels with the serving prefix in the future
+		// and not break downgrading.
+		name: "allow unknown uses of knative.dev/serving prefix",
 		r: &Route{
 			ObjectMeta: metav1.ObjectMeta{
 				Name: "byo-name",
@@ -595,7 +597,7 @@ func TestRouteLabelValidation(t *testing.T) {
 			},
 			Spec: validRouteSpec,
 		},
-		want: apis.ErrInvalidKeyName("serving.knative.dev/testlabel", "metadata.labels"),
+		want: nil,
 	}}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
diff --git a/pkg/apis/serving/v1/service_validation_test.go b/pkg/apis/serving/v1/service_validation_test.go
index 6710ecc1f..07cd57dbe 100644
--- a/pkg/apis/serving/v1/service_validation_test.go
+++ b/pkg/apis/serving/v1/service_validation_test.go
@@ -83,21 +83,6 @@ func TestServiceValidation(t *testing.T) {
 				RouteSpec:         goodRouteSpec,
 			},
 		},
-	}, {
-		name: "invalid knative label",
-		r: &Service{
-			ObjectMeta: metav1.ObjectMeta{
-				Name: "valid",
-				Labels: map[string]string{
-					"serving.knative.dev/name": "some-value",
-				},
-			},
-			Spec: ServiceSpec{
-				ConfigurationSpec: goodConfigSpec,
-				RouteSpec:         goodRouteSpec,
-			},
-		},
-		wantErr: apis.ErrInvalidKeyName("serving.knative.dev/name", "metadata.labels"),
 	}, {
 		name: "valid non knative label",
 		r: &Service{
@@ -259,27 +244,49 @@ func TestServiceValidation(t *testing.T) {
 					"autoscaling.knative.dev/foo": "bar",
 				},
 			},
-			Spec: ServiceSpec{
-				ConfigurationSpec: ConfigurationSpec{
-					Template: RevisionTemplateSpec{
-						Spec: RevisionSpec{
-							PodSpec: corev1.PodSpec{
-								Containers: []corev1.Container{{
-									Image: "hellworld",
-								}},
-							},
-						},
-					},
+			Spec: getServiceSpec("helloworld:foo"),
+		},
+		wantErr: apis.ErrInvalidKeyName("autoscaling.knative.dev/foo", "metadata.annotations", `autoscaling annotations must be put under "spec.template.metadata.annotations" to work`),
+	}, {
+		// We want to be able to introduce new labels with the serving prefix in the future
+		// and not break downgrading.
+		name: "allow unknown uses of knative.dev/serving prefix for labels",
+		r: &Service{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "byo-name",
+				Labels: map[string]string{
+					"serving.knative.dev/testlabel": "value",
 				},
-				RouteSpec: RouteSpec{
-					Traffic: []TrafficTarget{{
-						LatestRevision: ptr.Bool(true),
-						Percent:        ptr.Int64(100),
-					}},
+			},
+			Spec: getServiceSpec("helloworld:foo"),
+		},
+		wantErr: nil,
+	}, {
+		// We want to be able to introduce new annotations with the serving prefix in the future
+		// and not break downgrading.
+		name: "allow unknown uses of serving.knative.dev prefix for annotations",
+		r: &Service{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "serving-annotation",
+				Annotations: map[string]string{
+					"serving.knative.dev/foo": "bar",
 				},
 			},
+			Spec: getServiceSpec("helloworld:foo"),
+		},
+		wantErr: nil,
+	}, {
+		name: "invalid name",
+		r: &Service{
+			ObjectMeta: metav1.ObjectMeta{
+				Name: "",
+			},
+			Spec: getServiceSpec("helloworld:foo"),
+		},
+		wantErr: &apis.FieldError{
+			Message: "name or generateName is required",
+			Paths:   []string{"metadata.name"},
 		},
-		wantErr: apis.ErrInvalidKeyName("autoscaling.knative.dev/foo", "metadata.annotations", `autoscaling annotations must be put under "spec.template.metadata.annotations" to work`),
 	}}
 
 	// TODO(dangerd): PodSpec validation failures.
diff --git a/pkg/autoscaler/aggregation/bucketing_test.go b/pkg/autoscaler/aggregation/bucketing_test.go
index 2d454a083..c7c5a290b 100644
--- a/pkg/autoscaler/aggregation/bucketing_test.go
+++ b/pkg/autoscaler/aggregation/bucketing_test.go
@@ -18,6 +18,7 @@ package aggregation
 
 import (
 	"fmt"
+	"math"
 	"math/rand"
 	"testing"
 	"time"
@@ -25,9 +26,34 @@ import (
 	"github.com/google/go-cmp/cmp"
 )
 
-const (
-	granularity = time.Second
-)
+const granularity = time.Second
+
+func TestComputeDecayMultiplier(t *testing.T) {
+	tests := []struct {
+		numBuckets float64
+		want       float64
+	}{{
+		numBuckets: 100,
+		want:       minExponent,
+	}, {
+		numBuckets: 60,
+		want:       minExponent,
+	}, {
+		numBuckets: 40,
+		want:       0.20567,
+	}, {
+		numBuckets: 6,
+		want:       0.78456,
+	}}
+
+	for _, tc := range tests {
+		t.Run(fmt.Sprint("nb=", tc.numBuckets), func(t *testing.T) {
+			if got, want := computeSmoothingCoeff(tc.numBuckets), tc.want; math.Abs(got-want) > weightPrecision {
+				t.Errorf("Decay multiplier = %v, want: %v", got, want)
+			}
+		})
+	}
+}
 
 func TestTimedFloat64BucketsSimple(t *testing.T) {
 	trunc1 := time.Now().Truncate(1 * time.Second)
@@ -173,6 +199,49 @@ func TestTimedFloat64BucketsManyRepsWithNonMonotonicalOrder(t *testing.T) {
 	}
 }
 
+func TestTimedFloat64BucketsWeightedAverage(t *testing.T) {
+	now := time.Now()
+	buckets := NewWeightedFloat64Buckets(5*time.Second, granularity)
+
+	buckets.Record(now, 2)
+	want := 2 * buckets.smoothingCoeff // 2*dm = dm.
+	if got, want := buckets.WindowAverage(now), want; got != want {
+		t.Errorf("WeightedAverage = %v, want: %v", got, want)
+	}
+
+	// Let's read one second in future, but no writes.
+	want *= (1 - buckets.smoothingCoeff)
+	if got, want := buckets.WindowAverage(now.Add(time.Second)), want; got != want {
+		t.Errorf("WeightedAverage = %v, want: %v", got, want)
+	}
+	// Record some more data.
+	buckets.Record(now.Add(time.Second), 2)
+	want += 2 * buckets.smoothingCoeff
+	if got, want := buckets.WindowAverage(now.Add(time.Second)), want; got != want {
+		t.Errorf("WeightedAverage = %v, want: %v", got, want)
+	}
+
+	// Fill the whole window, with [2, 3, 4, 5, 6]
+	for i := 0; i < 5; i++ {
+		buckets.Record(now.Add(time.Duration(2+i)*time.Second), float64(i+2))
+	}
+	// Manually compute wanted average.
+	m := buckets.smoothingCoeff
+	want = 6*m +
+		5*m*(1-m) +
+		4*m*(1-m)*(1-m) +
+		3*m*(1-m)*(1-m)*(1-m) +
+		2*m*(1-m)*(1-m)*(1-m)*(1-m)
+	if got, want := buckets.WindowAverage(now.Add(6*time.Second)), want; got != want {
+		t.Errorf("WeightedAverage = %v, want: %v", got, want)
+	}
+
+	// Read from an empty window.
+	if got, want := buckets.WindowAverage(now.Add(16*time.Second)), 0.; got != want {
+		t.Errorf("WeightedAverage = %v, want: %v", got, want)
+	}
+}
+
 func TestTimedFloat64BucketsWindowAverage(t *testing.T) {
 	now := time.Now()
 	buckets := NewTimedFloat64Buckets(5*time.Second, granularity)
@@ -306,7 +375,52 @@ func TestTimedFloat64BucketsHoles(t *testing.T) {
 	}
 }
 
-func TestTimedFloat64BucketsWindowUpdate(t *testing.T) {
+func TestWeightedFloat64BucketsResizeWindow(t *testing.T) {
+	startTime := time.Now()
+	buckets := NewWeightedFloat64Buckets(5*time.Second, granularity)
+
+	if got, want := buckets.smoothingCoeff, computeSmoothingCoeff(5); math.Abs(got-want) > weightPrecision {
+		t.Errorf("DecayMultipler = %v, want: %v", got, want)
+	}
+
+	// Fill the whole bucketing list with rollover.
+	buckets.Record(startTime, 1)
+	buckets.Record(startTime.Add(1*time.Second), 2)
+	buckets.Record(startTime.Add(2*time.Second), 3)
+	buckets.Record(startTime.Add(3*time.Second), 4)
+	buckets.Record(startTime.Add(4*time.Second), 5)
+	buckets.Record(startTime.Add(5*time.Second), 6)
+	now := startTime.Add(5 * time.Second)
+
+	sum := 0.
+	buckets.forEachBucket(now, func(t time.Time, b float64) {
+		sum += b
+	})
+	const wantInitial = 2. + 3 + 4 + 5 + 6
+	if got, want := sum, wantInitial; got != want {
+		t.Fatalf("Initial data set Sum = %v, want: %v", got, want)
+	}
+	if got, want := roundToNDigits(3, buckets.WindowAverage(now)), 5.811; /*computed a mano*/ got != want {
+		t.Fatalf("Initial data set Sum = %v, want: %v", got, want)
+	}
+
+	// Increase window. Most of the heavy lifting is delegated to regular buckets
+	// so just do a cursory check.
+	buckets.ResizeWindow(10 * time.Second)
+	if got, want := len(buckets.buckets), 10; got != want {
+		t.Fatalf("Resized bucket count = %d, want: %d", got, want)
+	}
+	if got, want := buckets.window, 10*time.Second; got != want {
+		t.Fatalf("Resized bucket windows = %v, want: %v", got, want)
+	}
+
+	// And this is the main logic that was added in this type.
+	if got, want := buckets.smoothingCoeff, computeSmoothingCoeff(10); math.Abs(got-want) > weightPrecision {
+		t.Errorf("DecayMultipler = %v, want: %v", got, want)
+	}
+}
+
+func TestTimedFloat64BucketsResizeWindow(t *testing.T) {
 	startTime := time.Now()
 	buckets := NewTimedFloat64Buckets(5*time.Second, granularity)
 
diff --git a/pkg/autoscaler/metrics/collector_test.go b/pkg/autoscaler/metrics/collector_test.go
index 2d6c08b91..61388fb81 100644
--- a/pkg/autoscaler/metrics/collector_test.go
+++ b/pkg/autoscaler/metrics/collector_test.go
@@ -31,6 +31,7 @@ import (
 	"k8s.io/apimachinery/pkg/util/wait"
 
 	. "knative.dev/pkg/logging/testing"
+	"knative.dev/serving/pkg/apis/autoscaling"
 	autoscalingv1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
 	"knative.dev/serving/pkg/apis/serving"
 	"knative.dev/serving/pkg/autoscaler/aggregation"
@@ -106,11 +107,33 @@ func TestMetricCollectorCRUD(t *testing.T) {
 			t.Error("Update() didn't return the same metric:", cmp.Diff(&defaultMetric, got))
 		}
 
-		newURL := (coll.collections[key]).scraper.(*testScraper).url
+		newURL := coll.collections[key].scraper.(*testScraper).url
 		if got, want := newURL, "slightly-off"; got != want {
 			t.Errorf("Updated scraper URL = %s, want: %s, diff: %s", got, want, cmp.Diff(got, want))
 		}
 
+		if want, ok := coll.collections[key].concurrencyBuckets.(*aggregation.TimedFloat64Buckets); !ok {
+			t.Errorf("Buckets Type = %T, want: %T", coll.collections[key].concurrencyBuckets, want)
+		}
+
+		coll.Delete(defaultNamespace, defaultName)
+	})
+
+	t.Run("different aggregation", func(t *testing.T) {
+		special := defaultMetric.DeepCopy()
+		special.Annotations = map[string]string{
+			autoscaling.MetricAggregationAlgorithmKey: autoscaling.MetricAggregationAlgorithmWeightedExponential,
+		}
+		key := types.NamespacedName{Namespace: special.Namespace, Name: special.Name}
+		coll := NewMetricCollector(factory, logger)
+		if err := coll.CreateOrUpdate(special); err != nil {
+			t.Errorf("CreateOrUpdate() = %v, want no error", err)
+		}
+
+		if want, ok := coll.collections[key].concurrencyBuckets.(*aggregation.WeightedFloat64Buckets); !ok {
+			t.Errorf("Buckets Type = %T, want: %T", coll.collections[key].concurrencyBuckets, want)
+		}
+
 		coll.Delete(defaultNamespace, defaultName)
 	})
 }
diff --git a/pkg/autoscaler/statserver/server_test.go b/pkg/autoscaler/statserver/server_test.go
index 1846c5d07..ed287798e 100644
--- a/pkg/autoscaler/statserver/server_test.go
+++ b/pkg/autoscaler/statserver/server_test.go
@@ -81,7 +81,7 @@ func TestProbe(t *testing.T) {
 	if err != nil {
 		t.Fatal("Error creating request:", err)
 	}
-	req.Header.Set("User-Agent", "kube-probe/1.15.i.wish")
+	req.Header.Set(network.UserAgentKey, network.KubeProbeUAPrefix+"1.15.i.wish")
 
 	resp, err := http.DefaultClient.Do(req)
 	if err != nil {
diff --git a/pkg/http/handler/timeout_test.go b/pkg/http/handler/timeout_test.go
index 1b9b25d55..88066022a 100644
--- a/pkg/http/handler/timeout_test.go
+++ b/pkg/http/handler/timeout_test.go
@@ -81,7 +81,7 @@ func TestTimeToFirstByteTimeoutHandler(t *testing.T) {
 
 	tests := []struct {
 		name           string
-		timeoutFunc    TimeoutFunc
+		timeout        time.Duration
 		handler        func(mux *sync.Mutex, writeErrors chan error) http.Handler
 		timeoutMessage string
 		wantStatus     int
@@ -89,8 +89,8 @@ func TestTimeToFirstByteTimeoutHandler(t *testing.T) {
 		wantWriteError bool
 		wantPanic      bool
 	}{{
-		name:        "all good",
-		timeoutFunc: StaticTimeoutFunc(longTimeout),
+		name:    "all good",
+		timeout: longTimeout,
 		handler: func(*sync.Mutex, chan error) http.Handler {
 			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				w.Write([]byte("hi"))
@@ -99,8 +99,8 @@ func TestTimeToFirstByteTimeoutHandler(t *testing.T) {
 		wantStatus: http.StatusOK,
 		wantBody:   "hi",
 	}, {
-		name:        "custom timeout message",
-		timeoutFunc: StaticTimeoutFunc(immediateTimeout),
+		name:    "custom timeout message",
+		timeout: immediateTimeout,
 		handler: func(mux *sync.Mutex, writeErrors chan error) http.Handler {
 			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				mux.Lock()
@@ -114,8 +114,8 @@ func TestTimeToFirstByteTimeoutHandler(t *testing.T) {
 		wantBody:       "request timeout",
 		wantWriteError: true,
 	}, {
-		name:        "propagate panic",
-		timeoutFunc: StaticTimeoutFunc(longTimeout),
+		name:    "propagate panic",
+		timeout: longTimeout,
 		handler: func(*sync.Mutex, chan error) http.Handler {
 			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				panic(http.ErrAbortHandler)
@@ -125,8 +125,8 @@ func TestTimeToFirstByteTimeoutHandler(t *testing.T) {
 		wantBody:   "request timeout",
 		wantPanic:  true,
 	}, {
-		name:        "timeout before panic",
-		timeoutFunc: StaticTimeoutFunc(immediateTimeout),
+		name:    "timeout before panic",
+		timeout: immediateTimeout,
 		handler: func(*sync.Mutex, chan error) http.Handler {
 			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 				time.Sleep(1 * time.Second)
@@ -145,7 +145,7 @@ func TestTimeToFirstByteTimeoutHandler(t *testing.T) {
 			var reqMux sync.Mutex
 			writeErrors := make(chan error, 1)
 			rr := httptest.NewRecorder()
-			handler := NewTimeToFirstByteTimeoutHandler(test.handler(&reqMux, writeErrors), test.timeoutMessage, test.timeoutFunc)
+			handler := NewTimeToFirstByteTimeoutHandler(test.handler(&reqMux, writeErrors), test.timeoutMessage, test.timeout)
 
 			defer func() {
 				if test.wantPanic {
diff --git a/pkg/http/proxy_test.go b/pkg/http/proxy_test.go
index 01e808b75..5ff134c8e 100644
--- a/pkg/http/proxy_test.go
+++ b/pkg/http/proxy_test.go
@@ -24,6 +24,8 @@ import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+
+	"knative.dev/pkg/network"
 )
 
 func TestNewHeaderPruningProxy(t *testing.T) {
@@ -58,10 +60,10 @@ func TestNewHeaderPruningProxy(t *testing.T) {
 		name: "explicit user agent header not removed",
 		url:  "http://example.com/",
 		header: http.Header{
-			"User-Agent": []string{"gold"},
+			network.UserAgentKey: []string{"gold"},
 		},
 		expectHeaders: http.Header{
-			"User-Agent": []string{"gold"},
+			network.UserAgentKey: []string{"gold"},
 		},
 	}}
 
diff --git a/pkg/queue/health/probe_test.go b/pkg/queue/health/probe_test.go
index 72bd6c4b1..5d0922023 100644
--- a/pkg/queue/health/probe_test.go
+++ b/pkg/queue/health/probe_test.go
@@ -17,17 +17,22 @@ limitations under the License.
 package health
 
 import (
+	"context"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
 	"strings"
+	"sync/atomic"
 	"testing"
 	"time"
 
 	"github.com/google/go-cmp/cmp"
+	"golang.org/x/net/http2"
+	"golang.org/x/net/http2/h2c"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	network "knative.dev/networking/pkg"
+	apicfg "knative.dev/serving/pkg/apis/config"
 )
 
 func TestTCPProbe(t *testing.T) {
@@ -51,26 +56,23 @@ func TestTCPProbe(t *testing.T) {
 }
 
 func TestHTTPProbeSuccess(t *testing.T) {
-	var gotHeader corev1.HTTPHeader
-	var gotKubeletHeader bool
+	var (
+		gotHeader        corev1.HTTPHeader
+		gotKubeletHeader bool
+	)
 	expectedHeader := corev1.HTTPHeader{
 		Name:  "Testkey",
 		Value: "Testval",
 	}
 	var gotPath string
-	expectedPath := "/health"
+	const expectedPath = "/health"
 	server := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
-		for headerKey, headerValue := range r.Header {
-			// Filtering for expectedHeader.TestKey to avoid other HTTP probe headers
-			if expectedHeader.Name == headerKey {
-				gotHeader = corev1.HTTPHeader{Name: headerKey, Value: headerValue[0]}
-			}
-
-			if headerKey == "User-Agent" && strings.HasPrefix(headerValue[0], network.KubeProbeUAPrefix) {
-				gotKubeletHeader = true
-			}
+		if v := r.Header.Get(expectedHeader.Name); v != "" {
+			gotHeader = corev1.HTTPHeader{Name: expectedHeader.Name, Value: v}
+		}
+		if v := r.Header.Get(network.UserAgentKey); strings.HasPrefix(v, network.KubeProbeUAPrefix) {
+			gotKubeletHeader = true
 		}
-
 		gotPath = r.URL.Path
 		w.WriteHeader(http.StatusOK)
 	})
@@ -83,26 +85,118 @@ func TestHTTPProbeSuccess(t *testing.T) {
 		Timeout:       time.Second,
 		HTTPGetAction: action,
 	}
+
 	// Connecting to the server should work
-	if err := HTTPProbe(config); err != nil {
+	if err := HTTPProbe(context.Background(), config); err != nil {
 		t.Error("Expected probe to succeed but it failed with", err)
 	}
 	if d := cmp.Diff(gotHeader, expectedHeader); d != "" {
-		t.Error("Expected probe headers to match but got", d)
+		t.Error("Expected probe headers to match; diff:\n", d)
 	}
 	if !gotKubeletHeader {
 		t.Error("Expected kubelet probe header to be added to request")
 	}
 	if !cmp.Equal(gotPath, expectedPath) {
-		t.Errorf("Expected %s path to match but got %s", expectedPath, gotPath)
+		t.Errorf("Path = %s, want: %s", gotPath, expectedPath)
 	}
-	// Close the server so probing fails afterwards
+	// Close the server so probing fails afterwards.
 	server.Close()
-	if err := HTTPProbe(config); err == nil {
+	if err := HTTPProbe(context.Background(), config); err == nil {
 		t.Error("Expected probe to fail but it didn't")
 	}
 }
 
+func TestHTTPProbeNoAutoHTTP2IfDisabled(t *testing.T) {
+	ctx := context.Background()
+	cfg := apicfg.FromContextOrDefaults(ctx)
+	cfg.Features.AutoDetectHTTP2 = apicfg.Disabled
+	ctx = apicfg.ToContext(ctx, cfg)
+
+	h2cHeaders := map[string]string{
+		"Connection": "Upgrade, HTTP2-Settings",
+		"Upgrade":    "h2c",
+	}
+	expectedPath := "/health"
+	var callCount int32 = 0
+
+	server := newH2cTestServer(t, func(w http.ResponseWriter, r *http.Request) {
+		count := atomic.AddInt32(&callCount, 1)
+		if count == 1 {
+			// This is the h2c handshake, we won't do anything.
+			for key, value := range h2cHeaders {
+				if r.Header.Get(key) == value {
+					t.Errorf("Key %v = %v was NOT supposed to be present in the request", key, value)
+				}
+			}
+		} else {
+			t.Errorf("Handler should only have one calls, this is call %d", count)
+		}
+	})
+
+	action := newHTTPGetAction(t, server.URL)
+	action.Path = expectedPath
+
+	config := HTTPProbeConfigOptions{
+		Timeout:       time.Second,
+		HTTPGetAction: action,
+	}
+	if err := HTTPProbe(ctx, config); err != nil {
+		t.Error("Expected probe to succeed but it failed with", err)
+	}
+	if count := atomic.LoadInt32(&callCount); count != 1 {
+		t.Errorf("Unexpected call count %d", count)
+	}
+}
+
+func TestHTTPProbeAutoHTTP2(t *testing.T) {
+	ctx := context.Background()
+	cfg := apicfg.FromContextOrDefaults(ctx)
+	cfg.Features.AutoDetectHTTP2 = apicfg.Enabled
+	ctx = apicfg.ToContext(ctx, cfg)
+
+	h2cHeaders := map[string]string{
+		"Connection": "Upgrade, HTTP2-Settings",
+		"Upgrade":    "h2c",
+	}
+	expectedPath := "/health"
+	var callCount int32 = 0
+
+	server := newH2cTestServer(t, func(w http.ResponseWriter, r *http.Request) {
+		count := atomic.AddInt32(&callCount, 1)
+		if count == 1 {
+			// This is the h2c handshake, we won't do anything.
+			for key, value := range h2cHeaders {
+				if r.Header.Get(key) != value {
+					t.Errorf("Key %v = %v was supposed to be present in the request", key, value)
+				}
+			}
+		} else if count == 2 {
+			// This is the expected call. It should not have any of the h2c upgrade stuff, since the h2c test server will handle that for us.
+			for key, value := range h2cHeaders {
+				if r.Header.Get(key) == value {
+					t.Errorf("Key %v = %v was NOT supposed to be present in the request", key, value)
+				}
+			}
+		} else {
+			t.Errorf("Handler should only have two calls, this is call %d", count)
+		}
+	})
+
+	action := newHTTPGetAction(t, server.URL)
+	action.Path = expectedPath
+
+	config := HTTPProbeConfigOptions{
+		Timeout:       time.Second,
+		HTTPGetAction: action,
+	}
+	if err := HTTPProbe(ctx, config); err != nil {
+		t.Error("Expected probe to succeed but it failed with", err)
+	}
+	if count := atomic.LoadInt32(&callCount); count != 2 {
+		t.Errorf("Unexpected call count %d", count)
+	}
+}
+
 func TestHTTPSchemeProbeSuccess(t *testing.T) {
 	server := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
 		w.WriteHeader(http.StatusOK)
@@ -114,13 +208,13 @@ func TestHTTPSchemeProbeSuccess(t *testing.T) {
 	}
 
 	// Connecting to the server should work
-	if err := HTTPProbe(config); err != nil {
+	if err := HTTPProbe(context.Background(), config); err != nil {
 		t.Error("Expected probe to succeed but failed with error", err)
 	}
 
 	// Close the server so probing fails afterwards
 	server.Close()
-	if err := HTTPProbe(config); err == nil {
+	if err := HTTPProbe(context.Background(), config); err == nil {
 		t.Error("Expected probe to fail but it didn't")
 	}
 }
@@ -139,7 +233,7 @@ func TestHTTPProbeTimeoutFailure(t *testing.T) {
 		Timeout:       1 * time.Millisecond,
 		HTTPGetAction: newHTTPGetAction(t, server.URL),
 	}
-	if err := HTTPProbe(config); err == nil {
+	if err := HTTPProbe(context.Background(), config); err == nil {
 		t.Error("Expected probe to fail but it succeeded")
 	}
 }
@@ -153,7 +247,7 @@ func TestHTTPProbeResponseStatusCodeFailure(t *testing.T) {
 		Timeout:       time.Second,
 		HTTPGetAction: newHTTPGetAction(t, server.URL),
 	}
-	if err := HTTPProbe(config); err == nil {
+	if err := HTTPProbe(context.Background(), config); err == nil {
 		t.Error("Expected probe to fail but it succeeded")
 	}
 }
@@ -162,7 +256,7 @@ func TestHTTPProbeResponseErrorFailure(t *testing.T) {
 	config := HTTPProbeConfigOptions{
 		HTTPGetAction: newHTTPGetAction(t, "http://localhost:0"),
 	}
-	if err := HTTPProbe(config); err == nil {
+	if err := HTTPProbe(context.Background(), config); err == nil {
 		t.Error("Expected probe to fail but it succeeded")
 	}
 }
@@ -202,6 +296,17 @@ func TestIsHTTPProbeShuttingDown(t *testing.T) {
 	}
 }
 
+func newH2cTestServer(t *testing.T, handler http.HandlerFunc) *httptest.Server {
+	h2s := &http2.Server{}
+	t.Helper()
+	server := httptest.NewServer(h2c.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		handler(w, r)
+	}), h2s))
+	t.Cleanup(server.Close)
+
+	return server
+}
+
 func newTestServer(t *testing.T, handler http.HandlerFunc) *httptest.Server {
 	t.Helper()
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -217,7 +322,7 @@ func newHTTPGetAction(t *testing.T, serverURL string) *corev1.HTTPGetAction {
 
 	u, err := url.Parse(serverURL)
 	if err != nil {
-		t.Fatalf("Error parsing URL")
+		t.Fatal("Error parsing URL")
 	}
 
 	return &corev1.HTTPGetAction{
diff --git a/pkg/queue/readiness/probe_test.go b/pkg/queue/readiness/probe_test.go
index 6c64599df..fccf5dc54 100644
--- a/pkg/queue/readiness/probe_test.go
+++ b/pkg/queue/readiness/probe_test.go
@@ -18,6 +18,7 @@ package readiness
 
 import (
 	"bytes"
+	"context"
 	"errors"
 	"fmt"
 	"net"
@@ -50,7 +51,7 @@ func TestNewProbe(t *testing.T) {
 		},
 	}
 
-	p := NewProbe(v1p)
+	p := NewProbe(context.Background(), v1p)
 
 	if diff := cmp.Diff(p.Probe, v1p); diff != "" {
 		t.Error("NewProbe (-want, +got) =", diff)
@@ -62,7 +63,7 @@ func TestNewProbe(t *testing.T) {
 }
 
 func TestTCPFailure(t *testing.T) {
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    1,
 		TimeoutSeconds:   1,
 		SuccessThreshold: 1,
@@ -81,7 +82,7 @@ func TestTCPFailure(t *testing.T) {
 }
 
 func TestAggressiveFailureOnlyLogsOnce(t *testing.T) {
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    0, // Aggressive probe.
 		TimeoutSeconds:   1,
 		SuccessThreshold: 1,
@@ -120,7 +121,7 @@ func TestAggressiveFailureNotLoggedOnSuccess(t *testing.T) {
 		w.WriteHeader(http.StatusInternalServerError)
 	})
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    0, // Aggressive probe.
 		TimeoutSeconds:   1,
 		SuccessThreshold: 1,
@@ -144,7 +145,7 @@ func TestAggressiveFailureNotLoggedOnSuccess(t *testing.T) {
 }
 
 func TestEmptyHandler(t *testing.T) {
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    1,
 		TimeoutSeconds:   1,
 		SuccessThreshold: 1,
@@ -158,7 +159,7 @@ func TestEmptyHandler(t *testing.T) {
 }
 
 func TestExecHandler(t *testing.T) {
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    1,
 		TimeoutSeconds:   1,
 		SuccessThreshold: 1,
@@ -179,7 +180,7 @@ func TestTCPSuccess(t *testing.T) {
 		w.WriteHeader(http.StatusOK)
 	})
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    1,
 		TimeoutSeconds:   2,
 		SuccessThreshold: 1,
@@ -198,7 +199,7 @@ func TestTCPSuccess(t *testing.T) {
 }
 
 func TestHTTPFailureToConnect(t *testing.T) {
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    1,
 		TimeoutSeconds:   2,
 		SuccessThreshold: 1,
@@ -222,7 +223,7 @@ func TestHTTPBadResponse(t *testing.T) {
 		w.WriteHeader(http.StatusBadRequest)
 	})
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    1,
 		TimeoutSeconds:   5,
 		SuccessThreshold: 1,
@@ -246,7 +247,7 @@ func TestHTTPSuccess(t *testing.T) {
 		w.WriteHeader(http.StatusOK)
 	})
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    1,
 		TimeoutSeconds:   5,
 		SuccessThreshold: 1,
@@ -274,7 +275,7 @@ func TestHTTPManyParallel(t *testing.T) {
 		}
 	})
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    1,
 		TimeoutSeconds:   5,
 		SuccessThreshold: 1,
@@ -320,7 +321,7 @@ func TestHTTPTimeout(t *testing.T) {
 		w.WriteHeader(http.StatusOK)
 	})
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    1,
 		TimeoutSeconds:   1,
 		SuccessThreshold: 1,
@@ -345,7 +346,7 @@ func TestHTTPSuccessWithDelay(t *testing.T) {
 		w.WriteHeader(http.StatusOK)
 	})
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    1,
 		TimeoutSeconds:   2,
 		SuccessThreshold: 1,
@@ -375,7 +376,7 @@ func TestKnHTTPSuccessWithRetry(t *testing.T) {
 		w.WriteHeader(http.StatusOK)
 	})
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    0,
 		TimeoutSeconds:   0,
 		SuccessThreshold: 1,
@@ -403,7 +404,7 @@ func TestKnHTTPSuccessWithThreshold(t *testing.T) {
 		w.WriteHeader(http.StatusOK)
 	})
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    0,
 		TimeoutSeconds:   0,
 		SuccessThreshold: threshold,
@@ -440,7 +441,7 @@ func TestKnHTTPSuccessWithThresholdAndFailure(t *testing.T) {
 		w.WriteHeader(http.StatusOK)
 	})
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    0,
 		TimeoutSeconds:   0,
 		SuccessThreshold: threshold,
@@ -477,7 +478,7 @@ func TestKnHTTPTimeoutFailure(t *testing.T) {
 		w.WriteHeader(http.StatusOK)
 	})
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    0,
 		TimeoutSeconds:   0,
 		SuccessThreshold: 1,
@@ -507,7 +508,7 @@ func TestKnTCPProbeSuccess(t *testing.T) {
 	defer listener.Close()
 	addr := listener.Addr().(*net.TCPAddr)
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    0,
 		TimeoutSeconds:   0,
 		SuccessThreshold: 1,
@@ -526,7 +527,7 @@ func TestKnTCPProbeSuccess(t *testing.T) {
 }
 
 func TestKnUnimplementedProbe(t *testing.T) {
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    0,
 		TimeoutSeconds:   0,
 		SuccessThreshold: 1,
@@ -540,7 +541,7 @@ func TestKnUnimplementedProbe(t *testing.T) {
 }
 
 func TestKnTCPProbeFailure(t *testing.T) {
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    0,
 		TimeoutSeconds:   0,
 		SuccessThreshold: 1,
@@ -569,7 +570,7 @@ func TestKnTCPProbeSuccessWithThreshold(t *testing.T) {
 	defer listener.Close()
 	addr := listener.Addr().(*net.TCPAddr)
 
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    0,
 		TimeoutSeconds:   0,
 		SuccessThreshold: 3,
@@ -599,7 +600,7 @@ func TestKnTCPProbeSuccessThresholdIncludesFailure(t *testing.T) {
 	addr := listener.Addr().(*net.TCPAddr)
 
 	var successThreshold int32 = 3
-	pb := NewProbe(&corev1.Probe{
+	pb := NewProbe(context.Background(), &corev1.Probe{
 		PeriodSeconds:    0,
 		TimeoutSeconds:   0,
 		SuccessThreshold: successThreshold,
diff --git a/pkg/reconciler/route/reconcile_resources_test.go b/pkg/reconciler/route/reconcile_resources_test.go
index 767b3a903..51d35b2af 100644
--- a/pkg/reconciler/route/reconcile_resources_test.go
+++ b/pkg/reconciler/route/reconcile_resources_test.go
@@ -165,12 +165,13 @@ func TestReconcileIngressUpdateReenqueueRollout(t *testing.T) {
 			ing = updated
 
 			// Advance the time by 5s.
-			fakeClock.SetTime(fakeClock.Now().Add(5 * time.Second))
+			const drift = 5
+			fakeClock.SetTime(fakeClock.Now().Add(drift * time.Second))
 
-			// 5s of the duration already spent.
-			totalDuration := float64(rd - 5)
+			// Drift seconds of the duration already spent.
+			totalDuration := float64(rd - drift)
 			// Step count is minimum of % points of traffic allocated to the config (1% already shifted).
-			steps := math.Min(totalDuration/5, allocatedTraffic-1)
+			steps := math.Min(totalDuration/drift, allocatedTraffic-1)
 			stepSize := math.Max(1, math.Round((allocatedTraffic-1)/steps)) // we round the step size.
 			stepDuration := time.Duration(int(totalDuration * float64(time.Second) / steps))
 
@@ -270,7 +271,7 @@ func TestReconcileIngressUpdateReenqueueRolloutAnnotation(t *testing.T) {
 				wasReenqueued = true
 				duration = d
 			}
-			ctx := updateContext(baseCtx, 311) // This should be ignored, since we set annotation.
+			ctx := updateContext(baseCtx, 311 /*rolloutDuration*/) // This should be ignored, since we set annotation.
 			_, ro, err := reconciler.reconcileIngress(ctx, r, tc, tls, "foo-ingress-class")
 			if err != nil {
 				t.Fatal("Unexpected error:", err)
@@ -326,12 +327,13 @@ func TestReconcileIngressUpdateReenqueueRolloutAnnotation(t *testing.T) {
 			ing = updated
 
 			// Advance the time by 5s.
-			fakeClock.SetTime(fakeClock.Now().Add(5 * time.Second))
+			const drift = 5
+			fakeClock.SetTime(fakeClock.Now().Add(drift * time.Second))
 
-			// 5s of the duration already spent.
-			totalDuration := float64(rd - 5)
+			// Drift seconds of the duration already spent.
+			totalDuration := float64(rd - drift)
 			// Step count is minimum of % points of traffic allocated to the config (1% already shifted).
-			steps := math.Min(totalDuration/5, allocatedTraffic-1)
+			steps := math.Min(totalDuration/drift, allocatedTraffic-1)
 			stepSize := math.Max(1, math.Round((allocatedTraffic-1)/steps)) // we round the step size.
 			stepDuration := time.Duration(int(totalDuration * float64(time.Second) / steps))
 
diff --git a/pkg/reconciler/route/resources/service_test.go b/pkg/reconciler/route/resources/service_test.go
index 7a61b73aa..df4a5699e 100644
--- a/pkg/reconciler/route/resources/service_test.go
+++ b/pkg/reconciler/route/resources/service_test.go
@@ -24,9 +24,7 @@ import (
 
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/util/intstr"
-	"k8s.io/apimachinery/pkg/util/sets"
 
 	network "knative.dev/networking/pkg"
 	"knative.dev/networking/pkg/apis/networking"
@@ -356,13 +354,6 @@ func TestMakeK8sPlaceholderService(t *testing.T) {
 	}
 }
 
-func TestSelectorFromRoute(t *testing.T) {
-	selector := SelectorFromRoute(r)
-	if !selector.Matches(labels.Set{serving.RouteLabelKey: r.Name}) {
-		t.Errorf("Unexpected labels in selector")
-	}
-}
-
 func testConfig() *config.Config {
 	return &config.Config{
 		Domain: &config.Domain{
@@ -390,41 +381,3 @@ func testConfig() *config.Config {
 		},
 	}
 }
-
-func TestGetNames(t *testing.T) {
-	tests := []struct {
-		name     string
-		services []*corev1.Service
-		want     sets.String
-	}{
-		{
-			name: "nil services",
-			want: sets.String{},
-		},
-		{
-			name: "multiple services",
-			services: []*corev1.Service{
-				{ObjectMeta: metav1.ObjectMeta{Name: "svc1"}},
-				{ObjectMeta: metav1.ObjectMeta{Name: "svc2"}},
-				{ObjectMeta: metav1.ObjectMeta{Name: "svc3"}},
-			},
-			want: sets.NewString("svc1", "svc2", "svc3"),
-		},
-		{
-			name: "duplicate services",
-			services: []*corev1.Service{
-				{ObjectMeta: metav1.ObjectMeta{Name: "svc1"}},
-				{ObjectMeta: metav1.ObjectMeta{Name: "svc1"}},
-				{ObjectMeta: metav1.ObjectMeta{Name: "svc1"}},
-			},
-			want: sets.NewString("svc1"),
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := GetNames(tt.services); !cmp.Equal(got, tt.want) {
-				t.Error("GetNames() (-want, +got) =", cmp.Diff(tt.want, got))
-			}
-		})
-	}
-}
diff --git a/pkg/reconciler/route/route_test.go b/pkg/reconciler/route/route_test.go
index b8b451c01..efe05a5d9 100644
--- a/pkg/reconciler/route/route_test.go
+++ b/pkg/reconciler/route/route_test.go
@@ -82,9 +82,9 @@ func testConfiguration() *v1.Configuration {
 	}
 }
 
-func revisionForConfig(config *v1.Configuration) *v1.Revision {
+func testRevision() *v1.Revision {
 	return Revision(testNamespace, "p-deadbeef", func(r *v1.Revision) {
-		r.Spec = *config.Spec.GetTemplate().Spec.DeepCopy()
+		r.Spec = *(&v1.ConfigurationSpec{}).GetTemplate().Spec.DeepCopy()
 	}, MarkRevisionReady, WithK8sServiceName)
 }
 
@@ -97,7 +97,7 @@ func newTestSetup(t *testing.T, opts ...reconcilerOption) (
 
 	ctx, cf, informers = SetupFakeContextWithCancel(t)
 	configMapWatcher = &configmap.ManualWatcher{Namespace: system.Namespace()}
-	ctrl = newControllerWithClock(ctx, configMapWatcher, &clock.RealClock{}, opts...)
+	ctrl = newController(ctx, configMapWatcher, &clock.RealClock{}, opts...)
 
 	for _, cfg := range []*corev1.ConfigMap{{
 		ObjectMeta: metav1.ObjectMeta{
@@ -284,7 +284,7 @@ func TestCreateRouteForOneReserveRevision(t *testing.T) {
 			t.Errorf("<-Events = %s, wanted %s", got, want)
 		}
 	case <-time.After(3 * time.Second):
-		t.Error("timed out waiting for expected events.")
+		t.Error("Timed out waiting for expected events.")
 	}
 	select {
 	case got := <-fakeRecorder.Events:
@@ -293,7 +293,7 @@ func TestCreateRouteForOneReserveRevision(t *testing.T) {
 			t.Errorf("<-Events = %s, wanted prefix %s", got, wantPrefix)
 		}
 	case <-time.After(3 * time.Second):
-		t.Error("timed out waiting for expected events.")
+		t.Error("Timed out waiting for expected events.")
 	}
 }
 
@@ -315,7 +315,7 @@ func TestCreateRouteWithMultipleTargets(t *testing.T) {
 	// A configuration and associated revision. Normally the revision would be
 	// created by the configuration reconciler.
 	config := testConfiguration()
-	cfgrev := revisionForConfig(config)
+	cfgrev := testRevision()
 	config.Status.SetLatestCreatedRevisionName(cfgrev.Name)
 	config.Status.SetLatestReadyRevisionName(cfgrev.Name)
 	fakeservingclient.Get(ctx).ServingV1().Configurations(testNamespace).Create(ctx, config, metav1.CreateOptions{})
@@ -431,7 +431,7 @@ func TestCreateRouteWithOneTargetReserve(t *testing.T) {
 	// A configuration and associated revision. Normally the revision would be
 	// created by the configuration reconciler.
 	config := testConfiguration()
-	cfgrev := revisionForConfig(config)
+	cfgrev := testRevision()
 	config.Status.SetLatestCreatedRevisionName(cfgrev.Name)
 	config.Status.SetLatestReadyRevisionName(cfgrev.Name)
 	fakeservingclient.Get(ctx).ServingV1().Configurations(testNamespace).Create(ctx, config, metav1.CreateOptions{})
@@ -545,7 +545,7 @@ func TestCreateRouteWithDuplicateTargets(t *testing.T) {
 	// A configuration and associated revision. Normally the revision would be
 	// created by the configuration reconciler.
 	config := testConfiguration()
-	cfgrev := revisionForConfig(config)
+	cfgrev := testRevision()
 	config.Status.SetLatestCreatedRevisionName(cfgrev.Name)
 	config.Status.SetLatestReadyRevisionName(cfgrev.Name)
 	fakeservingclient.Get(ctx).ServingV1().Configurations(testNamespace).Create(ctx, config, metav1.CreateOptions{})
@@ -749,7 +749,6 @@ func TestCreateRouteWithDuplicateTargets(t *testing.T) {
 	}
 
 	if diff := cmp.Diff(expectedSpec, ci.Spec); diff != "" {
-		fmt.Printf("%+v\n", ci.Spec)
 		t.Error("Unexpected rule spec diff (-want +got):", diff)
 	}
 }
@@ -765,7 +764,7 @@ func TestCreateRouteWithNamedTargets(t *testing.T) {
 	// A configuration and associated revision. Normally the revision would be
 	// created by the configuration reconciler.
 	config := testConfiguration()
-	cfgrev := revisionForConfig(config)
+	cfgrev := testRevision()
 	config.Status.SetLatestCreatedRevisionName(cfgrev.Name)
 	config.Status.SetLatestReadyRevisionName(cfgrev.Name)
 	fakeservingclient.Get(ctx).ServingV1().Configurations(testNamespace).Create(ctx, config, metav1.CreateOptions{})
@@ -832,9 +831,7 @@ func TestCreateRouteWithNamedTargets(t *testing.T) {
 			},
 			Visibility: v1alpha1.IngressVisibilityClusterLocal,
 		}, {
-			Hosts: []string{
-				domain,
-			},
+			Hosts: []string{domain},
 			HTTP: &v1alpha1.HTTPIngressRuleValue{
 				Paths: []v1alpha1.HTTPIngressPath{{
 					Splits: []v1alpha1.IngressBackendSplit{{
@@ -887,9 +884,7 @@ func TestCreateRouteWithNamedTargets(t *testing.T) {
 			},
 			Visibility: v1alpha1.IngressVisibilityClusterLocal,
 		}, {
-			Hosts: []string{
-				"bar-test-route.test.test-domain.dev",
-			},
+			Hosts: []string{"bar-test-route.test.test-domain.dev"},
 			HTTP: &v1alpha1.HTTPIngressRuleValue{
 				Paths: []v1alpha1.HTTPIngressPath{{
 					Splits: []v1alpha1.IngressBackendSplit{{
@@ -931,9 +926,7 @@ func TestCreateRouteWithNamedTargets(t *testing.T) {
 			},
 			Visibility: v1alpha1.IngressVisibilityClusterLocal,
 		}, {
-			Hosts: []string{
-				"foo-test-route.test.test-domain.dev",
-			},
+			Hosts: []string{"foo-test-route.test.test-domain.dev"},
 			HTTP: &v1alpha1.HTTPIngressRuleValue{
 				Paths: []v1alpha1.HTTPIngressPath{{
 					Splits: []v1alpha1.IngressBackendSplit{{
@@ -980,7 +973,7 @@ func TestCreateRouteWithNamedTargetsAndTagBasedRouting(t *testing.T) {
 	// A configuration and associated revision. Normally the revision would be
 	// created by the configuration reconciler.
 	config := testConfiguration()
-	cfgrev := revisionForConfig(config)
+	cfgrev := testRevision()
 	config.Status.SetLatestCreatedRevisionName(cfgrev.Name)
 	config.Status.SetLatestReadyRevisionName(cfgrev.Name)
 	fakeservingclient.Get(ctx).ServingV1().Configurations(testNamespace).Create(ctx, config, metav1.CreateOptions{})
@@ -1089,9 +1082,7 @@ func TestCreateRouteWithNamedTargetsAndTagBasedRouting(t *testing.T) {
 			},
 			Visibility: v1alpha1.IngressVisibilityClusterLocal,
 		}, {
-			Hosts: []string{
-				domain,
-			},
+			Hosts: []string{domain},
 			HTTP: &v1alpha1.HTTPIngressRuleValue{
 				Paths: []v1alpha1.HTTPIngressPath{{
 					Headers: map[string]v1alpha1.HeaderMatch{
@@ -1240,9 +1231,7 @@ func TestCreateRouteWithNamedTargetsAndTagBasedRouting(t *testing.T) {
 			},
 			Visibility: v1alpha1.IngressVisibilityClusterLocal,
 		}, {
-			Hosts: []string{
-				"foo-test-route.test.test-domain.dev",
-			},
+			Hosts: []string{"foo-test-route.test.test-domain.dev"},
 			HTTP: &v1alpha1.HTTPIngressRuleValue{
 				Paths: []v1alpha1.HTTPIngressPath{{
 					Splits: []v1alpha1.IngressBackendSplit{{
@@ -1267,7 +1256,6 @@ func TestCreateRouteWithNamedTargetsAndTagBasedRouting(t *testing.T) {
 	}
 
 	if diff := cmp.Diff(expectedSpec, ci.Spec); diff != "" {
-		fmt.Printf("%+v\n", ci.Spec)
 		t.Error("Unexpected rule spec diff (-want +got):", diff)
 	}
 }
diff --git a/pkg/reconciler/route/table_test.go b/pkg/reconciler/route/table_test.go
index 6f48d08a3..6637c273f 100644
--- a/pkg/reconciler/route/table_test.go
+++ b/pkg/reconciler/route/table_test.go
@@ -61,11 +61,12 @@ import (
 	. "knative.dev/serving/pkg/testing/v1"
 )
 
-const TestIngressClass = "ingress-class-foo"
+const testIngressClass = "ingress-class-foo"
 
-var fakeCurTime = time.Unix(1e9, 0)
-
-var rolloutDurationKey = struct{}{}
+var (
+	fakeCurTime        = time.Unix(1e9, 0)
+	rolloutDurationKey = struct{}{}
+)
 
 // This is heavily based on the way the OpenShift Ingress controller tests its reconciliation method.
 func TestReconcile(t *testing.T) {
@@ -237,7 +238,7 @@ func TestReconcile(t *testing.T) {
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithK8sServiceName),
 		},
 		WantCreates: []runtime.Object{
-			ingressWithClass(
+			simpleIngress(
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithURL, WithRouteUID("12-34")),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -251,7 +252,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
-				"custom-ingress-class",
+				withClass("custom-ingress-class"),
 			),
 			simplePlaceholderK8sService(
 				getContext(),
@@ -341,7 +342,7 @@ func TestReconcile(t *testing.T) {
 			cfg("default", "config",
 				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -355,6 +356,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 		},
 		WantCreates: []runtime.Object{
@@ -388,7 +390,7 @@ func TestReconcile(t *testing.T) {
 			cfg("default", "config",
 				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -407,6 +409,7 @@ func TestReconcile(t *testing.T) {
 				}, {
 					RevisionName: "config-00001", Percent: 1,
 				}}, fakeCurTime.Add(-3*time.Second)),
+				withReadyIngress,
 			),
 		},
 		WantCreates: []runtime.Object{
@@ -453,7 +456,9 @@ func TestReconcile(t *testing.T) {
 						// StepDuration is 3, and so next step is `now` + 3.
 						r.Configurations[0].StepParams.NextStepTime = fakeCurTime.Add(3 * time.Second).UnixNano()
 					},
-				)),
+				),
+				withReadyIngress,
+			),
 		}, {
 			Object: simpleK8sService(
 				Route("default", "becomes-ready", WithConfigTarget("config")),
@@ -489,7 +494,7 @@ func TestReconcile(t *testing.T) {
 			cfg("default", "config",
 				WithConfigGeneration(1), WithLatestCreated("config-00001"), WithLatestReady("config-00001")),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithK8sServiceName),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -514,6 +519,7 @@ func TestReconcile(t *testing.T) {
 						StartTime:    fakeCurTime.Add(-time.Hour).UnixNano(),
 						StepDuration: int64(time.Second),
 					})),
+				withReadyIngress,
 			),
 		},
 		WantCreates: []runtime.Object{
@@ -521,7 +527,7 @@ func TestReconcile(t *testing.T) {
 		},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
 			// ingress should be updated with the new rollout data.
-			Object: simpleReadyIngress(
+			Object: simpleIngress(
 				Route("default", "becomes-ready", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -534,7 +540,9 @@ func TestReconcile(t *testing.T) {
 							},
 						}},
 					},
-				}),
+				},
+				withReadyIngress,
+			),
 		}, {
 			Object: simpleK8sService(
 				Route("default", "becomes-ready", WithConfigTarget("config")),
@@ -676,7 +684,7 @@ func TestReconcile(t *testing.T) {
 				WithConfigLabel("serving.knative.dev/route", "steady-state"),
 			),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "steady-state", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -690,6 +698,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 			simpleK8sService(Route("default", "steady-state", WithConfigTarget("config")),
 				WithExternalName(pkgnet.GetServiceHostname("private-istio-ingressgateway", "istio-system"))),
@@ -752,7 +761,7 @@ func TestReconcile(t *testing.T) {
 				WithConfigLabel("serving.knative.dev/route", "different-domain"),
 			),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithK8sServiceName),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "different-domain", WithConfigTarget("config"),
 					WithAnotherDomain),
 				&traffic.Config{
@@ -767,11 +776,12 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 			simpleK8sService(Route("default", "different-domain", WithConfigTarget("config"))),
 		},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
-			Object: simpleReadyIngress(
+			Object: simpleIngress(
 				Route("default", "different-domain", WithConfigTarget("config"),
 					WithAnotherDomain, WithRouteGeneration(1), WithRouteLabel(map[string]string{"app": "prod"})),
 				&traffic.Config{
@@ -787,6 +797,7 @@ func TestReconcile(t *testing.T) {
 					},
 				},
 				WithHosts(1, "different-domain.default.another-example.com"),
+				withReadyIngress,
 			),
 		}},
 		Key: "default/different-domain",
@@ -808,7 +819,7 @@ func TestReconcile(t *testing.T) {
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithK8sServiceName),
 			// This is the name of the new revision we're referencing above.
 			rev("default", "config", 2, WithInitRevConditions, WithRevName("config-00002")),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "new-latest-created", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -822,6 +833,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 			simpleK8sService(Route("default", "new-latest-created", WithConfigTarget("config"))),
 		},
@@ -846,7 +858,7 @@ func TestReconcile(t *testing.T) {
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithK8sServiceName),
 			// This is the name of the new revision we're referencing above.
 			rev("default", "config", 2, MarkRevisionReady, WithRevName("config-00002"), WithK8sServiceName),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "new-latest-ready", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -860,6 +872,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 			simpleK8sService(Route("default", "new-latest-ready", WithConfigTarget("config"))),
 		},
@@ -896,7 +909,9 @@ func TestReconcile(t *testing.T) {
 					RevisionName: "config-00001", Percent: 99,
 				}, {
 					RevisionName: "config-00002", Percent: 1,
-				}}, fakeCurTime)),
+				}}, fakeCurTime),
+				withReadyIngress,
+			),
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "new-latest-ready", WithConfigTarget("config"),
@@ -932,7 +947,7 @@ func TestReconcile(t *testing.T) {
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithK8sServiceName),
 			// This is the name of the new revision we're referencing above.
 			rev("default", "config", 2, MarkRevisionReady, WithRevName("config-00002"), WithK8sServiceName),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "new-latest-ready", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -946,12 +961,13 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 			simpleK8sService(Route("default", "new-latest-ready", WithConfigTarget("config"))),
 		},
 		// A new LatestReadyRevisionName on the Configuration should result in the new Revision being rolled out.
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
-			Object: simpleReadyIngress(
+			Object: simpleIngress(
 				Route("default", "new-latest-ready", WithConfigTarget("config"), WithURL, WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -965,7 +981,9 @@ func TestReconcile(t *testing.T) {
 							},
 						}},
 					},
-				}),
+				},
+				withReadyIngress,
+			),
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "new-latest-ready", WithConfigTarget("config"),
@@ -1126,7 +1144,7 @@ func TestReconcile(t *testing.T) {
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithK8sServiceName),
 			// This is the name of the new revision we're referencing above.
 			rev("default", "config", 2, MarkRevisionReady, WithRevName("config-00002"), WithK8sServiceName),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "update-ci-failure", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -1140,11 +1158,12 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 			simpleK8sService(Route("default", "update-ci-failure", WithConfigTarget("config"))),
 		},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
-			Object: simpleReadyIngress(
+			Object: simpleIngress(
 				Route("default", "update-ci-failure", WithConfigTarget("config"), WithURL, WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -1158,7 +1177,9 @@ func TestReconcile(t *testing.T) {
 							},
 						}},
 					},
-				}),
+				},
+				withReadyIngress,
+			),
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "update-ci-failure", WithConfigTarget("config"),
@@ -1191,7 +1212,7 @@ func TestReconcile(t *testing.T) {
 				WithConfigLabel("serving.knative.dev/route", "svc-mutation"),
 			),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "svc-mutation", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -1205,6 +1226,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 			simpleK8sService(Route("default", "svc-mutation",
 				WithConfigTarget("config")), MutateK8sService),
@@ -1235,7 +1257,7 @@ func TestReconcile(t *testing.T) {
 				WithConfigLabel("serving.knative.dev/route", "svc-mutation"),
 			),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "svc-mutation", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -1249,6 +1271,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 			simpleK8sService(Route("default", "svc-mutation",
 				WithConfigTarget("config")), MutateK8sService),
@@ -1280,7 +1303,7 @@ func TestReconcile(t *testing.T) {
 				WithConfigLabel("serving.knative.dev/route", "cluster-ip"),
 			),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "cluster-ip", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -1294,6 +1317,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 			simpleK8sService(Route("default", "cluster-ip",
 				WithConfigTarget("config")), WithClusterIP("127.0.0.1")),
@@ -1320,7 +1344,7 @@ func TestReconcile(t *testing.T) {
 				WithConfigLabel("serving.knative.dev/route", "external-name"),
 			),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "external-name", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -1334,6 +1358,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 			simpleK8sService(Route("default", "external-name",
 				WithConfigTarget("config")), WithExternalName("this-is-the-wrong-name")),
@@ -1359,7 +1384,7 @@ func TestReconcile(t *testing.T) {
 				WithConfigLabel("serving.knative.dev/route", "ingress-mutation"),
 			),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001"), WithK8sServiceName),
-			mutateIngress(simpleReadyIngress(
+			mutateIngress(simpleIngress(
 				Route("default", "ingress-mutation", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -1371,11 +1396,12 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			)),
 			simpleK8sService(Route("default", "ingress-mutation", WithConfigTarget("config"))),
 		},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
-			Object: simpleReadyIngress(
+			Object: simpleIngress(
 				Route("default", "ingress-mutation", WithConfigTarget("config"), WithURL, WithRouteGeneration(1)),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -1389,6 +1415,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 		}},
 		Key: "default/ingress-mutation",
@@ -1448,7 +1475,7 @@ func TestReconcile(t *testing.T) {
 				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(
+			simpleIngress(
 				Route("default", "pinned-becomes-ready", WithConfigTarget("config"),
 					WithURL),
 				&traffic.Config{
@@ -1461,6 +1488,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
@@ -1740,7 +1768,7 @@ func TestReconcile(t *testing.T) {
 				WithConfigGeneration(2020), WithLatestCreated("green-02021"), WithLatestReady("green-02020")),
 			rev("default", "blue", 1, MarkRevisionReady, WithRevName("blue-00001"), WithK8sServiceName),
 			rev("default", "green", 2020, MarkRevisionReady, WithRevName("green-02020"), WithK8sServiceName),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "switch-configs", WithConfigTarget("blue"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -1755,11 +1783,12 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 			simpleK8sService(Route("default", "switch-configs", WithConfigTarget("blue"))),
 		},
 		WantUpdates: []clientgotesting.UpdateActionImpl{{
-			Object: simpleReadyIngress(
+			Object: simpleIngress(
 				Route("default", "switch-configs", WithConfigTarget("green"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -1773,6 +1802,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 		}},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
@@ -1860,7 +1890,7 @@ func TestReconcile(t *testing.T) {
 				WithConfigLabel("serving.knative.dev/route", "steady-state"),
 			),
 			rev("default", "config", 1, MarkRevisionReady, WithRevName("config-00001")),
-			simpleReadyIngress(
+			simpleIngress(
 				Route("default", "my-route", WithConfigTarget("config"), WithURL),
 				&traffic.Config{
 					Targets: map[string]traffic.RevisionTargets{
@@ -1874,6 +1904,7 @@ func TestReconcile(t *testing.T) {
 						}},
 					},
 				},
+				withReadyIngress,
 			),
 			simpleK8sService(Route("default", "my-route", WithConfigTarget("config"))),
 			simpleK8sService(Route("default", "my-route"), OverrideServiceName("old-service-name")),
@@ -2664,7 +2695,9 @@ func simpleK8sService(r *v1.Route, so ...K8sServiceOption) *corev1.Service {
 
 	// omit the error here, as we are sure the loadbalancer info is provided.
 	// return the service instance only, so that the result can be used in TableRow.
-	svc, _ := resources.MakeK8sService(ctx, r, "", &netv1alpha1.Ingress{Status: readyIngressStatus()}, false, "")
+	svc, _ := resources.MakeK8sService(ctx, r, "", /*targetName*/
+		simpleIngress(r, &traffic.Config{}, withReadyIngress),
+		false, "" /*clusterIP*/)
 
 	for _, opt := range so {
 		opt(svc)
@@ -2674,39 +2707,11 @@ func simpleK8sService(r *v1.Route, so ...K8sServiceOption) *corev1.Service {
 }
 
 func simpleIngress(r *v1.Route, tc *traffic.Config, io ...IngressOption) *netv1alpha1.Ingress {
-	return baseIngressWithClass(r, tc, TestIngressClass, io...)
-}
-
-func ingressWithClass(r *v1.Route, tc *traffic.Config, class string, io ...IngressOption) *netv1alpha1.Ingress {
-	return baseIngressWithClass(r, tc, class, io...)
-}
-
-func baseIngressWithClass(r *v1.Route, tc *traffic.Config, class string, io ...IngressOption) *netv1alpha1.Ingress {
-	ingress, _ := resources.MakeIngress(getContext(), r, tc, nil /*tls*/, class)
-
-	for _, opt := range io {
-		opt(ingress)
-	}
-
-	return ingress
+	return ingressWithTLS(r, tc, nil /*tls*/, nil /*challenges*/, io...)
 }
 
 func ingressWithTLS(r *v1.Route, tc *traffic.Config, tls []netv1alpha1.IngressTLS, challenges []netv1alpha1.HTTP01Challenge, io ...IngressOption) *netv1alpha1.Ingress {
-	return baseIngressWithTLS(r, tc, tls, challenges, io...)
-}
-
-func baseIngressWithTLS(r *v1.Route, tc *traffic.Config, tls []netv1alpha1.IngressTLS, challenges []netv1alpha1.HTTP01Challenge, io ...IngressOption) *netv1alpha1.Ingress {
-	ingress, _ := resources.MakeIngress(getContext(), r, tc, tls, TestIngressClass, challenges...)
-
-	for _, opt := range io {
-		opt(ingress)
-	}
-
-	return ingress
-}
-
-func simpleReadyIngress(r *v1.Route, tc *traffic.Config, io ...IngressOption) *netv1alpha1.Ingress {
-	ingress := ingressWithStatus(r, tc, readyIngressStatus())
+	ingress, _ := resources.MakeIngress(getContext(), r, tc, tls, testIngressClass, challenges...)
 
 	for _, opt := range io {
 		opt(ingress)
@@ -2716,15 +2721,20 @@ func simpleReadyIngress(r *v1.Route, tc *traffic.Config, io ...IngressOption) *n
 }
 
 func ingressWithRollout(r *v1.Route, tc *traffic.Config, ro *traffic.Rollout, io ...IngressOption) *netv1alpha1.Ingress {
-	ingress, _ := resources.MakeIngressWithRollout(getContext(), r, tc, ro, nil /*tls*/, TestIngressClass)
-	ingress.Status = readyIngressStatus()
+	ingress, _ := resources.MakeIngressWithRollout(getContext(), r, tc, ro, nil /*tls*/, testIngressClass)
 	for _, o := range io {
 		o(ingress)
 	}
 	return ingress
 }
 
-func readyIngressStatus() netv1alpha1.IngressStatus {
+func withClass(class string) IngressOption {
+	return func(i *netv1alpha1.Ingress) {
+		i.Annotations[networking.IngressClassAnnotationKey] = class
+	}
+}
+
+func withReadyIngress(i *netv1alpha1.Ingress) {
 	status := netv1alpha1.IngressStatus{}
 	status.InitializeConditions()
 	status.MarkNetworkConfigured()
@@ -2737,19 +2747,11 @@ func readyIngressStatus() netv1alpha1.IngressStatus {
 		}},
 	)
 
-	return status
-}
-
-func ingressWithStatus(r *v1.Route, tc *traffic.Config, status netv1alpha1.IngressStatus) *netv1alpha1.Ingress {
-	ci := simpleIngress(r, tc)
-	ci.SetName(r.Name)
-	ci.Status = status
-
-	return ci
+	i.Status = status
 }
 
 func mutateIngress(ci *netv1alpha1.Ingress) *netv1alpha1.Ingress {
-	// Thor's Hammer.
+	// Thor's Hammer 🔨.
 	ci.Spec = netv1alpha1.IngressSpec{}
 	return ci
 }
@@ -2794,7 +2796,7 @@ func reconcilerTestConfig(enableAutoTLS bool) *config.Config {
 			},
 		},
 		Network: &network.Config{
-			DefaultIngressClass:     TestIngressClass,
+			DefaultIngressClass:     testIngressClass,
 			DefaultCertificateClass: network.CertManagerCertificateClassName,
 			AutoTLS:                 enableAutoTLS,
 			DomainTemplate:          network.DefaultDomainTemplate,
diff --git a/test/config/autotls/certmanager/http01/issuer.yaml b/test/config/autotls/certmanager/http01/issuer.yaml
index b2df27e3f..f3e9c9de3 100644
--- a/test/config/autotls/certmanager/http01/issuer.yaml
+++ b/test/config/autotls/certmanager/http01/issuer.yaml
@@ -18,7 +18,7 @@ metadata:
   name: http01-issuer
 spec:
   acme:
-    server: https://acme-staging-v02.api.letsencrypt.org/directory
+    server: https://acme-v02.api.letsencrypt.org/directory
     privateKeySecretRef:
       name: "http01-key"
     solvers:
diff --git a/test/e2e-auto-tls-tests.sh b/test/e2e-auto-tls-tests.sh
index a38efbcfa..ced3682fc 100755
--- a/test/e2e-auto-tls-tests.sh
+++ b/test/e2e-auto-tls-tests.sh
@@ -210,14 +210,14 @@ add_trap "cleanup_per_selfsigned_namespace_auto_tls" SIGKILL SIGTERM SIGQUIT
 go_test_e2e -timeout=10m ./test/e2e/autotls/ || failed=1
 cleanup_per_selfsigned_namespace_auto_tls
 
-# TODO(#10486): resume the HTTP01 E2E tests after figuring out the DNS setup failure.
-
-# subheader "Auto TLS test for per-ksvc certificate provision using HTTP01 challenge"
-# setup_http01_auto_tls
-# add_trap "delete_dns_record" SIGKILL SIGTERM SIGQUIT
-# go_test_e2e -timeout=10m ./test/e2e/autotls/ || failed=1
-# kubectl delete -f ${TMP_DIR}/test/config/autotls/certmanager/http01/
-# delete_dns_record
+if [[ ${RUN_HTTP01_AUTO_TLS_TESTS} -eq 1 ]]; then
+  subheader "Auto TLS test for per-ksvc certificate provision using HTTP01 challenge"
+  setup_http01_auto_tls
+  add_trap "delete_dns_record" SIGKILL SIGTERM SIGQUIT
+  go_test_e2e -timeout=10m ./test/e2e/autotls/ || failed=1
+  kubectl delete -f ${TMP_DIR}/test/config/autotls/certmanager/http01/
+  delete_dns_record
+fi
 
 (( failed )) && fail_test
 
diff --git a/test/e2e-common.sh b/test/e2e-common.sh
index 453517015..6158d3b23 100644
--- a/test/e2e-common.sh
+++ b/test/e2e-common.sh
@@ -28,6 +28,7 @@ KOURIER_VERSION=""
 AMBASSADOR_VERSION=""
 CONTOUR_VERSION=""
 CERTIFICATE_CLASS=""
+export RUN_HTTP01_AUTO_TLS_TESTS=0
 # Only build linux/amd64 bit images
 KO_FLAGS="--platform=linux/amd64"
 
@@ -80,6 +81,10 @@ function parse_flags() {
       readonly CERTIFICATE_CLASS="cert-manager.certificate.networking.knative.dev"
       return 2
       ;;
+    --run-http01-auto-tls-tests)
+      readonly RUN_HTTP01_AUTO_TLS_TESTS=1
+      return 1
+      ;;
     --mesh)
       readonly MESH=1
       return 1
diff --git a/test/test_images/runtime/handlers/handler.go b/test/test_images/runtime/handlers/handler.go
index 9189b96a5..cbae195e2 100644
--- a/test/test_images/runtime/handlers/handler.go
+++ b/test/test_images/runtime/handlers/handler.go
@@ -21,7 +21,6 @@ import (
 	"log"
 	"net/http"
 	"net/http/httputil"
-	"strings"
 
 	network "knative.dev/networking/pkg"
 )
@@ -50,7 +49,7 @@ func withRequestLog(next http.HandlerFunc) http.HandlerFunc {
 
 // withKubeletProbeHeaderCheck checks each health request has Kubelet probe header
 func withKubeletProbeHeaderCheck(w http.ResponseWriter, r *http.Request) {
-	if !strings.HasPrefix(r.Header.Get("User-Agent"), network.KubeProbeUAPrefix) {
+	if !network.IsKubeletProbe(r) {
 		w.WriteHeader(http.StatusBadRequest)
 	} else {
 		w.WriteHeader(http.StatusOK)
diff --git a/test/upgrade/probe.go b/test/upgrade/probe.go
index 38c4ebc31..37a3e8261 100644
--- a/test/upgrade/probe.go
+++ b/test/upgrade/probe.go
@@ -51,7 +51,7 @@ func ProbeTest() pkgupgrade.BackgroundOperation {
 			// This polls until we get a 200 with the right body.
 			assertServiceResourcesUpdated(c.T, clients, *names, url, test.PizzaPlanetText1)
 
-			prober = test.RunRouteProber(c.T.Logf, clients, url, test.AddRootCAtoTransport(context.Background(), c.T.Logf, clients, test.ServingFlags.HTTPS))
+			prober = test.RunRouteProber(c.Log.Infof, clients, url, test.AddRootCAtoTransport(context.Background(), c.T.Logf, clients, test.ServingFlags.HTTPS))
 		},
 		func(c pkgupgrade.Context) {
 			// Verify
diff --git a/third_party/cert-manager-latest/net-certmanager.yaml b/third_party/cert-manager-latest/net-certmanager.yaml
index 054620a26..e037e57c1 100644
--- a/third_party/cert-manager-latest/net-certmanager.yaml
+++ b/third_party/cert-manager-latest/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: "v20210217-99cfbfb6"
+    serving.knative.dev/release: "v20210222-ffa73df9"
     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: "v20210217-99cfbfb6"
+    serving.knative.dev/release: "v20210222-ffa73df9"
 webhooks:
   - admissionReviewVersions:
       - v1beta1
@@ -86,7 +86,7 @@ metadata:
   name: net-certmanager-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20210217-99cfbfb6"
+    serving.knative.dev/release: "v20210222-ffa73df9"
 
 ---
 # Copyright 2020 The Knative Authors
@@ -109,7 +109,7 @@ metadata:
   name: config-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20210217-99cfbfb6"
+    serving.knative.dev/release: "v20210222-ffa73df9"
     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: "v20210217-99cfbfb6"
+    serving.knative.dev/release: "v20210222-ffa73df9"
     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: "v20210217-99cfbfb6"
+        serving.knative.dev/release: "v20210222-ffa73df9"
     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:f7f43bb5e0c252d8be413927986dac17bece3c3b27493ae1da3b1d38154e0b96
+          image: gcr.io/knative-nightly/knative.dev/net-certmanager/cmd/controller@sha256:1a7158142daef46614217077f974bfcea23cca1f8c704b0d794d4d369562657f
           resources:
             requests:
               cpu: 30m
@@ -213,7 +213,7 @@ kind: Service
 metadata:
   labels:
     app: networking-certmanager
-    serving.knative.dev/release: "v20210217-99cfbfb6"
+    serving.knative.dev/release: "v20210222-ffa73df9"
     networking.knative.dev/certificate-provider: cert-manager
   name: networking-certmanager
   namespace: knative-serving
@@ -250,7 +250,7 @@ metadata:
   name: net-certmanager-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20210217-99cfbfb6"
+    serving.knative.dev/release: "v20210222-ffa73df9"
 spec:
   selector:
     matchLabels:
@@ -263,14 +263,14 @@ spec:
       labels:
         app: net-certmanager-webhook
         role: net-certmanager-webhook
-        serving.knative.dev/release: "v20210217-99cfbfb6"
+        serving.knative.dev/release: "v20210222-ffa73df9"
     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:75970ff398180e8245f4cf108c5156ac3d2bf5241b187db2482fad4a8f2971dd
+          image: gcr.io/knative-nightly/knative.dev/net-certmanager/cmd/webhook@sha256:2a615c24090e7ffb3a146bdb310a1cec2b933f8faea5ec015cbb0859ddabe341
           resources:
             requests:
               cpu: 20m
@@ -329,7 +329,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: net-certmanager-webhook
-    serving.knative.dev/release: "v20210217-99cfbfb6"
+    serving.knative.dev/release: "v20210222-ffa73df9"
 spec:
   ports:
     # Define metrics and profiling for them to be accessible within service meshes.
diff --git a/third_party/contour-latest/contour.yaml b/third_party/contour-latest/contour.yaml
index 3ba9899fc..07413b3bb 100644
--- a/third_party/contour-latest/contour.yaml
+++ b/third_party/contour-latest/contour.yaml
@@ -65,6 +65,11 @@ data:
     #   determine which XDS Server implementation to utilize in Contour.
     #   xds-server-type: contour
     #
+    # Specify the gateway-api Gateway Contour should watch.
+    # gateway:
+    #   name: contour
+    #   namespace: contour-external
+    #
     # should contour expect to be running inside a k8s cluster
     # incluster: true
     #
@@ -79,6 +84,13 @@ data:
     tls:
     # minimum TLS version that Contour will negotiate
     # minimum-protocol-version: "1.2"
+    # TLS ciphers to be supported by Envoy TLS listeners when negotiating
+    # TLS 1.2.
+    # cipher-suites:
+    # - '[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305]'
+    # - '[ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305]'
+    # - 'ECDHE-ECDSA-AES256-GCM-SHA384'
+    # - 'ECDHE-RSA-AES256-GCM-SHA384'
     # Defines the Kubernetes name/namespace matching a secret to use
     # as the fallback certificate when requests which don't match the
     # SNI defined for a vhost.
@@ -134,6 +146,7 @@ data:
     #   connection-idle-timeout: 60s
     #   stream-idle-timeout: 5m
     #   max-connection-duration: infinity
+    #   delayed-close-timeout: 1s
     #   connection-shutdown-grace-period: 5s
     #
     # Envoy cluster settings.
@@ -141,12 +154,32 @@ data:
     #   configure the cluster dns lookup family
     #   valid options are: auto (default), v4, v6
     #   dns-lookup-family: auto
+    #
+    # Envoy network settings.
+    # network:
+    #   Configure the number of additional ingress proxy hops from the
+    #   right side of the x-forwarded-for HTTP header to trust.
+    #   num-trusted-hops: 0
+    #
+    # Configure an optional global rate limit service.
+    # rateLimitService:
+    #   Identifies the extension service defining the rate limit service,
+    #   formatted as <namespace>/<name>.
+    #   extensionService: projectcontour/ratelimit
+    #   Defines the rate limit domain to pass to the rate limit service.
+    #   Acts as a container for a set of rate limit definitions within
+    #   the RLS.
+    #   domain: contour
+    #   Defines whether to allow requests to proceed when the rate limit
+    #   service fails to respond with a valid rate limit decision within
+    #   the timeout defined on the extension service.
+    #   failOpen: false
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.4.0
+    controller-gen.kubebuilder.io/version: v0.4.1
   creationTimestamp: null
   name: extensionservices.projectcontour.io
   labels:
@@ -403,7 +436,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.4.0
+    controller-gen.kubebuilder.io/version: v0.4.1
   creationTimestamp: null
   name: httpproxies.projectcontour.io
   labels:
@@ -639,6 +672,52 @@ spec:
                       rateLimitPolicy:
                         description: The policy for rate limiting on the route.
                         properties:
+                          global:
+                            description: Global defines global rate limiting parameters, i.e. parameters defining descriptors that are sent to an external rate limit service (RLS) for a rate limit decision on each request.
+                            properties:
+                              descriptors:
+                                description: Descriptors defines the list of descriptors that will be generated and sent to the rate limit service. Each descriptor contains 1+ key-value pair entries.
+                                items:
+                                  description: RateLimitDescriptor defines a list of key-value pair generators.
+                                  properties:
+                                    entries:
+                                      description: Entries is the list of key-value pair generators.
+                                      items:
+                                        description: RateLimitDescriptorEntry is a key-value pair generator. Exactly one field on this struct must be non-nil.
+                                        properties:
+                                          genericKey:
+                                            description: GenericKey defines a descriptor entry with a static key and value.
+                                            properties:
+                                              key:
+                                                description: Key defines the key of the descriptor entry. If not set, the key is set to "generic_key".
+                                                type: string
+                                              value:
+                                                description: Value defines the value of the descriptor entry.
+                                                minLength: 1
+                                                type: string
+                                            type: object
+                                          remoteAddress:
+                                            description: RemoteAddress defines a descriptor entry with a key of "remote_address" and a value equal to the client's IP address (from x-forwarded-for).
+                                            type: object
+                                          requestHeader:
+                                            description: RequestHeader defines a descriptor entry that's populated only if a given header is present on the request. The descriptor key is static, and the descriptor value is equal to the value of the header.
+                                            properties:
+                                              descriptorKey:
+                                                description: DescriptorKey defines the key to use on the descriptor entry.
+                                                minLength: 1
+                                                type: string
+                                              headerName:
+                                                description: HeaderName defines the name of the header to look for on the request.
+                                                minLength: 1
+                                                type: string
+                                            type: object
+                                        type: object
+                                      minItems: 1
+                                      type: array
+                                  type: object
+                                minItems: 1
+                                type: array
+                            type: object
                           local:
                             description: Local defines local rate limiting parameters, i.e. parameters for rate limiting that occurs within each Envoy pod as requests are handled.
                             properties:
@@ -1161,6 +1240,52 @@ spec:
                     rateLimitPolicy:
                       description: The policy for rate limiting on the virtual host.
                       properties:
+                        global:
+                          description: Global defines global rate limiting parameters, i.e. parameters defining descriptors that are sent to an external rate limit service (RLS) for a rate limit decision on each request.
+                          properties:
+                            descriptors:
+                              description: Descriptors defines the list of descriptors that will be generated and sent to the rate limit service. Each descriptor contains 1+ key-value pair entries.
+                              items:
+                                description: RateLimitDescriptor defines a list of key-value pair generators.
+                                properties:
+                                  entries:
+                                    description: Entries is the list of key-value pair generators.
+                                    items:
+                                      description: RateLimitDescriptorEntry is a key-value pair generator. Exactly one field on this struct must be non-nil.
+                                      properties:
+                                        genericKey:
+                                          description: GenericKey defines a descriptor entry with a static key and value.
+                                          properties:
+                                            key:
+                                              description: Key defines the key of the descriptor entry. If not set, the key is set to "generic_key".
+                                              type: string
+                                            value:
+                                              description: Value defines the value of the descriptor entry.
+                                              minLength: 1
+                                              type: string
+                                          type: object
+                                        remoteAddress:
+                                          description: RemoteAddress defines a descriptor entry with a key of "remote_address" and a value equal to the client's IP address (from x-forwarded-for).
+                                          type: object
+                                        requestHeader:
+                                          description: RequestHeader defines a descriptor entry that's populated only if a given header is present on the request. The descriptor key is static, and the descriptor value is equal to the value of the header.
+                                          properties:
+                                            descriptorKey:
+                                              description: DescriptorKey defines the key to use on the descriptor entry.
+                                              minLength: 1
+                                              type: string
+                                            headerName:
+                                              description: HeaderName defines the name of the header to look for on the request.
+                                              minLength: 1
+                                              type: string
+                                          type: object
+                                      type: object
+                                    minItems: 1
+                                    type: array
+                                type: object
+                              minItems: 1
+                              type: array
+                          type: object
                         local:
                           description: Local defines local rate limiting parameters, i.e. parameters for rate limiting that occurs within each Envoy pod as requests are handled.
                           properties:
@@ -1375,6 +1500,29 @@ spec:
                           ip:
                             description: IP is set for load-balancer ingress points that are IP based (typically GCE or OpenStack load-balancers)
                             type: string
+                          ports:
+                            description: Ports is a list of records of service ports If used, every port defined in the service should have an entry in it
+                            items:
+                              properties:
+                                error:
+                                  description: 'Error is to record the problem with the service port The format of the error shall comply with the following rules: - built-in error values shall be specified in this file and those shall use   CamelCase names - cloud provider specific error values must have names that comply with the   format foo.example.com/CamelCase. --- The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)'
+                                  maxLength: 316
+                                  pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                                  type: string
+                                port:
+                                  description: Port is the port number of the service port of which status is recorded here
+                                  format: int32
+                                  type: integer
+                                protocol:
+                                  default: TCP
+                                  description: 'Protocol is the protocol of the service port of which status is recorded here The supported values are: "TCP", "UDP", "SCTP"'
+                                  type: string
+                              required:
+                                - port
+                                - protocol
+                              type: object
+                            type: array
+                            x-kubernetes-list-type: atomic
                         type: object
                       type: array
                   type: object
@@ -1398,7 +1546,7 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.4.0
+    controller-gen.kubebuilder.io/version: v0.4.1
   creationTimestamp: null
   name: tlscertificatedelegations.projectcontour.io
   labels:
@@ -1628,7 +1776,7 @@ rules:
 apiVersion: batch/v1
 kind: Job
 metadata:
-  name: contour-certgen-v1.12.0
+  name: contour-certgen-v1.13.0
   namespace: contour-external
   labels:
     networking.knative.dev/ingress-provider: contour
@@ -1641,7 +1789,7 @@ spec:
     spec:
       containers:
         - name: contour
-          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7b2cdcf1aebdfdc85ee8c69d07cf8ea8fbf9d11195e9aca91c31a1ca633e001c
+          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7923f24f99965cda151a96ca5b0dc20c5a5664eecfef6dc34265c2c17e836821
           imagePullPolicy: Always
           command:
             - contour
@@ -1712,6 +1860,14 @@ rules:
       - customresourcedefinitions
     verbs:
       - list
+  - apiGroups:
+      - networking.k8s.io
+    resources:
+      - ingressclasses
+    verbs:
+      - get
+      - list
+      - watch
   - apiGroups:
       - networking.k8s.io
     resources:
@@ -1868,7 +2024,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:7b2cdcf1aebdfdc85ee8c69d07cf8ea8fbf9d11195e9aca91c31a1ca633e001c
+          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7923f24f99965cda151a96ca5b0dc20c5a5664eecfef6dc34265c2c17e836821
           imagePullPolicy: IfNotPresent
           name: contour
           ports:
@@ -1957,7 +2113,7 @@ spec:
           args:
             - envoy
             - shutdown-manager
-          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7b2cdcf1aebdfdc85ee8c69d07cf8ea8fbf9d11195e9aca91c31a1ca633e001c
+          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7923f24f99965cda151a96ca5b0dc20c5a5664eecfef6dc34265c2c17e836821
           imagePullPolicy: IfNotPresent
           lifecycle:
             preStop:
@@ -2036,7 +2192,7 @@ spec:
             - --envoy-key-file=/certs/tls.key
           command:
             - contour
-          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7b2cdcf1aebdfdc85ee8c69d07cf8ea8fbf9d11195e9aca91c31a1ca633e001c
+          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7923f24f99965cda151a96ca5b0dc20c5a5664eecfef6dc34265c2c17e836821
           imagePullPolicy: IfNotPresent
           name: envoy-initconfig
           volumeMounts:
@@ -2128,6 +2284,11 @@ data:
     #   determine which XDS Server implementation to utilize in Contour.
     #   xds-server-type: contour
     #
+    # Specify the gateway-api Gateway Contour should watch.
+    # gateway:
+    #   name: contour
+    #   namespace: contour-internal
+    #
     # should contour expect to be running inside a k8s cluster
     # incluster: true
     #
@@ -2142,6 +2303,13 @@ data:
     tls:
     # minimum TLS version that Contour will negotiate
     # minimum-protocol-version: "1.2"
+    # TLS ciphers to be supported by Envoy TLS listeners when negotiating
+    # TLS 1.2.
+    # cipher-suites:
+    # - '[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305]'
+    # - '[ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305]'
+    # - 'ECDHE-ECDSA-AES256-GCM-SHA384'
+    # - 'ECDHE-RSA-AES256-GCM-SHA384'
     # Defines the Kubernetes name/namespace matching a secret to use
     # as the fallback certificate when requests which don't match the
     # SNI defined for a vhost.
@@ -2197,6 +2365,7 @@ data:
     #   connection-idle-timeout: 60s
     #   stream-idle-timeout: 5m
     #   max-connection-duration: infinity
+    #   delayed-close-timeout: 1s
     #   connection-shutdown-grace-period: 5s
     #
     # Envoy cluster settings.
@@ -2204,6 +2373,26 @@ data:
     #   configure the cluster dns lookup family
     #   valid options are: auto (default), v4, v6
     #   dns-lookup-family: auto
+    #
+    # Envoy network settings.
+    # network:
+    #   Configure the number of additional ingress proxy hops from the
+    #   right side of the x-forwarded-for HTTP header to trust.
+    #   num-trusted-hops: 0
+    #
+    # Configure an optional global rate limit service.
+    # rateLimitService:
+    #   Identifies the extension service defining the rate limit service,
+    #   formatted as <namespace>/<name>.
+    #   extensionService: projectcontour/ratelimit
+    #   Defines the rate limit domain to pass to the rate limit service.
+    #   Acts as a container for a set of rate limit definitions within
+    #   the RLS.
+    #   domain: contour
+    #   Defines whether to allow requests to proceed when the rate limit
+    #   service fails to respond with a valid rate limit decision within
+    #   the timeout defined on the extension service.
+    #   failOpen: false
 ---
 apiVersion: v1
 kind: ServiceAccount
@@ -2248,7 +2437,7 @@ rules:
 apiVersion: batch/v1
 kind: Job
 metadata:
-  name: contour-certgen-v1.12.0
+  name: contour-certgen-v1.13.0
   namespace: contour-internal
   labels:
     networking.knative.dev/ingress-provider: contour
@@ -2261,7 +2450,7 @@ spec:
     spec:
       containers:
         - name: contour
-          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7b2cdcf1aebdfdc85ee8c69d07cf8ea8fbf9d11195e9aca91c31a1ca633e001c
+          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7923f24f99965cda151a96ca5b0dc20c5a5664eecfef6dc34265c2c17e836821
           imagePullPolicy: Always
           command:
             - contour
@@ -2381,7 +2570,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:7b2cdcf1aebdfdc85ee8c69d07cf8ea8fbf9d11195e9aca91c31a1ca633e001c
+          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7923f24f99965cda151a96ca5b0dc20c5a5664eecfef6dc34265c2c17e836821
           imagePullPolicy: IfNotPresent
           name: contour
           ports:
@@ -2470,7 +2659,7 @@ spec:
           args:
             - envoy
             - shutdown-manager
-          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7b2cdcf1aebdfdc85ee8c69d07cf8ea8fbf9d11195e9aca91c31a1ca633e001c
+          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7923f24f99965cda151a96ca5b0dc20c5a5664eecfef6dc34265c2c17e836821
           imagePullPolicy: IfNotPresent
           lifecycle:
             preStop:
@@ -2549,7 +2738,7 @@ spec:
             - --envoy-key-file=/certs/tls.key
           command:
             - contour
-          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7b2cdcf1aebdfdc85ee8c69d07cf8ea8fbf9d11195e9aca91c31a1ca633e001c
+          image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:7923f24f99965cda151a96ca5b0dc20c5a5664eecfef6dc34265c2c17e836821
           imagePullPolicy: IfNotPresent
           name: envoy-initconfig
           volumeMounts:
diff --git a/third_party/contour-latest/net-contour.yaml b/third_party/contour-latest/net-contour.yaml
index c53255d5b..17655008a 100644
--- a/third_party/contour-latest/net-contour.yaml
+++ b/third_party/contour-latest/net-contour.yaml
@@ -33,7 +33,7 @@ metadata:
   namespace: knative-serving
   labels:
     networking.knative.dev/ingress-provider: contour
-    serving.knative.dev/release: "v20210217-73f0cfdf"
+    serving.knative.dev/release: "v20210226-2188be8e"
 data:
   _example: |
     ################################
@@ -103,7 +103,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:2525c370c649f570786393fe351be333cb15a5498122538589d9f7613879fa52
+          image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:0cca4b63bf1744b730ce788e582f2585dec7873577ed6f542536251738af71a9
           resources:
             requests:
               cpu: 40m
diff --git a/third_party/istio-latest/net-istio.yaml b/third_party/istio-latest/net-istio.yaml
index f4e648897..2e871f3dc 100644
--- a/third_party/istio-latest/net-istio.yaml
+++ b/third_party/istio-latest/net-istio.yaml
@@ -1,4 +1,4 @@
-# Generated when HEAD was b4a6e6838774ed5705699b20e560025734e5020f
+# Generated when HEAD was 9bfac144f5712645f1c96fbdb5a23c8218cfefa3
 #
 # Copyright 2019 The Knative Authors
 #
@@ -20,7 +20,7 @@ metadata:
   # These are the permissions needed by the Istio Ingress implementation.
   name: knative-serving-istio
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     serving.knative.dev/controller: "true"
     networking.knative.dev/ingress-provider: istio
 rules:
@@ -50,7 +50,7 @@ metadata:
   name: knative-ingress-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -87,7 +87,7 @@ metadata:
   name: knative-local-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -106,7 +106,7 @@ metadata:
   name: knative-local-gateway
   namespace: istio-system
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 spec:
   type: ClusterIP
@@ -126,7 +126,7 @@ metadata:
   name: "webhook"
   namespace: "knative-serving"
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -142,7 +142,7 @@ metadata:
   name: "domainmapping-webhook"
   namespace: "knative-serving"
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -158,7 +158,7 @@ metadata:
   name: "istio-webhook"
   namespace: "knative-serving"
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -188,7 +188,7 @@ kind: MutatingWebhookConfiguration
 metadata:
   name: webhook.istio.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 webhooks:
   - admissionReviewVersions:
@@ -224,7 +224,7 @@ kind: ValidatingWebhookConfiguration
 metadata:
   name: config.webhook.istio.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 webhooks:
   - admissionReviewVersions:
@@ -262,7 +262,7 @@ metadata:
   name: istio-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 
 ---
@@ -286,7 +286,7 @@ metadata:
   name: config-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 data:
   _example: |
@@ -360,7 +360,7 @@ metadata:
   name: networking-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -376,14 +376,14 @@ spec:
         sidecar.istio.io/inject: "false"
       labels:
         app: networking-istio
-        serving.knative.dev/release: "v20210218-b4a6e683"
+        serving.knative.dev/release: "v20210225-9bfac144"
     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:f78b8db57a76209c5d2b18730e7e948757c49cd5507f4c649ad1b3452b5f553a
+          image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/controller@sha256:4c8621795f451fb1ca450175ba29097f45d049aaaa06404348fabaac6a322c7a
           resources:
             requests:
               cpu: 30m
@@ -440,7 +440,7 @@ metadata:
   name: istio-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -454,14 +454,14 @@ spec:
       labels:
         app: istio-webhook
         role: istio-webhook
-        serving.knative.dev/release: "v20210218-b4a6e683"
+        serving.knative.dev/release: "v20210225-9bfac144"
     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:414ea880641e9a2d472b459cfc75279d746db1161506881997e78072a4dd35bc
+          image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/webhook@sha256:1602867264fe5dd97a70e157b75bbdf4be685cc8329e9e7145de7d3e0c36e9ef
           resources:
             requests:
               cpu: 20m
@@ -515,7 +515,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: istio-webhook
-    serving.knative.dev/release: "v20210218-b4a6e683"
+    serving.knative.dev/release: "v20210225-9bfac144"
     networking.knative.dev/ingress-provider: istio
 spec:
   ports:
diff --git a/third_party/kourier-latest/kourier.yaml b/third_party/kourier-latest/kourier.yaml
index d7582e97c..fb537022b 100644
--- a/third_party/kourier-latest/kourier.yaml
+++ b/third_party/kourier-latest/kourier.yaml
@@ -18,7 +18,7 @@ metadata:
   name: kourier-system
   labels:
     networking.knative.dev/ingress-provider: kourier
-    serving.knative.dev/release: "v20210217-364c1a22"
+    serving.knative.dev/release: "v20210222-4a442c4c"
 
 ---
 # Copyright 2020 The Knative Authors
@@ -42,7 +42,7 @@ metadata:
   namespace: kourier-system
   labels:
     networking.knative.dev/ingress-provider: kourier
-    serving.knative.dev/release: "v20210217-364c1a22"
+    serving.knative.dev/release: "v20210222-4a442c4c"
 data:
   envoy-bootstrap.yaml: |
     dynamic_resources:
@@ -142,7 +142,7 @@ metadata:
   namespace: knative-serving
   labels:
     networking.knative.dev/ingress-provider: kourier
-    serving.knative.dev/release: "v20210217-364c1a22"
+    serving.knative.dev/release: "v20210222-4a442c4c"
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
@@ -151,7 +151,7 @@ metadata:
   namespace: knative-serving
   labels:
     networking.knative.dev/ingress-provider: kourier
-    serving.knative.dev/release: "v20210217-364c1a22"
+    serving.knative.dev/release: "v20210222-4a442c4c"
 rules:
   - apiGroups: [""]
     resources: ["events"]
@@ -178,7 +178,7 @@ metadata:
   name: 3scale-kourier
   labels:
     networking.knative.dev/ingress-provider: kourier
-    serving.knative.dev/release: "v20210217-364c1a22"
+    serving.knative.dev/release: "v20210222-4a442c4c"
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: ClusterRole
@@ -210,7 +210,7 @@ metadata:
   namespace: knative-serving
   labels:
     networking.knative.dev/ingress-provider: kourier
-    serving.knative.dev/release: "v20210217-364c1a22"
+    serving.knative.dev/release: "v20210222-4a442c4c"
 spec:
   replicas: 1
   selector:
@@ -222,7 +222,7 @@ spec:
         app: 3scale-kourier-control
     spec:
       containers:
-        - image: gcr.io/knative-nightly/knative.dev/net-kourier/cmd/kourier@sha256:5cdf979fc948b490764a8d5efd264a00d787feb32c9c9521b091517803f1e55d
+        - image: gcr.io/knative-nightly/knative.dev/net-kourier/cmd/kourier@sha256:5ec9c41be4475d325e3cba284f3725d6147ca1f0ac766cd9ff22f97c36a9def5
           name: kourier-control
           env:
             - name: CERTS_SECRET_NAMESPACE
@@ -258,7 +258,7 @@ metadata:
   namespace: knative-serving
   labels:
     networking.knative.dev/ingress-provider: kourier
-    serving.knative.dev/release: "v20210217-364c1a22"
+    serving.knative.dev/release: "v20210222-4a442c4c"
 spec:
   ports:
     - name: grpc-xds
@@ -291,7 +291,7 @@ metadata:
   namespace: kourier-system
   labels:
     networking.knative.dev/ingress-provider: kourier
-    serving.knative.dev/release: "v20210217-364c1a22"
+    serving.knative.dev/release: "v20210222-4a442c4c"
 spec:
   selector:
     matchLabels:
@@ -357,7 +357,7 @@ metadata:
   namespace: kourier-system
   labels:
     networking.knative.dev/ingress-provider: kourier
-    serving.knative.dev/release: "v20210217-364c1a22"
+    serving.knative.dev/release: "v20210222-4a442c4c"
 spec:
   ports:
     - name: http2
@@ -379,7 +379,7 @@ metadata:
   namespace: kourier-system
   labels:
     networking.knative.dev/ingress-provider: kourier
-    serving.knative.dev/release: "v20210217-364c1a22"
+    serving.knative.dev/release: "v20210222-4a442c4c"
 spec:
   ports:
     - name: http2
diff --git a/vendor/knative.dev/pkg/reconciler/testing/clock.go b/vendor/knative.dev/pkg/reconciler/testing/clock.go
deleted file mode 100644
index 44ba77cdb..000000000
--- a/vendor/knative.dev/pkg/reconciler/testing/clock.go
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-Copyright 2019 The Knative Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package testing
-
-import (
-	"time"
-)
-
-type FakeClock struct {
-	Time time.Time
-}
-
-func (c FakeClock) Now() time.Time {
-	return c.Time
-}