Repository: knative/serving
Pull request #8119 (2020-05-28 14:37:13)

references issue for metrics server versioning

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

This pull-request has been approved by: *<a href="https://github.com/knative/serving/pull/8119#" title="Author self-approved">dprotaso</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:

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

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":[]} -->
knative-prow-robot: @dprotaso: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-autotls-tests | 5941fdcfde7ee47d4ad5a41e3e2176455eff0f5c | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_serving/8119/pull-knative-serving-autotls-tests/1266015824406122496) | `/test pull-knative-serving-autotls-tests`

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

<details>

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

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-autotls-tests | 5941fdcfde7ee47d4ad5a41e3e2176455eff0f5c | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_serving/8119/pull-knative-serving-autotls-tests/1266015824406122496) | `/test pull-knative-serving-autotls-tests`
pull-knative-serving-integration-tests | 5941fdcfde7ee47d4ad5a41e3e2176455eff0f5c | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_serving/8119/pull-knative-serving-integration-tests/1266015824410316803) | `/test pull-knative-serving-integration-tests`

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

<details>

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

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-autotls-tests | 5941fdcfde7ee47d4ad5a41e3e2176455eff0f5c | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_serving/8119/pull-knative-serving-autotls-tests/1266015824406122496) | `/test pull-knative-serving-autotls-tests`
pull-knative-serving-integration-tests | 5941fdcfde7ee47d4ad5a41e3e2176455eff0f5c | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_serving/8119/pull-knative-serving-integration-tests/1266015824410316803) | `/test pull-knative-serving-integration-tests`
pull-knative-serving-upgrade-tests | 5941fdcfde7ee47d4ad5a41e3e2176455eff0f5c | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_serving/8119/pull-knative-serving-upgrade-tests/1266015824410316800) | `/test pull-knative-serving-upgrade-tests`

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

<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 -->

Patch:
diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES
index 662e1c14d..cc54fcdb4 100644
--- a/OWNERS_ALIASES
+++ b/OWNERS_ALIASES
@@ -76,6 +76,7 @@ aliases:
   - vagababov
   - ZhiminXiang
   networking-reviewers:
+  - andrew-su
   - JRBANCEL
   - markusthoemmes
   - mdemirhan
diff --git a/cmd/queue/main.go b/cmd/queue/main.go
index 3e547bb5a..00a491c7b 100644
--- a/cmd/queue/main.go
+++ b/cmd/queue/main.go
@@ -62,7 +62,7 @@ const (
 	badProbeTemplate   = "unexpected probe header value: %s"
 	failingHealthcheck = "failing healthcheck"
 
-	healthURLTemplate = "http://127.0.0.1:%d"
+	healthURLPrefix = "http://127.0.0.1:"
 	// The 25 millisecond retry interval is an unscientific compromise between wanting to get
 	// started as early as possible while still wanting to give the container some breathing
 	// room to get up and running.
@@ -77,6 +77,13 @@ var (
 	readinessProbeTimeout = flag.Int("probe-period", -1, "run readiness probe with given timeout")
 )
 
+// Scaled down config to use during exec probing.
+type probeConfig struct {
+	QueueServingPort int `split_words:"true" required:"true"`
+	// DownwardAPI configuration for pod labels
+	DownwardAPILabelsPath string `split_words:"true"`
+}
+
 type config struct {
 	ContainerConcurrency   int    `split_words:"true" required:"true"`
 	QueueServingPort       int    `split_words:"true" required:"true"`
@@ -236,40 +243,35 @@ func knativeProbeHandler(healthState *health.State, prober func() bool, isAggres
 	}
 }
 
-func probeQueueHealthPath(port, timeoutSeconds int, env config) error {
-	if port <= 0 {
-		return fmt.Errorf("port must be a positive value, got %d", port)
+func probeQueueHealthPath(timeoutSeconds int, env probeConfig) error {
+	if env.QueueServingPort <= 0 {
+		return fmt.Errorf("port must be a positive value, got %d", env.QueueServingPort)
 	}
 
-	url := fmt.Sprintf(healthURLTemplate, port)
+	url := healthURLPrefix + strconv.Itoa(env.QueueServingPort)
 	timeoutDuration := readiness.PollTimeout
 	if timeoutSeconds != 0 {
 		timeoutDuration = time.Duration(timeoutSeconds) * time.Second
 	}
+
+	req, err := http.NewRequest(http.MethodGet, url, nil)
+	if err != nil {
+		return fmt.Errorf("probe failed: error creating request: %w", err)
+	}
+	// Add the header to indicate this is a probe request.
+	req.Header.Add(network.ProbeHeaderName, queue.Name)
+	req.Header.Add(network.UserAgentKey, network.QueueProxyUserAgent)
+
 	httpClient := &http.Client{
-		Transport: &http.Transport{
-			// Do not use the cached connection
-			DisableKeepAlives: true,
-		},
 		Timeout: timeoutDuration,
 	}
 	ctx, cancel := context.WithTimeout(context.Background(), timeoutDuration)
 	defer cancel()
-	stopCh := ctx.Done()
 
 	var lastErr error
 	// Using PollImmediateUntil instead of PollImmediate because if timeout is reached while waiting for first
 	// invocation of conditionFunc, it exits immediately without trying for a second time.
 	timeoutErr := wait.PollImmediateUntil(aggressivePollInterval, func() (bool, error) {
-		var req *http.Request
-		req, lastErr = http.NewRequest(http.MethodGet, url, nil)
-		if lastErr != nil {
-			// Return nil error for retrying
-			return false, nil
-		}
-		// Add the header to indicate this is a probe request.
-		req.Header.Add(network.ProbeHeaderName, queue.Name)
-		req.Header.Add(network.UserAgentKey, network.QueueProxyUserAgent)
 		res, lastErr := httpClient.Do(req)
 		if lastErr != nil {
 			// Return nil error for retrying
@@ -277,6 +279,7 @@ func probeQueueHealthPath(port, timeoutSeconds int, env config) error {
 		}
 		defer res.Body.Close()
 		success := health.IsHTTPProbeReady(res)
+
 		// The check for preferForScaledown() fails readiness faster
 		// in the presence of the label
 		if preferScaleDown, err := preferPodForScaledown(env.DownwardAPILabelsPath); err != nil {
@@ -285,7 +288,7 @@ func probeQueueHealthPath(port, timeoutSeconds int, env config) error {
 			return false, errors.New("failing probe deliberately for pod scaledown")
 		}
 		return success, nil
-	}, stopCh)
+	}, ctx.Done())
 
 	if lastErr != nil {
 		return fmt.Errorf("failed to probe: %w", lastErr)
@@ -302,16 +305,15 @@ func probeQueueHealthPath(port, timeoutSeconds int, env config) error {
 func main() {
 	flag.Parse()
 
-	// Parse the environment.
-	var env config
-	if err := envconfig.Process("", &env); err != nil {
-		fmt.Fprintln(os.Stderr, err)
-		os.Exit(1)
-	}
-
 	// If this is set, we run as a standalone binary to probe the queue-proxy.
 	if *readinessProbeTimeout >= 0 {
-		if err := probeQueueHealthPath(env.QueueServingPort, *readinessProbeTimeout, env); err != nil {
+		// Parse the environment.
+		var env probeConfig
+		if err := envconfig.Process("", &env); err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			os.Exit(1)
+		}
+		if err := probeQueueHealthPath(*readinessProbeTimeout, env); err != nil {
 			// used instead of the logger to produce a concise event message
 			fmt.Fprintln(os.Stderr, err)
 			os.Exit(1)
@@ -319,6 +321,13 @@ func main() {
 		os.Exit(0)
 	}
 
+	// Parse the environment.
+	var env config
+	if err := envconfig.Process("", &env); err != nil {
+		fmt.Fprintln(os.Stderr, err)
+		os.Exit(1)
+	}
+
 	// Setup the logger.
 	logger, _ = pkglogging.NewLogger(env.ServingLoggingConfig, env.ServingLoggingLevel)
 	logger = logger.Named("queueproxy")
@@ -350,7 +359,11 @@ func main() {
 	defer reportTicker.Stop()
 
 	stats := network.NewRequestStats(time.Now())
-	go queue.ReportStats(time.Now(), stats, reportTicker.C, promStatReporter.Report)
+	go func() {
+		for now := range reportTicker.C {
+			promStatReporter.Report(stats.Report(now))
+		}
+	}()
 
 	// Setup probe to run for checking user-application healthiness.
 	probe := buildProbe(env.ServingReadinessProbe)
diff --git a/config/core/configmaps/defaults.yaml b/config/core/configmaps/defaults.yaml
index 21dd61bb2..41af44219 100644
--- a/config/core/configmaps/defaults.yaml
+++ b/config/core/configmaps/defaults.yaml
@@ -58,6 +58,11 @@ data:
     # and the system default is used.
     revision-memory-request: "100M"  # 100 megabytes of memory
 
+    # revision-ephemeral-storage-request contains the ephemeral storage
+    # allocation to assign to revisions by default.  If omitted, no value is
+    # specified and the system default is used.
+    revision-ephemeral-storage-request: "500M"  # 500 megabytes of storage
+
     # revision-cpu-limit contains the cpu allocation to limit
     # revisions to by default.  If omitted, no value is specified
     # and the system default is used.
@@ -68,6 +73,11 @@ data:
     # and the system default is used.
     revision-memory-limit: "200M"  # 200 megabytes of memory
 
+    # revision-ephemeral-storage-limit contains the ephemeral storage
+    # allocation to limit revisions to by default.  If omitted, no value is
+    # specified and the system default is used.
+    revision-ephemeral-storage-limit: "750M"  # 750 megabytes of storage
+
     # container-name-template contains a template for the default
     # container name, if none is specified.  This field supports
     # Go templating and is supplied with the ObjectMeta of the
diff --git a/go.mod b/go.mod
index 01bcda4f4..134f2cbf8 100644
--- a/go.mod
+++ b/go.mod
@@ -42,8 +42,8 @@ require (
 	k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29
 	k8s.io/metrics v0.17.6
 	knative.dev/caching v0.0.0-20200521155757-e78d17bc250e
-	knative.dev/pkg v0.0.0-20200527173759-2d1a04d1ff82
-	knative.dev/test-infra v0.0.0-20200527185659-fc87694eb879
+	knative.dev/pkg v0.0.0-20200528190300-08a86da47d28
+	knative.dev/test-infra v0.0.0-20200528222301-350178ab2a0e
 )
 
 // pin the older grpc - see: https://github.com/grpc/grpc-go/issues/3180
diff --git a/go.sum b/go.sum
index fcb33da85..4feb2f05d 100644
--- a/go.sum
+++ b/go.sum
@@ -1321,8 +1321,8 @@ knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7/go.mod h1:QgNZTxnwpB/oSpNcfnL
 knative.dev/pkg v0.0.0-20200520073958-94316e20e860/go.mod h1:QgNZTxnwpB/oSpNcfnLVlw+WpEwwyKAvJlvR3hgeltA=
 knative.dev/pkg v0.0.0-20200527024749-495174c96651 h1:/LhCktTMhJrCDXGg3KCWeSQTLLGy3Ms4AaqM9O/05dI=
 knative.dev/pkg v0.0.0-20200527024749-495174c96651/go.mod h1:4ipprwHqpqiyYHbrEjSf1kNwL+sJOqHhj1ujO2WxvpE=
-knative.dev/pkg v0.0.0-20200527173759-2d1a04d1ff82 h1:uC65bOUmC5UsVJgUI28QWis/oiqnZrU49wLM828ioJM=
-knative.dev/pkg v0.0.0-20200527173759-2d1a04d1ff82/go.mod h1:ywZfX/0D8hoOf4uJVgeEvJpSmsOauQvr1IuAWld9UjM=
+knative.dev/pkg v0.0.0-20200528190300-08a86da47d28 h1:tLWWfrpGYT8vs/QLWYc7s0pqaw+ISz4UmM9AhrWBc8o=
+knative.dev/pkg v0.0.0-20200528190300-08a86da47d28/go.mod h1:ywZfX/0D8hoOf4uJVgeEvJpSmsOauQvr1IuAWld9UjM=
 knative.dev/test-infra v0.0.0-20200407185800-1b88cb3b45a5/go.mod h1:xcdUkMJrLlBswIZqL5zCuBFOC22WIPMQoVX1L35i0vQ=
 knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55/go.mod h1:WqF1Azka+FxPZ20keR2zCNtiQA1MP9ZB4BH4HuI+SIU=
 knative.dev/test-infra v0.0.0-20200513011557-d03429a76034 h1:JxqONCZVS7or+Fv3ebVQoipuIBH7Ig3Qbx170hgIF+A=
@@ -1333,8 +1333,8 @@ knative.dev/test-infra v0.0.0-20200519161858-554a95a37986 h1:ZDy43jkWPQ75d4l4DGy
 knative.dev/test-infra v0.0.0-20200519161858-554a95a37986/go.mod h1:LeNa1Wvn47efeQUkpkn3XG7Fx9Ga+rhAP13SZyjaEGg=
 knative.dev/test-infra v0.0.0-20200522180958-6a0a9b9d893a h1:c0qTABRcNoxZVu5gsryLWPZtGa/s4zsvovz0nGefuzg=
 knative.dev/test-infra v0.0.0-20200522180958-6a0a9b9d893a/go.mod h1:n9eQkzmSNj8BiqNFl1lzoz68D09uMeJfyOjc132Gbik=
-knative.dev/test-infra v0.0.0-20200527185659-fc87694eb879 h1:3spi67ya78+ofPZLHpJkZlBSj6pBowXa8/1Zdha6MCY=
-knative.dev/test-infra v0.0.0-20200527185659-fc87694eb879/go.mod h1:7JtOmoYiYxh2oFO23nwGanuJ6qT6mFrRlZV1LL6gomY=
+knative.dev/test-infra v0.0.0-20200528222301-350178ab2a0e h1:ZzmzPgCKdrFg2MA2sGtd9HrlIQ7XidnmMJB8RDEduO0=
+knative.dev/test-infra v0.0.0-20200528222301-350178ab2a0e/go.mod h1:7JtOmoYiYxh2oFO23nwGanuJ6qT6mFrRlZV1LL6gomY=
 modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
diff --git a/hack/update-k8s-deps.sh b/hack/update-k8s-deps.sh
index 2190eb56a..698f80a4a 100755
--- a/hack/update-k8s-deps.sh
+++ b/hack/update-k8s-deps.sh
@@ -57,6 +57,7 @@ OPENAPI_SHA=$(curl -L "https://api.github.com/repos/kubernetes/kube-openapi/git/
 
 update_module "k8s.io/kube-openapi" "${OPENAPI_SHA}"
 
+# See: https://github.com/kubernetes-sigs/custom-metrics-apiserver/issues/67
 echo "YOU MANUALLY NEED TO BUMP github.com/kubernetes-incubator/custom-metrics-apiserver"
 
 ./hack/update-deps.sh
diff --git a/pkg/apis/config/defaults.go b/pkg/apis/config/defaults.go
index a458e71fd..214e73641 100644
--- a/pkg/apis/config/defaults.go
+++ b/pkg/apis/config/defaults.go
@@ -83,10 +83,12 @@ func NewDefaultsConfigFromMap(data map[string]string) (*Defaults, error) {
 		cm.AsInt64("container-concurrency", &nc.ContainerConcurrency),
 		cm.AsInt64("container-concurrency-max-limit", &nc.ContainerConcurrencyMaxLimit),
 
-		asQuantity("revision-cpu-request", &nc.RevisionCPURequest),
-		asQuantity("revision-memory-request", &nc.RevisionMemoryRequest),
-		asQuantity("revision-cpu-limit", &nc.RevisionCPULimit),
-		asQuantity("revision-memory-limit", &nc.RevisionMemoryLimit),
+		cm.AsQuantity("revision-cpu-request", &nc.RevisionCPURequest),
+		cm.AsQuantity("revision-memory-request", &nc.RevisionMemoryRequest),
+		cm.AsQuantity("revision-ephemeral-storage-request", &nc.RevisionEphemeralStorageRequest),
+		cm.AsQuantity("revision-cpu-limit", &nc.RevisionCPULimit),
+		cm.AsQuantity("revision-memory-limit", &nc.RevisionMemoryLimit),
+		cm.AsQuantity("revision-ephemeral-storage-limit", &nc.RevisionEphemeralStorageLimit),
 	); err != nil {
 		return nil, err
 	}
@@ -139,10 +141,12 @@ type Defaults struct {
 	// a containerConcurrency of 0 (i.e. unbounded).
 	AllowContainerConcurrencyZero bool
 
-	RevisionCPURequest    *resource.Quantity
-	RevisionCPULimit      *resource.Quantity
-	RevisionMemoryRequest *resource.Quantity
-	RevisionMemoryLimit   *resource.Quantity
+	RevisionCPURequest              *resource.Quantity
+	RevisionCPULimit                *resource.Quantity
+	RevisionMemoryRequest           *resource.Quantity
+	RevisionMemoryLimit             *resource.Quantity
+	RevisionEphemeralStorageRequest *resource.Quantity
+	RevisionEphemeralStorageLimit   *resource.Quantity
 }
 
 // UserContainerName returns the name of the user container based on the context.
@@ -155,17 +159,3 @@ func (d *Defaults) UserContainerName(ctx context.Context) string {
 	}
 	return buf.String()
 }
-
-// asQuantity parses the value at key as a *resource.Quantity into the target, if it exists.
-func asQuantity(key string, target **resource.Quantity) cm.ParseFunc {
-	return func(data map[string]string) error {
-		if raw, ok := data[key]; !ok {
-			*target = nil
-		} else if val, err := resource.ParseQuantity(raw); err != nil {
-			return err
-		} else {
-			*target = &val
-		}
-		return nil
-	}
-}
diff --git a/pkg/apis/config/zz_generated.deepcopy.go b/pkg/apis/config/zz_generated.deepcopy.go
index 484d01ca3..bc124553d 100644
--- a/pkg/apis/config/zz_generated.deepcopy.go
+++ b/pkg/apis/config/zz_generated.deepcopy.go
@@ -43,6 +43,16 @@ func (in *Defaults) DeepCopyInto(out *Defaults) {
 		x := (*in).DeepCopy()
 		*out = &x
 	}
+	if in.RevisionEphemeralStorageRequest != nil {
+		in, out := &in.RevisionEphemeralStorageRequest, &out.RevisionEphemeralStorageRequest
+		x := (*in).DeepCopy()
+		*out = &x
+	}
+	if in.RevisionEphemeralStorageLimit != nil {
+		in, out := &in.RevisionEphemeralStorageLimit, &out.RevisionEphemeralStorageLimit
+		x := (*in).DeepCopy()
+		*out = &x
+	}
 	return
 }
 
diff --git a/pkg/apis/networking/v1alpha1/ingress_types.go b/pkg/apis/networking/v1alpha1/ingress_types.go
index 607249fd4..c004cb095 100644
--- a/pkg/apis/networking/v1alpha1/ingress_types.go
+++ b/pkg/apis/networking/v1alpha1/ingress_types.go
@@ -221,6 +221,14 @@ type HTTPIngressPath struct {
 	// If RewriteHost is specified, Splits must not be.
 	RewriteHost string `json:"rewriteHost,omitempty"`
 
+	// Headers defines header matching rules which is a map from a header name
+	// to HeaderMatch which specify a matching condition.
+	// When a request matched with all the header matching rules,
+	// the request is routed by the corresponding ingress rule.
+	// If it is empty, the headers are not used for matching
+	// +optional
+	Headers map[string]HeaderMatch `json:"headers,omitempty"`
+
 	// Splits defines the referenced service endpoints to which the traffic
 	// will be forwarded to.
 	//
@@ -361,3 +369,9 @@ const (
 func (t *Ingress) GetStatus() *duckv1.Status {
 	return &t.Status.Status
 }
+
+// HeaderMatch represents a matching value of Headers in HTTPIngressPath.
+// Currently, only the exact matching is supported.
+type HeaderMatch struct {
+	Exact string `json:"exact"`
+}
diff --git a/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go
index 0f4641d8b..bd179bb46 100644
--- a/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go
@@ -161,6 +161,13 @@ func (in *HTTP01Challenge) DeepCopy() *HTTP01Challenge {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *HTTPIngressPath) DeepCopyInto(out *HTTPIngressPath) {
 	*out = *in
+	if in.Headers != nil {
+		in, out := &in.Headers, &out.Headers
+		*out = make(map[string]HeaderMatch, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
 	if in.Splits != nil {
 		in, out := &in.Splits, &out.Splits
 		*out = make([]IngressBackendSplit, len(*in))
@@ -242,6 +249,22 @@ func (in *HTTPRetry) DeepCopy() *HTTPRetry {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *HeaderMatch) DeepCopyInto(out *HeaderMatch) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeaderMatch.
+func (in *HeaderMatch) DeepCopy() *HeaderMatch {
+	if in == nil {
+		return nil
+	}
+	out := new(HeaderMatch)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Ingress) DeepCopyInto(out *Ingress) {
 	*out = *in
diff --git a/pkg/apis/serving/v1/revision_defaults.go b/pkg/apis/serving/v1/revision_defaults.go
index 0849af374..224288f61 100644
--- a/pkg/apis/serving/v1/revision_defaults.go
+++ b/pkg/apis/serving/v1/revision_defaults.go
@@ -22,6 +22,7 @@ import (
 	"github.com/google/uuid"
 
 	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/api/resource"
 	"knative.dev/pkg/apis"
 	"knative.dev/pkg/kmeta"
 	"knative.dev/pkg/ptr"
@@ -69,28 +70,33 @@ func (rs *RevisionSpec) applyDefault(container *corev1.Container, cfg *config.Co
 	if container.Resources.Requests == nil {
 		container.Resources.Requests = corev1.ResourceList{}
 	}
-	if _, ok := container.Resources.Requests[corev1.ResourceCPU]; !ok {
-		if rc := cfg.Defaults.RevisionCPURequest; rc != nil {
-			container.Resources.Requests[corev1.ResourceCPU] = *rc
-		}
-	}
-	if _, ok := container.Resources.Requests[corev1.ResourceMemory]; !ok {
-		if rm := cfg.Defaults.RevisionMemoryRequest; rm != nil {
-			container.Resources.Requests[corev1.ResourceMemory] = *rm
-		}
-	}
 
 	if container.Resources.Limits == nil {
 		container.Resources.Limits = corev1.ResourceList{}
 	}
-	if _, ok := container.Resources.Limits[corev1.ResourceCPU]; !ok {
-		if rc := cfg.Defaults.RevisionCPULimit; rc != nil {
-			container.Resources.Limits[corev1.ResourceCPU] = *rc
+
+	for _, r := range []struct {
+		Name    corev1.ResourceName
+		Request *resource.Quantity
+		Limit   *resource.Quantity
+	}{{
+		Name:    corev1.ResourceCPU,
+		Request: cfg.Defaults.RevisionCPURequest,
+		Limit:   cfg.Defaults.RevisionCPULimit,
+	}, {
+		Name:    corev1.ResourceMemory,
+		Request: cfg.Defaults.RevisionMemoryRequest,
+		Limit:   cfg.Defaults.RevisionMemoryLimit,
+	}, {
+		Name:    corev1.ResourceEphemeralStorage,
+		Request: cfg.Defaults.RevisionEphemeralStorageRequest,
+		Limit:   cfg.Defaults.RevisionEphemeralStorageLimit,
+	}} {
+		if _, ok := container.Resources.Requests[r.Name]; !ok && r.Request != nil {
+			container.Resources.Requests[r.Name] = *r.Request
 		}
-	}
-	if _, ok := container.Resources.Limits[corev1.ResourceMemory]; !ok {
-		if rm := cfg.Defaults.RevisionMemoryLimit; rm != nil {
-			container.Resources.Limits[corev1.ResourceMemory] = *rm
+		if _, ok := container.Resources.Limits[r.Name]; !ok && r.Limit != nil {
+			container.Resources.Limits[r.Name] = *r.Limit
 		}
 	}
 
diff --git a/pkg/autoscaler/fake/fake_metric_client.go b/pkg/autoscaler/fake/fake_metric_client.go
index 17ffe5037..9454fa2af 100644
--- a/pkg/autoscaler/fake/fake_metric_client.go
+++ b/pkg/autoscaler/fake/fake_metric_client.go
@@ -67,32 +67,29 @@ func (mtp *ManualTickProvider) NewTicker(time.Duration) *time.Ticker {
 	}
 }
 
+// SetStableAndPanicConcurrency sets the stable and panic concurrencies.
+func (mc *MetricClient) SetStableAndPanicConcurrency(s, p float64) {
+	mc.StableConcurrency, mc.PanicConcurrency = s, p
+}
+
 // StableAndPanicConcurrency returns stable/panic concurrency stored in the object
 // and the result of Errf as the error.
-func (t *MetricClient) StableAndPanicConcurrency(key types.NamespacedName, now time.Time) (float64, float64, error) {
+func (mc *MetricClient) StableAndPanicConcurrency(key types.NamespacedName, now time.Time) (float64, float64, error) {
 	var err error
-	if t.ErrF != nil {
-		err = t.ErrF(key, now)
+	if mc.ErrF != nil {
+		err = mc.ErrF(key, now)
 	}
-	return t.StableConcurrency, t.PanicConcurrency, err
+	return mc.StableConcurrency, mc.PanicConcurrency, err
 }
 
 // StableAndPanicRPS returns stable/panic RPS stored in the object
 // and the result of Errf as the error.
-func (t *MetricClient) StableAndPanicRPS(key types.NamespacedName, now time.Time) (float64, float64, error) {
+func (mc *MetricClient) StableAndPanicRPS(key types.NamespacedName, now time.Time) (float64, float64, error) {
 	var err error
-	if t.ErrF != nil {
-		err = t.ErrF(key, now)
+	if mc.ErrF != nil {
+		err = mc.ErrF(key, now)
 	}
-	return t.StableRPS, t.PanicRPS, err
-}
-
-// StaticMetricClient returns stable/panic concurrency and RPS with static value, i.e. 10.
-var StaticMetricClient = MetricClient{
-	StableConcurrency: 10.0,
-	PanicConcurrency:  10.0,
-	StableRPS:         10.0,
-	PanicRPS:          10.0,
+	return mc.StableRPS, mc.PanicRPS, err
 }
 
 // Endpoints is used to create endpoints.
diff --git a/pkg/autoscaler/scaling/autoscaler.go b/pkg/autoscaler/scaling/autoscaler.go
index 42997a0b5..31eb7ed90 100644
--- a/pkg/autoscaler/scaling/autoscaler.go
+++ b/pkg/autoscaler/scaling/autoscaler.go
@@ -200,6 +200,9 @@ func (a *Autoscaler) Scale(ctx context.Context, now time.Time) ScaleResult {
 		logger.Info("PANICKING.")
 		a.panicTime = now
 		pkgmetrics.Record(a.reporterCtx, panicM.M(1))
+	} else if isOverPanicThreshold {
+		// If we're still over panic threshold right now — extend the panic window.
+		a.panicTime = now
 	} else if !a.panicTime.IsZero() && !isOverPanicThreshold && a.panicTime.Add(spec.StableWindow).Before(now) {
 		// Stop panicking after the surge has made its way into the stable metric.
 		logger.Info("Un-panicking.")
@@ -220,7 +223,6 @@ func (a *Autoscaler) Scale(ctx context.Context, now time.Time) ScaleResult {
 		// We do not scale down while in panic mode. Only increases will be applied.
 		if desiredPodCount > a.maxPanicPods {
 			logger.Infof("Increasing pods from %d to %d.", originalReadyPodsCount, desiredPodCount)
-			a.panicTime = now
 			a.maxPanicPods = desiredPodCount
 		} else if desiredPodCount < a.maxPanicPods {
 			logger.Infof("Skipping decrease from %d to %d.", a.maxPanicPods, desiredPodCount)
diff --git a/pkg/network/network.go b/pkg/network/network.go
index 7eef7a361..89400e881 100644
--- a/pkg/network/network.go
+++ b/pkg/network/network.go
@@ -137,6 +137,19 @@ const (
 	// AutoscalingUserAgent is the user-agent header value set in probe
 	// requests sent by autoscaling implementations.
 	AutoscalingUserAgent = "Knative-Autoscaling-Probe"
+
+	// TagHeaderName is the name of the header entry which has a tag name as value.
+	// The tag name specifies which route was expected to be chosen by Ingress.
+	TagHeaderName = "Knative-Serving-Tag"
+
+	// DefaultRouteHeaderName is the name of the header entry
+	// identifying whether a request is routed via the default route or not.
+	// It has one of the string value "true" or "false".
+	DefaultRouteHeaderName = "Knative-Serving-Default-Route"
+
+	// TagHeaderBasedRoutingKey is the name of the configuration entry
+	// that specifies enabling tag header based routing or not.
+	TagHeaderBasedRoutingKey = "tagHeaderBasedRouting"
 )
 
 // DomainTemplateValues are the available properties people can choose from
@@ -195,6 +208,9 @@ type Config struct {
 
 	// DefaultCertificateClass specifies the default Certificate class.
 	DefaultCertificateClass string
+
+	// TagHeaderBasedRouting specifies if TagHeaderBasedRouting is enabled or not.
+	TagHeaderBasedRouting bool
 }
 
 // HTTPProtocol indicates a type of HTTP endpoint behavior
@@ -270,6 +286,7 @@ func NewConfigFromMap(data map[string]string) (*Config, error) {
 	templateCache.Add(nc.TagTemplate, t)
 
 	nc.AutoTLS = strings.EqualFold(data[AutoTLSKey], "enabled")
+	nc.TagHeaderBasedRouting = strings.EqualFold(data[TagHeaderBasedRoutingKey], "enabled")
 
 	switch strings.ToLower(data[HTTPProtocolKey]) {
 	case "", string(HTTPEnabled):
diff --git a/pkg/queue/prometheus_stats_reporter.go b/pkg/queue/prometheus_stats_reporter.go
index eb301245b..95bb7de16 100644
--- a/pkg/queue/prometheus_stats_reporter.go
+++ b/pkg/queue/prometheus_stats_reporter.go
@@ -24,6 +24,7 @@ import (
 
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
+	"knative.dev/serving/pkg/network"
 )
 
 const (
@@ -125,13 +126,13 @@ func NewPrometheusStatsReporter(namespace, config, revision, pod string, reporti
 }
 
 // Report captures request metrics.
-func (r *PrometheusStatsReporter) Report(acr float64, apcr float64, rc float64, prc float64) {
+func (r *PrometheusStatsReporter) Report(stats network.RequestStatsReport) {
 	// Requests per second is a rate over time while concurrency is not.
 	rp := r.reportingPeriod.Seconds()
-	r.requestsPerSecond.Set(rc / rp)
-	r.proxiedRequestsPerSecond.Set(prc / rp)
-	r.averageConcurrentRequests.Set(acr)
-	r.averageProxiedConcurrentRequests.Set(apcr)
+	r.requestsPerSecond.Set(stats.RequestCount / rp)
+	r.proxiedRequestsPerSecond.Set(stats.ProxiedRequestCount / rp)
+	r.averageConcurrentRequests.Set(stats.AverageConcurrency)
+	r.averageProxiedConcurrentRequests.Set(stats.AverageProxiedConcurrency)
 	r.processUptime.Set(time.Since(r.startTime).Seconds())
 }
 
diff --git a/pkg/queue/stats.go b/pkg/queue/stats.go
deleted file mode 100644
index 0c7216463..000000000
--- a/pkg/queue/stats.go
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-Copyright 2018 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 queue
-
-import (
-	"time"
-
-	"knative.dev/serving/pkg/network"
-)
-
-// ReportStats continually processes network events from reqCh and reports
-// aggregated stats via the `report` function whenever reportCh ticks.
-func ReportStats(startedAt time.Time, stats *network.RequestStats, reportCh <-chan time.Time, report func(float64, float64, float64, float64)) {
-	for {
-		select {
-		case now := <-reportCh:
-			stat := stats.Report(now)
-			report(
-				stat.AverageConcurrency,
-				stat.AverageProxiedConcurrency,
-				stat.RequestCount,
-				stat.ProxiedRequestCount,
-			)
-		}
-	}
-}
diff --git a/pkg/reconciler/revision/resources/queue.go b/pkg/reconciler/revision/resources/queue.go
index 40ed65db9..18094b400 100644
--- a/pkg/reconciler/revision/resources/queue.go
+++ b/pkg/reconciler/revision/resources/queue.go
@@ -148,11 +148,19 @@ func makeQueueProbe(in *corev1.Probe) *corev1.Probe {
 					Command: []string{"/ko-app/queue", "-probe-period", "0"},
 				},
 			},
-			// We want to mark the service as not ready as soon as the
-			// PreStop handler is called, so we need to check a little
-			// bit more often than the default.  It is a small
-			// sacrifice for a low rate of 503s.
-			PeriodSeconds: 1,
+			// The exec probe enables us to retry failed probes quickly to get sub-second
+			// resolution and achieve faster cold-starts.  However, for draining pods as
+			// part of the K8s lifecycle this period will bound the tail of how quickly
+			// we can remove a Pod's endpoint from the K8s service.
+			//
+			// The trade-off here is that exec probes cost CPU to run, and for idle pods
+			// (e.g. due to minScale) we see ~50m/{period} of idle CPU usage in the
+			// queue-proxy.  So while setting this to 1s results in slightly faster drains
+			// it also means that in the steady state the queue-proxy is consuming 10x
+			// more CPU due to probes than with a period of 10s.
+			//
+			// See also: https://github.com/knative/serving/issues/8147
+			PeriodSeconds: 10,
 			// We keep the connection open for a while because we're
 			// actively probing the user-container on that endpoint and
 			// thus don't want to be limited by K8s granularity here.
diff --git a/pkg/reconciler/route/resources/ingress.go b/pkg/reconciler/route/resources/ingress.go
index fa4e7956b..9f143e846 100644
--- a/pkg/reconciler/route/resources/ingress.go
+++ b/pkg/reconciler/route/resources/ingress.go
@@ -30,6 +30,8 @@ import (
 	netv1alpha1 "knative.dev/serving/pkg/apis/networking/v1alpha1"
 	"knative.dev/serving/pkg/apis/serving"
 	servingv1 "knative.dev/serving/pkg/apis/serving/v1"
+	"knative.dev/serving/pkg/network"
+	"knative.dev/serving/pkg/reconciler/route/config"
 	"knative.dev/serving/pkg/reconciler/route/domains"
 	"knative.dev/serving/pkg/reconciler/route/resources/labels"
 	"knative.dev/serving/pkg/reconciler/route/resources/names"
@@ -99,6 +101,8 @@ func MakeIngressSpec(
 	rules := make([]netv1alpha1.IngressRule, 0, len(names))
 	challengeHosts := getChallengeHosts(acmeChallenges)
 
+	networkConfig := config.FromContext(ctx).Network
+
 	for _, name := range names {
 		visibilities := []netv1alpha1.IngressVisibility{netv1alpha1.IngressVisibilityClusterLocal}
 		// If this is a public target (or not being marked as cluster-local), we also make public rule.
@@ -111,6 +115,33 @@ func MakeIngressSpec(
 				return netv1alpha1.IngressSpec{}, err
 			}
 			rule := *makeIngressRule([]string{domain}, r.Namespace, visibility, targets[name])
+			if networkConfig.TagHeaderBasedRouting {
+				if rule.HTTP.Paths[0].AppendHeaders == nil {
+					rule.HTTP.Paths[0].AppendHeaders = make(map[string]string)
+				}
+
+				if name == traffic.DefaultTarget {
+					// To provide a information if a request is routed via the "default route" or not,
+					// the header "Knative-Serving-Default-Route: true" is appended here.
+					// If the header has "true" and there is a "Knative-Serving-Tag" header,
+					// then the request is having the undefined tag header, which will be observed in queue-proxy.
+					rule.HTTP.Paths[0].AppendHeaders[network.DefaultRouteHeaderName] = "true"
+					// Add ingress paths for a request with the tag header.
+					// If a request has one of the `names`(tag name) except the default path,
+					// the request will be routed via one of the ingress paths, corresponding to the tag name.
+					rule.HTTP.Paths = append(
+						makeTagBasedRoutingIngressPaths(r.Namespace, targets, names), rule.HTTP.Paths...)
+				} else {
+					// If a request is routed by a tag-attached hostname instead of the tag header,
+					// the request may not have the tag header "Knative-Serving-Tag",
+					// even though the ingress path used in the case is also originated
+					// from the same Knative route with the ingress path for the tag based routing.
+					//
+					// To prevent such inconsistency,
+					// the tag header is appended with the tag corresponding to the tag-attached hostname
+					rule.HTTP.Paths[0].AppendHeaders[network.TagHeaderName] = name
+				}
+			}
 			// If this is a public rule, we need to configure ACME challenge paths.
 			if visibility == netv1alpha1.IngressVisibilityExternalIP {
 				rule.HTTP.Paths = append(
@@ -173,6 +204,32 @@ func makeACMEIngressPaths(challenges map[string]netv1alpha1.HTTP01Challenge, dom
 }
 
 func makeIngressRule(domains []string, ns string, visibility netv1alpha1.IngressVisibility, targets traffic.RevisionTargets) *netv1alpha1.IngressRule {
+	return &netv1alpha1.IngressRule{
+		Hosts:      domains,
+		Visibility: visibility,
+		HTTP: &netv1alpha1.HTTPIngressRuleValue{
+			Paths: []netv1alpha1.HTTPIngressPath{
+				*makeBaseIngressPath(ns, targets),
+			},
+		},
+	}
+}
+
+func makeTagBasedRoutingIngressPaths(ns string, targets map[string]traffic.RevisionTargets, names []string) []netv1alpha1.HTTPIngressPath {
+	paths := make([]netv1alpha1.HTTPIngressPath, 0, len(names))
+
+	for _, name := range names {
+		if name != traffic.DefaultTarget {
+			path := makeBaseIngressPath(ns, targets[name])
+			path.Headers = map[string]netv1alpha1.HeaderMatch{network.TagHeaderName: {Exact: name}}
+			paths = append(paths, *path)
+		}
+	}
+
+	return paths
+}
+
+func makeBaseIngressPath(ns string, targets traffic.RevisionTargets) *netv1alpha1.HTTPIngressPath {
 	// Optimistically allocate |targets| elements.
 	splits := make([]netv1alpha1.IngressBackendSplit, 0, len(targets))
 	for _, t := range targets {
@@ -196,14 +253,8 @@ func makeIngressRule(domains []string, ns string, visibility netv1alpha1.Ingress
 		})
 	}
 
-	return &netv1alpha1.IngressRule{
-		Hosts:      domains,
-		Visibility: visibility,
-		HTTP: &netv1alpha1.HTTPIngressRuleValue{
-			Paths: []netv1alpha1.HTTPIngressPath{{
-				Splits: splits,
-				// TODO(lichuqiang): #2201, plumbing to config timeout and retries.
-			}},
-		},
+	return &netv1alpha1.HTTPIngressPath{
+		Splits: splits,
+		// TODO(lichuqiang): #2201, plumbing to config timeout and retries.
 	}
 }
diff --git a/third_party/cert-manager-0.12.0/net-certmanager.yaml b/third_party/cert-manager-0.12.0/net-certmanager.yaml
index 281d85256..f8cca12a7 100644
--- a/third_party/cert-manager-0.12.0/net-certmanager.yaml
+++ b/third_party/cert-manager-0.12.0/net-certmanager.yaml
@@ -18,7 +18,7 @@ metadata:
   # These are the permissions needed by the `cert-manager` `Certificate` implementation.
   name: knative-serving-certmanager
   labels:
-    serving.knative.dev/release: "v20200527-9146236"
+    serving.knative.dev/release: "v20200529-9146236"
     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: "v20200527-9146236"
+    serving.knative.dev/release: "v20200529-9146236"
 webhooks:
 - admissionReviewVersions:
   - v1beta1
@@ -86,7 +86,7 @@ metadata:
   name: net-certmanager-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200527-9146236"
+    serving.knative.dev/release: "v20200529-9146236"
 
 ---
 # Copyright 2020 The Knative Authors
@@ -109,7 +109,7 @@ metadata:
   name: config-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200527-9146236"
+    serving.knative.dev/release: "v20200529-9146236"
     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: "v20200527-9146236"
+    serving.knative.dev/release: "v20200529-9146236"
     networking.knative.dev/certificate-provider: cert-manager
 spec:
   selector:
@@ -168,7 +168,7 @@ spec:
         cluster-autoscaler.kubernetes.io/safe-to-evict: "true"
       labels:
         app: networking-certmanager
-        serving.knative.dev/release: "v20200527-9146236"
+        serving.knative.dev/release: "v20200529-9146236"
     spec:
       serviceAccountName: controller
       containers:
@@ -208,7 +208,7 @@ kind: Service
 metadata:
   labels:
     app: networking-certmanager
-    serving.knative.dev/release: "v20200527-9146236"
+    serving.knative.dev/release: "v20200529-9146236"
     networking.knative.dev/certificate-provider: cert-manager
   name: networking-certmanager
   namespace: knative-serving
@@ -245,7 +245,7 @@ metadata:
   name: net-certmanager-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200527-9146236"
+    serving.knative.dev/release: "v20200529-9146236"
 spec:
   selector:
     matchLabels:
@@ -258,7 +258,7 @@ spec:
       labels:
         app: net-certmanager-webhook
         role: net-certmanager-webhook
-        serving.knative.dev/release: "v20200527-9146236"
+        serving.knative.dev/release: "v20200529-9146236"
     spec:
       serviceAccountName: controller
       containers:
@@ -319,7 +319,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: net-certmanager-webhook
-    serving.knative.dev/release: "v20200527-9146236"
+    serving.knative.dev/release: "v20200529-9146236"
 spec:
   ports:
   - # Define metrics and profiling for them to be accessible within service meshes.
diff --git a/third_party/net-istio.yaml b/third_party/net-istio.yaml
index d3ba63e49..736377594 100644
--- a/third_party/net-istio.yaml
+++ b/third_party/net-istio.yaml
@@ -18,7 +18,7 @@ metadata:
   # These are the permissions needed by the Istio Ingress implementation.
   name: knative-serving-istio
   labels:
-    serving.knative.dev/release: "v20200527-e824460"
+    serving.knative.dev/release: "v20200531-cb895b4"
     serving.knative.dev/controller: "true"
     networking.knative.dev/ingress-provider: istio
 rules:
@@ -48,7 +48,7 @@ metadata:
   name: knative-ingress-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200527-e824460"
+    serving.knative.dev/release: "v20200531-cb895b4"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -85,7 +85,7 @@ metadata:
   name: cluster-local-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200527-e824460"
+    serving.knative.dev/release: "v20200531-cb895b4"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -118,7 +118,7 @@ kind: MutatingWebhookConfiguration
 metadata:
   name: webhook.istio.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200527-e824460"
+    serving.knative.dev/release: "v20200531-cb895b4"
     networking.knative.dev/ingress-provider: istio
 webhooks:
 - admissionReviewVersions:
@@ -154,7 +154,7 @@ kind: ValidatingWebhookConfiguration
 metadata:
   name: config.webhook.istio.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200527-e824460"
+    serving.knative.dev/release: "v20200531-cb895b4"
     networking.knative.dev/ingress-provider: istio
 webhooks:
 - admissionReviewVersions:
@@ -192,7 +192,7 @@ metadata:
   name: istio-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200527-e824460"
+    serving.knative.dev/release: "v20200531-cb895b4"
     networking.knative.dev/ingress-provider: istio
 
 ---
@@ -216,7 +216,7 @@ metadata:
   name: config-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200527-e824460"
+    serving.knative.dev/release: "v20200531-cb895b4"
     networking.knative.dev/ingress-provider: istio
 data:
   _example: |
@@ -286,7 +286,7 @@ metadata:
   name: networking-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200527-e824460"
+    serving.knative.dev/release: "v20200531-cb895b4"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -302,14 +302,14 @@ spec:
         sidecar.istio.io/inject: "false"
       labels:
         app: networking-istio
-        serving.knative.dev/release: "v20200527-e824460"
+        serving.knative.dev/release: "v20200531-cb895b4"
     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:3be0005c07a7681d2f122833835f277e8441246d3fd66d2ae91b1ea66ddef080
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/controller@sha256:7b787833cbd5706ce9fd58f802b6b622608e9bbf8c2e6593579ae4081b6c32d3
         resources:
           requests:
             cpu: 30m
@@ -361,7 +361,7 @@ metadata:
   name: istio-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200527-e824460"
+    serving.knative.dev/release: "v20200531-cb895b4"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -375,14 +375,14 @@ spec:
       labels:
         app: istio-webhook
         role: istio-webhook
-        serving.knative.dev/release: "v20200527-e824460"
+        serving.knative.dev/release: "v20200531-cb895b4"
     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:d7230116a940946f12aef40a13c0f127e7c30eba165f88e58f4a425744fa8bfd
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/webhook@sha256:8511f6b143a2b804c9623e6f07ba2b468e778aba9c74a55782fd32de373703a2
         resources:
           requests:
             cpu: 20m
@@ -436,7 +436,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: istio-webhook
-    serving.knative.dev/release: "v20200527-e824460"
+    serving.knative.dev/release: "v20200531-cb895b4"
     networking.knative.dev/ingress-provider: istio
 spec:
   ports:
diff --git a/vendor/knative.dev/pkg/apis/duck/v1/status_types.go b/vendor/knative.dev/pkg/apis/duck/v1/status_types.go
index 53006708f..aab246b1f 100644
--- a/vendor/knative.dev/pkg/apis/duck/v1/status_types.go
+++ b/vendor/knative.dev/pkg/apis/duck/v1/status_types.go
@@ -96,7 +96,7 @@ func (source *Status) ConvertTo(ctx context.Context, sink *Status, predicates ..
 	sink.ObservedGeneration = source.ObservedGeneration
 	if source.Annotations != nil {
 		// This will deep copy the map.
-		sink.Annotations = kmeta.UnionMaps(source.Annotations, nil)
+		sink.Annotations = kmeta.UnionMaps(source.Annotations)
 	}
 
 	conditions := make(apis.Conditions, 0, len(source.Conditions))
diff --git a/vendor/knative.dev/pkg/apis/duck/v1beta1/status_types.go b/vendor/knative.dev/pkg/apis/duck/v1beta1/status_types.go
index d90e7dc69..107592e85 100644
--- a/vendor/knative.dev/pkg/apis/duck/v1beta1/status_types.go
+++ b/vendor/knative.dev/pkg/apis/duck/v1beta1/status_types.go
@@ -109,7 +109,8 @@ func (s *Status) GetCondition(t apis.ConditionType) *apis.Condition {
 func (source *Status) ConvertTo(ctx context.Context, sink *Status) {
 	sink.ObservedGeneration = source.ObservedGeneration
 	if source.Annotations != nil {
-		sink.Annotations = kmeta.UnionMaps(source.Annotations, nil)
+		// This will deep copy the map.
+		sink.Annotations = kmeta.UnionMaps(source.Annotations)
 	}
 	for _, c := range source.Conditions {
 		switch c.Type {
diff --git a/vendor/knative.dev/pkg/configmap/parse.go b/vendor/knative.dev/pkg/configmap/parse.go
index 6479598b7..210d2114e 100644
--- a/vendor/knative.dev/pkg/configmap/parse.go
+++ b/vendor/knative.dev/pkg/configmap/parse.go
@@ -22,6 +22,7 @@ import (
 	"strings"
 	"time"
 
+	"k8s.io/apimachinery/pkg/api/resource"
 	"k8s.io/apimachinery/pkg/util/sets"
 )
 
@@ -114,6 +115,21 @@ func AsStringSet(key string, target *sets.String) ParseFunc {
 	}
 }
 
+// AsQuantity parses the value at key as a *resource.Quantity into the target, if it exists
+func AsQuantity(key string, target **resource.Quantity) ParseFunc {
+	return func(data map[string]string) error {
+		if raw, ok := data[key]; ok {
+			val, err := resource.ParseQuantity(raw)
+			if err != nil {
+				return fmt.Errorf("failed to parse %q: %w", key, err)
+			}
+
+			*target = &val
+		}
+		return nil
+	}
+}
+
 // Parse parses the given map using the parser functions passed in.
 func Parse(data map[string]string, parsers ...ParseFunc) error {
 	for _, parse := range parsers {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 41b59cddf..19eb8bc0c 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1196,7 +1196,7 @@ knative.dev/caching/pkg/client/injection/informers/caching/v1alpha1/image/fake
 knative.dev/caching/pkg/client/injection/informers/factory
 knative.dev/caching/pkg/client/injection/informers/factory/fake
 knative.dev/caching/pkg/client/listers/caching/v1alpha1
-# knative.dev/pkg v0.0.0-20200527173759-2d1a04d1ff82
+# knative.dev/pkg v0.0.0-20200528190300-08a86da47d28
 ## explicit
 knative.dev/pkg/apiextensions/storageversion
 knative.dev/pkg/apiextensions/storageversion/cmd/migrate
@@ -1312,7 +1312,7 @@ knative.dev/pkg/webhook/resourcesemantics/conversion
 knative.dev/pkg/webhook/resourcesemantics/defaulting
 knative.dev/pkg/webhook/resourcesemantics/validation
 knative.dev/pkg/websocket
-# knative.dev/test-infra v0.0.0-20200527185659-fc87694eb879
+# knative.dev/test-infra v0.0.0-20200528222301-350178ab2a0e
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/structured-merge-diff/v2 v2.0.1


Test patch:
diff --git a/cmd/autoscaler/main_test.go b/cmd/autoscaler/main_test.go
index fbbf16393..cfcbb28c0 100644
--- a/cmd/autoscaler/main_test.go
+++ b/cmd/autoscaler/main_test.go
@@ -21,7 +21,6 @@ import (
 	"strings"
 	"testing"
 
-	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	kubeinformers "k8s.io/client-go/informers"
 	fakek8s "k8s.io/client-go/kubernetes/fake"
@@ -30,11 +29,6 @@ import (
 	"knative.dev/serving/pkg/autoscaler/scaling"
 )
 
-const (
-	testNamespace = "test-namespace"
-	testRevision  = "test-Revision"
-)
-
 var (
 	kubeClient   = fakek8s.NewSimpleClientset()
 	kubeInformer = kubeinformers.NewSharedInformerFactory(kubeClient, 0)
@@ -66,11 +60,11 @@ func TestUniscalerFactoryFailures(t *testing.T) {
 		}, "max length must be 255 characters",
 	}}
 
-	uniScalerFactory := getTestUniScalerFactory()
+	uniScalerFactory := testUniScalerFactory()
 	decider := &scaling.Decider{
 		ObjectMeta: metav1.ObjectMeta{
-			Namespace: testNamespace,
-			Name:      testRevision,
+			Namespace: autoscalerfake.TestNamespace,
+			Name:      autoscalerfake.TestRevision,
 		},
 		Spec: scaling.DeciderSpec{
 			ServiceName: "wholesome-service",
@@ -94,7 +88,7 @@ func TestUniscalerFactoryFailures(t *testing.T) {
 	// Now blank out service name and give correct labels.
 	decider.Spec.ServiceName = ""
 	decider.Labels = map[string]string{
-		serving.RevisionLabelKey:      testRevision,
+		serving.RevisionLabelKey:      autoscalerfake.TestRevision,
 		serving.ServiceLabelKey:       "some-nice-service",
 		serving.ConfigurationLabelKey: "test-config",
 	}
@@ -108,28 +102,16 @@ func TestUniscalerFactoryFailures(t *testing.T) {
 	}
 }
 
-func endpoints(ns, n string) {
-	ep := &corev1.Endpoints{
-		ObjectMeta: metav1.ObjectMeta{
-			Namespace: ns,
-			Name:      n,
-		},
-		Subsets: []corev1.EndpointSubset{{}},
-	}
-	kubeClient.CoreV1().Endpoints(ns).Create(ep)
-	kubeInformer.Core().V1().Endpoints().Informer().GetIndexer().Add(ep)
-}
-
 func TestUniScalerFactoryFunc(t *testing.T) {
-	endpoints(testNamespace, "magic-services-offered")
-	uniScalerFactory := getTestUniScalerFactory()
+	autoscalerfake.Endpoints(1, "magic-services-offered")
+	uniScalerFactory := testUniScalerFactory()
 	for _, srv := range []string{"some", ""} {
 		decider := &scaling.Decider{
 			ObjectMeta: metav1.ObjectMeta{
-				Namespace: testNamespace,
-				Name:      testRevision,
+				Namespace: autoscalerfake.TestNamespace,
+				Name:      autoscalerfake.TestRevision,
 				Labels: map[string]string{
-					serving.RevisionLabelKey:      testRevision,
+					serving.RevisionLabelKey:      autoscalerfake.TestRevision,
 					serving.ServiceLabelKey:       srv,
 					serving.ConfigurationLabelKey: "test-config",
 				},
@@ -145,6 +127,6 @@ func TestUniScalerFactoryFunc(t *testing.T) {
 	}
 }
 
-func getTestUniScalerFactory() func(decider *scaling.Decider) (scaling.UniScaler, error) {
-	return uniScalerFactoryFunc(kubeInformer.Core().V1().Endpoints(), &autoscalerfake.StaticMetricClient)
+func testUniScalerFactory() func(decider *scaling.Decider) (scaling.UniScaler, error) {
+	return uniScalerFactoryFunc(kubeInformer.Core().V1().Endpoints(), &autoscalerfake.MetricClient{})
 }
diff --git a/cmd/queue/main_test.go b/cmd/queue/main_test.go
index 50f66b638..25664ccbb 100644
--- a/cmd/queue/main_test.go
+++ b/cmd/queue/main_test.go
@@ -169,9 +169,7 @@ func TestProbeHandler(t *testing.T) {
 }
 
 func TestProbeQueueInvalidPort(t *testing.T) {
-	const port = 0 // invalid port
-
-	if err := probeQueueHealthPath(port, 1, config{}); err == nil {
+	if err := probeQueueHealthPath(1, probeConfig{QueueServingPort: 0}); err == nil {
 		t.Error("Expected error, got nil")
 	} else if diff := cmp.Diff(err.Error(), "port must be a positive value, got 0"); diff != "" {
 		t.Errorf("Unexpected not ready message: %s", diff)
@@ -179,9 +177,7 @@ func TestProbeQueueInvalidPort(t *testing.T) {
 }
 
 func TestProbeQueueConnectionFailure(t *testing.T) {
-	port := 12345 // some random port (that's not listening)
-
-	if err := probeQueueHealthPath(port, 1, config{}); err == nil {
+	if err := probeQueueHealthPath(1, probeConfig{QueueServingPort: 12345}); err == nil {
 		t.Error("Expected error, got nil")
 	}
 }
@@ -205,7 +201,7 @@ func TestProbeQueueNotReady(t *testing.T) {
 		t.Fatalf("Failed to convert port(%s) to int: %v", u.Port(), err)
 	}
 
-	err = probeQueueHealthPath(port, 1, config{})
+	err = probeQueueHealthPath(1, probeConfig{QueueServingPort: port})
 
 	if diff := cmp.Diff(err.Error(), "probe returned not ready"); diff != "" {
 		t.Errorf("Unexpected not ready message: %s", diff)
@@ -235,7 +231,7 @@ func TestProbeQueueReady(t *testing.T) {
 		t.Fatalf("Failed to convert port(%s) to int: %v", u.Port(), err)
 	}
 
-	if err = probeQueueHealthPath(port, 1, config{}); err != nil {
+	if err = probeQueueHealthPath(1, probeConfig{QueueServingPort: port}); err != nil {
 		t.Errorf("probeQueueHealthPath(%d, 1s) = %s", port, err)
 	}
 
@@ -276,7 +272,10 @@ func TestProbeFailFast(t *testing.T) {
 	f.Close()
 
 	start := time.Now()
-	if err = probeQueueHealthPath(port, 1 /*seconds*/, config{DownwardAPILabelsPath: f.Name()}); err == nil {
+	if err = probeQueueHealthPath(1 /*seconds*/, probeConfig{
+		QueueServingPort:      port,
+		DownwardAPILabelsPath: f.Name(),
+	}); err == nil {
 		t.Error("probeQueueHealthPath did not fail")
 	}
 
@@ -307,7 +306,7 @@ func TestProbeQueueTimeout(t *testing.T) {
 	}
 
 	timeout := 1
-	if err = probeQueueHealthPath(port, timeout, config{}); err == nil {
+	if err = probeQueueHealthPath(timeout, probeConfig{QueueServingPort: port}); err == nil {
 		t.Errorf("Expected probeQueueHealthPath(%d, %v) to return timeout error", port, timeout)
 	}
 
@@ -341,7 +340,7 @@ func TestProbeQueueDelayedReady(t *testing.T) {
 	}
 
 	timeout := 0
-	if err := probeQueueHealthPath(port, timeout, config{}); err != nil {
+	if err := probeQueueHealthPath(timeout, probeConfig{QueueServingPort: port}); err != nil {
 		t.Errorf("probeQueueHealthPath(%d) = %s", port, err)
 	}
 }
@@ -510,7 +509,11 @@ func BenchmarkProxyHandler(b *testing.B) {
 	if err != nil {
 		b.Fatal("Failed to create stats reporter:", err)
 	}
-	go queue.ReportStats(time.Now(), stats, reportTicker.C, promStatReporter.Report)
+	go func() {
+		for now := range reportTicker.C {
+			promStatReporter.Report(stats.Report(now))
+		}
+	}()
 	req := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
 	req.Header.Set(network.OriginalHostHeader, wantHost)
 
diff --git a/pkg/apis/config/defaults_test.go b/pkg/apis/config/defaults_test.go
index 1115f649c..5fdaf9207 100644
--- a/pkg/apis/config/defaults_test.go
+++ b/pkg/apis/config/defaults_test.go
@@ -48,6 +48,7 @@ func TestDefaultsConfigurationFromFile(t *testing.T) {
 	// So for this test we ignore those, but verify the other fields.
 	got.RevisionCPULimit, got.RevisionCPURequest = nil, nil
 	got.RevisionMemoryLimit, got.RevisionMemoryRequest = nil, nil
+	got.RevisionEphemeralStorageLimit, got.RevisionEphemeralStorageRequest = nil, nil
 	want := defaultDefaultsConfig()
 	if diff := cmp.Diff(want, got); diff != "" {
 		t.Error("Example does not represent default config: diff(-want,+got)\n", diff)
diff --git a/pkg/apis/serving/k8s_validation_test.go b/pkg/apis/serving/k8s_validation_test.go
index e78a74a2d..20ed14685 100644
--- a/pkg/apis/serving/k8s_validation_test.go
+++ b/pkg/apis/serving/k8s_validation_test.go
@@ -386,10 +386,10 @@ func TestContainerValidation(t *testing.T) {
 			Image: "foo",
 			Resources: corev1.ResourceRequirements{
 				Limits: corev1.ResourceList{
-					corev1.ResourceName("memory"): resource.MustParse("250M"),
+					corev1.ResourceMemory: resource.MustParse("250M"),
 				},
 				Requests: corev1.ResourceList{
-					corev1.ResourceName("cpu"): resource.MustParse("25m"),
+					corev1.ResourceCPU: resource.MustParse("25m"),
 				},
 			},
 		},
diff --git a/pkg/apis/serving/v1/revision_defaults_test.go b/pkg/apis/serving/v1/revision_defaults_test.go
index f327de9ee..c4f97fe6e 100644
--- a/pkg/apis/serving/v1/revision_defaults_test.go
+++ b/pkg/apis/serving/v1/revision_defaults_test.go
@@ -269,10 +269,12 @@ func TestRevisionDefaulting(t *testing.T) {
 					Name: config.DefaultsConfigName,
 				},
 				Data: map[string]string{
-					"revision-cpu-request":    "100m",
-					"revision-memory-request": "200M",
-					"revision-cpu-limit":      "300m",
-					"revision-memory-limit":   "400M",
+					"revision-cpu-request":               "100m",
+					"revision-memory-request":            "200M",
+					"revision-ephemeral-storage-request": "300m",
+					"revision-cpu-limit":                 "400M",
+					"revision-memory-limit":              "500m",
+					"revision-ephemeral-storage-limit":   "600M",
 				},
 			})
 
@@ -287,12 +289,14 @@ func TestRevisionDefaulting(t *testing.T) {
 						Name: config.DefaultUserContainerName,
 						Resources: corev1.ResourceRequirements{
 							Requests: corev1.ResourceList{
-								corev1.ResourceCPU:    resource.MustParse("100m"),
-								corev1.ResourceMemory: resource.MustParse("200M"),
+								corev1.ResourceCPU:              resource.MustParse("100m"),
+								corev1.ResourceMemory:           resource.MustParse("200M"),
+								corev1.ResourceEphemeralStorage: resource.MustParse("300m"),
 							},
 							Limits: corev1.ResourceList{
-								corev1.ResourceCPU:    resource.MustParse("300m"),
-								corev1.ResourceMemory: resource.MustParse("400M"),
+								corev1.ResourceCPU:              resource.MustParse("400M"),
+								corev1.ResourceMemory:           resource.MustParse("500m"),
+								corev1.ResourceEphemeralStorage: resource.MustParse("600M"),
 							},
 						},
 						ReadinessProbe: defaultProbe,
diff --git a/pkg/apis/serving/v1/revision_validation_test.go b/pkg/apis/serving/v1/revision_validation_test.go
index 20dc82e9e..da6ab75ef 100644
--- a/pkg/apis/serving/v1/revision_validation_test.go
+++ b/pkg/apis/serving/v1/revision_validation_test.go
@@ -542,7 +542,7 @@ func TestImmutableFields(t *testing.T) {
 					Containers: []corev1.Container{{
 						Resources: corev1.ResourceRequirements{
 							Requests: corev1.ResourceList{
-								corev1.ResourceName("cpu"): resource.MustParse("50m"),
+								corev1.ResourceCPU: resource.MustParse("50m"),
 							},
 						},
 					}},
@@ -558,7 +558,7 @@ func TestImmutableFields(t *testing.T) {
 					Containers: []corev1.Container{{
 						Resources: corev1.ResourceRequirements{
 							Requests: corev1.ResourceList{
-								corev1.ResourceName("cpu"): resource.MustParse("100m"),
+								corev1.ResourceCPU: resource.MustParse("100m"),
 							},
 						},
 					}},
diff --git a/pkg/apis/serving/v1alpha1/revision_validation_test.go b/pkg/apis/serving/v1alpha1/revision_validation_test.go
index 2c81f043c..080db721b 100644
--- a/pkg/apis/serving/v1alpha1/revision_validation_test.go
+++ b/pkg/apis/serving/v1alpha1/revision_validation_test.go
@@ -669,7 +669,7 @@ func TestImmutableFields(t *testing.T) {
 					Image: "busybox",
 					Resources: corev1.ResourceRequirements{
 						Requests: corev1.ResourceList{
-							corev1.ResourceName("cpu"): resource.MustParse("50m"),
+							corev1.ResourceCPU: resource.MustParse("50m"),
 						},
 					},
 				},
@@ -684,7 +684,7 @@ func TestImmutableFields(t *testing.T) {
 					Image: "busybox",
 					Resources: corev1.ResourceRequirements{
 						Requests: corev1.ResourceList{
-							corev1.ResourceName("cpu"): resource.MustParse("100m"),
+							corev1.ResourceCPU: resource.MustParse("100m"),
 						},
 					},
 				},
diff --git a/pkg/apis/serving/v1beta1/revision_validation_test.go b/pkg/apis/serving/v1beta1/revision_validation_test.go
index 3ee08493b..1b6a36a98 100644
--- a/pkg/apis/serving/v1beta1/revision_validation_test.go
+++ b/pkg/apis/serving/v1beta1/revision_validation_test.go
@@ -543,7 +543,7 @@ func TestImmutableFields(t *testing.T) {
 					Containers: []corev1.Container{{
 						Resources: corev1.ResourceRequirements{
 							Requests: corev1.ResourceList{
-								corev1.ResourceName("cpu"): resource.MustParse("50m"),
+								corev1.ResourceCPU: resource.MustParse("50m"),
 							},
 						},
 					}},
@@ -559,7 +559,7 @@ func TestImmutableFields(t *testing.T) {
 					Containers: []corev1.Container{{
 						Resources: corev1.ResourceRequirements{
 							Requests: corev1.ResourceList{
-								corev1.ResourceName("cpu"): resource.MustParse("100m"),
+								corev1.ResourceCPU: resource.MustParse("100m"),
 							},
 						},
 					}},
diff --git a/pkg/autoscaler/scaling/autoscaler_test.go b/pkg/autoscaler/scaling/autoscaler_test.go
index 96f17fa71..1fd8452fc 100644
--- a/pkg/autoscaler/scaling/autoscaler_test.go
+++ b/pkg/autoscaler/scaling/autoscaler_test.go
@@ -183,6 +183,70 @@ func TestAutoscalerStableModeIncreaseWithRPS(t *testing.T) {
 	expectScale(t, a, time.Now(), ScaleResult{10, expectedEBC(10, 101, 99, 1), na, true})
 }
 
+func TestAutoscalerUnpanicAfterSlowIncrease(t *testing.T) {
+	// Do initial jump from 10 to 25 pods.
+	metrics := &fake.MetricClient{StableConcurrency: 11, PanicConcurrency: 25}
+	a := newTestAutoscaler(t, 1, 98, metrics)
+	fake.Endpoints(10, fake.TestService)
+
+	na := expectedNA(a, 10)
+	start := time.Now()
+	tm := start
+	expectScale(t, a, tm, ScaleResult{25, expectedEBC(1, 98, 25, 10), na, true})
+	if a.panicTime != tm {
+		t.Errorf("PanicTime = %v, want: %v", a.panicTime, tm)
+	}
+	// Now the half of the stable window has passed, and we've been adding 1 pod per cycle.
+	// For window of 60s, that's +15 pods.
+	fake.Endpoints(25+15, fake.TestService)
+
+	metrics.SetStableAndPanicConcurrency(30, 41)
+	tm = tm.Add(stableWindow / 2)
+
+	na = expectedNA(a, 40)
+	expectScale(t, a, tm, ScaleResult{41, expectedEBC(1, 98, 41, 40), na, true})
+	if a.panicTime != start {
+		t.Error("Panic Time should not have moved")
+	}
+
+	// Now at the end. Panic must end. And +30 pods.
+	fake.Endpoints(25+30, fake.TestService)
+	metrics.SetStableAndPanicConcurrency(50, 56)
+	tm = tm.Add(stableWindow/2 + tickInterval)
+
+	na = expectedNA(a, 55)
+	expectScale(t, a, tm, ScaleResult{50 /* no longer in panic*/, expectedEBC(1, 98, 56, 55), na, true})
+	if !a.panicTime.IsZero() {
+		t.Errorf("PanicTime = %v, want: 0", a.panicTime)
+	}
+}
+
+func TestAutoscalerExtendPanicWindow(t *testing.T) {
+	// Do initial jump from 10 to 25 pods.
+	metrics := &fake.MetricClient{StableConcurrency: 11, PanicConcurrency: 25}
+	a := newTestAutoscaler(t, 1, 98, metrics)
+	fake.Endpoints(10, fake.TestService)
+
+	na := expectedNA(a, 10)
+	start := time.Now()
+	tm := start
+	expectScale(t, a, tm, ScaleResult{25, expectedEBC(1, 98, 25, 10), na, true})
+	if a.panicTime != tm {
+		t.Errorf("PanicTime = %v, want: %v", a.panicTime, tm)
+	}
+	// Now the half of the stable window has passed, and we're still surging.
+	fake.Endpoints(25+15, fake.TestService)
+
+	metrics.SetStableAndPanicConcurrency(30, 80)
+	tm = tm.Add(stableWindow / 2)
+
+	na = expectedNA(a, 40)
+	expectScale(t, a, tm, ScaleResult{80, expectedEBC(1, 98, 80, 40), na, true})
+	if a.panicTime != tm {
+		t.Errorf("PanicTime = %v, want: %v", a.panicTime, tm)
+	}
+}
+
 func TestAutoscalerStableModeDecrease(t *testing.T) {
 	metrics := &fake.MetricClient{StableConcurrency: 100.0, PanicConcurrency: 100}
 	a := newTestAutoscaler(t, 10, 98, metrics)
@@ -190,8 +254,7 @@ func TestAutoscalerStableModeDecrease(t *testing.T) {
 	na := expectedNA(a, 8)
 	expectScale(t, a, time.Now(), ScaleResult{10, expectedEBC(10, 98, 100, 8), na, true})
 
-	metrics.StableConcurrency = 50
-	metrics.PanicConcurrency = 50
+	metrics.SetStableAndPanicConcurrency(50, 50)
 	expectScale(t, a, time.Now(), ScaleResult{5, expectedEBC(10, 98, 50, 8), na, true})
 }
 
@@ -214,23 +277,36 @@ func TestAutoscalerPanicModeExponentialTrackAndStablize(t *testing.T) {
 	na := expectedNA(a, 1)
 	expectScale(t, a, time.Now(), ScaleResult{6, expectedEBC(1, 101, 6, 1), na, true})
 
+	tm := time.Now()
 	fake.Endpoints(6, fake.TestService)
 	na = expectedNA(a, 6)
-	metrics.PanicConcurrency, metrics.StableConcurrency = 36, 36
-	expectScale(t, a, time.Now(), ScaleResult{36, expectedEBC(1, 101, 36, 6), na, true})
+	metrics.SetStableAndPanicConcurrency(36, 36)
+	expectScale(t, a, tm, ScaleResult{36, expectedEBC(1, 101, 36, 6), na, true})
+	if got, want := a.panicTime, tm; got != tm {
+		t.Errorf("PanicTime = %v, want: %v", got, want)
+	}
 
 	fake.Endpoints(36, fake.TestService)
 	na = expectedNA(a, 36)
-	metrics.PanicConcurrency, metrics.StableConcurrency = 216, 216
-	expectScale(t, a, time.Now(), ScaleResult{216, expectedEBC(1, 101, 216, 36), na, true})
+	metrics.SetStableAndPanicConcurrency(216, 216)
+	tm = tm.Add(time.Second)
+	expectScale(t, a, tm, ScaleResult{216, expectedEBC(1, 101, 216, 36), na, true})
+	if got, want := a.panicTime, tm; got != tm {
+		t.Errorf("PanicTime = %v, want: %v", got, want)
+	}
 
 	fake.Endpoints(216, fake.TestService)
 	na = expectedNA(a, 216)
-	metrics.PanicConcurrency, metrics.StableConcurrency = 1296, 1296
-	expectScale(t, a, time.Now(), ScaleResult{1296, expectedEBC(1, 101, 1296, 216), na, true})
+	metrics.SetStableAndPanicConcurrency(1296, 1296)
+	expectScale(t, a, tm, ScaleResult{1296, expectedEBC(1, 101, 1296, 216), na, true})
+	if got, want := a.panicTime, tm; got != tm {
+		t.Errorf("PanicTime = %v, want: %v", got, want)
+	}
+
 	fake.Endpoints(1296, fake.TestService)
 	na = expectedNA(a, 1296)
-	expectScale(t, a, time.Now(), ScaleResult{1296, expectedEBC(1, 101, 1296, 1296), na, true})
+	tm = tm.Add(time.Second)
+	expectScale(t, a, tm, ScaleResult{1296, expectedEBC(1, 101, 1296, 1296), na, true})
 }
 
 func TestAutoscalerScale(t *testing.T) {
@@ -353,8 +429,7 @@ func TestAutoscalerPanicThenUnPanicScaleDown(t *testing.T) {
 	expectScale(t, a, panicTime, ScaleResult{100, expectedEBC(10, 93, 1000, 10), na, true})
 
 	// Traffic dropped off, scale stays as we're still in panic.
-	metrics.PanicConcurrency = 1
-	metrics.StableConcurrency = 1
+	metrics.SetStableAndPanicConcurrency(1, 1)
 	expectScale(t, a, panicTime.Add(30*time.Second), ScaleResult{100, expectedEBC(10, 93, 1, 10), na, true})
 
 	// Scale down after the StableWindow
@@ -481,7 +556,7 @@ func expectScale(t *testing.T, a UniScaler, now time.Time, want ScaleResult) {
 }
 
 func TestStartInPanicMode(t *testing.T) {
-	metrics := &fake.StaticMetricClient
+	metrics := &staticMetricClient
 	deciderSpec := &DeciderSpec{
 		TargetValue:         100,
 		TotalValue:          120,
@@ -524,7 +599,7 @@ func TestStartInPanicMode(t *testing.T) {
 
 func TestNewFail(t *testing.T) {
 	eraseEndpoints()
-	metrics := &fake.StaticMetricClient
+	metrics := &staticMetricClient
 	deciderSpec := &DeciderSpec{
 		TargetValue:         100,
 		TotalValue:          120,
@@ -555,3 +630,11 @@ func reset() {
 		targetRPSM.Name(), panicM.Name())
 	register()
 }
+
+// staticMetricClient returns stable/panic concurrency and RPS with static value, i.e. 10.
+var staticMetricClient = fake.MetricClient{
+	StableConcurrency: 10.0,
+	PanicConcurrency:  10.0,
+	StableRPS:         10.0,
+	PanicRPS:          10.0,
+}
diff --git a/pkg/queue/prometheus_stats_reporter_test.go b/pkg/queue/prometheus_stats_reporter_test.go
index e32ed4f0c..0f24bef2e 100644
--- a/pkg/queue/prometheus_stats_reporter_test.go
+++ b/pkg/queue/prometheus_stats_reporter_test.go
@@ -22,6 +22,7 @@ import (
 	"time"
 
 	"github.com/prometheus/client_golang/prometheus"
+	"knative.dev/serving/pkg/network"
 
 	dto "github.com/prometheus/client_model/go"
 )
@@ -42,44 +43,39 @@ func TestNewPrometheusStatsReporter_negative(t *testing.T) {
 		config    string
 		revision  string
 		pod       string
-	}{
-		{
-			"Empty_Namespace_Value",
-			"Expected namespace empty error",
-			errors.New("namespace must not be empty"),
-			"",
-			config,
-			revision,
-			pod,
-		},
-		{
-			"Empty_Config_Value",
-			"Expected config empty error",
-			errors.New("config must not be empty"),
-			namespace,
-			"",
-			revision,
-			pod,
-		},
-		{
-			"Empty_Revision_Value",
-			"Expected revision empty error",
-			errors.New("revision must not be empty"),
-			namespace,
-			config,
-			"",
-			pod,
-		},
-		{
-			"Empty_Pod_Value",
-			"Expected pod empty error",
-			errors.New("pod must not be empty"),
-			namespace,
-			config,
-			revision,
-			"",
-		},
-	}
+	}{{
+		"Empty_Namespace_Value",
+		"Expected namespace empty error",
+		errors.New("namespace must not be empty"),
+		"",
+		config,
+		revision,
+		pod,
+	}, {
+		"Empty_Config_Value",
+		"Expected config empty error",
+		errors.New("config must not be empty"),
+		namespace,
+		"",
+		revision,
+		pod,
+	}, {
+		"Empty_Revision_Value",
+		"Expected revision empty error",
+		errors.New("revision must not be empty"),
+		namespace,
+		config,
+		"",
+		pod,
+	}, {
+		"Empty_Pod_Value",
+		"Expected pod empty error",
+		errors.New("pod must not be empty"),
+		namespace,
+		config,
+		revision,
+		"",
+	}}
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
@@ -154,8 +150,7 @@ func TestReporterReport(t *testing.T) {
 		expectedConcurrency:         3,
 		expectedProxiedRequestCount: 15,
 		expectedProxiedConcurrency:  2,
-	},
-	}
+	}}
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
@@ -165,7 +160,12 @@ func TestReporterReport(t *testing.T) {
 			}
 			// Make the value slightly more interesting, rather than microseconds.
 			reporter.startTime = reporter.startTime.Add(-5 * time.Second)
-			reporter.Report(test.concurrency, test.proxiedConcurrency, test.reqCount, test.proxiedReqCount)
+			reporter.Report(network.RequestStatsReport{
+				AverageConcurrency:        test.concurrency,
+				AverageProxiedConcurrency: test.proxiedConcurrency,
+				RequestCount:              test.reqCount,
+				ProxiedRequestCount:       test.proxiedReqCount,
+			})
 			checkData(t, requestsPerSecondGV, test.expectedReqCount)
 			checkData(t, averageConcurrentRequestsGV, test.expectedConcurrency)
 			checkData(t, proxiedRequestsPerSecondGV, test.expectedProxiedRequestCount)
diff --git a/pkg/queue/stats_test.go b/pkg/queue/stats_test.go
deleted file mode 100644
index a5b272d00..000000000
--- a/pkg/queue/stats_test.go
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
-Copyright 2018 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 queue
-
-import (
-	"testing"
-	"time"
-
-	"github.com/google/go-cmp/cmp"
-	"knative.dev/serving/pkg/network"
-)
-
-type reportedStat struct {
-	Concurrency         float64
-	ProxiedConcurrency  float64
-	RequestCount        float64
-	ProxiedRequestCount float64
-}
-
-func TestNoData(t *testing.T) {
-	now := time.Now()
-	s := newTestStats(t, now)
-
-	got := s.report(now)
-	want := reportedStat{
-		Concurrency:  0.0,
-		RequestCount: 0,
-	}
-	if diff := cmp.Diff(want, got); diff != "" {
-		t.Errorf("Unexpected stat (-want +got): %v", diff)
-	}
-}
-
-func TestSingleRequestWholeTime(t *testing.T) {
-	now := time.Now()
-	s := newTestStats(t, now)
-
-	s.requestStart(now)
-	now = now.Add(1 * time.Second)
-	s.requestEnd(now)
-
-	got := s.report(now)
-
-	want := reportedStat{
-		Concurrency:  1.0,
-		RequestCount: 1,
-	}
-	if diff := cmp.Diff(want, got); diff != "" {
-		t.Errorf("Unexpected stat (-want +got): %v", diff)
-	}
-}
-
-func TestSingleRequestHalfTime(t *testing.T) {
-	now := time.Now()
-	s := newTestStats(t, now)
-
-	s.requestStart(now)
-	now = now.Add(1 * time.Second)
-	s.requestEnd(now)
-	now = now.Add(1 * time.Second)
-	got := s.report(now)
-
-	want := reportedStat{
-		Concurrency:  0.5,
-		RequestCount: 1,
-	}
-	if diff := cmp.Diff(want, got); diff != "" {
-		t.Errorf("Unexpected stat (-want +got): %v", diff)
-	}
-}
-
-func TestVeryShortLivedRequest(t *testing.T) {
-	now := time.Now()
-	s := newTestStats(t, now)
-
-	s.requestStart(now)
-	now = now.Add(10 * time.Millisecond)
-	s.requestEnd(now)
-
-	now = now.Add(990 * time.Millisecond) // make the second full
-	got := s.report(now)
-
-	want := reportedStat{
-		Concurrency:  float64(10) / float64(1000),
-		RequestCount: 1,
-	}
-	if diff := cmp.Diff(want, got); diff != "" {
-		t.Errorf("Unexpected stat (-want +got): %v", diff)
-	}
-}
-
-func TestMultipleRequestsWholeTime(t *testing.T) {
-	now := time.Now()
-	s := newTestStats(t, now)
-
-	s.requestStart(now)
-	now = now.Add(300 * time.Millisecond)
-	s.requestEnd(now)
-
-	s.requestStart(now)
-	now = now.Add(300 * time.Millisecond)
-	s.requestEnd(now)
-
-	s.requestStart(now)
-	now = now.Add(400 * time.Millisecond)
-	s.requestEnd(now)
-
-	got := s.report(now)
-
-	want := reportedStat{
-		Concurrency:  1.0,
-		RequestCount: 3,
-	}
-	if diff := cmp.Diff(want, got); diff != "" {
-		t.Errorf("Unexpected stat (-want +got): %v", diff)
-	}
-}
-
-func TestMultipleRequestsInterleaved(t *testing.T) {
-	now := time.Now()
-	s := newTestStats(t, now)
-
-	s.requestStart(now)
-	now = now.Add(100 * time.Millisecond)
-	s.requestStart(now)
-	now = now.Add(500 * time.Millisecond)
-	s.requestEnd(now)
-	now = now.Add(400 * time.Millisecond)
-	s.requestEnd(now)
-
-	got := s.report(now)
-
-	want := reportedStat{
-		Concurrency:  1.5,
-		RequestCount: 2,
-	}
-	if diff := cmp.Diff(want, got); diff != "" {
-		t.Errorf("Unexpected stat (-want +got): %v", diff)
-	}
-}
-
-func TestOneRequestAcrossReportings(t *testing.T) {
-	now := time.Now()
-	s := newTestStats(t, now)
-
-	s.requestStart(now)
-	now = now.Add(1 * time.Second)
-	got1 := s.report(now)
-	want1 := reportedStat{
-		Concurrency:  1.0,
-		RequestCount: 1,
-	}
-
-	now = now.Add(500 * time.Millisecond)
-	s.requestEnd(now)
-	now = now.Add(500 * time.Millisecond)
-	got2 := s.report(now)
-	want2 := reportedStat{
-		Concurrency:  0.5,
-		RequestCount: 0,
-	}
-
-	if diff := cmp.Diff(want1, got1); diff != "" {
-		t.Errorf("Unexpected stat (-want +got): %v", diff)
-	}
-	if diff := cmp.Diff(want2, got2); diff != "" {
-		t.Errorf("Unexpected stat (-want +got): %v", diff)
-	}
-}
-
-func TestOneProxiedRequest(t *testing.T) {
-	now := time.Now()
-	s := newTestStats(t, now)
-	s.proxiedStart(now)
-	now = now.Add(1 * time.Second)
-	got := s.report(now)
-	want := reportedStat{
-		Concurrency:         1.0,
-		ProxiedConcurrency:  1.0,
-		RequestCount:        1,
-		ProxiedRequestCount: 1,
-	}
-	if diff := cmp.Diff(want, got); diff != "" {
-		t.Errorf("Unexpected stat (-want +got): %v", diff)
-	}
-}
-
-func TestOneEndedProxiedRequest(t *testing.T) {
-	now := time.Now()
-	s := newTestStats(t, now)
-	s.proxiedStart(now)
-	now = now.Add(500 * time.Millisecond)
-	s.proxiedEnd(now)
-	now = now.Add(500 * time.Millisecond)
-	got := s.report(now)
-	want := reportedStat{
-		Concurrency:         0.5,
-		ProxiedConcurrency:  0.5,
-		RequestCount:        1,
-		ProxiedRequestCount: 1,
-	}
-	if diff := cmp.Diff(want, got); diff != "" {
-		t.Errorf("Unexpected stat (-want +got): %v", diff)
-	}
-}
-
-func TestTwoRequestsOneProxied(t *testing.T) {
-	now := time.Now()
-	s := newTestStats(t, now)
-	s.proxiedStart(now)
-	now = now.Add(500 * time.Millisecond)
-	s.proxiedEnd(now)
-	s.requestStart(now)
-	now = now.Add(500 * time.Millisecond)
-	got := s.report(now)
-	want := reportedStat{
-		Concurrency:         1.0,
-		ProxiedConcurrency:  0.5,
-		RequestCount:        2,
-		ProxiedRequestCount: 1,
-	}
-	if diff := cmp.Diff(want, got); diff != "" {
-		t.Errorf("Unexpected stat (-want +got): %v", diff)
-	}
-}
-
-// Test type to hold the bi-directional time channels
-type testStats struct {
-	stats        *network.RequestStats
-	reportBiChan chan time.Time
-	statChan     chan reportedStat
-}
-
-func newTestStats(t *testing.T, now time.Time) *testStats {
-	stats := network.NewRequestStats(now)
-	reportBiChan := make(chan time.Time)
-	statChan := make(chan reportedStat)
-	report := func(acr float64, apcr float64, rc float64, prc float64) {
-		statChan <- reportedStat{
-			Concurrency:         acr,
-			ProxiedConcurrency:  apcr,
-			RequestCount:        rc,
-			ProxiedRequestCount: prc,
-		}
-	}
-	go ReportStats(now, stats, reportBiChan, report)
-	return &testStats{
-		stats:        stats,
-		reportBiChan: reportBiChan,
-		statChan:     statChan,
-	}
-}
-
-func (s *testStats) requestStart(now time.Time) {
-	s.stats.HandleEvent(network.ReqEvent{Time: now, Type: network.ReqIn})
-}
-
-func (s *testStats) requestEnd(now time.Time) {
-	s.stats.HandleEvent(network.ReqEvent{Time: now, Type: network.ReqOut})
-}
-
-func (s *testStats) proxiedStart(now time.Time) {
-	s.stats.HandleEvent(network.ReqEvent{Time: now, Type: network.ProxiedIn})
-}
-
-func (s *testStats) proxiedEnd(now time.Time) {
-	s.stats.HandleEvent(network.ReqEvent{Time: now, Type: network.ProxiedOut})
-}
-
-func (s *testStats) report(now time.Time) reportedStat {
-	s.reportBiChan <- now
-	return <-s.statChan
-}
diff --git a/pkg/reconciler/revision/resources/deploy_test.go b/pkg/reconciler/revision/resources/deploy_test.go
index 933ed2cab..6a97ef2be 100644
--- a/pkg/reconciler/revision/resources/deploy_test.go
+++ b/pkg/reconciler/revision/resources/deploy_test.go
@@ -87,7 +87,7 @@ var (
 					Command: []string{"/ko-app/queue", "-probe-period", "0"},
 				},
 			},
-			PeriodSeconds:  1,
+			PeriodSeconds:  10,
 			TimeoutSeconds: 10,
 		},
 		SecurityContext: queueSecurityContext,
@@ -203,7 +203,7 @@ var (
 					serving.RevisionUID: "1234",
 				},
 			},
-			ProgressDeadlineSeconds: ptr.Int32(int32(deployment.ProgressDeadlineDefault.Seconds())),
+			ProgressDeadlineSeconds: ptr.Int32(0),
 			Template: corev1.PodTemplateSpec{
 				ObjectMeta: metav1.ObjectMeta{
 					Labels: map[string]string{
@@ -341,7 +341,7 @@ func withAppendedVolumes(volumes ...corev1.Volume) podSpecOption {
 	}
 }
 
-func makeDeployment(opts ...deploymentOption) *appsv1.Deployment {
+func appsv1deployment(opts ...deploymentOption) *appsv1.Deployment {
 	deploy := defaultDeployment.DeepCopy()
 	for _, option := range opts {
 		option(deploy)
@@ -541,14 +541,7 @@ func TestMakePodSpec(t *testing.T) {
 	}, {
 		name: "with tcp readiness probe",
 		rev: revision("bar", "foo", func(revision *v1.Revision) {
-			container(revision.Spec.GetContainer(),
-				withReadinessProbe(corev1.Handler{
-					TCPSocket: &corev1.TCPSocketAction{
-						Host: "127.0.0.1",
-						Port: intstr.FromInt(12345),
-					},
-				}),
-			)
+			container(revision.Spec.GetContainer(), withTCPReadinessProbe())
 		}),
 		want: podSpec(
 			[]corev1.Container{
@@ -741,59 +734,48 @@ func TestMakeDeployment(t *testing.T) {
 		name string
 		rev  *v1.Revision
 		want *appsv1.Deployment
+		dc   deployment.Config
 	}{{
 		name: "with concurrency=1",
 		rev: revision("bar", "foo",
 			withoutLabels,
 			withContainerConcurrency(1),
 			func(revision *v1.Revision) {
-				container(revision.Spec.GetContainer(),
-					withReadinessProbe(corev1.Handler{
-						TCPSocket: &corev1.TCPSocketAction{
-							Host: "127.0.0.1",
-							Port: intstr.FromInt(12345),
-						},
-					}),
-				)
-			},
-		),
-		want: makeDeployment(),
+				container(revision.Spec.GetContainer(), withTCPReadinessProbe())
+			}),
+		want: appsv1deployment(),
 	}, {
 		name: "with owner",
 		rev: revision("bar", "foo",
 			withoutLabels,
 			withOwnerReference("parent-config"),
 			func(revision *v1.Revision) {
-				container(revision.Spec.GetContainer(),
-					withReadinessProbe(corev1.Handler{
-						TCPSocket: &corev1.TCPSocketAction{
-							Host: "127.0.0.1",
-							Port: intstr.FromInt(12345),
-						},
-					}),
-				)
-			},
-		),
-		want: makeDeployment(),
+				container(revision.Spec.GetContainer(), withTCPReadinessProbe())
+			}),
+		want: appsv1deployment(),
 	}, {
 		name: "with sidecar annotation override",
 		rev: revision("bar", "foo", withoutLabels, func(revision *v1.Revision) {
 			revision.ObjectMeta.Annotations = map[string]string{
 				sidecarIstioInjectAnnotation: "false",
 			}
-			container(revision.Spec.GetContainer(),
-				withReadinessProbe(corev1.Handler{
-					TCPSocket: &corev1.TCPSocketAction{
-						Host: "127.0.0.1",
-						Port: intstr.FromInt(12345),
-					},
-				}),
-			)
+			container(revision.Spec.GetContainer(), withTCPReadinessProbe())
 		}),
-		want: makeDeployment(func(deploy *appsv1.Deployment) {
+		want: appsv1deployment(func(deploy *appsv1.Deployment) {
 			deploy.ObjectMeta.Annotations[sidecarIstioInjectAnnotation] = "false"
 			deploy.Spec.Template.ObjectMeta.Annotations[sidecarIstioInjectAnnotation] = "false"
 		}),
+	}, {
+		name: "with ProgressDeadline override",
+		dc: deployment.Config{
+			ProgressDeadline: 42 * time.Second,
+		},
+		rev: revision("bar", "foo", withoutLabels, func(revision *v1.Revision) {
+			container(revision.Spec.GetContainer(), withTCPReadinessProbe())
+		}),
+		want: appsv1deployment(func(deploy *appsv1.Deployment) {
+			deploy.Spec.ProgressDeadlineSeconds = ptr.Int32(42)
+		}),
 	}}
 
 	for _, test := range tests {
@@ -806,7 +788,7 @@ func TestMakeDeployment(t *testing.T) {
 			}
 			test.want.Spec.Template.Spec = *podSpec
 			got, err := MakeDeployment(test.rev, &logConfig, &traceConfig,
-				&network.Config{}, &obsConfig, &asConfig, &deploymentConfig)
+				&network.Config{}, &obsConfig, &asConfig, &test.dc)
 			if err != nil {
 				t.Fatal("got unexpected error:", err)
 			}
@@ -816,39 +798,3 @@ func TestMakeDeployment(t *testing.T) {
 		})
 	}
 }
-
-func TestProgressDeadlineOverride(t *testing.T) {
-	rev := revision("bar", "foo",
-		withoutLabels,
-		func(revision *v1.Revision) {
-			container(revision.Spec.GetContainer(),
-				withReadinessProbe(corev1.Handler{
-					TCPSocket: &corev1.TCPSocketAction{
-						Host: "127.0.0.1",
-						Port: intstr.FromInt(12345),
-					},
-				}),
-			)
-		},
-	)
-	want := makeDeployment(func(d *appsv1.Deployment) {
-		d.Spec.ProgressDeadlineSeconds = ptr.Int32(42)
-	})
-
-	dc := &deployment.Config{
-		ProgressDeadline: 42 * time.Second,
-	}
-	podSpec, err := makePodSpec(rev, &logConfig, &traceConfig, &obsConfig, &asConfig, dc)
-	if err != nil {
-		t.Fatal("makePodSpec returned error:", err)
-	}
-	want.Spec.Template.Spec = *podSpec
-	got, err := MakeDeployment(rev, &logConfig, &traceConfig,
-		&network.Config{}, &obsConfig, &asConfig, dc)
-	if err != nil {
-		t.Fatal("MakeDeployment returned error:", err)
-	}
-	if !cmp.Equal(want, got, cmp.AllowUnexported(resource.Quantity{})) {
-		t.Error("MakeDeployment (-want, +got) =", cmp.Diff(want, got, cmp.AllowUnexported(resource.Quantity{})))
-	}
-}
diff --git a/pkg/reconciler/revision/resources/queue_test.go b/pkg/reconciler/revision/resources/queue_test.go
index 901fdd3ad..6d421c6c6 100644
--- a/pkg/reconciler/revision/resources/queue_test.go
+++ b/pkg/reconciler/revision/resources/queue_test.go
@@ -61,9 +61,7 @@ var (
 	traceConfig      tracingconfig.Config
 	obsConfig        metrics.ObservabilityConfig
 	asConfig         autoscalerconfig.Config
-	deploymentConfig = deployment.Config{
-		ProgressDeadline: deployment.ProgressDeadlineDefault,
-	}
+	deploymentConfig deployment.Config
 )
 
 const testProbeJSONTemplate = `{"tcpSocket":{"port":%d,"host":"127.0.0.1"}}`
@@ -81,7 +79,6 @@ func TestMakeQueueContainer(t *testing.T) {
 		rev: revision("bar", "foo",
 			withContainerConcurrency(1),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
 				container(revision.Spec.GetContainer(), withTCPReadinessProbe())
 			}),
 		want: queueContainer(func(c *corev1.Container) {
@@ -90,11 +87,10 @@ func TestMakeQueueContainer(t *testing.T) {
 			})
 		}),
 	}, {
-		name: "no owner no autoscaler single",
+		name: "custom sidecar image, container port, protocol",
 		rev: revision("bar", "foo",
 			withContainerConcurrency(1),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
 				revision.Spec.PodSpec = corev1.PodSpec{
 					Containers: []corev1.Container{{
 						Name:           containerName,
@@ -117,35 +113,27 @@ func TestMakeQueueContainer(t *testing.T) {
 				"QUEUE_SERVING_PORT":    "8013",
 				"CONTAINER_CONCURRENCY": "1",
 			})
-		},
-		),
+		}),
 	}, {
 		name: "service name in labels",
 		rev: revision("bar", "foo",
 			withContainerConcurrency(1),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
 				revision.ObjectMeta.Labels = map[string]string{
 					serving.ServiceLabelKey: "svc",
 				}
 				container(revision.Spec.GetContainer(), withTCPReadinessProbe())
 			}),
-		cc: deployment.Config{
-			QueueSidecarImage: "alpine",
-		},
 		want: queueContainer(func(c *corev1.Container) {
 			c.Env = env(map[string]string{
 				"SERVING_SERVICE": "svc",
 			})
-			c.Image = "alpine"
-			c.Ports = append(queueNonServingPorts, queueHTTPPort)
 		}),
 	}, {
 		name: "config owner as env var, zero concurrency",
 		rev: revision("blah", "baz",
 			withContainerConcurrency(0),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
 				revision.ObjectMeta.OwnerReferences = []metav1.OwnerReference{{
 					APIVersion:         v1.SchemeGroupVersion.String(),
 					Kind:               "Configuration",
@@ -162,14 +150,12 @@ func TestMakeQueueContainer(t *testing.T) {
 				"SERVING_NAMESPACE":     "baz",
 				"SERVING_REVISION":      "blah",
 			})
-			c.Ports = append(queueNonServingPorts, queueHTTPPort)
 		}),
 	}, {
 		name: "logging configuration as env var",
 		rev: revision("this", "log",
-			withContainerConcurrency(0),
+			withContainerConcurrency(1),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
 				container(revision.Spec.GetContainer(), withTCPReadinessProbe())
 			}),
 		lc: logging.Config{
@@ -180,34 +166,29 @@ func TestMakeQueueContainer(t *testing.T) {
 		},
 		want: queueContainer(func(c *corev1.Container) {
 			c.Env = env(map[string]string{
-				"CONTAINER_CONCURRENCY":  "0",
 				"SERVING_LOGGING_CONFIG": "The logging configuration goes here",
 				"SERVING_LOGGING_LEVEL":  "error",
 				"SERVING_NAMESPACE":      "log",
 				"SERVING_REVISION":       "this",
 			})
-			c.Ports = append(queueNonServingPorts, queueHTTPPort)
 		}),
 	}, {
 		name: "container concurrency 10",
 		rev: revision("bar", "foo",
 			withContainerConcurrency(10),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
 				container(revision.Spec.GetContainer(), withTCPReadinessProbe())
 			}),
 		want: queueContainer(func(c *corev1.Container) {
 			c.Env = env(map[string]string{
 				"CONTAINER_CONCURRENCY": "10",
 			})
-			c.Ports = append(queueNonServingPorts, queueHTTPPort)
 		}),
 	}, {
 		name: "request log configuration as env var",
 		rev: revision("bar", "foo",
-			withContainerConcurrency(0),
+			withContainerConcurrency(1),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
 				container(revision.Spec.GetContainer(), withTCPReadinessProbe())
 			}),
 		oc: metrics.ObservabilityConfig{
@@ -216,18 +197,15 @@ func TestMakeQueueContainer(t *testing.T) {
 		},
 		want: queueContainer(func(c *corev1.Container) {
 			c.Env = env(map[string]string{
-				"CONTAINER_CONCURRENCY":            "0",
 				"SERVING_REQUEST_LOG_TEMPLATE":     "test template",
 				"SERVING_ENABLE_PROBE_REQUEST_LOG": "true",
 			})
-			c.Ports = append(queueNonServingPorts, queueHTTPPort)
 		}),
 	}, {
 		name: "request metrics backend as env var",
 		rev: revision("bar", "foo",
-			withContainerConcurrency(0),
+			withContainerConcurrency(1),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
 				container(revision.Spec.GetContainer(), withTCPReadinessProbe())
 			}),
 		oc: metrics.ObservabilityConfig{
@@ -235,27 +213,36 @@ func TestMakeQueueContainer(t *testing.T) {
 		},
 		want: queueContainer(func(c *corev1.Container) {
 			c.Env = env(map[string]string{
-				"CONTAINER_CONCURRENCY":           "0",
 				"SERVING_REQUEST_METRICS_BACKEND": "prometheus",
 			})
-			c.Ports = append(queueNonServingPorts, queueHTTPPort)
 		}),
 	}, {
 		name: "enable profiling",
 		rev: revision("bar", "foo",
-			withContainerConcurrency(0),
+			withContainerConcurrency(1),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
 				container(revision.Spec.GetContainer(), withTCPReadinessProbe())
 			}),
 		oc: metrics.ObservabilityConfig{EnableProfiling: true},
 		want: queueContainer(func(c *corev1.Container) {
 			c.Env = env(map[string]string{
-				"CONTAINER_CONCURRENCY": "0",
-				"ENABLE_PROFILING":      "true",
+				"ENABLE_PROFILING": "true",
 			})
 			c.Ports = append(queueNonServingPorts, profilingPort, queueHTTPPort)
 		}),
+	}, {
+		name: "custom TimeoutSeconds",
+		rev: revision("bar", "foo",
+			withContainerConcurrency(1),
+			func(revision *v1.Revision) {
+				container(revision.Spec.GetContainer(), withTCPReadinessProbe())
+				revision.Spec.TimeoutSeconds = ptr.Int64(99)
+			}),
+		want: queueContainer(func(c *corev1.Container) {
+			c.Env = env(map[string]string{
+				"REVISION_TIMEOUT_SECONDS": "99",
+			})
+		}),
 	}}
 
 	for _, test := range tests {
@@ -296,8 +283,6 @@ func TestMakeQueueContainerWithPercentageAnnotation(t *testing.T) {
 		rev: revision("bar", "foo",
 			withContainerConcurrency(1),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
-				revision.ObjectMeta.Labels = map[string]string{}
 				revision.ObjectMeta.Annotations = map[string]string{
 					serving.QueueSideCarResourcePercentageAnnotation: "20",
 				}
@@ -306,8 +291,8 @@ func TestMakeQueueContainerWithPercentageAnnotation(t *testing.T) {
 					ReadinessProbe: testProbe,
 					Resources: corev1.ResourceRequirements{
 						Limits: corev1.ResourceList{
-							corev1.ResourceName("memory"): resource.MustParse("2Gi"),
-							corev1.ResourceName("cpu"):    resource.MustParse("2"),
+							corev1.ResourceMemory: resource.MustParse("2Gi"),
+							corev1.ResourceCPU:    resource.MustParse("2"),
 						},
 					}},
 				}
@@ -315,18 +300,15 @@ func TestMakeQueueContainerWithPercentageAnnotation(t *testing.T) {
 		want: queueContainer(func(c *corev1.Container) {
 			c.Env = env(map[string]string{})
 			c.Resources.Limits = corev1.ResourceList{
-				corev1.ResourceName("memory"): *resource.NewMilliQuantity(429496729600, resource.BinarySI),
-				corev1.ResourceName("cpu"):    *resource.NewMilliQuantity(400, resource.BinarySI),
+				corev1.ResourceMemory: resource.MustParse("0.4Gi"),
+				corev1.ResourceCPU:    resource.MustParse("0.4"),
 			}
-			c.Image = "alpine"
 		}),
 	}, {
-		name: "resources percentage in annotations small than min allowed",
+		name: "resources percentage in annotations smaller than min allowed",
 		rev: revision("bar", "foo",
 			withContainerConcurrency(1),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
-				revision.ObjectMeta.Labels = map[string]string{}
 				revision.ObjectMeta.Annotations = map[string]string{
 					serving.QueueSideCarResourcePercentageAnnotation: "0.2",
 				}
@@ -335,8 +317,8 @@ func TestMakeQueueContainerWithPercentageAnnotation(t *testing.T) {
 					ReadinessProbe: testProbe,
 					Resources: corev1.ResourceRequirements{
 						Requests: corev1.ResourceList{
-							corev1.ResourceName("cpu"):    resource.MustParse("50m"),
-							corev1.ResourceName("memory"): resource.MustParse("128Mi"),
+							corev1.ResourceCPU:    resource.MustParse("50m"),
+							corev1.ResourceMemory: resource.MustParse("128Mi"),
 						},
 					},
 				}}
@@ -344,18 +326,15 @@ func TestMakeQueueContainerWithPercentageAnnotation(t *testing.T) {
 		want: queueContainer(func(c *corev1.Container) {
 			c.Env = env(map[string]string{})
 			c.Resources.Requests = corev1.ResourceList{
-				corev1.ResourceName("cpu"):    resource.MustParse("25m"),
-				corev1.ResourceName("memory"): resource.MustParse("50Mi"),
+				corev1.ResourceCPU:    resource.MustParse("25m"), // clamped to boundary in resourceboundary.go
+				corev1.ResourceMemory: resource.MustParse("50Mi"),
 			}
-			c.Image = "alpine"
 		}),
 	}, {
-		name: "Invalid resources percentage in annotations",
+		name: "invalid resources percentage in annotations",
 		rev: revision("bar", "foo",
 			withContainerConcurrency(1),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
-				revision.ObjectMeta.Labels = map[string]string{}
 				revision.ObjectMeta.Annotations = map[string]string{
 					serving.QueueSideCarResourcePercentageAnnotation: "foo",
 				}
@@ -364,8 +343,8 @@ func TestMakeQueueContainerWithPercentageAnnotation(t *testing.T) {
 					ReadinessProbe: testProbe,
 					Resources: corev1.ResourceRequirements{
 						Requests: corev1.ResourceList{
-							corev1.ResourceName("cpu"):    resource.MustParse("50m"),
-							corev1.ResourceName("memory"): resource.MustParse("128Mi"),
+							corev1.ResourceCPU:    resource.MustParse("50m"),
+							corev1.ResourceMemory: resource.MustParse("128Mi"),
 						},
 					},
 				}}
@@ -373,17 +352,14 @@ func TestMakeQueueContainerWithPercentageAnnotation(t *testing.T) {
 		want: queueContainer(func(c *corev1.Container) {
 			c.Env = env(map[string]string{})
 			c.Resources.Requests = corev1.ResourceList{
-				corev1.ResourceName("cpu"): resource.MustParse("25m"),
+				corev1.ResourceCPU: resource.MustParse("25m"),
 			}
-			c.Image = "alpine"
 		}),
 	}, {
 		name: "resources percentage in annotations bigger than than math.MaxInt64",
 		rev: revision("bar", "foo",
 			withContainerConcurrency(1),
 			func(revision *v1.Revision) {
-				revision.Spec.TimeoutSeconds = ptr.Int64(45)
-				revision.ObjectMeta.Labels = map[string]string{}
 				revision.ObjectMeta.Annotations = map[string]string{
 					serving.QueueSideCarResourcePercentageAnnotation: "100",
 				}
@@ -392,7 +368,7 @@ func TestMakeQueueContainerWithPercentageAnnotation(t *testing.T) {
 					ReadinessProbe: testProbe,
 					Resources: corev1.ResourceRequirements{
 						Requests: corev1.ResourceList{
-							corev1.ResourceName("memory"): resource.MustParse("900000Pi"),
+							corev1.ResourceMemory: resource.MustParse("900000Pi"),
 						},
 					},
 				}}
@@ -400,17 +376,15 @@ func TestMakeQueueContainerWithPercentageAnnotation(t *testing.T) {
 		want: queueContainer(func(c *corev1.Container) {
 			c.Env = env(map[string]string{})
 			c.Resources.Requests = corev1.ResourceList{
-				corev1.ResourceName("cpu"):    resource.MustParse("25m"),
-				corev1.ResourceName("memory"): resource.MustParse("200Mi"),
+				corev1.ResourceCPU:    resource.MustParse("25m"),
+				corev1.ResourceMemory: resource.MustParse("200Mi"),
 			}
-			c.Image = "alpine"
 		}),
 	}}
 
-	cc := deployment.Config{QueueSidecarImage: "alpine"}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			got, err := makeQueueContainer(test.rev, &logConfig, &traceConfig, &obsConfig, &asConfig, &cc)
+			got, err := makeQueueContainer(test.rev, &logConfig, &traceConfig, &obsConfig, &asConfig, &deploymentConfig)
 			if err != nil {
 				t.Fatal("makeQueueContainer returned error:", err)
 			}
@@ -421,19 +395,7 @@ func TestMakeQueueContainerWithPercentageAnnotation(t *testing.T) {
 			sortEnv(got.Env)
 			sortEnv(test.want.Env)
 			if diff := cmp.Diff(test.want, *got, cmp.AllowUnexported(resource.Quantity{})); diff != "" {
-				t.Error("makeQueueContainerWithPercentageAnnotation (-want, +got) =", diff)
-			}
-			if test.want.Resources.Limits.Memory().Cmp(*got.Resources.Limits.Memory()) != 0 {
-				t.Errorf("Resources.Limits.Memory = %v, want: %v", got.Resources.Limits.Memory(), test.want.Resources.Limits.Memory())
-			}
-			if test.want.Resources.Requests.Cpu().Cmp(*got.Resources.Requests.Cpu()) != 0 {
-				t.Errorf("Resources.Request.CPU = %v, want: %v", got.Resources.Requests.Cpu(), test.want.Resources.Requests.Cpu())
-			}
-			if test.want.Resources.Requests.Memory().Cmp(*got.Resources.Requests.Memory()) != 0 {
-				t.Errorf("Resources.Requests.Memory = %v, want: %v", got.Resources.Requests.Memory(), test.want.Resources.Requests.Memory())
-			}
-			if test.want.Resources.Limits.Cpu().Cmp(*got.Resources.Limits.Cpu()) != 0 {
-				t.Errorf("Resources.Limits.CPU  = %v, want: %v", got.Resources.Limits.Cpu(), test.want.Resources.Limits.Cpu())
+				t.Error("makeQueueContainer (-want, +got) =", diff)
 			}
 		})
 	}
@@ -443,8 +405,6 @@ func TestProbeGenerationHTTPDefaults(t *testing.T) {
 	rev := revision("bar", "foo",
 		withContainerConcurrency(1),
 		func(revision *v1.Revision) {
-			revision.Spec.TimeoutSeconds = ptr.Int64(45)
-			revision.ObjectMeta.Labels = map[string]string{}
 			revision.Spec.PodSpec.Containers = []corev1.Container{{
 				Name: containerName,
 				ReadinessProbe: &corev1.Probe{
@@ -486,7 +446,7 @@ func TestProbeGenerationHTTPDefaults(t *testing.T) {
 			"SERVING_READINESS_PROBE": string(wantProbeJSON),
 		})
 		c.Resources.Requests = corev1.ResourceList{
-			corev1.ResourceName("cpu"): resource.MustParse("25m"),
+			corev1.ResourceCPU: resource.MustParse("25m"),
 		}
 		c.ReadinessProbe = &corev1.Probe{
 			Handler: corev1.Handler{
@@ -497,8 +457,7 @@ func TestProbeGenerationHTTPDefaults(t *testing.T) {
 			PeriodSeconds:  1,
 			TimeoutSeconds: 10,
 		}
-	},
-	)
+	})
 
 	got, err := makeQueueContainer(rev, &logConfig, &traceConfig, &obsConfig, &asConfig, &deploymentConfig)
 	if err != nil {
@@ -517,8 +476,6 @@ func TestProbeGenerationHTTP(t *testing.T) {
 	rev := revision("bar", "foo",
 		withContainerConcurrency(1),
 		func(revision *v1.Revision) {
-			revision.Spec.TimeoutSeconds = ptr.Int64(45)
-			revision.ObjectMeta.Labels = map[string]string{}
 			revision.Spec.PodSpec.Containers = []corev1.Container{{
 				Name: containerName,
 				Ports: []corev1.ContainerPort{{
@@ -572,8 +529,7 @@ func TestProbeGenerationHTTP(t *testing.T) {
 			PeriodSeconds:  2,
 			TimeoutSeconds: 10,
 		}
-	},
-	)
+	})
 
 	got, err := makeQueueContainer(rev, &logConfig, &traceConfig, &obsConfig, &asConfig, &deploymentConfig)
 	if err != nil {
@@ -625,7 +581,7 @@ func TestTCPProbeGeneration(t *testing.T) {
 		},
 		want: queueContainer(func(c *corev1.Container) {
 			c.Resources.Requests = corev1.ResourceList{
-				corev1.ResourceName("cpu"): resource.MustParse("25m"),
+				corev1.ResourceCPU: resource.MustParse("25m"),
 			}
 			c.ReadinessProbe = &corev1.Probe{
 				Handler: corev1.Handler{
@@ -633,7 +589,7 @@ func TestTCPProbeGeneration(t *testing.T) {
 						Command: []string{"/ko-app/queue", "-probe-period", "0"},
 					},
 				},
-				PeriodSeconds:  1,
+				PeriodSeconds:  10,
 				TimeoutSeconds: 10,
 			}
 			c.Env = env(map[string]string{"USER_PORT": strconv.Itoa(userPort)})
@@ -667,7 +623,7 @@ func TestTCPProbeGeneration(t *testing.T) {
 		},
 		want: queueContainer(func(c *corev1.Container) {
 			c.Resources.Requests = corev1.ResourceList{
-				corev1.ResourceName("cpu"): resource.MustParse("25m"),
+				corev1.ResourceCPU: resource.MustParse("25m"),
 			}
 			c.ReadinessProbe = &corev1.Probe{
 				Handler: corev1.Handler{
@@ -719,7 +675,7 @@ func TestTCPProbeGeneration(t *testing.T) {
 		},
 		want: queueContainer(func(c *corev1.Container) {
 			c.Resources.Requests = corev1.ResourceList{
-				corev1.ResourceName("cpu"): resource.MustParse("25m"),
+				corev1.ResourceCPU: resource.MustParse("25m"),
 			}
 			c.ReadinessProbe = &corev1.Probe{
 				Handler: corev1.Handler{
diff --git a/pkg/reconciler/route/resources/ingress_test.go b/pkg/reconciler/route/resources/ingress_test.go
index 88fe7003e..d94dc057c 100644
--- a/pkg/reconciler/route/resources/ingress_test.go
+++ b/pkg/reconciler/route/resources/ingress_test.go
@@ -306,6 +306,178 @@ func TestMakeIngressSpec_CorrectRuleVisibility(t *testing.T) {
 	}
 }
 
+func TestMakeIngressSpec_CorrectRulesWithTagBasedRouting(t *testing.T) {
+	targets := map[string]traffic.RevisionTargets{
+		traffic.DefaultTarget: {{
+			TrafficTarget: v1.TrafficTarget{
+				ConfigurationName: "config",
+				RevisionName:      "v2",
+				Percent:           ptr.Int64(100),
+			},
+			ServiceName: "gilberto",
+			Active:      true,
+		}},
+		"v1": {{
+			TrafficTarget: v1.TrafficTarget{
+				ConfigurationName: "config",
+				RevisionName:      "v1",
+				Percent:           ptr.Int64(100),
+			},
+			ServiceName: "jobim",
+			Active:      true,
+		}},
+	}
+
+	r := Route(ns, "test-route", WithURL)
+
+	expected := []netv1alpha1.IngressRule{{
+		Hosts: []string{
+			"test-route." + ns + ".svc.cluster.local",
+		},
+		HTTP: &netv1alpha1.HTTPIngressRuleValue{
+			Paths: []netv1alpha1.HTTPIngressPath{{
+				Headers: map[string]netv1alpha1.HeaderMatch{
+					network.TagHeaderName: {
+						Exact: "v1",
+					},
+				},
+				Splits: []netv1alpha1.IngressBackendSplit{{
+					IngressBackend: netv1alpha1.IngressBackend{
+						ServiceNamespace: ns,
+						ServiceName:      "jobim",
+						ServicePort:      intstr.FromInt(80),
+					},
+					Percent: 100,
+					AppendHeaders: map[string]string{
+						"Knative-Serving-Revision":  "v1",
+						"Knative-Serving-Namespace": ns,
+					},
+				}},
+			}, {
+				AppendHeaders: map[string]string{
+					network.DefaultRouteHeaderName: "true",
+				},
+				Splits: []netv1alpha1.IngressBackendSplit{{
+					IngressBackend: netv1alpha1.IngressBackend{
+						ServiceNamespace: ns,
+						ServiceName:      "gilberto",
+						ServicePort:      intstr.FromInt(80),
+					},
+					Percent: 100,
+					AppendHeaders: map[string]string{
+						"Knative-Serving-Revision":  "v2",
+						"Knative-Serving-Namespace": ns,
+					},
+				}},
+			}},
+		},
+		Visibility: netv1alpha1.IngressVisibilityClusterLocal,
+	}, {
+		Hosts: []string{
+			"test-route." + ns + ".example.com",
+		},
+		HTTP: &netv1alpha1.HTTPIngressRuleValue{
+			Paths: []netv1alpha1.HTTPIngressPath{{
+				Headers: map[string]netv1alpha1.HeaderMatch{
+					network.TagHeaderName: {
+						Exact: "v1",
+					},
+				},
+				Splits: []netv1alpha1.IngressBackendSplit{{
+					IngressBackend: netv1alpha1.IngressBackend{
+						ServiceNamespace: ns,
+						ServiceName:      "jobim",
+						ServicePort:      intstr.FromInt(80),
+					},
+					Percent: 100,
+					AppendHeaders: map[string]string{
+						"Knative-Serving-Revision":  "v1",
+						"Knative-Serving-Namespace": ns,
+					},
+				}},
+			}, {
+				AppendHeaders: map[string]string{
+					network.DefaultRouteHeaderName: "true",
+				},
+				Splits: []netv1alpha1.IngressBackendSplit{{
+					IngressBackend: netv1alpha1.IngressBackend{
+						ServiceNamespace: ns,
+						ServiceName:      "gilberto",
+						ServicePort:      intstr.FromInt(80),
+					},
+					Percent: 100,
+					AppendHeaders: map[string]string{
+						"Knative-Serving-Revision":  "v2",
+						"Knative-Serving-Namespace": ns,
+					},
+				}},
+			}},
+		},
+		Visibility: netv1alpha1.IngressVisibilityExternalIP,
+	}, {
+		Hosts: []string{
+			"v1-test-route." + ns + ".svc.cluster.local",
+		},
+		HTTP: &netv1alpha1.HTTPIngressRuleValue{
+			Paths: []netv1alpha1.HTTPIngressPath{{
+				AppendHeaders: map[string]string{
+					network.TagHeaderName: "v1",
+				},
+				Splits: []netv1alpha1.IngressBackendSplit{{
+					IngressBackend: netv1alpha1.IngressBackend{
+						ServiceNamespace: ns,
+						ServiceName:      "jobim",
+						ServicePort:      intstr.FromInt(80),
+					},
+					Percent: 100,
+					AppendHeaders: map[string]string{
+						"Knative-Serving-Revision":  "v1",
+						"Knative-Serving-Namespace": ns,
+					},
+				}},
+			}},
+		},
+		Visibility: netv1alpha1.IngressVisibilityClusterLocal,
+	}, {
+		Hosts: []string{
+			"v1-test-route." + ns + ".example.com",
+		},
+		HTTP: &netv1alpha1.HTTPIngressRuleValue{
+			Paths: []netv1alpha1.HTTPIngressPath{{
+				AppendHeaders: map[string]string{
+					network.TagHeaderName: "v1",
+				},
+				Splits: []netv1alpha1.IngressBackendSplit{{
+					IngressBackend: netv1alpha1.IngressBackend{
+						ServiceNamespace: ns,
+						ServiceName:      "jobim",
+						ServicePort:      intstr.FromInt(80),
+					},
+					Percent: 100,
+					AppendHeaders: map[string]string{
+						"Knative-Serving-Revision":  "v1",
+						"Knative-Serving-Namespace": ns,
+					},
+				}},
+			}},
+		},
+		Visibility: netv1alpha1.IngressVisibilityExternalIP,
+	}}
+
+	cfg := testConfig()
+	cfg.Network.TagHeaderBasedRouting = true
+	ctx := config.ToContext(context.Background(), cfg)
+
+	ci, err := MakeIngressSpec(ctx, r, nil, targets, nil /* visibility */)
+	if err != nil {
+		t.Errorf("Unexpected error %v", err)
+	}
+
+	if !cmp.Equal(expected, ci.Rules) {
+		t.Errorf("Unexpected rules (-want, +got): %s", cmp.Diff(expected, ci.Rules))
+	}
+}
+
 // One active target.
 func TestMakeIngressRule_Vanilla(t *testing.T) {
 	targets := []traffic.RevisionTarget{{
diff --git a/pkg/reconciler/route/route_test.go b/pkg/reconciler/route/route_test.go
index 585da1d18..3e19d4e5a 100644
--- a/pkg/reconciler/route/route_test.go
+++ b/pkg/reconciler/route/route_test.go
@@ -51,6 +51,7 @@ import (
 	"knative.dev/pkg/controller"
 	"knative.dev/pkg/ptr"
 	"knative.dev/pkg/system"
+	"knative.dev/serving/pkg/apis/networking/v1alpha1"
 	netv1alpha1 "knative.dev/serving/pkg/apis/networking/v1alpha1"
 	"knative.dev/serving/pkg/apis/serving"
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
@@ -989,6 +990,314 @@ func TestCreateRouteWithNamedTargets(t *testing.T) {
 	}
 }
 
+func TestCreateRouteWithNamedTargetsAndTagBasedRouting(t *testing.T) {
+	ctx, _, ctl, watcher, cf := newTestSetup(t)
+	defer cf()
+
+	watcher.OnChange(&corev1.ConfigMap{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      network.ConfigName,
+			Namespace: system.Namespace(),
+		},
+		Data: map[string]string{
+			network.TagHeaderBasedRoutingKey: "enabled",
+		},
+	})
+	// A standalone revision
+	rev := getTestRevision("test-rev")
+	fakeservingclient.Get(ctx).ServingV1().Revisions(testNamespace).Create(rev)
+	fakerevisioninformer.Get(ctx).Informer().GetIndexer().Add(rev)
+
+	// A configuration and associated revision. Normally the revision would be
+	// created by the configuration reconciler.
+	config := getTestConfiguration()
+	cfgrev := getTestRevisionForConfig(config)
+	config.Status.SetLatestCreatedRevisionName(cfgrev.Name)
+	config.Status.SetLatestReadyRevisionName(cfgrev.Name)
+	fakeservingclient.Get(ctx).ServingV1().Configurations(testNamespace).Create(config)
+	// Since Reconcile looks in the lister, we need to add it to the informer
+	fakecfginformer.Get(ctx).Informer().GetIndexer().Add(config)
+	fakeservingclient.Get(ctx).ServingV1().Revisions(testNamespace).Create(cfgrev)
+	fakerevisioninformer.Get(ctx).Informer().GetIndexer().Add(cfgrev)
+
+	// A route targeting both the config and standalone revision with named
+	// targets
+	route := getTestRouteWithTrafficTargets(WithSpecTraffic(
+		v1.TrafficTarget{
+			Tag:          "foo",
+			RevisionName: "test-rev",
+			Percent:      ptr.Int64(50),
+		}, v1.TrafficTarget{
+			Tag:               "bar",
+			ConfigurationName: "test-config",
+			Percent:           ptr.Int64(50),
+		}))
+
+	fakeservingclient.Get(ctx).ServingV1().Routes(testNamespace).Create(route)
+	// Since Reconcile looks in the lister, we need to add it to the informer
+	fakerouteinformer.Get(ctx).Informer().GetIndexer().Add(route)
+
+	ctl.Reconciler.Reconcile(context.Background(), KeyOrDie(route))
+
+	ci := getRouteIngressFromClient(ctx, t, route)
+	domain := strings.Join([]string{route.Name, route.Namespace, defaultDomainSuffix}, ".")
+	expectedSpec := netv1alpha1.IngressSpec{
+		TLS: []netv1alpha1.IngressTLS{},
+		Rules: []netv1alpha1.IngressRule{{
+			Hosts: []string{
+				"test-route.test.svc.cluster.local",
+			},
+			HTTP: &netv1alpha1.HTTPIngressRuleValue{
+				Paths: []netv1alpha1.HTTPIngressPath{{
+					Headers: map[string]netv1alpha1.HeaderMatch{
+						network.TagHeaderName: {
+							Exact: "bar",
+						},
+					},
+					Splits: []v1alpha1.IngressBackendSplit{
+						{
+							IngressBackend: v1alpha1.IngressBackend{
+								ServiceNamespace: "test",
+								ServiceName:      "p-deadbeef",
+								ServicePort:      intstr.IntOrString{IntVal: 80},
+							},
+							Percent: 100,
+							AppendHeaders: map[string]string{
+								"Knative-Serving-Namespace": "test",
+								"Knative-Serving-Revision":  "p-deadbeef",
+							},
+						},
+					},
+				}, {
+					Headers: map[string]netv1alpha1.HeaderMatch{
+						network.TagHeaderName: {
+							Exact: "foo",
+						},
+					},
+					Splits: []v1alpha1.IngressBackendSplit{
+						{
+							IngressBackend: v1alpha1.IngressBackend{
+								ServiceNamespace: "test",
+								ServiceName:      "test-rev",
+								ServicePort:      intstr.IntOrString{IntVal: 80},
+							},
+							Percent: 100,
+							AppendHeaders: map[string]string{
+								"Knative-Serving-Namespace": "test",
+								"Knative-Serving-Revision":  "test-rev",
+							},
+						},
+					},
+				}, {
+					AppendHeaders: map[string]string{
+						network.DefaultRouteHeaderName: "true",
+					},
+					Splits: []netv1alpha1.IngressBackendSplit{{
+						IngressBackend: netv1alpha1.IngressBackend{
+							ServiceNamespace: testNamespace,
+							ServiceName:      rev.Name,
+							ServicePort:      intstr.FromInt(80),
+						},
+						Percent: 50,
+						AppendHeaders: map[string]string{
+							"Knative-Serving-Revision":  rev.Name,
+							"Knative-Serving-Namespace": testNamespace,
+						},
+					}, {
+						IngressBackend: netv1alpha1.IngressBackend{
+							ServiceNamespace: testNamespace,
+							ServiceName:      cfgrev.Name,
+							ServicePort:      intstr.FromInt(80),
+						},
+						Percent: 50,
+						AppendHeaders: map[string]string{
+							"Knative-Serving-Revision":  cfgrev.Name,
+							"Knative-Serving-Namespace": testNamespace,
+						},
+					}},
+				}},
+			},
+			Visibility: netv1alpha1.IngressVisibilityClusterLocal,
+		}, {
+			Hosts: []string{
+				domain,
+			},
+			HTTP: &netv1alpha1.HTTPIngressRuleValue{
+				Paths: []netv1alpha1.HTTPIngressPath{{
+					Headers: map[string]netv1alpha1.HeaderMatch{
+						network.TagHeaderName: {
+							Exact: "bar",
+						},
+					},
+					Splits: []v1alpha1.IngressBackendSplit{
+						{
+							IngressBackend: v1alpha1.IngressBackend{
+								ServiceNamespace: "test",
+								ServiceName:      "p-deadbeef",
+								ServicePort:      intstr.IntOrString{IntVal: 80},
+							},
+							Percent: 100,
+							AppendHeaders: map[string]string{
+								"Knative-Serving-Namespace": "test",
+								"Knative-Serving-Revision":  "p-deadbeef",
+							},
+						},
+					},
+				}, {
+					Headers: map[string]netv1alpha1.HeaderMatch{
+						network.TagHeaderName: {
+							Exact: "foo",
+						},
+					},
+					Splits: []v1alpha1.IngressBackendSplit{
+						{
+							IngressBackend: v1alpha1.IngressBackend{
+								ServiceNamespace: "test",
+								ServiceName:      "test-rev",
+								ServicePort:      intstr.IntOrString{IntVal: 80},
+							},
+							Percent: 100,
+							AppendHeaders: map[string]string{
+								"Knative-Serving-Namespace": "test",
+								"Knative-Serving-Revision":  "test-rev",
+							},
+						},
+					},
+				}, {
+					AppendHeaders: map[string]string{
+						network.DefaultRouteHeaderName: "true",
+					},
+					Splits: []netv1alpha1.IngressBackendSplit{{
+						IngressBackend: netv1alpha1.IngressBackend{
+							ServiceNamespace: testNamespace,
+							ServiceName:      rev.Name,
+							ServicePort:      intstr.FromInt(80),
+						},
+						Percent: 50,
+						AppendHeaders: map[string]string{
+							"Knative-Serving-Revision":  rev.Name,
+							"Knative-Serving-Namespace": testNamespace,
+						},
+					}, {
+						IngressBackend: netv1alpha1.IngressBackend{
+							ServiceNamespace: testNamespace,
+							ServiceName:      cfgrev.Name,
+							ServicePort:      intstr.FromInt(80),
+						},
+						Percent: 50,
+						AppendHeaders: map[string]string{
+							"Knative-Serving-Revision":  cfgrev.Name,
+							"Knative-Serving-Namespace": testNamespace,
+						},
+					}},
+				}},
+			},
+			Visibility: netv1alpha1.IngressVisibilityExternalIP,
+		}, {
+			Hosts: []string{
+				"bar-test-route.test.svc.cluster.local",
+			},
+			HTTP: &netv1alpha1.HTTPIngressRuleValue{
+				Paths: []netv1alpha1.HTTPIngressPath{{
+					Splits: []netv1alpha1.IngressBackendSplit{{
+						IngressBackend: netv1alpha1.IngressBackend{
+							ServiceNamespace: testNamespace,
+							ServiceName:      cfgrev.Name,
+							ServicePort:      intstr.FromInt(80),
+						},
+						Percent: 100,
+						AppendHeaders: map[string]string{
+							"Knative-Serving-Revision":  cfgrev.Name,
+							"Knative-Serving-Namespace": testNamespace,
+						},
+					}},
+					AppendHeaders: map[string]string{
+						network.TagHeaderName: "bar",
+					},
+				}},
+			},
+			Visibility: netv1alpha1.IngressVisibilityClusterLocal,
+		}, {
+			Hosts: []string{
+				"bar-test-route.test.test-domain.dev",
+			},
+			HTTP: &netv1alpha1.HTTPIngressRuleValue{
+				Paths: []netv1alpha1.HTTPIngressPath{{
+					Splits: []netv1alpha1.IngressBackendSplit{{
+						IngressBackend: netv1alpha1.IngressBackend{
+							ServiceNamespace: testNamespace,
+							ServiceName:      cfgrev.Name,
+							ServicePort:      intstr.FromInt(80),
+						},
+						Percent: 100,
+						AppendHeaders: map[string]string{
+							"Knative-Serving-Revision":  cfgrev.Name,
+							"Knative-Serving-Namespace": testNamespace,
+						},
+					}},
+					AppendHeaders: map[string]string{
+						network.TagHeaderName: "bar",
+					},
+				}},
+			},
+			Visibility: netv1alpha1.IngressVisibilityExternalIP,
+		}, {
+			Hosts: []string{
+				"foo-test-route.test.svc.cluster.local",
+			},
+			HTTP: &netv1alpha1.HTTPIngressRuleValue{
+				Paths: []netv1alpha1.HTTPIngressPath{{
+					Splits: []netv1alpha1.IngressBackendSplit{{
+						IngressBackend: netv1alpha1.IngressBackend{
+							ServiceNamespace: testNamespace,
+							ServiceName:      rev.Name,
+							ServicePort:      intstr.FromInt(80),
+						},
+						Percent: 100,
+						AppendHeaders: map[string]string{
+							"Knative-Serving-Revision":  rev.Name,
+							"Knative-Serving-Namespace": testNamespace,
+						},
+					}},
+					AppendHeaders: map[string]string{
+						network.TagHeaderName: "foo",
+					},
+				}},
+			},
+			Visibility: netv1alpha1.IngressVisibilityClusterLocal,
+		}, {
+			Hosts: []string{
+				"foo-test-route.test.test-domain.dev",
+			},
+			HTTP: &netv1alpha1.HTTPIngressRuleValue{
+				Paths: []netv1alpha1.HTTPIngressPath{{
+					Splits: []netv1alpha1.IngressBackendSplit{{
+						IngressBackend: netv1alpha1.IngressBackend{
+							ServiceNamespace: testNamespace,
+							ServiceName:      rev.Name,
+							ServicePort:      intstr.FromInt(80),
+						},
+						Percent: 100,
+						AppendHeaders: map[string]string{
+							"Knative-Serving-Revision":  rev.Name,
+							"Knative-Serving-Namespace": testNamespace,
+						},
+					}},
+					AppendHeaders: map[string]string{
+						network.TagHeaderName: "foo",
+					},
+				}},
+			},
+			Visibility: netv1alpha1.IngressVisibilityExternalIP,
+		}},
+	}
+
+	if diff := cmp.Diff(expectedSpec, ci.Spec); diff != "" {
+		fmt.Printf("%+v\n", ci.Spec)
+		t.Errorf("Unexpected rule spec diff (-want +got): %v", diff)
+	}
+}
+
 func TestUpdateDomainConfigMap(t *testing.T) {
 	templateCM := corev1.ConfigMap{
 		ObjectMeta: metav1.ObjectMeta{
diff --git a/test/config/config-features.yaml b/test/config/config-features.yaml
new file mode 100644
index 000000000..6336e627b
--- /dev/null
+++ b/test/config/config-features.yaml
@@ -0,0 +1,41 @@
+# Copyright 2020 The Knative Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: config-features
+  namespace: knative-serving
+  labels:
+    serving.knative.dev/release: devel
+
+data:
+  _example: |
+    ################################
+    #                              #
+    #    EXAMPLE CONFIGURATION     #
+    #                              #
+    ################################
+
+    # This block is not actually functional configuration,
+    # but serves to illustrate the available configuration
+    # options and document them in a way that is accessible
+    # to users that `kubectl edit` this config map.
+    #
+    # These sample configuration options may be copied out of
+    # this example block and unindented to be in the data block
+    # to actually change the configuration.
+
+    # Indicates whether multi container support is enabled
+    multi-container: "disabled"
diff --git a/test/e2e/helloworld_test.go b/test/e2e/helloworld_test.go
index 3d7b2bb1f..4495719e7 100644
--- a/test/e2e/helloworld_test.go
+++ b/test/e2e/helloworld_test.go
@@ -105,12 +105,12 @@ func TestQueueSideCarResourceLimit(t *testing.T) {
 	resources, err := v1test.CreateServiceReady(t, clients, &names,
 		rtesting.WithResourceRequirements(corev1.ResourceRequirements{
 			Requests: corev1.ResourceList{
-				corev1.ResourceName("cpu"):    resource.MustParse("50m"),
-				corev1.ResourceName("memory"): resource.MustParse("128Mi"),
+				corev1.ResourceCPU:    resource.MustParse("50m"),
+				corev1.ResourceMemory: resource.MustParse("128Mi"),
 			},
 			Limits: corev1.ResourceList{
-				corev1.ResourceName("cpu"):    resource.MustParse("100m"),
-				corev1.ResourceName("memory"): resource.MustParse("258Mi"),
+				corev1.ResourceCPU:    resource.MustParse("100m"),
+				corev1.ResourceMemory: resource.MustParse("258Mi"),
 			},
 		}), rtesting.WithConfigAnnotations(map[string]string{
 			serving.QueueSideCarResourcePercentageAnnotation: "0.2",
diff --git a/test/test_images/httpproxy/service.yaml b/test/test_images/httpproxy/service.yaml
index b4bac824c..d9ee2b608 100644
--- a/test/test_images/httpproxy/service.yaml
+++ b/test/test_images/httpproxy/service.yaml
@@ -1,16 +1,5 @@
 apiVersion: serving.knative.dev/v1
 kind: Service
-metadata:
-  name: helloworld-test-image
-  namespace: default
-spec:
-  template:
-    spec:
-      containers:
-      - image: ko://knative.dev/serving/test/test_images/helloworld
----
-apiVersion: serving.knative.dev/v1
-kind: Service
 metadata:
   name: httpproxy-test-image
   namespace: default
@@ -22,4 +11,3 @@ spec:
         env:
         - name: TARGET_HOST
           value: "helloworld-test-image.default.svc.cluster.local"
-
diff --git a/third_party/contour-latest/contour.yaml b/third_party/contour-latest/contour.yaml
index c890192f0..898c622f7 100644
--- a/third_party/contour-latest/contour.yaml
+++ b/third_party/contour-latest/contour.yaml
@@ -1432,7 +1432,7 @@ spec:
         # if we change this on each version, you can no longer upgrade
         # just by applying the deployment YAML.
         # See #2423, #2395, #2150, and #2030 for earlier questions about this.
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:25776cd920a8961746503d4bf923774a5b312a793e13ef4bcd01ae948ef0b748
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:69f3a2d31b2a14e8cabea4ed38ac83845ff7920c31a0d836a494383307737926
         imagePullPolicy: Always
         command:
         - contour
@@ -1672,7 +1672,7 @@ spec:
         - --contour-key-file=/certs/tls.key
         - --config-path=/config/contour.yaml
         command: ["contour"]
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:25776cd920a8961746503d4bf923774a5b312a793e13ef4bcd01ae948ef0b748
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:69f3a2d31b2a14e8cabea4ed38ac83845ff7920c31a0d836a494383307737926
         imagePullPolicy: Always
         name: contour
         ports:
@@ -1764,7 +1764,7 @@ spec:
         args:
         - envoy
         - shutdown-manager
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:25776cd920a8961746503d4bf923774a5b312a793e13ef4bcd01ae948ef0b748
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:69f3a2d31b2a14e8cabea4ed38ac83845ff7920c31a0d836a494383307737926
         imagePullPolicy: Always
         lifecycle:
           preStop:
@@ -1840,7 +1840,7 @@ spec:
         - --envoy-key-file=/certs/tls.key
         command:
         - contour
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:25776cd920a8961746503d4bf923774a5b312a793e13ef4bcd01ae948ef0b748
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:69f3a2d31b2a14e8cabea4ed38ac83845ff7920c31a0d836a494383307737926
         imagePullPolicy: Always
         name: envoy-initconfig
         volumeMounts:
@@ -3305,7 +3305,7 @@ spec:
         # if we change this on each version, you can no longer upgrade
         # just by applying the deployment YAML.
         # See #2423, #2395, #2150, and #2030 for earlier questions about this.
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:25776cd920a8961746503d4bf923774a5b312a793e13ef4bcd01ae948ef0b748
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:69f3a2d31b2a14e8cabea4ed38ac83845ff7920c31a0d836a494383307737926
         imagePullPolicy: Always
         command:
         - contour
@@ -3545,7 +3545,7 @@ spec:
         - --contour-key-file=/certs/tls.key
         - --config-path=/config/contour.yaml
         command: ["contour"]
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:25776cd920a8961746503d4bf923774a5b312a793e13ef4bcd01ae948ef0b748
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:69f3a2d31b2a14e8cabea4ed38ac83845ff7920c31a0d836a494383307737926
         imagePullPolicy: Always
         name: contour
         ports:
@@ -3637,7 +3637,7 @@ spec:
         args:
         - envoy
         - shutdown-manager
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:25776cd920a8961746503d4bf923774a5b312a793e13ef4bcd01ae948ef0b748
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:69f3a2d31b2a14e8cabea4ed38ac83845ff7920c31a0d836a494383307737926
         imagePullPolicy: Always
         lifecycle:
           preStop:
@@ -3713,7 +3713,7 @@ spec:
         - --envoy-key-file=/certs/tls.key
         command:
         - contour
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:25776cd920a8961746503d4bf923774a5b312a793e13ef4bcd01ae948ef0b748
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:69f3a2d31b2a14e8cabea4ed38ac83845ff7920c31a0d836a494383307737926
         imagePullPolicy: Always
         name: envoy-initconfig
         volumeMounts:
diff --git a/third_party/contour-latest/net-contour.yaml b/third_party/contour-latest/net-contour.yaml
index 282116932..f2ae8a3e1 100644
--- a/third_party/contour-latest/net-contour.yaml
+++ b/third_party/contour-latest/net-contour.yaml
@@ -35,7 +35,7 @@ metadata:
   namespace: knative-serving
   labels:
     networking.knative.dev/ingress-provider: contour
-    serving.knative.dev/release: "v20200527-d62309d"
+    serving.knative.dev/release: "v20200601-b5d9707"
 data:
   _example: |
     ################################
@@ -94,7 +94,7 @@ spec:
       - name: controller
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:e6228f84f06f950240e6cc236f18cabcad890386ce2ba358d40c4f9b69652bfa
+        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:a00111f41edd33143e10295aa5423fa28a38006262f20a58586ed1eddb161f63
         resources:
           requests:
             cpu: 40m
diff --git a/third_party/kourier-latest/kourier.yaml b/third_party/kourier-latest/kourier.yaml
index 102a71942..a7f4ef466 100644
--- a/third_party/kourier-latest/kourier.yaml
+++ b/third_party/kourier-latest/kourier.yaml
@@ -166,7 +166,7 @@ spec:
         app: 3scale-kourier-control
     spec:
       containers:
-      - image: gcr.io/knative-nightly/knative.dev/net-kourier/cmd/kourier@sha256:32dc25a012cf385ede0e40eabd797966e230751f521ffcb0689814994ed599f3
+      - image: gcr.io/knative-nightly/knative.dev/net-kourier/cmd/kourier@sha256:3eaf148ca1da1637a84c39e0aa6423abefdaacacc0a7739ec7a4422b8b4c83a0
         imagePullPolicy: Always
         name: kourier-control
         env: