Repository: knative/serving
Pull request #8060 (2020-05-25 14:00:57)

[master] Update net-istio nightly
<!--[master] Update net-istio nightly-->
<!---->
Produced via:
  `curl https://storage.googleapis.com/knative-nightly/net-istio/latest/$x > /workspace/source/./third_party//$x`
/assign tcnghia
/cc tcnghia
/test pull-knative-serving-istio-1.4-mesh


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

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

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

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: @mattmoor: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-istio-1.4-mesh | 6e81b53c06d75e1a5410c88ee9e1017e495416b0 | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_serving/8060/pull-knative-serving-istio-1.4-mesh/1264919463157829632) | `/test pull-knative-serving-istio-1.4-mesh`

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

<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: @mattmoor: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-istio-1.4-mesh | 6e81b53c06d75e1a5410c88ee9e1017e495416b0 | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_serving/8060/pull-knative-serving-istio-1.4-mesh/1264919463157829632) | `/test pull-knative-serving-istio-1.4-mesh`
pull-knative-serving-integration-tests | 6e81b53c06d75e1a5410c88ee9e1017e495416b0 | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_serving/8060/pull-knative-serving-integration-tests/1264919463128469504) | `/test pull-knative-serving-integration-tests`

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

<details>

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

Test name | Triggers | Retries
--- | --- | ---
pull-knative-serving-integration-tests |  | 0/3

Failed non-flaky tests preventing automatic retry of pull-knative-serving-integration-tests:

```
test/conformance/api/v1.TestContainerErrorMsg
test/conformance/api/v1.TestAnnotationPropagation
test/conformance/api/v1.TestContainerExitingMsg/http
test/conformance/api/v1alpha1.TestUpdateConfigurationMetadata
test/conformance/api/v1alpha1.TestContainerErrorMsg
test/conformance/api/v1alpha1.TestContainerExitingMsg
test/conformance/api/v1alpha1.TestContainerExitingMsg/http
test/conformance/api/v1alpha1.TestContainerExitingMsg/tcp
```

and 29 more.
ZhiminXiang: /test pull-knative-serving-integration-tests
/test pull-knative-serving-istio-1.4-mesh
knative-prow-robot: @mattmoor: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-istio-1.4-mesh | 6e81b53c06d75e1a5410c88ee9e1017e495416b0 | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_serving/8060/pull-knative-serving-istio-1.4-mesh/1264960608277630976) | `/test pull-knative-serving-istio-1.4-mesh`

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

<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/cmd/webhook/main.go b/cmd/webhook/main.go
index 1c59a288f..e9f8d029c 100644
--- a/cmd/webhook/main.go
+++ b/cmd/webhook/main.go
@@ -86,7 +86,7 @@ var callbacks = map[schema.GroupVersionKind]validation.Callback{
 	v1.SchemeGroupVersion.WithKind("Service"):       serviceValidation,
 }
 
-func NewDefaultingAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
+func newDefaultingAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
 	// Decorate contexts with the current state of the config.
 	store := defaultconfig.NewStore(logging.FromContext(ctx).Named("config-store"))
 	store.WatchConfigs(cmw)
@@ -112,7 +112,7 @@ func NewDefaultingAdmissionController(ctx context.Context, cmw configmap.Watcher
 	)
 }
 
-func NewValidationAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
+func newValidationAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
 	// Decorate contexts with the current state of the config.
 	store := defaultconfig.NewStore(logging.FromContext(ctx).Named("config-store"))
 	store.WatchConfigs(cmw)
@@ -141,7 +141,7 @@ func NewValidationAdmissionController(ctx context.Context, cmw configmap.Watcher
 	)
 }
 
-func NewConfigValidationController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
+func newConfigValidationController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
 	return configmaps.NewAdmissionController(ctx,
 
 		// Name of the configmap webhook.
@@ -166,7 +166,7 @@ func NewConfigValidationController(ctx context.Context, cmw configmap.Watcher) *
 	)
 }
 
-func NewConversionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
+func newConversionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
 	var (
 		v1alpha1_ = v1alpha1.SchemeGroupVersion.Version
 		v1beta1_  = v1beta1.SchemeGroupVersion.Version
@@ -225,7 +225,6 @@ func NewConversionController(ctx context.Context, cmw configmap.Watcher) *contro
 }
 
 func main() {
-
 	// Set up a signal context with our webhook options
 	ctx := webhook.WithOptions(signals.NewContext(), webhook.Options{
 		ServiceName: "webhook",
@@ -235,9 +234,9 @@ func main() {
 
 	sharedmain.WebhookMainWithContext(ctx, "webhook",
 		certificates.NewController,
-		NewDefaultingAdmissionController,
-		NewValidationAdmissionController,
-		NewConfigValidationController,
-		NewConversionController,
+		newDefaultingAdmissionController,
+		newValidationAdmissionController,
+		newConfigValidationController,
+		newConversionController,
 	)
 }
diff --git a/config/core/configmaps/features.yaml b/config/core/configmaps/features.yaml
new file mode 100644
index 000000000..6336e627b
--- /dev/null
+++ b/config/core/configmaps/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/go.mod b/go.mod
index aab2a0681..3478acd77 100644
--- a/go.mod
+++ b/go.mod
@@ -44,8 +44,8 @@ require (
 	k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
 	k8s.io/metrics v0.17.2
 	knative.dev/caching v0.0.0-20200521155757-e78d17bc250e
-	knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7
-	knative.dev/test-infra v0.0.0-20200519161858-554a95a37986
+	knative.dev/pkg v0.0.0-20200526194649-c48d2ebdae96
+	knative.dev/test-infra v0.0.0-20200526171149-39b911556285
 )
 
 replace (
diff --git a/go.sum b/go.sum
index 42ce87121..7ca3f8c12 100644
--- a/go.sum
+++ b/go.sum
@@ -1441,6 +1441,9 @@ knative.dev/pkg v0.0.0-20200505191044-3da93ebb24c2/go.mod h1:Q6sL35DdGs8hIQZKdaC
 knative.dev/pkg v0.0.0-20200515002500-16d7b963416f/go.mod h1:tMOHGbxtRz8zYFGEGpV/bpoTEM1o89MwYFC4YJXl3GY=
 knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7 h1:9S2r59HZJF9nKvoRLg5zJzx6XpVlVyvVRqz/C/h6h2s=
 knative.dev/pkg v0.0.0-20200519155757-14eb3ae3a5a7/go.mod h1:QgNZTxnwpB/oSpNcfnLVlw+WpEwwyKAvJlvR3hgeltA=
+knative.dev/pkg v0.0.0-20200520073958-94316e20e860/go.mod h1:QgNZTxnwpB/oSpNcfnLVlw+WpEwwyKAvJlvR3hgeltA=
+knative.dev/pkg v0.0.0-20200526194649-c48d2ebdae96 h1:nSd1ujNjNFWEC2TOlWZyqDAZ9d2l/mQ+Twnhukkby+s=
+knative.dev/pkg v0.0.0-20200526194649-c48d2ebdae96/go.mod h1:4ipprwHqpqiyYHbrEjSf1kNwL+sJOqHhj1ujO2WxvpE=
 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=
@@ -1449,6 +1452,10 @@ knative.dev/test-infra v0.0.0-20200519015156-82551620b0a9 h1:kKfV3QWsxugwXsqgjFd
 knative.dev/test-infra v0.0.0-20200519015156-82551620b0a9/go.mod h1:A5b2OAXTOeHT3hHhVQm3dmtbuWvIDP7qzgtqxA3/2pE=
 knative.dev/test-infra v0.0.0-20200519161858-554a95a37986 h1:ZDy43jkWPQ75d4l4DGy+ENQIXlNcnHIh4tB6XxgovNc=
 knative.dev/test-infra v0.0.0-20200519161858-554a95a37986/go.mod h1:LeNa1Wvn47efeQUkpkn3XG7Fx9Ga+rhAP13SZyjaEGg=
+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-20200526171149-39b911556285 h1:UphI5NLnVNlpqDc/ad44lhxl9yFBQJYX7wIEh5y4KTI=
+knative.dev/test-infra v0.0.0-20200526171149-39b911556285/go.mod h1:n9eQkzmSNj8BiqNFl1lzoz68D09uMeJfyOjc132Gbik=
 modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
diff --git a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go
index 7bca3dd52..327d06612 100644
--- a/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go
+++ b/pkg/apis/autoscaling/v1alpha1/pa_lifecycle.go
@@ -37,10 +37,12 @@ func (*PodAutoscaler) GetConditionSet() apis.ConditionSet {
 	return podCondSet
 }
 
+// GetGroupVersionKind returns the GVK for the PodAutoscaler.
 func (pa *PodAutoscaler) GetGroupVersionKind() schema.GroupVersionKind {
 	return SchemeGroupVersion.WithKind("PodAutoscaler")
 }
 
+// Class returns the Autoscaler class from Annotation or `KPA` if none is set.
 func (pa *PodAutoscaler) Class() string {
 	if c, ok := pa.Annotations[autoscaling.ClassAnnotationKey]; ok {
 		return c
diff --git a/pkg/apis/autoscaling/v1alpha1/pa_validation.go b/pkg/apis/autoscaling/v1alpha1/pa_validation.go
index 4e123aa50..1a17d8ab7 100644
--- a/pkg/apis/autoscaling/v1alpha1/pa_validation.go
+++ b/pkg/apis/autoscaling/v1alpha1/pa_validation.go
@@ -24,6 +24,7 @@ import (
 	"knative.dev/serving/pkg/apis/serving"
 )
 
+// Validate implements apis.Validatable interface.
 func (pa *PodAutoscaler) Validate(ctx context.Context) *apis.FieldError {
 	return serving.ValidateObjectMetadata(ctx, pa.GetObjectMeta()).ViaField("metadata").
 		Also(pa.Spec.Validate(apis.WithinSpec(ctx)).ViaField("spec"))
diff --git a/pkg/apis/config/defaults.go b/pkg/apis/config/defaults.go
index 75b5db4e1..a458e71fd 100644
--- a/pkg/apis/config/defaults.go
+++ b/pkg/apis/config/defaults.go
@@ -58,7 +58,7 @@ const (
 	DefaultAllowContainerConcurrencyZero bool = true
 )
 
-func defaultConfig() *Defaults {
+func defaultDefaultsConfig() *Defaults {
 	return &Defaults{
 		RevisionTimeoutSeconds:        DefaultRevisionTimeoutSeconds,
 		MaxRevisionTimeoutSeconds:     DefaultMaxRevisionTimeoutSeconds,
@@ -71,12 +71,11 @@ func defaultConfig() *Defaults {
 
 // NewDefaultsConfigFromMap creates a Defaults from the supplied Map.
 func NewDefaultsConfigFromMap(data map[string]string) (*Defaults, error) {
-	nc := defaultConfig()
+	nc := defaultDefaultsConfig()
 
 	if err := cm.Parse(data,
 		cm.AsString("container-name-template", &nc.UserContainerNameTemplate),
 
-		cm.AsBool("enable-multi-container", &nc.EnableMultiContainer),
 		cm.AsBool("allow-container-concurrency-zero", &nc.AllowContainerConcurrencyZero),
 
 		cm.AsInt64("revision-timeout-seconds", &nc.RevisionTimeoutSeconds),
@@ -123,9 +122,6 @@ func NewDefaultsConfigFromConfigMap(config *corev1.ConfigMap) (*Defaults, error)
 
 // Defaults includes the default values to be populated by the webhook.
 type Defaults struct {
-	// Feature flag to enable multi container support.
-	EnableMultiContainer bool
-
 	RevisionTimeoutSeconds int64
 	// This is the timeout set for ingress.
 	// RevisionTimeoutSeconds must be less than this value.
diff --git a/pkg/apis/config/features.go b/pkg/apis/config/features.go
new file mode 100644
index 000000000..b81fb6796
--- /dev/null
+++ b/pkg/apis/config/features.go
@@ -0,0 +1,76 @@
+/*
+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.
+*/
+
+package config
+
+import (
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	cm "knative.dev/pkg/configmap"
+)
+
+type Flag string
+
+const (
+	// FeaturesConfigName is the name of the ConfigMap for the features.
+	FeaturesConfigName = "config-features"
+
+	Enabled  Flag = "Enabled"
+	Allowed  Flag = "Allowed"
+	Disabled Flag = "Disabled"
+)
+
+func defaultFeaturesConfig() *Features {
+	return &Features{
+		MultiContainer: Disabled,
+	}
+}
+
+// NewFeaturesConfigFromMap creates a Features from the supplied Map
+func NewFeaturesConfigFromMap(data map[string]string) (*Features, error) {
+	nc := defaultFeaturesConfig()
+
+	if err := cm.Parse(data, AsFlag("multi-container", &nc.MultiContainer)); err != nil {
+		return nil, err
+	}
+	return nc, nil
+}
+
+// NewFeaturesConfigFromConfigMap creates a Features from the supplied ConfigMap
+func NewFeaturesConfigFromConfigMap(config *corev1.ConfigMap) (*Features, error) {
+	return NewFeaturesConfigFromMap(config.Data)
+}
+
+// Features specifies which features are allowed by the webhook.
+type Features struct {
+	MultiContainer Flag
+}
+
+// AsFlag parses the value at key as a Flag into the target, if it exists.
+func AsFlag(key string, target *Flag) cm.ParseFunc {
+	return func(data map[string]string) error {
+		if raw, ok := data[key]; ok {
+			for _, flag := range []Flag{Enabled, Allowed, Disabled} {
+				if strings.EqualFold(raw, string(flag)) {
+					*target = flag
+					return nil
+				}
+			}
+		}
+		return nil
+	}
+}
diff --git a/pkg/apis/config/store.go b/pkg/apis/config/store.go
index 2d28d2090..5288d0b05 100644
--- a/pkg/apis/config/store.go
+++ b/pkg/apis/config/store.go
@@ -29,6 +29,7 @@ type cfgKey struct{}
 // +k8s:deepcopy-gen=false
 type Config struct {
 	Defaults   *Defaults
+	Features   *Features
 	Autoscaler *autoscalerconfig.Config
 }
 
@@ -48,9 +49,11 @@ func FromContextOrDefaults(ctx context.Context) *Config {
 		return cfg
 	}
 	defaults, _ := NewDefaultsConfigFromMap(map[string]string{})
+	features, _ := NewFeaturesConfigFromMap(map[string]string{})
 	autoscaler, _ := autoscalerconfig.NewConfigFromMap(map[string]string{})
 	return &Config{
 		Defaults:   defaults,
+		Features:   features,
 		Autoscaler: autoscaler,
 	}
 }
@@ -71,10 +74,11 @@ type Store struct {
 func NewStore(logger configmap.Logger, onAfterStore ...func(name string, value interface{})) *Store {
 	store := &Store{
 		UntypedStore: configmap.NewUntypedStore(
-			"defaults",
+			"apis",
 			logger,
 			configmap.Constructors{
 				DefaultsConfigName:          NewDefaultsConfigFromConfigMap,
+				FeaturesConfigName:          NewFeaturesConfigFromConfigMap,
 				autoscalerconfig.ConfigName: autoscalerconfig.NewConfigFromConfigMap,
 			},
 			onAfterStore...,
@@ -93,6 +97,7 @@ func (s *Store) ToContext(ctx context.Context) context.Context {
 func (s *Store) Load() *Config {
 	return &Config{
 		Defaults:   s.UntypedLoad(DefaultsConfigName).(*Defaults).DeepCopy(),
+		Features:   s.UntypedLoad(FeaturesConfigName).(*Features).DeepCopy(),
 		Autoscaler: s.UntypedLoad(autoscalerconfig.ConfigName).(*autoscalerconfig.Config).DeepCopy(),
 	}
 }
diff --git a/pkg/apis/config/zz_generated.deepcopy.go b/pkg/apis/config/zz_generated.deepcopy.go
index 341f6334b..484d01ca3 100644
--- a/pkg/apis/config/zz_generated.deepcopy.go
+++ b/pkg/apis/config/zz_generated.deepcopy.go
@@ -55,3 +55,19 @@ func (in *Defaults) DeepCopy() *Defaults {
 	in.DeepCopyInto(out)
 	return out
 }
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Features) DeepCopyInto(out *Features) {
+	*out = *in
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Features.
+func (in *Features) DeepCopy() *Features {
+	if in == nil {
+		return nil
+	}
+	out := new(Features)
+	in.DeepCopyInto(out)
+	return out
+}
diff --git a/pkg/apis/networking/v1alpha1/ingress_types.go b/pkg/apis/networking/v1alpha1/ingress_types.go
index de465c013..607249fd4 100644
--- a/pkg/apis/networking/v1alpha1/ingress_types.go
+++ b/pkg/apis/networking/v1alpha1/ingress_types.go
@@ -212,8 +212,19 @@ type HTTPIngressPath struct {
 	// +optional
 	Path string `json:"path,omitempty"`
 
+	// RewriteHost rewrites the incoming request's host header. The request will
+	// then be re-evaluated based on the new host header.
+	//
+	// This field is currently experimental and not supported by all Ingress
+	// implementations.
+	//
+	// If RewriteHost is specified, Splits must not be.
+	RewriteHost string `json:"rewriteHost,omitempty"`
+
 	// Splits defines the referenced service endpoints to which the traffic
 	// will be forwarded to.
+	//
+	// If Splits are specified, RewriteHost must not be.
 	Splits []IngressBackendSplit `json:"splits"`
 
 	// AppendHeaders allow specifying additional HTTP headers to add
diff --git a/pkg/apis/networking/v1alpha1/ingress_validation.go b/pkg/apis/networking/v1alpha1/ingress_validation.go
index 22ecaed51..369995494 100644
--- a/pkg/apis/networking/v1alpha1/ingress_validation.go
+++ b/pkg/apis/networking/v1alpha1/ingress_validation.go
@@ -87,10 +87,12 @@ func (h HTTPIngressPath) Validate(ctx context.Context) *apis.FieldError {
 		return apis.ErrMissingField(apis.CurrentField)
 	}
 	var all *apis.FieldError
-	// Must provide as least one split.
-	if len(h.Splits) == 0 {
-		all = all.Also(apis.ErrMissingField("splits"))
-	} else {
+	switch {
+	case len(h.Splits) == 0 && h.RewriteHost == "":
+		all = all.Also(apis.ErrMissingOneOf("splits", "rewriteHost"))
+	case len(h.Splits) != 0 && h.RewriteHost != "":
+		all = all.Also(apis.ErrMultipleOneOf("splits", "rewriteHost"))
+	case len(h.Splits) != 0:
 		totalPct := 0
 		for idx, split := range h.Splits {
 			if err := split.Validate(ctx); err != nil {
@@ -108,6 +110,7 @@ func (h HTTPIngressPath) Validate(ctx context.Context) *apis.FieldError {
 			})
 		}
 	}
+
 	if h.Retries != nil {
 		all = all.Also(h.Retries.Validate(ctx).ViaField("retries"))
 	}
diff --git a/pkg/apis/serving/k8s_validation.go b/pkg/apis/serving/k8s_validation.go
index 8de357613..c363073f7 100644
--- a/pkg/apis/serving/k8s_validation.go
+++ b/pkg/apis/serving/k8s_validation.go
@@ -272,9 +272,9 @@ func ValidatePodSpec(ctx context.Context, ps corev1.PodSpec) *apis.FieldError {
 
 func validateContainers(ctx context.Context, containers []corev1.Container, volumes sets.String) *apis.FieldError {
 	var errs *apis.FieldError
-	cfg := config.FromContextOrDefaults(ctx).Defaults
-	if !cfg.EnableMultiContainer {
-		errs = errs.Also(&apis.FieldError{Message: fmt.Sprintf("enable-multi-container is off, "+
+	features := config.FromContextOrDefaults(ctx).Features
+	if features.MultiContainer != config.Enabled {
+		errs = errs.Also(&apis.FieldError{Message: fmt.Sprintf("multi-container is off, "+
 			"but found %d containers", len(containers))})
 	} else {
 		errs = errs.Also(validateContainersPorts(containers).ViaField("containers"))
diff --git a/pkg/apis/serving/metadata_validation.go b/pkg/apis/serving/metadata_validation.go
index 36c138617..43cdea380 100644
--- a/pkg/apis/serving/metadata_validation.go
+++ b/pkg/apis/serving/metadata_validation.go
@@ -98,7 +98,7 @@ func ValidateContainerConcurrency(ctx context.Context, containerConcurrency *int
 	if containerConcurrency != nil {
 		cfg := config.FromContextOrDefaults(ctx).Defaults
 
-		var minContainerConcurrency int64 = 0
+		var minContainerConcurrency int64
 		if !cfg.AllowContainerConcurrencyZero {
 			minContainerConcurrency = 1
 		}
diff --git a/pkg/apis/serving/v1/revision_types.go b/pkg/apis/serving/v1/revision_types.go
index 36696ea15..72fc15fd4 100644
--- a/pkg/apis/serving/v1/revision_types.go
+++ b/pkg/apis/serving/v1/revision_types.go
@@ -25,7 +25,7 @@ import (
 )
 
 // +genclient
-// +genreconciler
+// +genreconciler:krshapedlogic=true
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // Revision is an immutable snapshot of code and configuration.  A revision
diff --git a/pkg/apis/serving/v1/route_lifecycle.go b/pkg/apis/serving/v1/route_lifecycle.go
index 81de8ed8a..32071031f 100644
--- a/pkg/apis/serving/v1/route_lifecycle.go
+++ b/pkg/apis/serving/v1/route_lifecycle.go
@@ -126,12 +126,16 @@ func (rs *RouteStatus) MarkCertificateNotOwned(name string) {
 		"There is an existing certificate %s that we don't own.", name)
 }
 
-// MarkAutoTLSNotEnabled sets RouteConditionCertificateProvisioned to true when
-// certificate config such as autoTLS is not enabled.
-func (rs *RouteStatus) MarkAutoTLSNotEnabled() {
+const (
+	AutoTLSNotEnabledMessage            = "autoTLS is not enabled"
+	TLSNotEnabledForClusterLocalMessage = "TLS is not enabled for cluster-local"
+)
+
+// MarkTLSNotEnabled sets RouteConditionCertificateProvisioned to true when
+// certificate config such as autoTLS is not enabled or private cluster-local service.
+func (rs *RouteStatus) MarkTLSNotEnabled(msg string) {
 	routeCondSet.Manage(rs).MarkTrueWithReason(RouteConditionCertificateProvisioned,
-		"AutoTLSNotEnabled",
-		"autoTLS is not enabled")
+		"TLSNotEnabled", msg)
 }
 
 // MarkHTTPDowngrade sets RouteConditionCertificateProvisioned to true when plain
diff --git a/pkg/apis/serving/v1/route_types.go b/pkg/apis/serving/v1/route_types.go
index 2d810ebf5..c63866062 100644
--- a/pkg/apis/serving/v1/route_types.go
+++ b/pkg/apis/serving/v1/route_types.go
@@ -25,7 +25,7 @@ import (
 )
 
 // +genclient
-// +genreconciler
+// +genreconciler:krshapedlogic=true
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // Route is responsible for configuring ingress over a collection of Revisions.
diff --git a/pkg/apis/serving/v1/service_types.go b/pkg/apis/serving/v1/service_types.go
index ce5174319..d91818e05 100644
--- a/pkg/apis/serving/v1/service_types.go
+++ b/pkg/apis/serving/v1/service_types.go
@@ -25,7 +25,7 @@ import (
 )
 
 // +genclient
-// +genreconciler
+// +genreconciler:krshapedlogic=true
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // Service acts as a top-level container that manages a Route and Configuration
diff --git a/pkg/apis/serving/v1alpha1/route_lifecycle.go b/pkg/apis/serving/v1alpha1/route_lifecycle.go
index 523187d49..9644c7ade 100644
--- a/pkg/apis/serving/v1alpha1/route_lifecycle.go
+++ b/pkg/apis/serving/v1alpha1/route_lifecycle.go
@@ -134,12 +134,16 @@ func (rs *RouteStatus) MarkCertificateNotOwned(name string) {
 		"There is an existing certificate %s that we don't own.", name)
 }
 
-// MarkAutoTLSNotEnabled sets RouteConditionCertificateProvisioned to true when
-// certificate config such as autoTLS is not enabled.
-func (rs *RouteStatus) MarkAutoTLSNotEnabled() {
+const (
+	AutoTLSNotEnabledMessage            = "autoTLS is not enabled"
+	TLSNotEnabledForClusterLocalMessage = "TLS is not enabled for cluster-local"
+)
+
+// MarkTLSNotEnabled sets RouteConditionCertificateProvisioned to true when
+// certificate config such as autoTLS is not enabled or private cluster-local service.
+func (rs *RouteStatus) MarkTLSNotEnabled(msg string) {
 	routeCondSet.Manage(rs).MarkTrueWithReason(RouteConditionCertificateProvisioned,
-		"AutoTLSNotEnabled",
-		"autoTLS is not enabled")
+		"TLSNotEnabled", msg)
 }
 
 // MarkHTTPDowngrade sets RouteConditionCertificateProvisioned to true when plain
diff --git a/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go b/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
index 25c59cd4c..ed3c6d2e3 100644
--- a/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
@@ -164,10 +164,14 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 			logger.Warnw("Failed to set finalizers", zap.Error(err))
 		}
 
+		reconciler.PreProcessReconcile(ctx, resource)
+
 		// Reconcile this copy of the resource and then write back any status
 		// updates regardless of whether the reconciliation errored out.
 		reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
 
+		reconciler.PostProcessReconcile(ctx, resource)
+
 	} else if fin, ok := r.reconciler.(Finalizer); ok {
 		// Append the target method to the logger.
 		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
diff --git a/pkg/client/injection/reconciler/serving/v1/route/reconciler.go b/pkg/client/injection/reconciler/serving/v1/route/reconciler.go
index 81fc99446..2e49f4546 100644
--- a/pkg/client/injection/reconciler/serving/v1/route/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/route/reconciler.go
@@ -164,10 +164,14 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 			logger.Warnw("Failed to set finalizers", zap.Error(err))
 		}
 
+		reconciler.PreProcessReconcile(ctx, resource)
+
 		// Reconcile this copy of the resource and then write back any status
 		// updates regardless of whether the reconciliation errored out.
 		reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
 
+		reconciler.PostProcessReconcile(ctx, resource)
+
 	} else if fin, ok := r.reconciler.(Finalizer); ok {
 		// Append the target method to the logger.
 		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
diff --git a/pkg/client/injection/reconciler/serving/v1/service/reconciler.go b/pkg/client/injection/reconciler/serving/v1/service/reconciler.go
index ba494db10..3680eb6aa 100644
--- a/pkg/client/injection/reconciler/serving/v1/service/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/service/reconciler.go
@@ -164,10 +164,14 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 			logger.Warnw("Failed to set finalizers", zap.Error(err))
 		}
 
+		reconciler.PreProcessReconcile(ctx, resource)
+
 		// Reconcile this copy of the resource and then write back any status
 		// updates regardless of whether the reconciliation errored out.
 		reconcileEvent = r.reconciler.ReconcileKind(ctx, resource)
 
+		reconciler.PostProcessReconcile(ctx, resource)
+
 	} else if fin, ok := r.reconciler.(Finalizer); ok {
 		// Append the target method to the logger.
 		logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
diff --git a/pkg/network/network.go b/pkg/network/network.go
index 0d0c61f21..7eef7a361 100644
--- a/pkg/network/network.go
+++ b/pkg/network/network.go
@@ -291,13 +291,12 @@ func NewConfigFromMap(data map[string]string) (*Config, error) {
 func (c *Config) GetDomainTemplate() *template.Template {
 	if tt, ok := templateCache.Get(c.DomainTemplate); ok {
 		return tt.(*template.Template)
-	} else {
-		// Should not really happen outside of route/ingress unit tests.
-		nt := template.Must(template.New("domain-template").Parse(
-			c.DomainTemplate))
-		templateCache.Add(c.DomainTemplate, nt)
-		return nt
 	}
+	// Should not really happen outside of route/ingress unit tests.
+	nt := template.Must(template.New("domain-template").Parse(
+		c.DomainTemplate))
+	templateCache.Add(c.DomainTemplate, nt)
+	return nt
 }
 
 func checkDomainTemplate(t *template.Template) error {
@@ -331,16 +330,16 @@ func checkDomainTemplate(t *template.Template) error {
 	return nil
 }
 
+// GetTagTemplate returns the go template for the route tag.
 func (c *Config) GetTagTemplate() *template.Template {
 	if tt, ok := templateCache.Get(c.TagTemplate); ok {
 		return tt.(*template.Template)
-	} else {
-		// Should not really happen outside of route/ingress unit tests.
-		nt := template.Must(template.New("tag-template").Parse(
-			c.TagTemplate))
-		templateCache.Add(c.TagTemplate, nt)
-		return nt
 	}
+	// Should not really happen outside of route/ingress unit tests.
+	nt := template.Must(template.New("tag-template").Parse(
+		c.TagTemplate))
+	templateCache.Add(c.TagTemplate, nt)
+	return nt
 }
 
 func checkTagTemplate(t *template.Template) error {
diff --git a/pkg/reconciler/revision/config/store.go b/pkg/reconciler/revision/config/store.go
index abea39841..10aa41eb8 100644
--- a/pkg/reconciler/revision/config/store.go
+++ b/pkg/reconciler/revision/config/store.go
@@ -33,6 +33,7 @@ import (
 type cfgKey struct{}
 
 // +k8s:deepcopy-gen=false
+// Config contains the configmaps requires for revision reconciliation.
 type Config struct {
 	Autoscaler    *autoscalerconfig.Config
 	Defaults      *config.Defaults
diff --git a/pkg/reconciler/revision/revision.go b/pkg/reconciler/revision/revision.go
index caec98227..47546b435 100644
--- a/pkg/reconciler/revision/revision.go
+++ b/pkg/reconciler/revision/revision.go
@@ -168,7 +168,6 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, rev *v1.Revision) pkgrec
 			"Revision becomes ready upon all resources being ready")
 	}
 
-	rev.Status.ObservedGeneration = rev.Generation
 	return nil
 }
 
diff --git a/pkg/reconciler/route/route.go b/pkg/reconciler/route/route.go
index 52272d3bb..6465d427b 100644
--- a/pkg/reconciler/route/route.go
+++ b/pkg/reconciler/route/route.go
@@ -116,10 +116,6 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, r *v1.Route) pkgreconcil
 	traffic, err := c.configureTraffic(ctx, r)
 	if traffic == nil || err != nil {
 		// Traffic targets aren't ready, no need to configure child resources.
-		// Need to update ObservedGeneration, otherwise Route's Ready state won't
-		// be propagated to Service and the Service's RoutesReady will stay in
-		// 'Unknown'.
-		r.Status.ObservedGeneration = r.Generation
 		return err
 	}
 
@@ -166,7 +162,6 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, r *v1.Route) pkgreconcil
 		return err
 	}
 
-	r.Status.ObservedGeneration = r.Generation
 	logger.Info("Route successfully synced")
 	return nil
 }
@@ -190,7 +185,7 @@ func (c *Reconciler) reconcileIngressResources(ctx context.Context, r *v1.Route,
 func (c *Reconciler) tls(ctx context.Context, host string, r *v1.Route, traffic *traffic.Config) ([]netv1alpha1.IngressTLS, []netv1alpha1.HTTP01Challenge, error) {
 	tls := []netv1alpha1.IngressTLS{}
 	if !config.FromContext(ctx).Network.AutoTLS {
-		r.Status.MarkAutoTLSNotEnabled()
+		r.Status.MarkTLSNotEnabled(v1.AutoTLSNotEnabledMessage)
 		return tls, nil, nil
 	}
 	domainToTagMap, err := domains.GetAllDomainsAndTags(ctx, r, getTrafficNames(traffic.Targets), traffic.Visibility)
@@ -200,6 +195,7 @@ func (c *Reconciler) tls(ctx context.Context, host string, r *v1.Route, traffic
 
 	for domain := range domainToTagMap {
 		if domains.IsClusterLocal(domain) {
+			r.Status.MarkTLSNotEnabled(v1.TLSNotEnabledForClusterLocalMessage)
 			delete(domainToTagMap, domain)
 		}
 	}
diff --git a/pkg/reconciler/service/service.go b/pkg/reconciler/service/service.go
index e20b3174c..666b2dc7f 100644
--- a/pkg/reconciler/service/service.go
+++ b/pkg/reconciler/service/service.go
@@ -117,8 +117,6 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, service *v1.Service) pkg
 	}
 
 	c.checkRoutesNotReady(config, logger, route, service)
-	service.Status.ObservedGeneration = service.Generation
-
 	return nil
 }
 
diff --git a/pkg/resources/pods.go b/pkg/resources/pods.go
index 9d008a3a8..03348e728 100644
--- a/pkg/resources/pods.go
+++ b/pkg/resources/pods.go
@@ -70,6 +70,8 @@ func pendingTerminatingCount(pods []*corev1.Pod) (int, int, error) {
 	return pending, terminating, nil
 }
 
+// PodIPsByAge returns the list of running pod (terminating
+// and non-running are excluded) IP addresses, sorted descending by pod age.
 func (pc PodAccessor) PodIPsByAge() ([]string, error) {
 	pods, err := pc.podsLister.List(pc.selector)
 	if err != nil {
diff --git a/third_party/VENDOR-LICENSE/github.com/prometheus/client_golang/LICENSE b/third_party/VENDOR-LICENSE/github.com/prometheus/client_golang/LICENSE
deleted file mode 100644
index 261eeb9e9..000000000
--- a/third_party/VENDOR-LICENSE/github.com/prometheus/client_golang/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   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.
diff --git a/third_party/VENDOR-LICENSE/github.com/prometheus/client_golang/NOTICE b/third_party/VENDOR-LICENSE/github.com/prometheus/client_golang/NOTICE
deleted file mode 100644
index dd878a30e..000000000
--- a/third_party/VENDOR-LICENSE/github.com/prometheus/client_golang/NOTICE
+++ /dev/null
@@ -1,23 +0,0 @@
-Prometheus instrumentation library for Go applications
-Copyright 2012-2015 The Prometheus Authors
-
-This product includes software developed at
-SoundCloud Ltd. (http://soundcloud.com/).
-
-
-The following components are included in this product:
-
-perks - a fork of https://github.com/bmizerany/perks
-https://github.com/beorn7/perks
-Copyright 2013-2015 Blake Mizerany, Björn Rabenstein
-See https://github.com/beorn7/perks/blob/master/README.md for license details.
-
-Go support for Protocol Buffers - Google's data interchange format
-http://github.com/golang/protobuf/
-Copyright 2010 The Go Authors
-See source code for license details.
-
-Support for streaming Protocol Buffer messages for the Go language (golang).
-https://github.com/matttproud/golang_protobuf_extensions
-Copyright 2013 Matt T. Proud
-Licensed under the Apache License, Version 2.0
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 d3afecab7..261bdd27d 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: "v20200525-e037dfb"
+    serving.knative.dev/release: "v20200526-e037dfb"
     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: "v20200525-e037dfb"
+    serving.knative.dev/release: "v20200526-e037dfb"
 webhooks:
 - admissionReviewVersions:
   - v1beta1
@@ -86,7 +86,7 @@ metadata:
   name: net-certmanager-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200525-e037dfb"
+    serving.knative.dev/release: "v20200526-e037dfb"
 
 ---
 # Copyright 2020 The Knative Authors
@@ -109,7 +109,7 @@ metadata:
   name: config-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200525-e037dfb"
+    serving.knative.dev/release: "v20200526-e037dfb"
     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: "v20200525-e037dfb"
+    serving.knative.dev/release: "v20200526-e037dfb"
     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: "v20200525-e037dfb"
+        serving.knative.dev/release: "v20200526-e037dfb"
     spec:
       serviceAccountName: controller
       containers:
@@ -208,7 +208,7 @@ kind: Service
 metadata:
   labels:
     app: networking-certmanager
-    serving.knative.dev/release: "v20200525-e037dfb"
+    serving.knative.dev/release: "v20200526-e037dfb"
     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: "v20200525-e037dfb"
+    serving.knative.dev/release: "v20200526-e037dfb"
 spec:
   selector:
     matchLabels:
@@ -258,7 +258,7 @@ spec:
       labels:
         app: net-certmanager-webhook
         role: net-certmanager-webhook
-        serving.knative.dev/release: "v20200525-e037dfb"
+        serving.knative.dev/release: "v20200526-e037dfb"
     spec:
       serviceAccountName: controller
       containers:
@@ -319,7 +319,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: net-certmanager-webhook
-    serving.knative.dev/release: "v20200525-e037dfb"
+    serving.knative.dev/release: "v20200526-e037dfb"
 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 3a85c59df..968186108 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: "v20200522-dc16e1e"
+    serving.knative.dev/release: "v20200526-dc16e1e"
     serving.knative.dev/controller: "true"
     networking.knative.dev/ingress-provider: istio
 rules:
@@ -48,7 +48,7 @@ metadata:
   name: knative-ingress-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200522-dc16e1e"
+    serving.knative.dev/release: "v20200526-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -85,7 +85,7 @@ metadata:
   name: cluster-local-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200522-dc16e1e"
+    serving.knative.dev/release: "v20200526-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -118,7 +118,7 @@ kind: MutatingWebhookConfiguration
 metadata:
   name: webhook.istio.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200522-dc16e1e"
+    serving.knative.dev/release: "v20200526-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 webhooks:
 - admissionReviewVersions:
@@ -154,7 +154,7 @@ kind: ValidatingWebhookConfiguration
 metadata:
   name: config.webhook.istio.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200522-dc16e1e"
+    serving.knative.dev/release: "v20200526-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 webhooks:
 - admissionReviewVersions:
@@ -192,7 +192,7 @@ metadata:
   name: istio-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200522-dc16e1e"
+    serving.knative.dev/release: "v20200526-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 
 ---
@@ -216,7 +216,7 @@ metadata:
   name: config-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200522-dc16e1e"
+    serving.knative.dev/release: "v20200526-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 data:
   _example: |
@@ -286,7 +286,7 @@ metadata:
   name: networking-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200522-dc16e1e"
+    serving.knative.dev/release: "v20200526-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -302,7 +302,7 @@ spec:
         sidecar.istio.io/inject: "false"
       labels:
         app: networking-istio
-        serving.knative.dev/release: "v20200522-dc16e1e"
+        serving.knative.dev/release: "v20200526-dc16e1e"
     spec:
       serviceAccountName: controller
       containers:
@@ -361,7 +361,7 @@ metadata:
   name: istio-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200522-dc16e1e"
+    serving.knative.dev/release: "v20200526-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -375,7 +375,7 @@ spec:
       labels:
         app: istio-webhook
         role: istio-webhook
-        serving.knative.dev/release: "v20200522-dc16e1e"
+        serving.knative.dev/release: "v20200526-dc16e1e"
     spec:
       serviceAccountName: controller
       containers:
@@ -436,7 +436,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: istio-webhook
-    serving.knative.dev/release: "v20200522-dc16e1e"
+    serving.knative.dev/release: "v20200526-dc16e1e"
     networking.knative.dev/ingress-provider: istio
 spec:
   ports:
diff --git a/vendor/github.com/prometheus/client_golang/api/client.go b/vendor/github.com/prometheus/client_golang/api/client.go
deleted file mode 100644
index 09af749ca..000000000
--- a/vendor/github.com/prometheus/client_golang/api/client.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2015 The Prometheus 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.
-
-// +build go1.7
-
-// Package api provides clients for the HTTP APIs.
-package api
-
-import (
-	"context"
-	"io/ioutil"
-	"net"
-	"net/http"
-	"net/url"
-	"path"
-	"strings"
-	"time"
-)
-
-// DefaultRoundTripper is used if no RoundTripper is set in Config.
-var DefaultRoundTripper http.RoundTripper = &http.Transport{
-	Proxy: http.ProxyFromEnvironment,
-	DialContext: (&net.Dialer{
-		Timeout:   30 * time.Second,
-		KeepAlive: 30 * time.Second,
-	}).DialContext,
-	TLSHandshakeTimeout: 10 * time.Second,
-}
-
-// Config defines configuration parameters for a new client.
-type Config struct {
-	// The address of the Prometheus to connect to.
-	Address string
-
-	// RoundTripper is used by the Client to drive HTTP requests. If not
-	// provided, DefaultRoundTripper will be used.
-	RoundTripper http.RoundTripper
-}
-
-func (cfg *Config) roundTripper() http.RoundTripper {
-	if cfg.RoundTripper == nil {
-		return DefaultRoundTripper
-	}
-	return cfg.RoundTripper
-}
-
-// Client is the interface for an API client.
-type Client interface {
-	URL(ep string, args map[string]string) *url.URL
-	Do(context.Context, *http.Request) (*http.Response, []byte, error)
-}
-
-// NewClient returns a new Client.
-//
-// It is safe to use the returned Client from multiple goroutines.
-func NewClient(cfg Config) (Client, error) {
-	u, err := url.Parse(cfg.Address)
-	if err != nil {
-		return nil, err
-	}
-	u.Path = strings.TrimRight(u.Path, "/")
-
-	return &httpClient{
-		endpoint: u,
-		client:   http.Client{Transport: cfg.roundTripper()},
-	}, nil
-}
-
-type httpClient struct {
-	endpoint *url.URL
-	client   http.Client
-}
-
-func (c *httpClient) URL(ep string, args map[string]string) *url.URL {
-	p := path.Join(c.endpoint.Path, ep)
-
-	for arg, val := range args {
-		arg = ":" + arg
-		p = strings.Replace(p, arg, val, -1)
-	}
-
-	u := *c.endpoint
-	u.Path = p
-
-	return &u
-}
-
-func (c *httpClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
-	if ctx != nil {
-		req = req.WithContext(ctx)
-	}
-	resp, err := c.client.Do(req)
-	defer func() {
-		if resp != nil {
-			resp.Body.Close()
-		}
-	}()
-
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var body []byte
-	done := make(chan struct{})
-	go func() {
-		body, err = ioutil.ReadAll(resp.Body)
-		close(done)
-	}()
-
-	select {
-	case <-ctx.Done():
-		err = resp.Body.Close()
-		<-done
-		if err == nil {
-			err = ctx.Err()
-		}
-	case <-done:
-	}
-
-	return resp, body, err
-}
diff --git a/vendor/github.com/prometheus/client_golang/api/prometheus/v1/api.go b/vendor/github.com/prometheus/client_golang/api/prometheus/v1/api.go
deleted file mode 100644
index 642418890..000000000
--- a/vendor/github.com/prometheus/client_golang/api/prometheus/v1/api.go
+++ /dev/null
@@ -1,519 +0,0 @@
-// Copyright 2017 The Prometheus 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.
-
-// +build go1.7
-
-// Package v1 provides bindings to the Prometheus HTTP API v1:
-// http://prometheus.io/docs/querying/api/
-package v1
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"net/http"
-	"strconv"
-	"time"
-
-	"github.com/prometheus/client_golang/api"
-	"github.com/prometheus/common/model"
-)
-
-const (
-	statusAPIError = 422
-
-	apiPrefix = "/api/v1"
-
-	epAlertManagers   = apiPrefix + "/alertmanagers"
-	epQuery           = apiPrefix + "/query"
-	epQueryRange      = apiPrefix + "/query_range"
-	epLabelValues     = apiPrefix + "/label/:name/values"
-	epSeries          = apiPrefix + "/series"
-	epTargets         = apiPrefix + "/targets"
-	epSnapshot        = apiPrefix + "/admin/tsdb/snapshot"
-	epDeleteSeries    = apiPrefix + "/admin/tsdb/delete_series"
-	epCleanTombstones = apiPrefix + "/admin/tsdb/clean_tombstones"
-	epConfig          = apiPrefix + "/status/config"
-	epFlags           = apiPrefix + "/status/flags"
-)
-
-// ErrorType models the different API error types.
-type ErrorType string
-
-// HealthStatus models the health status of a scrape target.
-type HealthStatus string
-
-const (
-	// Possible values for ErrorType.
-	ErrBadData     ErrorType = "bad_data"
-	ErrTimeout     ErrorType = "timeout"
-	ErrCanceled    ErrorType = "canceled"
-	ErrExec        ErrorType = "execution"
-	ErrBadResponse ErrorType = "bad_response"
-	ErrServer      ErrorType = "server_error"
-	ErrClient      ErrorType = "client_error"
-
-	// Possible values for HealthStatus.
-	HealthGood    HealthStatus = "up"
-	HealthUnknown HealthStatus = "unknown"
-	HealthBad     HealthStatus = "down"
-)
-
-// Error is an error returned by the API.
-type Error struct {
-	Type   ErrorType
-	Msg    string
-	Detail string
-}
-
-func (e *Error) Error() string {
-	return fmt.Sprintf("%s: %s", e.Type, e.Msg)
-}
-
-// Range represents a sliced time range.
-type Range struct {
-	// The boundaries of the time range.
-	Start, End time.Time
-	// The maximum time between two slices within the boundaries.
-	Step time.Duration
-}
-
-// API provides bindings for Prometheus's v1 API.
-type API interface {
-	// AlertManagers returns an overview of the current state of the Prometheus alert manager discovery.
-	AlertManagers(ctx context.Context) (AlertManagersResult, error)
-	// CleanTombstones removes the deleted data from disk and cleans up the existing tombstones.
-	CleanTombstones(ctx context.Context) error
-	// Config returns the current Prometheus configuration.
-	Config(ctx context.Context) (ConfigResult, error)
-	// DeleteSeries deletes data for a selection of series in a time range.
-	DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error
-	// Flags returns the flag values that Prometheus was launched with.
-	Flags(ctx context.Context) (FlagsResult, error)
-	// LabelValues performs a query for the values of the given label.
-	LabelValues(ctx context.Context, label string) (model.LabelValues, error)
-	// Query performs a query for the given time.
-	Query(ctx context.Context, query string, ts time.Time) (model.Value, error)
-	// QueryRange performs a query for the given range.
-	QueryRange(ctx context.Context, query string, r Range) (model.Value, error)
-	// Series finds series by label matchers.
-	Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, error)
-	// Snapshot creates a snapshot of all current data into snapshots/<datetime>-<rand>
-	// under the TSDB's data directory and returns the directory as response.
-	Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, error)
-	// Targets returns an overview of the current state of the Prometheus target discovery.
-	Targets(ctx context.Context) (TargetsResult, error)
-}
-
-// AlertManagersResult contains the result from querying the alertmanagers endpoint.
-type AlertManagersResult struct {
-	Active  []AlertManager `json:"activeAlertManagers"`
-	Dropped []AlertManager `json:"droppedAlertManagers"`
-}
-
-// AlertManager models a configured Alert Manager.
-type AlertManager struct {
-	URL string `json:"url"`
-}
-
-// ConfigResult contains the result from querying the config endpoint.
-type ConfigResult struct {
-	YAML string `json:"yaml"`
-}
-
-// FlagsResult contains the result from querying the flag endpoint.
-type FlagsResult map[string]string
-
-// SnapshotResult contains the result from querying the snapshot endpoint.
-type SnapshotResult struct {
-	Name string `json:"name"`
-}
-
-// TargetsResult contains the result from querying the targets endpoint.
-type TargetsResult struct {
-	Active  []ActiveTarget  `json:"activeTargets"`
-	Dropped []DroppedTarget `json:"droppedTargets"`
-}
-
-// ActiveTarget models an active Prometheus scrape target.
-type ActiveTarget struct {
-	DiscoveredLabels model.LabelSet `json:"discoveredLabels"`
-	Labels           model.LabelSet `json:"labels"`
-	ScrapeURL        string         `json:"scrapeUrl"`
-	LastError        string         `json:"lastError"`
-	LastScrape       time.Time      `json:"lastScrape"`
-	Health           HealthStatus   `json:"health"`
-}
-
-// DroppedTarget models a dropped Prometheus scrape target.
-type DroppedTarget struct {
-	DiscoveredLabels model.LabelSet `json:"discoveredLabels"`
-}
-
-// queryResult contains result data for a query.
-type queryResult struct {
-	Type   model.ValueType `json:"resultType"`
-	Result interface{}     `json:"result"`
-
-	// The decoded value.
-	v model.Value
-}
-
-func (qr *queryResult) UnmarshalJSON(b []byte) error {
-	v := struct {
-		Type   model.ValueType `json:"resultType"`
-		Result json.RawMessage `json:"result"`
-	}{}
-
-	err := json.Unmarshal(b, &v)
-	if err != nil {
-		return err
-	}
-
-	switch v.Type {
-	case model.ValScalar:
-		var sv model.Scalar
-		err = json.Unmarshal(v.Result, &sv)
-		qr.v = &sv
-
-	case model.ValVector:
-		var vv model.Vector
-		err = json.Unmarshal(v.Result, &vv)
-		qr.v = vv
-
-	case model.ValMatrix:
-		var mv model.Matrix
-		err = json.Unmarshal(v.Result, &mv)
-		qr.v = mv
-
-	default:
-		err = fmt.Errorf("unexpected value type %q", v.Type)
-	}
-	return err
-}
-
-// NewAPI returns a new API for the client.
-//
-// It is safe to use the returned API from multiple goroutines.
-func NewAPI(c api.Client) API {
-	return &httpAPI{client: apiClient{c}}
-}
-
-type httpAPI struct {
-	client api.Client
-}
-
-func (h *httpAPI) AlertManagers(ctx context.Context) (AlertManagersResult, error) {
-	u := h.client.URL(epAlertManagers, nil)
-
-	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
-	if err != nil {
-		return AlertManagersResult{}, err
-	}
-
-	_, body, err := h.client.Do(ctx, req)
-	if err != nil {
-		return AlertManagersResult{}, err
-	}
-
-	var res AlertManagersResult
-	err = json.Unmarshal(body, &res)
-	return res, err
-}
-
-func (h *httpAPI) CleanTombstones(ctx context.Context) error {
-	u := h.client.URL(epCleanTombstones, nil)
-
-	req, err := http.NewRequest(http.MethodPost, u.String(), nil)
-	if err != nil {
-		return err
-	}
-
-	_, _, err = h.client.Do(ctx, req)
-	return err
-}
-
-func (h *httpAPI) Config(ctx context.Context) (ConfigResult, error) {
-	u := h.client.URL(epConfig, nil)
-
-	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
-	if err != nil {
-		return ConfigResult{}, err
-	}
-
-	_, body, err := h.client.Do(ctx, req)
-	if err != nil {
-		return ConfigResult{}, err
-	}
-
-	var res ConfigResult
-	err = json.Unmarshal(body, &res)
-	return res, err
-}
-
-func (h *httpAPI) DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error {
-	u := h.client.URL(epDeleteSeries, nil)
-	q := u.Query()
-
-	for _, m := range matches {
-		q.Add("match[]", m)
-	}
-
-	q.Set("start", startTime.Format(time.RFC3339Nano))
-	q.Set("end", endTime.Format(time.RFC3339Nano))
-
-	u.RawQuery = q.Encode()
-
-	req, err := http.NewRequest(http.MethodPost, u.String(), nil)
-	if err != nil {
-		return err
-	}
-
-	_, _, err = h.client.Do(ctx, req)
-	return err
-}
-
-func (h *httpAPI) Flags(ctx context.Context) (FlagsResult, error) {
-	u := h.client.URL(epFlags, nil)
-
-	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
-	if err != nil {
-		return FlagsResult{}, err
-	}
-
-	_, body, err := h.client.Do(ctx, req)
-	if err != nil {
-		return FlagsResult{}, err
-	}
-
-	var res FlagsResult
-	err = json.Unmarshal(body, &res)
-	return res, err
-}
-
-func (h *httpAPI) LabelValues(ctx context.Context, label string) (model.LabelValues, error) {
-	u := h.client.URL(epLabelValues, map[string]string{"name": label})
-	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-	_, body, err := h.client.Do(ctx, req)
-	if err != nil {
-		return nil, err
-	}
-	var labelValues model.LabelValues
-	err = json.Unmarshal(body, &labelValues)
-	return labelValues, err
-}
-
-func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, error) {
-	u := h.client.URL(epQuery, nil)
-	q := u.Query()
-
-	q.Set("query", query)
-	if !ts.IsZero() {
-		q.Set("time", ts.Format(time.RFC3339Nano))
-	}
-
-	u.RawQuery = q.Encode()
-
-	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	_, body, err := h.client.Do(ctx, req)
-	if err != nil {
-		return nil, err
-	}
-
-	var qres queryResult
-	err = json.Unmarshal(body, &qres)
-
-	return model.Value(qres.v), err
-}
-
-func (h *httpAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, error) {
-	u := h.client.URL(epQueryRange, nil)
-	q := u.Query()
-
-	var (
-		start = r.Start.Format(time.RFC3339Nano)
-		end   = r.End.Format(time.RFC3339Nano)
-		step  = strconv.FormatFloat(r.Step.Seconds(), 'f', 3, 64)
-	)
-
-	q.Set("query", query)
-	q.Set("start", start)
-	q.Set("end", end)
-	q.Set("step", step)
-
-	u.RawQuery = q.Encode()
-
-	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	_, body, err := h.client.Do(ctx, req)
-	if err != nil {
-		return nil, err
-	}
-
-	var qres queryResult
-	err = json.Unmarshal(body, &qres)
-
-	return model.Value(qres.v), err
-}
-
-func (h *httpAPI) Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, error) {
-	u := h.client.URL(epSeries, nil)
-	q := u.Query()
-
-	for _, m := range matches {
-		q.Add("match[]", m)
-	}
-
-	q.Set("start", startTime.Format(time.RFC3339Nano))
-	q.Set("end", endTime.Format(time.RFC3339Nano))
-
-	u.RawQuery = q.Encode()
-
-	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
-	if err != nil {
-		return nil, err
-	}
-
-	_, body, err := h.client.Do(ctx, req)
-	if err != nil {
-		return nil, err
-	}
-
-	var mset []model.LabelSet
-	err = json.Unmarshal(body, &mset)
-	return mset, err
-}
-
-func (h *httpAPI) Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, error) {
-	u := h.client.URL(epSnapshot, nil)
-	q := u.Query()
-
-	q.Set("skip_head", strconv.FormatBool(skipHead))
-
-	u.RawQuery = q.Encode()
-
-	req, err := http.NewRequest(http.MethodPost, u.String(), nil)
-	if err != nil {
-		return SnapshotResult{}, err
-	}
-
-	_, body, err := h.client.Do(ctx, req)
-	if err != nil {
-		return SnapshotResult{}, err
-	}
-
-	var res SnapshotResult
-	err = json.Unmarshal(body, &res)
-	return res, err
-}
-
-func (h *httpAPI) Targets(ctx context.Context) (TargetsResult, error) {
-	u := h.client.URL(epTargets, nil)
-
-	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
-	if err != nil {
-		return TargetsResult{}, err
-	}
-
-	_, body, err := h.client.Do(ctx, req)
-	if err != nil {
-		return TargetsResult{}, err
-	}
-
-	var res TargetsResult
-	err = json.Unmarshal(body, &res)
-	return res, err
-}
-
-// apiClient wraps a regular client and processes successful API responses.
-// Successful also includes responses that errored at the API level.
-type apiClient struct {
-	api.Client
-}
-
-type apiResponse struct {
-	Status    string          `json:"status"`
-	Data      json.RawMessage `json:"data"`
-	ErrorType ErrorType       `json:"errorType"`
-	Error     string          `json:"error"`
-}
-
-func apiError(code int) bool {
-	// These are the codes that Prometheus sends when it returns an error.
-	return code == statusAPIError || code == http.StatusBadRequest
-}
-
-func errorTypeAndMsgFor(resp *http.Response) (ErrorType, string) {
-	switch resp.StatusCode / 100 {
-	case 4:
-		return ErrClient, fmt.Sprintf("client error: %d", resp.StatusCode)
-	case 5:
-		return ErrServer, fmt.Sprintf("server error: %d", resp.StatusCode)
-	}
-	return ErrBadResponse, fmt.Sprintf("bad response code %d", resp.StatusCode)
-}
-
-func (c apiClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
-	resp, body, err := c.Client.Do(ctx, req)
-	if err != nil {
-		return resp, body, err
-	}
-
-	code := resp.StatusCode
-
-	if code/100 != 2 && !apiError(code) {
-		errorType, errorMsg := errorTypeAndMsgFor(resp)
-		return resp, body, &Error{
-			Type:   errorType,
-			Msg:    errorMsg,
-			Detail: string(body),
-		}
-	}
-
-	var result apiResponse
-
-	if http.StatusNoContent != code {
-		if err = json.Unmarshal(body, &result); err != nil {
-			return resp, body, &Error{
-				Type: ErrBadResponse,
-				Msg:  err.Error(),
-			}
-		}
-	}
-
-	if apiError(code) != (result.Status == "error") {
-		err = &Error{
-			Type: ErrBadResponse,
-			Msg:  "inconsistent body for response code",
-		}
-	}
-
-	if apiError(code) && result.Status == "error" {
-		err = &Error{
-			Type: result.ErrorType,
-			Msg:  result.Error,
-		}
-	}
-
-	return resp, []byte(result.Data), err
-}
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 9186e961f..ed2279273 100644
--- a/vendor/knative.dev/pkg/apis/duck/v1/status_types.go
+++ b/vendor/knative.dev/pkg/apis/duck/v1/status_types.go
@@ -21,6 +21,7 @@ import (
 
 	"knative.dev/pkg/apis"
 	"knative.dev/pkg/apis/duck"
+	"knative.dev/pkg/kmeta"
 )
 
 // +genduck
@@ -45,6 +46,12 @@ type Status struct {
 	// +patchMergeKey=type
 	// +patchStrategy=merge
 	Conditions Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
+
+	// Annotations is additional Status fields for the Resource to save some
+	// additional State as well as convey more information to the user. This is
+	// roughly akin to Annotations on any k8s resource, just the reconciler conveying
+	// richer information outwards.
+	Annotations map[string]string `json:"annotations,omitempty"`
 }
 
 var _ apis.ConditionsAccessor = (*Status)(nil)
@@ -87,6 +94,8 @@ func (s *Status) GetCondition(t apis.ConditionType) *apis.Condition {
 // return true the condition type will be copied to the sink
 func (source *Status) ConvertTo(ctx context.Context, sink *Status, predicates ...func(apis.ConditionType) bool) {
 	sink.ObservedGeneration = source.ObservedGeneration
+	// This will deep copy the map.
+	sink.Annotations = kmeta.UnionMaps(source.Annotations, nil)
 
 	conditions := make(apis.Conditions, 0, len(source.Conditions))
 	for _, c := range source.Conditions {
diff --git a/vendor/knative.dev/pkg/apis/duck/v1/zz_generated.deepcopy.go b/vendor/knative.dev/pkg/apis/duck/v1/zz_generated.deepcopy.go
index 3397713be..190d773e7 100644
--- a/vendor/knative.dev/pkg/apis/duck/v1/zz_generated.deepcopy.go
+++ b/vendor/knative.dev/pkg/apis/duck/v1/zz_generated.deepcopy.go
@@ -428,6 +428,13 @@ func (in *Status) DeepCopyInto(out *Status) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
+	if in.Annotations != nil {
+		in, out := &in.Annotations, &out.Annotations
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
 	return
 }
 
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 b2095fb58..a046dbedf 100644
--- a/vendor/knative.dev/pkg/apis/duck/v1beta1/status_types.go
+++ b/vendor/knative.dev/pkg/apis/duck/v1beta1/status_types.go
@@ -63,6 +63,12 @@ type Status struct {
 	// +patchMergeKey=type
 	// +patchStrategy=merge
 	Conditions Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
+
+	// Annotations is additional Status fields for the Resource to save some
+	// additional State as well as convey more information to the user. This is
+	// roughly akin to Annotations on any k8s resource, just the reconciler conveying
+	// richer information outwards.
+	Annotations map[string]string `json:"annotations,omitempty"`
 }
 
 var _ apis.ConditionsAccessor = (*Status)(nil)
diff --git a/vendor/knative.dev/pkg/apis/duck/v1beta1/zz_generated.deepcopy.go b/vendor/knative.dev/pkg/apis/duck/v1beta1/zz_generated.deepcopy.go
index 032dccb3c..152bb8828 100644
--- a/vendor/knative.dev/pkg/apis/duck/v1beta1/zz_generated.deepcopy.go
+++ b/vendor/knative.dev/pkg/apis/duck/v1beta1/zz_generated.deepcopy.go
@@ -374,6 +374,13 @@ func (in *Status) DeepCopyInto(out *Status) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
+	if in.Annotations != nil {
+		in, out := &in.Annotations, &out.Annotations
+		*out = make(map[string]string, len(*in))
+		for key, val := range *in {
+			(*out)[key] = val
+		}
+	}
 	return
 }
 
diff --git a/vendor/knative.dev/pkg/configmap/store.go b/vendor/knative.dev/pkg/configmap/store.go
index 825803c25..a994601ab 100644
--- a/vendor/knative.dev/pkg/configmap/store.go
+++ b/vendor/knative.dev/pkg/configmap/store.go
@@ -157,9 +157,7 @@ func (s *UntypedStore) OnConfigChanged(c *corev1.ConfigMap) {
 	s.logger.Infof("%s config %q config was added or updated: %#v", s.name, name, result)
 	storage.Store(result)
 
-	go func() {
-		for _, f := range s.onAfterStore {
-			f(name, result)
-		}
-	}()
+	for _, f := range s.onAfterStore {
+		f(name, result)
+	}
 }
diff --git a/vendor/knative.dev/pkg/hack/update-deps.sh b/vendor/knative.dev/pkg/hack/update-deps.sh
index 71b1bf26f..a39fc858f 100644
--- a/vendor/knative.dev/pkg/hack/update-deps.sh
+++ b/vendor/knative.dev/pkg/hack/update-deps.sh
@@ -26,7 +26,7 @@ cd ${ROOT_DIR}
 # The list of dependencies that we track at HEAD and periodically
 # float forward in this repository.
 FLOATING_DEPS=(
-  "knative.dev/test-infra@release-0.15"
+  "knative.dev/test-infra@master"
 )
 
 # Parse flags to determine any we should pass to dep.
diff --git a/vendor/knative.dev/pkg/logging/config b/vendor/knative.dev/pkg/logging/config
deleted file mode 100644
index 2480fc6d4..000000000
--- a/vendor/knative.dev/pkg/logging/config
+++ /dev/null
@@ -1,4 +0,0 @@
-# The OWNERS file is used by prow to automatically merge approved PRs.
-
-approvers:
-- configmap-approvers
diff --git a/vendor/knative.dev/pkg/logging/object_encoders.go b/vendor/knative.dev/pkg/logging/object_encoders.go
index b6f39fe21..2b98fb5b6 100644
--- a/vendor/knative.dev/pkg/logging/object_encoders.go
+++ b/vendor/knative.dev/pkg/logging/object_encoders.go
@@ -20,6 +20,7 @@ import (
 	"strings"
 
 	"go.uber.org/zap/zapcore"
+	"knative.dev/pkg/logging/logkey"
 
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/sets"
@@ -49,10 +50,10 @@ func StringSet(s sets.String) zapcore.ObjectMarshalerFunc {
 //	logger.Info("Enqueuing", zap.Object("key", logging.NamespacedName(n)))
 func NamespacedName(n types.NamespacedName) zapcore.ObjectMarshalerFunc {
 	return func(enc zapcore.ObjectEncoder) error {
-		if n.Namespace != "" {
-			enc.AddString("key", n.Name)
+		if n.Namespace == "" {
+			enc.AddString(logkey.Key, n.Name)
 		} else {
-			enc.AddString("key", n.Namespace+"/"+n.Name)
+			enc.AddString(logkey.Key, n.Namespace+"/"+n.Name)
 		}
 		return nil
 	}
diff --git a/vendor/knative.dev/pkg/webhook/resourcesemantics/conversion/conversion.go b/vendor/knative.dev/pkg/webhook/resourcesemantics/conversion/conversion.go
index e0d495b9f..3fab95bad 100644
--- a/vendor/knative.dev/pkg/webhook/resourcesemantics/conversion/conversion.go
+++ b/vendor/knative.dev/pkg/webhook/resourcesemantics/conversion/conversion.go
@@ -22,12 +22,16 @@ import (
 	"fmt"
 
 	"go.uber.org/zap"
+
 	apixv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
+
 	"knative.dev/pkg/apis"
+	"knative.dev/pkg/kmeta"
 	"knative.dev/pkg/logging"
+	"knative.dev/pkg/logging/logkey"
 )
 
 // Convert implements webhook.ConversionController
@@ -78,8 +82,6 @@ func (r *reconciler) convert(
 		return ret, err
 	}
 
-	logger.Infof("Converting %s to version %s", formatGVK(inGVK), targetVersion)
-
 	inGK := inGVK.GroupKind()
 	conv, ok := r.kinds[inGK]
 	if !ok {
@@ -109,17 +111,28 @@ func (r *reconciler) convert(
 	out := outZygote.DeepCopyObject().(ConvertibleObject)
 
 	hubGVK := inGVK.GroupKind().WithVersion(conv.HubVersion)
-	ctx = logging.WithLogger(ctx, logger.With(
+
+	logger = logger.With(
 		zap.String("inputType", formatGVK(inGVK)),
 		zap.String("outputType", formatGVK(outGVK)),
 		zap.String("hubType", formatGVK(hubGVK)),
-	))
+	)
 
 	// TODO(dprotaso) - potentially error on unknown fields
 	if err = json.Unmarshal(inRaw.Raw, &in); err != nil {
 		return ret, fmt.Errorf("unable to unmarshal input: %w", err)
 	}
 
+	if acc, err := kmeta.DeletionHandlingAccessor(in); err == nil {
+		// TODO: right now we don't convert any non-namespaced objects. If we ever do that
+		// this needs to updated to deal with it.
+		logger = logger.With(zap.String(logkey.Key, acc.GetNamespace()+"/"+acc.GetName()))
+	} else {
+		logger.Infof("Could not get Accessor for %s: %v", formatGK(inGVK.GroupKind()), err)
+	}
+	logger.Infof("Converting %s to version %s", formatGVK(inGVK), targetVersion)
+	ctx = logging.WithLogger(ctx, logger)
+
 	if inGVK.Version == conv.HubVersion {
 		hub = in
 	} else if err = hub.ConvertFrom(ctx, in); err != nil {
diff --git a/vendor/knative.dev/pkg/webhook/webhook.go b/vendor/knative.dev/pkg/webhook/webhook.go
index e29ea9611..ef7c238da 100644
--- a/vendor/knative.dev/pkg/webhook/webhook.go
+++ b/vendor/knative.dev/pkg/webhook/webhook.go
@@ -253,6 +253,7 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		default:
 			w.WriteHeader(http.StatusOK)
 		}
+		return
 	}
 
 	// Verify the content type is accurate.
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 32770bc3d..887f1b844 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -308,8 +308,6 @@ github.com/pborman/uuid
 github.com/pkg/errors
 # github.com/prometheus/client_golang v1.5.0 => github.com/prometheus/client_golang v0.9.2
 ## explicit
-github.com/prometheus/client_golang/api
-github.com/prometheus/client_golang/api/prometheus/v1
 github.com/prometheus/client_golang/prometheus
 github.com/prometheus/client_golang/prometheus/internal
 github.com/prometheus/client_golang/prometheus/promhttp
@@ -1169,7 +1167,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-20200519155757-14eb3ae3a5a7
+# knative.dev/pkg v0.0.0-20200526194649-c48d2ebdae96
 ## explicit
 knative.dev/pkg/apiextensions/storageversion
 knative.dev/pkg/apiextensions/storageversion/cmd/migrate
@@ -1257,7 +1255,6 @@ knative.dev/pkg/test/mako/alerter/slack
 knative.dev/pkg/test/mako/config
 knative.dev/pkg/test/monitoring
 knative.dev/pkg/test/performance
-knative.dev/pkg/test/prometheus
 knative.dev/pkg/test/prow
 knative.dev/pkg/test/slackutil
 knative.dev/pkg/test/spoof
@@ -1285,7 +1282,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-20200519161858-554a95a37986
+# knative.dev/test-infra v0.0.0-20200526171149-39b911556285
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/structured-merge-diff v1.0.1


Test patch:
diff --git a/pkg/apis/autoscaling/v1alpha1/pa_defaults_test.go b/pkg/apis/autoscaling/v1alpha1/pa_defaults_test.go
index 52e60a508..253d42002 100644
--- a/pkg/apis/autoscaling/v1alpha1/pa_defaults_test.go
+++ b/pkg/apis/autoscaling/v1alpha1/pa_defaults_test.go
@@ -60,6 +60,12 @@ func TestPodAutoscalerDefaulting(t *testing.T) {
 				},
 				Data: map[string]string{},
 			})
+			s.OnConfigChanged(&v1.ConfigMap{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: config.FeaturesConfigName,
+				},
+				Data: map[string]string{},
+			})
 			s.OnConfigChanged(&v1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: autoscalerconfig.ConfigName,
diff --git a/pkg/apis/config/defaults_test.go b/pkg/apis/config/defaults_test.go
index b534f4eb4..e5c99a163 100644
--- a/pkg/apis/config/defaults_test.go
+++ b/pkg/apis/config/defaults_test.go
@@ -35,21 +35,22 @@ func TestDefaultsConfigurationFromFile(t *testing.T) {
 	cm, example := ConfigMapsFromTestFile(t, DefaultsConfigName)
 
 	if _, err := NewDefaultsConfigFromConfigMap(cm); err != nil {
-		t.Errorf("NewDefaultsConfigFromConfigMap(actual) = %v", err)
+		t.Error("NewDefaultsConfigFromConfigMap(actual) =", err)
 	}
 
-	if got, err := NewDefaultsConfigFromConfigMap(example); err != nil {
-		t.Errorf("NewDefaultsConfigFromConfigMap(example) = %v", err)
-	} else {
-		// Those are in example, to show usage,
-		// but default is nil, i.e. inheriting k8s.
-		// So for this test we ignore those, but verify the other fields.
-		got.RevisionCPULimit, got.RevisionCPURequest = nil, nil
-		got.RevisionMemoryLimit, got.RevisionMemoryRequest = nil, nil
-		if want := defaultConfig(); !cmp.Equal(got, want) {
-			t.Errorf("Example does not represent default config: diff(-want,+got)\n%s",
-				cmp.Diff(want, got))
-		}
+	got, err := NewDefaultsConfigFromConfigMap(example)
+	if err != nil {
+		t.Fatal("NewDefaultsConfigFromConfigMap(example) =", err)
+	}
+
+	// Those are in example, to show usage,
+	// but default is nil, i.e. inheriting k8s.
+	// So for this test we ignore those, but verify the other fields.
+	got.RevisionCPULimit, got.RevisionCPURequest = nil, nil
+	got.RevisionMemoryLimit, got.RevisionMemoryRequest = nil, nil
+	want := defaultDefaultsConfig()
+	if diff := cmp.Diff(want, got); diff != "" {
+		t.Error("Example does not represent default config: diff(-want,+got)\n", diff)
 	}
 }
 
@@ -64,22 +65,19 @@ func TestDefaultsConfiguration(t *testing.T) {
 	}{{
 		name:         "default configuration",
 		wantErr:      false,
-		wantDefaults: defaultConfig(),
+		wantDefaults: defaultDefaultsConfig(),
 		data:         map[string]string{},
 	}, {
 		name:    "specified values",
 		wantErr: false,
 		wantDefaults: &Defaults{
-			EnableMultiContainer:          true,
-			RevisionTimeoutSeconds:        123,
-			MaxRevisionTimeoutSeconds:     456,
-			ContainerConcurrencyMaxLimit:  1984,
-			RevisionCPURequest:            &oneTwoThree,
-			UserContainerNameTemplate:     "{{.Name}}",
-			AllowContainerConcurrencyZero: false,
+			RevisionTimeoutSeconds:       123,
+			MaxRevisionTimeoutSeconds:    456,
+			ContainerConcurrencyMaxLimit: 1984,
+			RevisionCPURequest:           &oneTwoThree,
+			UserContainerNameTemplate:    "{{.Name}}",
 		},
 		data: map[string]string{
-			"enable-multi-container":           "true",
 			"revision-timeout-seconds":         "123",
 			"max-revision-timeout-seconds":     "456",
 			"revision-cpu-request":             "123m",
@@ -91,7 +89,6 @@ func TestDefaultsConfiguration(t *testing.T) {
 		name:    "invalid multi container flag value",
 		wantErr: false,
 		wantDefaults: &Defaults{
-			EnableMultiContainer:          false,
 			RevisionTimeoutSeconds:        DefaultRevisionTimeoutSeconds,
 			MaxRevisionTimeoutSeconds:     DefaultMaxRevisionTimeoutSeconds,
 			UserContainerNameTemplate:     DefaultUserContainerName,
@@ -105,7 +102,6 @@ func TestDefaultsConfiguration(t *testing.T) {
 		name:    "invalid allow container concurrency zero flag value",
 		wantErr: false,
 		wantDefaults: &Defaults{
-			EnableMultiContainer:          false,
 			RevisionTimeoutSeconds:        DefaultRevisionTimeoutSeconds,
 			MaxRevisionTimeoutSeconds:     DefaultMaxRevisionTimeoutSeconds,
 			UserContainerNameTemplate:     DefaultUserContainerName,
@@ -212,7 +208,7 @@ func TestTemplating(t *testing.T) {
 				},
 			})
 			if err != nil {
-				t.Errorf("Error parsing defaults: %v", err)
+				t.Fatal("Error parsing defaults:", err)
 			}
 
 			ctx := apis.WithinParent(context.Background(), metav1.ObjectMeta{
diff --git a/pkg/apis/config/features_test.go b/pkg/apis/config/features_test.go
new file mode 100644
index 000000000..d0972ca84
--- /dev/null
+++ b/pkg/apis/config/features_test.go
@@ -0,0 +1,93 @@
+/*
+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
+
+    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 config
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	corev1 "k8s.io/api/core/v1"
+	. "knative.dev/pkg/configmap/testing"
+	_ "knative.dev/pkg/system/testing"
+)
+
+func TestFeaturesConfigurationFromFile(t *testing.T) {
+	cm, example := ConfigMapsFromTestFile(t, FeaturesConfigName)
+
+	if _, err := NewFeaturesConfigFromConfigMap(cm); err != nil {
+		t.Error("NewFeaturesConfigFromConfigMap(actual) =", err)
+	}
+
+	got, err := NewFeaturesConfigFromConfigMap(example)
+	if err != nil {
+		t.Fatal("NewFeaturesConfigFromConfigMap(example) =", err)
+	}
+
+	want := defaultFeaturesConfig()
+	if diff := cmp.Diff(want, got); diff != "" {
+		t.Error("Example does not represent default config: diff(-want,+got)\n", diff)
+	}
+}
+
+func TestFeaturesConfiguration(t *testing.T) {
+	configTests := []struct {
+		name         string
+		wantErr      bool
+		wantFeatures *Features
+		data         map[string]string
+	}{{
+		name:         "default configuration",
+		wantErr:      false,
+		wantFeatures: defaultFeaturesConfig(),
+		data:         map[string]string{},
+	}, {
+		name:    "multi-container Allowed",
+		wantErr: false,
+		wantFeatures: &Features{
+			MultiContainer: Allowed,
+		},
+		data: map[string]string{
+			"multi-container": "Allowed",
+		},
+	}, {
+		name:    "multi-container Enabled",
+		wantErr: false,
+		wantFeatures: &Features{
+			MultiContainer: Enabled,
+		},
+		data: map[string]string{
+			"multi-container": "Enabled",
+		},
+	}}
+
+	for _, tt := range configTests {
+		t.Run(tt.name, func(t *testing.T) {
+			actualFeatures, err := NewFeaturesConfigFromConfigMap(&corev1.ConfigMap{
+				Data: tt.data,
+			})
+
+			if (err != nil) != tt.wantErr {
+				t.Fatalf("NewFeaturesConfigFromConfigMap() error = %v, WantErr %v", err, tt.wantErr)
+			}
+
+			got, want := actualFeatures, tt.wantFeatures
+			if diff := cmp.Diff(want, got); diff != "" {
+				t.Error("Config mismatch: diff(-want,+got):\n", diff)
+			}
+		})
+	}
+}
diff --git a/pkg/apis/config/store_test.go b/pkg/apis/config/store_test.go
index 11ec92d35..2debf2d59 100644
--- a/pkg/apis/config/store_test.go
+++ b/pkg/apis/config/store_test.go
@@ -37,9 +37,11 @@ func TestStoreLoadWithContext(t *testing.T) {
 	store := NewStore(logtesting.TestLogger(t))
 
 	defaultsConfig := ConfigMapFromTestFile(t, DefaultsConfigName)
+	featuresConfig := ConfigMapFromTestFile(t, FeaturesConfigName)
 	autoscalerConfig := ConfigMapFromTestFile(t, autoscalerconfig.ConfigName)
 
 	store.OnConfigChanged(defaultsConfig)
+	store.OnConfigChanged(featuresConfig)
 	store.OnConfigChanged(autoscalerConfig)
 
 	config := FromContextOrDefaults(store.ToContext(context.Background()))
@@ -47,34 +49,49 @@ func TestStoreLoadWithContext(t *testing.T) {
 	t.Run("defaults", func(t *testing.T) {
 		expected, _ := NewDefaultsConfigFromConfigMap(defaultsConfig)
 		if diff := cmp.Diff(expected, config.Defaults, ignoreStuff...); diff != "" {
-			t.Errorf("Unexpected defaults config (-want, +got): %v", diff)
+			t.Errorf("Unexpected defaults config (-want, +got):\n%v", diff)
+		}
+	})
+
+	t.Run("features", func(t *testing.T) {
+		expected, _ := NewFeaturesConfigFromConfigMap(featuresConfig)
+		if diff := cmp.Diff(expected, config.Features, ignoreStuff...); diff != "" {
+			t.Errorf("Unexpected features config (-want, +got):\n%v", diff)
 		}
 	})
 
 	t.Run("autoscaler", func(t *testing.T) {
 		expected, _ := autoscalerconfig.NewConfigFromConfigMap(autoscalerConfig)
 		if diff := cmp.Diff(expected, config.Autoscaler, ignoreStuff...); diff != "" {
-			t.Errorf("Unexpected autoscaler config (-want, +got): %v", diff)
+			t.Errorf("Unexpected autoscaler config (-want, +got):\n%v", diff)
 		}
 	})
 }
 
 func TestStoreLoadWithContextOrDefaults(t *testing.T) {
 	defaultsConfig := ConfigMapFromTestFile(t, DefaultsConfigName)
+	featuresConfig := ConfigMapFromTestFile(t, FeaturesConfigName)
 	autoscalerConfig := ConfigMapFromTestFile(t, autoscalerconfig.ConfigName)
 	config := FromContextOrDefaults(context.Background())
 
 	t.Run("defaults", func(t *testing.T) {
 		expected, _ := NewDefaultsConfigFromConfigMap(defaultsConfig)
 		if diff := cmp.Diff(expected, config.Defaults, ignoreStuff...); diff != "" {
-			t.Errorf("Unexpected defaults config (-want, +got): %v", diff)
+			t.Errorf("Unexpected defaults config (-want, +got):\n%v", diff)
+		}
+	})
+
+	t.Run("features", func(t *testing.T) {
+		expected, _ := NewFeaturesConfigFromConfigMap(featuresConfig)
+		if diff := cmp.Diff(expected, config.Features, ignoreStuff...); diff != "" {
+			t.Errorf("Unexpected features config (-want, +got):\n%v", diff)
 		}
 	})
 
 	t.Run("autoscaler", func(t *testing.T) {
 		expected, _ := autoscalerconfig.NewConfigFromConfigMap(autoscalerConfig)
 		if diff := cmp.Diff(expected, config.Autoscaler, ignoreStuff...); diff != "" {
-			t.Errorf("Unexpected autoscaler config (-want, +got): %v", diff)
+			t.Errorf("Unexpected autoscaler config (-want, +got):\n%v", diff)
 		}
 	})
 }
@@ -83,11 +100,13 @@ func TestStoreImmutableConfig(t *testing.T) {
 	store := NewStore(logtesting.TestLogger(t))
 
 	store.OnConfigChanged(ConfigMapFromTestFile(t, DefaultsConfigName))
+	store.OnConfigChanged(ConfigMapFromTestFile(t, FeaturesConfigName))
 	store.OnConfigChanged(ConfigMapFromTestFile(t, autoscalerconfig.ConfigName))
 
 	config := store.Load()
 
 	config.Defaults.RevisionTimeoutSeconds = 1234
+	config.Features.MultiContainer = Enabled
 	config.Autoscaler.TargetBurstCapacity = 99
 
 	newConfig := store.Load()
@@ -96,6 +115,10 @@ func TestStoreImmutableConfig(t *testing.T) {
 		t.Error("Defaults config is not immutable")
 	}
 
+	if newConfig.Features.MultiContainer == Enabled {
+		t.Error("Features config is not immutable")
+	}
+
 	if newConfig.Autoscaler.TargetBurstCapacity == 99 {
 		t.Error("Autoscaler config is not immutable")
 	}
diff --git a/pkg/apis/config/testdata/config-features.yaml b/pkg/apis/config/testdata/config-features.yaml
new file mode 120000
index 000000000..e9cda5cbe
--- /dev/null
+++ b/pkg/apis/config/testdata/config-features.yaml
@@ -0,0 +1,1 @@
+../../../../config/core/configmaps/features.yaml
\ No newline at end of file
diff --git a/pkg/apis/networking/v1alpha1/ingress_validation_test.go b/pkg/apis/networking/v1alpha1/ingress_validation_test.go
index 6d3c501f1..226c0476f 100644
--- a/pkg/apis/networking/v1alpha1/ingress_validation_test.go
+++ b/pkg/apis/networking/v1alpha1/ingress_validation_test.go
@@ -57,6 +57,23 @@ func TestIngressSpecValidation(t *testing.T) {
 			}},
 		},
 		want: nil,
+	}, {
+		name: "valid-rewrite",
+		is: &IngressSpec{
+			TLS: []IngressTLS{{
+				SecretNamespace: "secret-space",
+				SecretName:      "secret-name",
+			}},
+			Rules: []IngressRule{{
+				Hosts: []string{"example.com"},
+				HTTP: &HTTPIngressRuleValue{
+					Paths: []HTTPIngressPath{{
+						RewriteHost: "some-host.com",
+					}},
+				},
+			}},
+		},
+		want: nil,
 	}, {
 		name: "empty",
 		is:   &IngressSpec{},
@@ -139,7 +156,7 @@ func TestIngressSpecValidation(t *testing.T) {
 				},
 			}},
 		},
-		want: apis.ErrMissingField("rules[0].http.paths[0].splits"),
+		want: apis.ErrMissingOneOf("rules[0].http.paths[0].splits", "rules[0].http.paths[0].rewriteHost"),
 	}, {
 		name: "empty-split",
 		is: &IngressSpec{
@@ -156,6 +173,24 @@ func TestIngressSpecValidation(t *testing.T) {
 			}},
 		},
 		want: apis.ErrMissingField("rules[0].http.paths[0].splits[0]"),
+	}, {
+		name: "rewrite-host-with-split",
+		is: &IngressSpec{
+			Rules: []IngressRule{{
+				Hosts: []string{"example.com"},
+				HTTP: &HTTPIngressRuleValue{
+					Paths: []HTTPIngressPath{{
+						RewriteHost: "some-value.com",
+						Splits: []IngressBackendSplit{{
+							IngressBackend: IngressBackend{
+								ServiceName: "service-name",
+							},
+						}},
+					}},
+				},
+			}},
+		},
+		want: apis.ErrMultipleOneOf("rules[0].http.paths[0].splits", "rules[0].http.paths[0].rewriteHost"),
 	}, {
 		name: "missing-split-backend",
 		is: &IngressSpec{
diff --git a/pkg/apis/serving/k8s_validation_test.go b/pkg/apis/serving/k8s_validation_test.go
index a644e8380..e78a74a2d 100644
--- a/pkg/apis/serving/k8s_validation_test.go
+++ b/pkg/apis/serving/k8s_validation_test.go
@@ -34,8 +34,8 @@ import (
 
 func enableMultiContainer(ctx context.Context, t *testing.T) context.Context {
 	return config.ToContext(ctx, &config.Config{
-		Defaults: &config.Defaults{
-			EnableMultiContainer: true,
+		Features: &config.Features{
+			MultiContainer: config.Enabled,
 		},
 	})
 }
@@ -168,7 +168,7 @@ func TestPodSpecValidation(t *testing.T) {
 				Image: "helloworld",
 			}},
 		},
-		want: &apis.FieldError{Message: "enable-multi-container is off, but found 2 containers"},
+		want: &apis.FieldError{Message: "multi-container is off, but found 2 containers"},
 	}, {
 		name: "extra field",
 		ps: corev1.PodSpec{
@@ -219,7 +219,7 @@ func TestPodSpecMultiContainerValidation(t *testing.T) {
 				Image: "helloworld",
 			}},
 		},
-		want: &apis.FieldError{Message: "enable-multi-container is off, but found 2 containers"},
+		want: &apis.FieldError{Message: "multi-container is off, but found 2 containers"},
 	}, {
 		name: "flag enabled: more than one container with one container port",
 		ps: corev1.PodSpec{
diff --git a/pkg/apis/serving/v1/revision_defaults_test.go b/pkg/apis/serving/v1/revision_defaults_test.go
index 59583b8cb..f327de9ee 100644
--- a/pkg/apis/serving/v1/revision_defaults_test.go
+++ b/pkg/apis/serving/v1/revision_defaults_test.go
@@ -66,6 +66,7 @@ func TestRevisionDefaulting(t *testing.T) {
 		wc: func(ctx context.Context) context.Context {
 			s := config.NewStore(logger)
 			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: autoscalerconfig.ConfigName}})
+			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: config.FeaturesConfigName}})
 			s.OnConfigChanged(&corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: config.DefaultsConfigName,
@@ -130,6 +131,7 @@ func TestRevisionDefaulting(t *testing.T) {
 		wc: func(ctx context.Context) context.Context {
 			s := config.NewStore(logger)
 			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: autoscalerconfig.ConfigName}})
+			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: config.FeaturesConfigName}})
 			s.OnConfigChanged(&corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: config.DefaultsConfigName,
@@ -261,6 +263,7 @@ func TestRevisionDefaulting(t *testing.T) {
 		wc: func(ctx context.Context) context.Context {
 			s := config.NewStore(logger)
 			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: autoscalerconfig.ConfigName}})
+			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: config.FeaturesConfigName}})
 			s.OnConfigChanged(&corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: config.DefaultsConfigName,
diff --git a/pkg/apis/serving/v1/revision_validation_test.go b/pkg/apis/serving/v1/revision_validation_test.go
index 288594fb4..20dc82e9e 100644
--- a/pkg/apis/serving/v1/revision_validation_test.go
+++ b/pkg/apis/serving/v1/revision_validation_test.go
@@ -383,7 +383,7 @@ func TestRevisionSpecValidation(t *testing.T) {
 				}},
 			},
 		},
-		want: &apis.FieldError{Message: "enable-multi-container is off, but found 2 containers"},
+		want: &apis.FieldError{Message: "multi-container is off, but found 2 containers"},
 	}, {
 		name: "exceed max timeout",
 		rs: &RevisionSpec{
@@ -410,6 +410,7 @@ func TestRevisionSpecValidation(t *testing.T) {
 		wc: func(ctx context.Context) context.Context {
 			s := config.NewStore(logtesting.TestLogger(t))
 			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: autoscalerconfig.ConfigName}})
+			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: config.FeaturesConfigName}})
 			s.OnConfigChanged(&corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: config.DefaultsConfigName,
@@ -518,6 +519,7 @@ func TestImmutableFields(t *testing.T) {
 		wc: func(ctx context.Context) context.Context {
 			s := config.NewStore(logtesting.TestLogger(t))
 			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: autoscalerconfig.ConfigName}})
+			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: config.FeaturesConfigName}})
 			s.OnConfigChanged(&corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: config.DefaultsConfigName,
diff --git a/pkg/apis/serving/v1/route_lifecycle_test.go b/pkg/apis/serving/v1/route_lifecycle_test.go
index 7f35cb6ab..8aa5ff4ed 100644
--- a/pkg/apis/serving/v1/route_lifecycle_test.go
+++ b/pkg/apis/serving/v1/route_lifecycle_test.go
@@ -177,7 +177,7 @@ func TestTypicalRouteFlow(t *testing.T) {
 	apistest.CheckConditionOngoing(r, RouteConditionReady, t)
 
 	r.MarkTrafficAssigned()
-	r.MarkAutoTLSNotEnabled()
+	r.MarkTLSNotEnabled(AutoTLSNotEnabledMessage)
 	apistest.CheckConditionSucceeded(r, RouteConditionAllTrafficAssigned, t)
 	apistest.CheckConditionOngoing(r, RouteConditionIngressReady, t)
 	apistest.CheckConditionOngoing(r, RouteConditionReady, t)
@@ -291,7 +291,7 @@ func TestIngressFailureRecovery(t *testing.T) {
 	apistest.CheckConditionOngoing(r, RouteConditionReady, t)
 
 	r.MarkTrafficAssigned()
-	r.MarkAutoTLSNotEnabled()
+	r.MarkTLSNotEnabled(AutoTLSNotEnabledMessage)
 	r.PropagateIngressStatus(netv1alpha1.IngressStatus{
 		Status: duckv1.Status{
 			Conditions: duckv1.Conditions{{
@@ -386,7 +386,7 @@ func TestRouteNotOwnCertificate(t *testing.T) {
 func TestRouteAutoTLSNotEnabled(t *testing.T) {
 	r := &RouteStatus{}
 	r.InitializeConditions()
-	r.MarkAutoTLSNotEnabled()
+	r.MarkTLSNotEnabled(AutoTLSNotEnabledMessage)
 
 	apistest.CheckConditionSucceeded(r, RouteConditionCertificateProvisioned, t)
 }
diff --git a/pkg/apis/serving/v1alpha1/configuration_conversion_test.go b/pkg/apis/serving/v1alpha1/configuration_conversion_test.go
index e9371ff20..26e4095ed 100644
--- a/pkg/apis/serving/v1alpha1/configuration_conversion_test.go
+++ b/pkg/apis/serving/v1alpha1/configuration_conversion_test.go
@@ -21,6 +21,7 @@ import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -201,7 +202,7 @@ func TestConfigurationConversion(t *testing.T) {
 				if err := got.ConvertFrom(context.Background(), ver); err != nil {
 					t.Errorf("ConvertFrom() = %v", err)
 				}
-				if diff := cmp.Diff(test.in, got); diff != "" {
+				if diff := cmp.Diff(test.in, got, cmpopts.EquateEmpty()); diff != "" {
 					t.Errorf("roundtrip (-want, +got) = %v", diff)
 				}
 			})
@@ -228,7 +229,7 @@ func TestConfigurationConversion(t *testing.T) {
 				if err := got.ConvertFrom(context.Background(), ver); err != nil {
 					t.Errorf("ConvertFrom() = %v", err)
 				}
-				if diff := cmp.Diff(test.in, got); diff != "" {
+				if diff := cmp.Diff(test.in, got, cmpopts.EquateEmpty()); diff != "" {
 					t.Errorf("roundtrip (-want, +got) = %v", diff)
 				}
 			})
diff --git a/pkg/apis/serving/v1alpha1/revision_conversion_test.go b/pkg/apis/serving/v1alpha1/revision_conversion_test.go
index c5c3a8b4e..875e3dbf2 100644
--- a/pkg/apis/serving/v1alpha1/revision_conversion_test.go
+++ b/pkg/apis/serving/v1alpha1/revision_conversion_test.go
@@ -21,6 +21,7 @@ import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
@@ -123,7 +124,7 @@ func TestRevisionConversion(t *testing.T) {
 			if err := got.ConvertFrom(context.Background(), beta); err != nil {
 				t.Errorf("ConvertFrom() = %v", err)
 			}
-			if diff := cmp.Diff(test.in, got); diff != "" {
+			if diff := cmp.Diff(test.in, got, cmpopts.EquateEmpty()); diff != "" {
 				t.Errorf("Roundtrip (-want, +got): \n%s", diff)
 			}
 		})
@@ -150,7 +151,7 @@ func TestRevisionConversion(t *testing.T) {
 			if err := got.ConvertFrom(context.Background(), beta); err != nil {
 				t.Errorf("ConvertFrom() = %v", err)
 			}
-			if diff := cmp.Diff(test.in, got); diff != "" {
+			if diff := cmp.Diff(test.in, got, cmpopts.EquateEmpty()); diff != "" {
 				t.Errorf("Roundtrip (-want, +got): \n%s", diff)
 			}
 		})
@@ -197,7 +198,7 @@ func TestRevisionConversionForMultiContainer(t *testing.T) {
 	if err := got.ConvertFrom(context.Background(), beta); err != nil {
 		t.Errorf("ConvertFrom() = %v", err)
 	}
-	if diff := cmp.Diff(input, got); diff != "" {
+	if diff := cmp.Diff(input, got, cmpopts.EquateEmpty()); diff != "" {
 		t.Errorf("Roundtrip (-want, +got): \n%s", diff)
 	}
 }
diff --git a/pkg/apis/serving/v1alpha1/revision_defaults_test.go b/pkg/apis/serving/v1alpha1/revision_defaults_test.go
index b26d4b9a4..a577cd83d 100644
--- a/pkg/apis/serving/v1alpha1/revision_defaults_test.go
+++ b/pkg/apis/serving/v1alpha1/revision_defaults_test.go
@@ -89,6 +89,7 @@ func TestRevisionDefaulting(t *testing.T) {
 		wc: func(ctx context.Context) context.Context {
 			s := config.NewStore(logtesting.TestLogger(t))
 			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: autoscalerconfig.ConfigName}})
+			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: config.FeaturesConfigName}})
 			s.OnConfigChanged(&corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: config.DefaultsConfigName,
diff --git a/pkg/apis/serving/v1alpha1/revision_validation_test.go b/pkg/apis/serving/v1alpha1/revision_validation_test.go
index 9e21de31f..2c81f043c 100644
--- a/pkg/apis/serving/v1alpha1/revision_validation_test.go
+++ b/pkg/apis/serving/v1alpha1/revision_validation_test.go
@@ -244,6 +244,7 @@ func TestRevisionSpecValidation(t *testing.T) {
 		wc: func(ctx context.Context) context.Context {
 			s := config.NewStore(logtesting.TestLogger(t))
 			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: autoscalerconfig.ConfigName}})
+			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: config.FeaturesConfigName}})
 			s.OnConfigChanged(&corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: config.DefaultsConfigName,
@@ -645,6 +646,7 @@ func TestImmutableFields(t *testing.T) {
 		wc: func(ctx context.Context) context.Context {
 			s := config.NewStore(logtesting.TestLogger(t))
 			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: autoscalerconfig.ConfigName}})
+			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: config.FeaturesConfigName}})
 			s.OnConfigChanged(&corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: config.DefaultsConfigName,
diff --git a/pkg/apis/serving/v1alpha1/route_conversion_test.go b/pkg/apis/serving/v1alpha1/route_conversion_test.go
index 0dfe0c1cd..c46748e13 100644
--- a/pkg/apis/serving/v1alpha1/route_conversion_test.go
+++ b/pkg/apis/serving/v1alpha1/route_conversion_test.go
@@ -21,6 +21,7 @@ import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	"knative.dev/pkg/apis"
@@ -300,7 +301,7 @@ func TestRouteConversion(t *testing.T) {
 			if err := got.ConvertFrom(context.Background(), beta); err != nil {
 				t.Errorf("ConvertFrom() = %v", err)
 			}
-			if diff := cmp.Diff(test.in, got); diff != "" {
+			if diff := cmp.Diff(test.in, got, cmpopts.EquateEmpty()); diff != "" {
 				t.Errorf("roundtrip (-want, +got) = %v", diff)
 			}
 		})
@@ -320,7 +321,7 @@ func TestRouteConversion(t *testing.T) {
 			if err := got.ConvertFrom(context.Background(), beta); err != nil {
 				t.Errorf("ConvertFrom() = %v", err)
 			}
-			if diff := cmp.Diff(test.in, got); diff != "" {
+			if diff := cmp.Diff(test.in, got, cmpopts.EquateEmpty()); diff != "" {
 				t.Errorf("roundtrip (-want, +got) = %v", diff)
 			}
 		})
diff --git a/pkg/apis/serving/v1alpha1/route_lifecycle_test.go b/pkg/apis/serving/v1alpha1/route_lifecycle_test.go
index b1685869e..8f626abd3 100644
--- a/pkg/apis/serving/v1alpha1/route_lifecycle_test.go
+++ b/pkg/apis/serving/v1alpha1/route_lifecycle_test.go
@@ -160,7 +160,7 @@ func TestTypicalRouteFlow(t *testing.T) {
 	apistest.CheckConditionOngoing(r, RouteConditionReady, t)
 
 	r.MarkTrafficAssigned()
-	r.MarkAutoTLSNotEnabled()
+	r.MarkTLSNotEnabled(AutoTLSNotEnabledMessage)
 	apistest.CheckConditionSucceeded(r, RouteConditionAllTrafficAssigned, t)
 	apistest.CheckConditionOngoing(r, RouteConditionIngressReady, t)
 	apistest.CheckConditionOngoing(r, RouteConditionReady, t)
@@ -274,7 +274,7 @@ func TestIngressFailureRecovery(t *testing.T) {
 	apistest.CheckConditionOngoing(r, RouteConditionReady, t)
 
 	r.MarkTrafficAssigned()
-	r.MarkAutoTLSNotEnabled()
+	r.MarkTLSNotEnabled(AutoTLSNotEnabledMessage)
 	r.PropagateIngressStatus(netv1alpha1.IngressStatus{
 		Status: duckv1.Status{
 			Conditions: duckv1.Conditions{{
@@ -381,7 +381,7 @@ func TestRouteNotOwnCertificate(t *testing.T) {
 func TestRouteAutoTLSNotEnabled(t *testing.T) {
 	r := &RouteStatus{}
 	r.InitializeConditions()
-	r.MarkAutoTLSNotEnabled()
+	r.MarkTLSNotEnabled(AutoTLSNotEnabledMessage)
 
 	apistest.CheckConditionSucceeded(r, RouteConditionCertificateProvisioned, t)
 }
diff --git a/pkg/apis/serving/v1alpha1/service_conversion_test.go b/pkg/apis/serving/v1alpha1/service_conversion_test.go
index d57dfc2aa..c18d1b3eb 100644
--- a/pkg/apis/serving/v1alpha1/service_conversion_test.go
+++ b/pkg/apis/serving/v1alpha1/service_conversion_test.go
@@ -21,6 +21,7 @@ import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-cmp/cmp/cmpopts"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
@@ -136,7 +137,7 @@ func TestServiceConversion(t *testing.T) {
 					t.Errorf("ConvertFrom() = %v", err)
 				}
 				t.Logf("ConvertFrom() = %#v", got)
-				if diff := cmp.Diff(test.in, got); diff != "" {
+				if diff := cmp.Diff(test.in, got, cmpopts.EquateEmpty()); diff != "" {
 					t.Errorf("roundtrip (-want, +got) = %v", diff)
 				}
 			})
@@ -697,7 +698,7 @@ func TestServiceConversionFromDeprecated(t *testing.T) {
 					t.Errorf("ConvertFrom() = %v", err)
 				}
 				t.Logf("ConvertFrom() = %#v", got)
-				if diff := cmp.Diff(test.want, got); diff != "" {
+				if diff := cmp.Diff(test.want, got, cmpopts.EquateEmpty()); diff != "" {
 					t.Errorf("roundtrip (-want, +got) = %v", diff)
 				}
 			})
diff --git a/pkg/apis/serving/v1beta1/revision_defaults_test.go b/pkg/apis/serving/v1beta1/revision_defaults_test.go
index 2b131d917..e88286b62 100644
--- a/pkg/apis/serving/v1beta1/revision_defaults_test.go
+++ b/pkg/apis/serving/v1beta1/revision_defaults_test.go
@@ -67,6 +67,7 @@ func TestRevisionDefaulting(t *testing.T) {
 		wc: func(ctx context.Context) context.Context {
 			s := config.NewStore(logger)
 			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: autoscalerconfig.ConfigName}})
+			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: config.FeaturesConfigName}})
 			s.OnConfigChanged(&corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: config.DefaultsConfigName,
@@ -131,6 +132,7 @@ func TestRevisionDefaulting(t *testing.T) {
 		wc: func(ctx context.Context) context.Context {
 			s := config.NewStore(logger)
 			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: autoscalerconfig.ConfigName}})
+			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: config.FeaturesConfigName}})
 			s.OnConfigChanged(&corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: config.DefaultsConfigName,
diff --git a/pkg/apis/serving/v1beta1/revision_validation_test.go b/pkg/apis/serving/v1beta1/revision_validation_test.go
index 4e08987c7..3ee08493b 100644
--- a/pkg/apis/serving/v1beta1/revision_validation_test.go
+++ b/pkg/apis/serving/v1beta1/revision_validation_test.go
@@ -384,7 +384,7 @@ func TestRevisionSpecValidation(t *testing.T) {
 				}},
 			},
 		},
-		want: &apis.FieldError{Message: "enable-multi-container is off, but found 2 containers"},
+		want: &apis.FieldError{Message: "multi-container is off, but found 2 containers"},
 	}, {
 		name: "exceed max timeout",
 		rs: &v1.RevisionSpec{
@@ -411,6 +411,7 @@ func TestRevisionSpecValidation(t *testing.T) {
 		wc: func(ctx context.Context) context.Context {
 			s := config.NewStore(logtesting.TestLogger(t))
 			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: autoscalerconfig.ConfigName}})
+			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: config.FeaturesConfigName}})
 			s.OnConfigChanged(&corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: config.DefaultsConfigName,
@@ -519,6 +520,7 @@ func TestImmutableFields(t *testing.T) {
 		wc: func(ctx context.Context) context.Context {
 			s := config.NewStore(logtesting.TestLogger(t))
 			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: autoscalerconfig.ConfigName}})
+			s.OnConfigChanged(&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: config.FeaturesConfigName}})
 			s.OnConfigChanged(&corev1.ConfigMap{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: config.DefaultsConfigName,
diff --git a/pkg/reconciler/route/reconcile_resources_test.go b/pkg/reconciler/route/reconcile_resources_test.go
index 40be3ba9b..79c4b4705 100644
--- a/pkg/reconciler/route/reconcile_resources_test.go
+++ b/pkg/reconciler/route/reconcile_resources_test.go
@@ -44,6 +44,15 @@ import (
 	. "knative.dev/serving/pkg/testing/v1"
 )
 
+func getCertificateFromClient(ctx context.Context, t *testing.T, desired *netv1alpha1.Certificate) *netv1alpha1.Certificate {
+	t.Helper()
+	created, err := fakeservingclient.Get(ctx).NetworkingV1alpha1().Certificates(desired.Namespace).Get(desired.Name, metav1.GetOptions{})
+	if err != nil {
+		t.Errorf("Certificates(%s).Get(%s) = %v", desired.Namespace, desired.Name, err)
+	}
+	return created
+}
+
 func TestReconcileIngressInsert(t *testing.T) {
 	var reconciler *Reconciler
 	ctx, _, _, _, cancel := newTestSetup(t, func(r *Reconciler) {
@@ -240,7 +249,7 @@ func TestReconcileCertificatesInsert(t *testing.T) {
 	if _, err := reconciler.reconcileCertificate(ctx, r, certificate); err != nil {
 		t.Errorf("Unexpected error: %v", err)
 	}
-	created := getCertificateFromClient(t, ctx, certificate)
+	created := getCertificateFromClient(ctx, t, certificate)
 	if diff := cmp.Diff(certificate, created); diff != "" {
 		t.Errorf("Unexpected diff (-want +got): %s", diff)
 	}
@@ -259,7 +268,7 @@ func TestReconcileCertificateUpdate(t *testing.T) {
 		t.Errorf("Unexpected error: %v", err)
 	}
 
-	storedCert := getCertificateFromClient(t, ctx, certificate)
+	storedCert := getCertificateFromClient(ctx, t, certificate)
 	fakecertinformer.Get(ctx).Informer().GetIndexer().Add(storedCert)
 
 	newCertificate := newCerts([]string{"new.example.com"}, r)
@@ -267,7 +276,7 @@ func TestReconcileCertificateUpdate(t *testing.T) {
 		t.Errorf("Unexpected error: %v", err)
 	}
 
-	updated := getCertificateFromClient(t, ctx, newCertificate)
+	updated := getCertificateFromClient(ctx, t, newCertificate)
 	if diff := cmp.Diff(newCertificate, updated); diff != "" {
 		t.Errorf("Unexpected diff (-want +got): %s", diff)
 	}
diff --git a/pkg/reconciler/route/route_test.go b/pkg/reconciler/route/route_test.go
index a64434bec..585da1d18 100644
--- a/pkg/reconciler/route/route_test.go
+++ b/pkg/reconciler/route/route_test.go
@@ -203,16 +203,7 @@ func getRouteIngressFromClient(ctx context.Context, t *testing.T, route *v1.Rout
 	return &ingresses.Items[0]
 }
 
-func getCertificateFromClient(t *testing.T, ctx context.Context, desired *netv1alpha1.Certificate) *netv1alpha1.Certificate {
-	t.Helper()
-	created, err := fakeservingclient.Get(ctx).NetworkingV1alpha1().Certificates(desired.Namespace).Get(desired.Name, metav1.GetOptions{})
-	if err != nil {
-		t.Errorf("Certificates(%s).Get(%s) = %v", desired.Namespace, desired.Name, err)
-	}
-	return created
-}
-
-func addRouteToInformers(t *testing.T, ctx context.Context, route *v1.Route) {
+func addRouteToInformers(ctx context.Context, t *testing.T, route *v1.Route) {
 	t.Helper()
 
 	ns := route.Namespace
@@ -1067,7 +1058,7 @@ func TestUpdateDomainConfigMap(t *testing.T) {
 			if err := ctl.Reconciler.Reconcile(context.Background(), KeyOrDie(route)); err != nil {
 				t.Fatal("Reconcile() =", err)
 			}
-			addRouteToInformers(t, ctx, route)
+			addRouteToInformers(ctx, t, route)
 
 			// Wait initial reconcile to finish.
 			rl := fakerouteinformer.Get(ctx).Lister()
diff --git a/pkg/reconciler/route/table_test.go b/pkg/reconciler/route/table_test.go
index f8694c521..b5cec4151 100644
--- a/pkg/reconciler/route/table_test.go
+++ b/pkg/reconciler/route/table_test.go
@@ -2350,7 +2350,7 @@ func TestReconcile_EnableAutoTLS(t *testing.T) {
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: Route("default", "becomes-local", WithConfigTarget("config"),
 				WithRouteUID("65-23"),
-				MarkTrafficAssigned, MarkIngressNotConfigured,
+				MarkTrafficAssigned, MarkIngressNotConfigured, WithRouteConditionsTLSNotEnabledForClusterLocalMessage,
 				WithLocalDomain, WithAddress, WithInitRouteConditions,
 				WithRouteLabel(map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal}),
 				WithStatusTraffic(
diff --git a/pkg/testing/v1/route.go b/pkg/testing/v1/route.go
index 54394f379..77b3aee67 100644
--- a/pkg/testing/v1/route.go
+++ b/pkg/testing/v1/route.go
@@ -159,10 +159,18 @@ func WithInitRouteConditions(rt *v1.Route) {
 	rt.Status.InitializeConditions()
 }
 
-// WithRouteConditionsAutoTLSDisabled calls MarkAutoTLSNotEnabled after initialized the Service's conditions.
+// WithRouteConditionsAutoTLSDisabled calls MarkTLSNotEnabled with AutoTLSNotEnabledMessage
+// after initialized the Service's conditions.
 func WithRouteConditionsAutoTLSDisabled(rt *v1.Route) {
 	rt.Status.InitializeConditions()
-	rt.Status.MarkAutoTLSNotEnabled()
+	rt.Status.MarkTLSNotEnabled(v1.AutoTLSNotEnabledMessage)
+}
+
+// TLSNotEnabledForClusterLocalMessage calls MarkTLSNotEnabled with TLSNotEnabledForClusterLocalMessage
+// after initialized the Service's conditions.
+func WithRouteConditionsTLSNotEnabledForClusterLocalMessage(rt *v1.Route) {
+	rt.Status.InitializeConditions()
+	rt.Status.MarkTLSNotEnabled(v1.TLSNotEnabledForClusterLocalMessage)
 }
 
 // WithRouteConditionsHTTPDowngrade calls MarkHTTPDowngrade after initialized the Service's conditions.
diff --git a/pkg/testing/v1alpha1/service.go b/pkg/testing/v1alpha1/service.go
index 11c4c851e..9a64e090e 100644
--- a/pkg/testing/v1alpha1/service.go
+++ b/pkg/testing/v1alpha1/service.go
@@ -178,7 +178,7 @@ func WithServiceGeneration(generation int64) ServiceOption {
 	}
 }
 
-// WithServiceGeneration sets the service's observed generation to it's generation
+// WithServiceObservedGeneration sets the service's observed generation to it's generation
 func WithServiceObservedGeneration(svc *v1alpha1.Service) {
 	svc.Status.ObservedGeneration = svc.Generation
 }
diff --git a/test/e2e/websocket.go b/test/e2e/websocket.go
index 3250b49a1..5ff83cc60 100644
--- a/test/e2e/websocket.go
+++ b/test/e2e/websocket.go
@@ -125,10 +125,9 @@ func uniqueHostConnections(t *testing.T, names test.ResourceNames, size int) (*s
 					if _, ok := uniqueHostConns.LoadOrStore(host, conn); !ok {
 						t.Log("New pod has been discovered:", host)
 						return nil
-					} else {
-						t.Log("Existing pod has been returned:", host)
-						conn.Close()
 					}
+					t.Log("Existing pod has been returned:", host)
+					conn.Close()
 				}
 			}
 		})
diff --git a/test/performance/latency_test.go b/test/performance/latency_test.go
index 7af54f07b..47081f451 100644
--- a/test/performance/latency_test.go
+++ b/test/performance/latency_test.go
@@ -49,19 +49,18 @@ const (
 func timeToServe(t *testing.T, img, query string, reqTimeout time.Duration) {
 	t.Helper()
 	tName := t.Name()
-	perfClients, err := Setup(t)
+	clients, err := Setup()
 	if err != nil {
-		t.Fatal("Cannot initialize performance client:", err)
+		t.Fatal("Cannot initialize performance clients:", err)
 	}
 
-	clients := perfClients.E2EClients
 	names := test.ResourceNames{
 		Service: test.ObjectNameForTest(t),
 		Image:   img,
 	}
 
-	defer TearDown(perfClients, names, t.Logf)
-	test.CleanupOnInterrupt(func() { TearDown(perfClients, names, t.Logf) })
+	defer TearDown(clients, names, t.Logf)
+	test.CleanupOnInterrupt(func() { TearDown(clients, names, t.Logf) })
 
 	t.Log("Creating a new Service")
 	objs, err := v1a1test.CreateRunLatestServiceReady(t, clients, &names)
diff --git a/test/performance/observed_concurency_test.go b/test/performance/observed_concurency_test.go
index 1efd838ef..7f6f4eba1 100644
--- a/test/performance/observed_concurency_test.go
+++ b/test/performance/observed_concurency_test.go
@@ -137,7 +137,7 @@ func TestObservedConcurrency(t *testing.T) {
 }
 
 func testConcurrencyN(t *testing.T, concurrency int) []junit.TestCase {
-	perfClients, err := Setup(t)
+	clients, err := Setup()
 	if err != nil {
 		t.Fatal("Cannot initialize performance client:", err)
 	}
@@ -146,10 +146,9 @@ func testConcurrencyN(t *testing.T, concurrency int) []junit.TestCase {
 		Service: test.ObjectNameForTest(t),
 		Image:   "observed-concurrency",
 	}
-	clients := perfClients.E2EClients
 
-	defer TearDown(perfClients, names, t.Logf)
-	test.CleanupOnInterrupt(func() { TearDown(perfClients, names, t.Logf) })
+	defer TearDown(clients, names, t.Logf)
+	test.CleanupOnInterrupt(func() { TearDown(clients, names, t.Logf) })
 
 	t.Log("Creating a new Service")
 	objs, err := v1test.CreateServiceReady(t, clients, &names,
diff --git a/test/performance/performance.go b/test/performance/performance.go
index 0984b4b91..763ced9d3 100644
--- a/test/performance/performance.go
+++ b/test/performance/performance.go
@@ -21,7 +21,6 @@ import (
 	"fmt"
 	"log"
 	"net/http"
-	"testing"
 	"time"
 
 	v1 "k8s.io/api/core/v1"
@@ -30,7 +29,6 @@ import (
 	podinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/pod"
 	pkgTest "knative.dev/pkg/test"
 	"knative.dev/pkg/test/logging"
-	"knative.dev/pkg/test/prometheus"
 	"knative.dev/serving/test"
 
 	"knative.dev/pkg/test/spoof"
@@ -40,50 +38,18 @@ import (
 	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
 )
 
-const (
-	monitoringNS = "knative-monitoring"
-	duration     = 1 * time.Minute
-
-	EnablePrometheus = iota
-)
-
-// Client is the client used in the performance tests.
-type Client struct {
-	E2EClients *test.Clients
-	PromClient *prometheus.PromProxy
-}
+const duration = 1 * time.Minute
 
 // Setup creates all the clients that we need to interact with in our tests
-func Setup(t *testing.T, monitoring ...int) (*Client, error) {
+func Setup() (*test.Clients, error) {
 	pkgTest.SetupLoggingFlags()
 	clients, err := test.NewClients(pkgTest.Flags.Kubeconfig, pkgTest.Flags.Cluster, test.ServingNamespace)
-	if err != nil {
-		return nil, err
-	}
-
-	var p *prometheus.PromProxy
-	for _, m := range monitoring {
-		switch m {
-		case EnablePrometheus:
-			t.Log("Creating prometheus proxy client")
-			p = &prometheus.PromProxy{Namespace: monitoringNS}
-			p.Setup(clients.KubeClient.Kube, t.Logf)
-		default:
-			t.Log("No monitoring components enabled")
-		}
-	}
-
-	return &Client{E2EClients: clients, PromClient: p}, nil
+	return clients, err
 }
 
 // TearDown cleans up resources used
-func TearDown(client *Client, names test.ResourceNames, logf logging.FormatLogger) {
-	test.TearDown(client.E2EClients, names)
-
-	// Teardown prometheus client
-	if client.PromClient != nil {
-		client.PromClient.Teardown(logf)
-	}
+func TearDown(clients *test.Clients, names test.ResourceNames, logf logging.FormatLogger) {
+	test.TearDown(clients, names)
 }
 
 // ProbeTargetTillReady will probe the target once per second for the given duration, until it's ready or error happens
@@ -113,7 +79,7 @@ func ProbeTargetTillReady(target string, duration time.Duration) error {
 func WaitForScaleToZero(ctx context.Context, namespace string, selector labels.Selector, duration time.Duration) error {
 	pl := podinformer.Get(ctx).Lister()
 	begin := time.Now()
-	return wait.PollImmediate(1*time.Second, duration, func() (bool, error) {
+	return wait.PollImmediate(time.Second, duration, func() (bool, error) {
 		pods, err := pl.Pods(namespace).List(selector)
 		if err != nil {
 			return false, err
diff --git a/third_party/contour-latest/contour.yaml b/third_party/contour-latest/contour.yaml
index 150871ea2..14c1ca8b1 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:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:05026eaed72965d6215b3d33578bc8bd35ff65e2c4373464305ee3173f0d9ccd
         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:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:05026eaed72965d6215b3d33578bc8bd35ff65e2c4373464305ee3173f0d9ccd
         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:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:05026eaed72965d6215b3d33578bc8bd35ff65e2c4373464305ee3173f0d9ccd
         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:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:05026eaed72965d6215b3d33578bc8bd35ff65e2c4373464305ee3173f0d9ccd
         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:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:05026eaed72965d6215b3d33578bc8bd35ff65e2c4373464305ee3173f0d9ccd
         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:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:05026eaed72965d6215b3d33578bc8bd35ff65e2c4373464305ee3173f0d9ccd
         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:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:05026eaed72965d6215b3d33578bc8bd35ff65e2c4373464305ee3173f0d9ccd
         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:68fa5b91335cca8c5d3dfc1bf611194eb1e0741d2f7a33f89839f1b496388f6c
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:05026eaed72965d6215b3d33578bc8bd35ff65e2c4373464305ee3173f0d9ccd
         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 0a5e9c3d9..665502523 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: "v20200525-d02a1c4"
+    serving.knative.dev/release: "v20200526-e8fb3d9"
 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:b6528a96949370697e2b7a96ad2ab847cf0c5a1ba0b5d5884241021f73cf3700
+        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:97b343f5065e35af98a6119f10e7d416a64b2c0c53a70208444cb84cfe3fc8c4
         resources:
           requests:
             cpu: 40m
diff --git a/vendor/knative.dev/pkg/test/prometheus/prometheus.go b/vendor/knative.dev/pkg/test/prometheus/prometheus.go
deleted file mode 100644
index 270927f23..000000000
--- a/vendor/knative.dev/pkg/test/prometheus/prometheus.go
+++ /dev/null
@@ -1,135 +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
-
-	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.
-*/
-
-package prometheus
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"sync"
-	"time"
-
-	"github.com/prometheus/client_golang/api"
-	v1 "github.com/prometheus/client_golang/api/prometheus/v1"
-	"github.com/prometheus/common/model"
-	"k8s.io/client-go/kubernetes"
-	"knative.dev/pkg/test/logging"
-	"knative.dev/pkg/test/monitoring"
-)
-
-const (
-	prometheusPort = 9090
-	appLabel       = "prometheus"
-)
-
-var (
-	// sync.Once variable to ensure we execute zipkin setup only once.
-	setupOnce sync.Once
-
-	// sync.Once variable to ensure we execute zipkin cleanup only if zipkin is setup and it is executed only once.
-	teardownOnce sync.Once
-)
-
-// PromProxy defines a proxy to the prometheus server
-type PromProxy struct {
-	Namespace string
-	processID int
-}
-
-// Setup performs a port forwarding for app prometheus-test in given namespace
-func (p *PromProxy) Setup(kubeClientset *kubernetes.Clientset, logf logging.FormatLogger) {
-	setupOnce.Do(func() {
-		if err := monitoring.CheckPortAvailability(prometheusPort); err != nil {
-			logf("Prometheus port not available: %v", err)
-			return
-		}
-
-		promPods, err := monitoring.GetPods(kubeClientset, appLabel, p.Namespace)
-		if err != nil {
-			logf("Error retrieving prometheus pod details: %v", err)
-			return
-		}
-
-		p.processID, err = monitoring.PortForward(logf, promPods, prometheusPort, prometheusPort, p.Namespace)
-		if err != nil {
-			logf("Error starting kubectl port-forward command: %v", err)
-			return
-		}
-	})
-}
-
-// Teardown will kill the port forwarding process if running.
-func (p *PromProxy) Teardown(logf logging.FormatLogger) {
-	teardownOnce.Do(func() {
-		if err := monitoring.Cleanup(p.processID); err != nil {
-			logf("Encountered error killing port-forward process: %v", err)
-			return
-		}
-	})
-}
-
-// PromAPI gets a handle to the prometheus API
-func PromAPI() (v1.API, error) {
-	client, err := api.NewClient(api.Config{Address: fmt.Sprintf("http://localhost:%d", prometheusPort)})
-	if err != nil {
-		return nil, err
-	}
-	return v1.NewAPI(client), nil
-}
-
-// AllowPrometheusSync sleeps for sometime to allow prometheus time to scrape the metrics.
-func AllowPrometheusSync(logf logging.FormatLogger) {
-	logf("Sleeping to allow prometheus to record metrics...")
-	time.Sleep(30 * time.Second)
-}
-
-// RunQuery runs a prometheus query and returns the metric value
-func RunQuery(ctx context.Context, logf logging.FormatLogger, promAPI v1.API, query string) (float64, error) {
-	logf("Running prometheus query: %s", query)
-
-	value, err := promAPI.Query(ctx, query, time.Now())
-	if err != nil {
-		return 0, err
-	}
-
-	return VectorValue(value)
-}
-
-// RunQueryRange runs a prometheus query over the given range
-func RunQueryRange(ctx context.Context, logf logging.FormatLogger, promAPI v1.API, query string, r v1.Range) (float64, error) {
-	logf("Running prometheus query: %s", query)
-
-	value, err := promAPI.QueryRange(ctx, query, r)
-	if err != nil {
-		return 0, err
-	}
-
-	return VectorValue(value)
-}
-
-// VectorValue gets the vector value from the value type
-func VectorValue(val model.Value) (float64, error) {
-	if val.Type() != model.ValVector {
-		return 0, fmt.Errorf("Value type is %s. Expected: Valvector", val.String())
-	}
-	value := val.(model.Vector)
-	if len(value) == 0 {
-		return 0, errors.New("Query returned no results")
-	}
-
-	return float64(value[0].Value), nil
-}
diff --git a/vendor/knative.dev/pkg/test/spoof/spoof.go b/vendor/knative.dev/pkg/test/spoof/spoof.go
index 2778fe9e7..c6e1ffae7 100644
--- a/vendor/knative.dev/pkg/test/spoof/spoof.go
+++ b/vendor/knative.dev/pkg/test/spoof/spoof.go
@@ -20,7 +20,9 @@ package spoof
 
 import (
 	"context"
+	"errors"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"net"
 	"net/http"
@@ -244,18 +246,22 @@ func (sc *SpoofingClient) Poll(req *http.Request, inState ResponseChecker, error
 // DefaultErrorRetryChecker implements the defaults for retrying on error.
 func DefaultErrorRetryChecker(err error) (bool, error) {
 	if isTCPTimeout(err) {
-		return true, fmt.Errorf("Retrying for TCP timeout: %w", err)
+		return true, fmt.Errorf("retrying for TCP timeout: %w", err)
 	}
 	// Retrying on DNS error, since we may be using xip.io or nip.io in tests.
 	if isDNSError(err) {
-		return true, fmt.Errorf("Retrying for DNS error: %w", err)
+		return true, fmt.Errorf("retrying for DNS error: %w", err)
 	}
 	// Repeat the poll on `connection refused` errors, which are usually transient Istio errors.
 	if isConnectionRefused(err) {
-		return true, fmt.Errorf("Retrying for connection refused: %w", err)
+		return true, fmt.Errorf("retrying for connection refused: %w", err)
 	}
 	if isConnectionReset(err) {
-		return true, fmt.Errorf("Retrying for connection reset: %w", err)
+		return true, fmt.Errorf("retrying for connection reset: %w", err)
+	}
+	// Retry on connection/network errors.
+	if errors.Is(err, io.EOF) {
+		return true, fmt.Errorf("retrying for: %w", err)
 	}
 	return false, err
 }
diff --git a/vendor/knative.dev/test-infra/scripts/update-test-infra.sh b/vendor/knative.dev/test-infra/scripts/update-test-infra.sh
new file mode 100644
index 000000000..d443f3c20
--- /dev/null
+++ b/vendor/knative.dev/test-infra/scripts/update-test-infra.sh
@@ -0,0 +1,91 @@
+#!/usr/bin/env bash
+
+# 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
+#
+#     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.
+
+set -e
+
+# This script updates test-infra scripts in-repo.
+# Run it to update (usually from hack/update-deps.sh) the current scripts.
+# Scripts are installed to REPO_ROOT/scripts/test-infra
+
+# The following arguments are accepted:
+# --update
+#  Do the update
+# --ref X
+#  Defines which ref (branch, tag, commit) of test-infra to get scripts from; defaults to master
+# --first-time
+#  Run this script from your repo root directory to install scripts for the first time
+#  Will also sed -i non-vendor scripts in the current repo to point to new path
+# TODO: --verify
+#  Verify the contents of scripts/test-infra match the contents from commit sha in scripts/test-infra/COMMIT
+#  One can verify manually by running the script with '--ref $(cat scripts/test-infra/COMMIT)' and ensuring no files are staged
+
+declare -i FIRST_TIME_SETUP=0
+declare -i DO_UPDATE=0
+declare SCRIPTS_REF=master
+
+while [[ $# -ne 0 ]]; do
+  parameter="$1"
+  case ${parameter} in
+    --ref)
+      shift
+      SCRIPTS_REF="$1"
+      ;;
+    --first-time)
+      FIRST_TIME_SETUP=1
+      ;;
+    --update)
+      DO_UPDATE=1
+      ;;
+    *)
+      echo "unknown option ${parameter}"
+      exit 1
+      ;;
+  esac
+  shift
+done
+
+function do_read_tree() {
+    mkdir -p scripts/test-infra
+    git read-tree --prefix=scripts/test-infra -u "test-infra/${SCRIPTS_REF}:scripts"
+    git show-ref -s -- "refs/remotes/test-infra/${SCRIPTS_REF}" > scripts/test-infra/COMMIT
+    git add scripts/test-infra/COMMIT
+    echo "test-infra scripts installed to scripts/test-infra from branch ${SCRIPTS_REF}"
+}
+
+function run() {
+  if (( FIRST_TIME_SETUP )); then
+    if [[ ! -d .git ]]; then
+      echo "I don't believe you are in a repo root; exiting"
+      exit 5
+    fi
+    git remote add test-infra https://github.com/knative/test-infra.git || echo "test-infra remote already set; not changing"
+    git fetch test-infra "${SCRIPTS_REF}"
+    do_read_tree
+    echo "Attempting to point all scripts to use this new path"
+    grep -RiIl vendor/knative.dev/test-infra | grep -v ^vendor | grep -v ^scripts/test-infra | xargs sed -i 's+vendor/knative.dev/test-infra/scripts+scripts/test-infra+'
+  elif (( DO_UPDATE )); then
+    pushd "$(dirname "${BASH_SOURCE[0]}")/../.."
+    trap popd EXIT
+
+    git remote add test-infra https://github.com/knative/test-infra.git || true
+    git fetch test-infra "${SCRIPTS_REF}"
+    git rm -fr scripts/test-infra
+    rm -fR scripts/test-infra
+    do_read_tree
+  fi
+}
+
+run