Repository: knative/serving
Pull request #8513 (2020-06-29 16:06:41)

Update allowed components to reflect other things that run in knative-serving

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

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

- ~~[config/OWNERS](https://github.com/knative/serving/blob/master/config/OWNERS)~~ [mattmoor]
- ~~[pkg/leaderelection/OWNERS](https://github.com/knative/serving/blob/master/pkg/leaderelection/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":[]} -->

Patch:
diff --git a/cmd/activator/main.go b/cmd/activator/main.go
index 3856fb708..85bdd5394 100644
--- a/cmd/activator/main.go
+++ b/cmd/activator/main.go
@@ -85,27 +85,21 @@ var (
 	kubeconfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
 )
 
-func statReporter(statSink *websocket.ManagedConnection, stopCh <-chan struct{},
-	statChan <-chan []asmetrics.StatMessage, logger *zap.SugaredLogger) {
-	for {
-		select {
-		case sm := <-statChan:
-			go func() {
-				for _, msg := range sm {
-					b, err := json.Marshal(msg)
-					if err != nil {
-						logger.Errorw("Error while marshaling stat", zap.Error(err))
-					}
-					if err := statSink.SendRaw(gorillawebsocket.TextMessage, b); err != nil {
-						logger.Errorw("Error while sending stat", zap.Error(err))
-					}
+func statReporter(statSink *websocket.ManagedConnection, statChan <-chan []asmetrics.StatMessage,
+	logger *zap.SugaredLogger) {
+	for sm := range statChan {
+		go func(msgs []asmetrics.StatMessage) {
+			for _, msg := range msgs {
+				b, err := json.Marshal(msg)
+				if err != nil {
+					logger.Errorw("Error while marshaling stat", zap.Error(err))
+					continue
 				}
-			}()
-		case <-stopCh:
-			// It's a sending connection, so no drainage required.
-			statSink.Shutdown()
-			return
-		}
+				if err := statSink.SendRaw(gorillawebsocket.TextMessage, b); err != nil {
+					logger.Errorw("Error while sending stat", zap.Error(err))
+				}
+			}
+		}(sm)
 	}
 }
 
@@ -204,7 +198,8 @@ func main() {
 	autoscalerEndpoint := fmt.Sprintf("ws://%s.%s.svc.%s%s", "autoscaler", system.Namespace(), pkgnet.GetClusterDomainName(), autoscalerPort)
 	logger.Info("Connecting to Autoscaler at ", autoscalerEndpoint)
 	statSink := websocket.NewDurableSendingConnection(autoscalerEndpoint, logger)
-	go statReporter(statSink, ctx.Done(), statCh, logger)
+	defer statSink.Shutdown()
+	go statReporter(statSink, statCh, logger)
 
 	// Create and run our concurrency reporter
 	cr := activatorhandler.NewConcurrencyReporter(ctx, env.PodName, reqCh, statCh)
diff --git a/config/core/configmaps/leader-election.yaml b/config/core/configmaps/leader-election.yaml
index 2ce6adbf9..33d6df515 100644
--- a/config/core/configmaps/leader-election.yaml
+++ b/config/core/configmaps/leader-election.yaml
@@ -20,7 +20,7 @@ metadata:
   labels:
     serving.knative.dev/release: devel
   annotations:
-    knative.dev/example-checksum: 2a1bda27
+    knative.dev/example-checksum: 83d5c126
 data:
   _example: |
     ################################
@@ -57,10 +57,4 @@ data:
     retryPeriod: "2s"
     # enabledComponents is a comma-delimited list of component names for which
     # leader election is enabled. Valid values are:
-    #
-    # - controller
-    # - hpaautoscaler
-    # - certcontroller
-    # - istiocontroller
-    # - nscontroller
-    enabledComponents: "controller,hpaautoscaler,certcontroller,istiocontroller,nscontroller,webhook"
+    enabledComponents: "controller,contour-ingress-controller,hpaautoscaler,certcontroller,istiocontroller,net-http01,nscontroller,webhook"
diff --git a/go.mod b/go.mod
index 5cd9bd5c6..4922df394 100644
--- a/go.mod
+++ b/go.mod
@@ -38,10 +38,10 @@ require (
 	k8s.io/client-go v11.0.1-0.20190805182717-6502b5e7b1b5+incompatible
 	k8s.io/code-generator v0.18.0
 	k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29
-	knative.dev/caching v0.0.0-20200606210318-787aec80f71c
-	knative.dev/networking v0.0.0-20200625171028-5efff1e07309
-	knative.dev/pkg v0.0.0-20200626022628-f1ee372577e1
-	knative.dev/test-infra v0.0.0-20200625195728-62651036b559
+	knative.dev/caching v0.0.0-20200630172829-a78409990d76
+	knative.dev/networking v0.0.0-20200630174930-fdf40c82309e
+	knative.dev/pkg v0.0.0-20200630170034-2c1a029eb97f
+	knative.dev/test-infra v0.0.0-20200630141629-15f40fe97047
 )
 
 replace (
diff --git a/go.sum b/go.sum
index 415a188fd..3c1345615 100644
--- a/go.sum
+++ b/go.sum
@@ -1373,12 +1373,12 @@ k8s.io/utils v0.0.0-20200124190032-861946025e34 h1:HjlUD6M0K3P8nRXmr2B9o4F9dUy9T
 k8s.io/utils v0.0.0-20200124190032-861946025e34/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
 knative.dev/caching v0.0.0-20190719140829-2032732871ff/go.mod h1:dHXFU6CGlLlbzaWc32g80cR92iuBSpsslDNBWI8C7eg=
 knative.dev/caching v0.0.0-20200116200605-67bca2c83dfa/go.mod h1:dHXFU6CGlLlbzaWc32g80cR92iuBSpsslDNBWI8C7eg=
-knative.dev/caching v0.0.0-20200606210318-787aec80f71c h1:4w9QOkxY1VNH/82LIefx8u3OyLSBRp8mMCojVRYM+r8=
-knative.dev/caching v0.0.0-20200606210318-787aec80f71c/go.mod h1:rWD+0zSqcE7L//jJyt866xtXsOi9hCdB0FoD4w2Rygg=
+knative.dev/caching v0.0.0-20200630172829-a78409990d76 h1:KzMMAumVi/ucxDepwF5zR+9YQOkpqbOQjfK9bH8XSNE=
+knative.dev/caching v0.0.0-20200630172829-a78409990d76/go.mod h1:iyrTBBqVs1H+bmj+mqyGEgNiLBzuGsY1LslIayGy0OE=
 knative.dev/eventing-contrib v0.6.1-0.20190723221543-5ce18048c08b/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
 knative.dev/eventing-contrib v0.11.2/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
-knative.dev/networking v0.0.0-20200625171028-5efff1e07309 h1:x0X9Eo1POQQxj3as2QbqqgJEgrWSWP5lNeffxmIjzis=
-knative.dev/networking v0.0.0-20200625171028-5efff1e07309/go.mod h1:EE40BtP1MSk2rCmgy5t2Gsui2a2kWkv5l2QbfKhK4a0=
+knative.dev/networking v0.0.0-20200630174930-fdf40c82309e h1:qx7ouFyjqI6Oy0ymSgK7lNuU2Jg+iVqmab0zWt4m2HM=
+knative.dev/networking v0.0.0-20200630174930-fdf40c82309e/go.mod h1:d8q9WJdg+XSBD2dtm/KBmiKyP2MfJWYhyF0Lh8neMf0=
 knative.dev/pkg v0.0.0-20191101194912-56c2594e4f11/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
 knative.dev/pkg v0.0.0-20191111150521-6d806b998379/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
 knative.dev/pkg v0.0.0-20200207155214-fef852970f43/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
@@ -1386,15 +1386,13 @@ knative.dev/pkg v0.0.0-20200428194351-90fc61bae7f7/go.mod h1:o+e8OVEJKIuvXPsGVPI
 knative.dev/pkg v0.0.0-20200505191044-3da93ebb24c2/go.mod h1:Q6sL35DdGs8hIQZKdaCXJGgY8f90BmNBKSb8z6d/BTM=
 knative.dev/pkg v0.0.0-20200509234445-b52862b1b3ea/go.mod h1:VR1a7CeToIFubOhOlipZn48szS34plPRwxJaOC6Rf9g=
 knative.dev/pkg v0.0.0-20200515002500-16d7b963416f/go.mod h1:tMOHGbxtRz8zYFGEGpV/bpoTEM1o89MwYFC4YJXl3GY=
-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-20200603222317-b79e4a24ca50/go.mod h1:8IfPj/lpuKHHg82xZCl2wuFZ3BM96To72sN1W8T9wjQ=
 knative.dev/pkg v0.0.0-20200611204322-2ddcfef739a2/go.mod h1:rA+FklsrVahwF4a+D63NyHJlzDoAFH81K4J5CYuE3bA=
-knative.dev/pkg v0.0.0-20200623204627-e0a0d63a9e86/go.mod h1:DquzK0hsLDcg2q63Sn+CngAyRwv4cKMpt5F19YzBfb0=
-knative.dev/pkg v0.0.0-20200624210428-eb05e8dd5b5b h1:NFi5BVmuZ81bWigwn3s/NNaf+GeeOGtNlADAbXPYg+E=
-knative.dev/pkg v0.0.0-20200624210428-eb05e8dd5b5b/go.mod h1:DquzK0hsLDcg2q63Sn+CngAyRwv4cKMpt5F19YzBfb0=
-knative.dev/pkg v0.0.0-20200626022628-f1ee372577e1 h1:4Fi/WnI7uwbop8gjebSfJbPdJd20XWt66ugzEYunlUA=
-knative.dev/pkg v0.0.0-20200626022628-f1ee372577e1/go.mod h1:DquzK0hsLDcg2q63Sn+CngAyRwv4cKMpt5F19YzBfb0=
+knative.dev/pkg v0.0.0-20200629195729-e16ecb840d88 h1:k+CKhEFTrpNFOvnGlcIK3tGjq1Spw7b9zJVASy/BbP8=
+knative.dev/pkg v0.0.0-20200629195729-e16ecb840d88/go.mod h1:DquzK0hsLDcg2q63Sn+CngAyRwv4cKMpt5F19YzBfb0=
+knative.dev/pkg v0.0.0-20200630170034-2c1a029eb97f h1:Y3fmAKacNnQvPBK6Onnb0uC87roq/ypS8rYekfIdci8=
+knative.dev/pkg v0.0.0-20200630170034-2c1a029eb97f/go.mod h1:7T15JzvjKXWnvIKcohz4brrsVq8jvwAcJwWY9xigAc0=
 knative.dev/sample-controller v0.0.0-20200510050845-bf7c19498b7e/go.mod h1:D2ZDLrR9Dq9LiiVN7TatzI7WMcEPgk1MHbbhgBKE6W8=
 knative.dev/test-infra v0.0.0-20200407185800-1b88cb3b45a5/go.mod h1:xcdUkMJrLlBswIZqL5zCuBFOC22WIPMQoVX1L35i0vQ=
 knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55/go.mod h1:WqF1Azka+FxPZ20keR2zCNtiQA1MP9ZB4BH4HuI+SIU=
@@ -1402,14 +1400,13 @@ knative.dev/test-infra v0.0.0-20200508015845-8d7d46a46176/go.mod h1:aMif0KXL4g19
 knative.dev/test-infra v0.0.0-20200509000045-c7114387eed5/go.mod h1:aMif0KXL4g19YCYwsy4Ocjjz5xgPlseYV+B95Oo4JGE=
 knative.dev/test-infra v0.0.0-20200513011557-d03429a76034/go.mod h1:aMif0KXL4g19YCYwsy4Ocjjz5xgPlseYV+B95Oo4JGE=
 knative.dev/test-infra v0.0.0-20200519015156-82551620b0a9/go.mod h1:A5b2OAXTOeHT3hHhVQm3dmtbuWvIDP7qzgtqxA3/2pE=
-knative.dev/test-infra v0.0.0-20200519161858-554a95a37986/go.mod h1:LeNa1Wvn47efeQUkpkn3XG7Fx9Ga+rhAP13SZyjaEGg=
 knative.dev/test-infra v0.0.0-20200522180958-6a0a9b9d893a/go.mod h1:n9eQkzmSNj8BiqNFl1lzoz68D09uMeJfyOjc132Gbik=
 knative.dev/test-infra v0.0.0-20200606045118-14ebc4a42974/go.mod h1://I6IZIF0QDgs5wotU243ZZ5cTpm6/GthayjUenBBc0=
 knative.dev/test-infra v0.0.0-20200617235125-6382dba95484/go.mod h1:+BfrTJpc++rH30gX/C0QY6NT2eYVzycll52uw6CrQnc=
-knative.dev/test-infra v0.0.0-20200624184627-7eae2aae38e4 h1:eB6B7ER/QPKNC2bBtkWmwzcGBp9f0/DQhRdNvxxuEt8=
-knative.dev/test-infra v0.0.0-20200624184627-7eae2aae38e4/go.mod h1:6w3uf0+KfozLkrysBm3QqONrEMC6Uo/oEgS2BCdHJXo=
-knative.dev/test-infra v0.0.0-20200625195728-62651036b559 h1:UnUP8ygaXWLi3LWSpoOvhLcHbOE/yf2De57/aBZIzTQ=
-knative.dev/test-infra v0.0.0-20200625195728-62651036b559/go.mod h1:HZw00KOJoOIzWOSF6tn+0jfJZHpXRCIZkHooc+hh7yg=
+knative.dev/test-infra v0.0.0-20200630000129-4b3a8597df67 h1:DStLV/qi8zvE/9qlunVXx9xyGMaA9THJybHu3t45GC8=
+knative.dev/test-infra v0.0.0-20200630000129-4b3a8597df67/go.mod h1:30tMsI1VXrG2m4ut7CFZbLg1VbcRsslPfGU+GWILm6E=
+knative.dev/test-infra v0.0.0-20200630141629-15f40fe97047 h1:MLcIL0EGkvxRIXcrbPfBRKDVY4wNLVsNbilnZOfHmdk=
+knative.dev/test-infra v0.0.0-20200630141629-15f40fe97047/go.mod h1:30tMsI1VXrG2m4ut7CFZbLg1VbcRsslPfGU+GWILm6E=
 modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
diff --git a/hack/generate-yamls.sh b/hack/generate-yamls.sh
index 9ff81b8f3..fc92d6394 100755
--- a/hack/generate-yamls.sh
+++ b/hack/generate-yamls.sh
@@ -56,7 +56,6 @@ readonly SERVING_CRD_YAML=${YAML_OUTPUT_DIR}/serving-crds.yaml
 readonly SERVING_NSCERT_YAML=${YAML_OUTPUT_DIR}/serving-nscert.yaml
 readonly SERVING_UPGRADE_YAML=${YAML_OUTPUT_DIR}/serving-upgrade.yaml
 
-readonly MONITORING_FILES=${YAML_OUTPUT_DIR}/monitoring.lst
 readonly MONITORING_YAML=${YAML_OUTPUT_DIR}/monitoring.yaml
 readonly MONITORING_CORE_YAML=${YAML_OUTPUT_DIR}/monitoring-core.yaml
 readonly MONITORING_METRIC_PROMETHEUS_YAML=${YAML_OUTPUT_DIR}/monitoring-metrics-prometheus.yaml
@@ -68,7 +67,7 @@ readonly MONITORING_LOG_ELASTICSEARCH_YAML=${YAML_OUTPUT_DIR}/monitoring-logs-el
 
 declare -A CONSOLIDATED_ARTIFACTS
 CONSOLIDATED_ARTIFACTS=(
-  ["${MONITORING_FILES}"]="${MONITORING_LOG_ELASTICSEARCH_YAML} ${MONITORING_METRIC_PROMETHEUS_YAML} ${MONITORING_TRACE_ZIPKIN_YAML}"
+  ["${MONITORING_YAML}"]="${MONITORING_LOG_ELASTICSEARCH_YAML} ${MONITORING_METRIC_PROMETHEUS_YAML} ${MONITORING_TRACE_ZIPKIN_YAML}"
   ["${SERVING_UPGRADE_YAML}"]="${SERVING_STORAGE_VERSION_MIGRATE_YAML}"
 )
 readonly CONSOLIDATED_ARTIFACTS
@@ -140,11 +139,6 @@ done
 ko resolve ${KO_YAML_FLAGS} -R -f config/monitoring/100-namespace.yaml \
     | "${LABEL_YAML_CMD[@]}" > "${MONITORING_CORE_YAML}"
 
-# Use ko to concatenate them all together.
-ko resolve ${KO_YAML_FLAGS} -R -f config/monitoring/100-namespace.yaml \
-    $(sed "s/^/-f /" < "${MONITORING_FILES}") \
-    | "${LABEL_YAML_CMD[@]}" > "${MONITORING_YAML}"
-
 # Traces via Jaeger when ElasticSearch is installed
 ko resolve ${KO_YAML_FLAGS} -R -f config/monitoring/tracing/jaeger/elasticsearch -f config/monitoring/tracing/jaeger/105-zipkin-service.yaml | "${LABEL_YAML_CMD[@]}" > "${MONITORING_TRACE_JAEGER_YAML}"
 
@@ -163,7 +157,6 @@ ${SERVING_UPGRADE_YAML}
 ${SERVING_HPA_YAML}
 ${SERVING_CRD_YAML}
 ${SERVING_NSCERT_YAML}
-${MONITORING_FILES}
 ${MONITORING_YAML}
 ${MONITORING_CORE_YAML}
 ${MONITORING_METRIC_PROMETHEUS_YAML}
diff --git a/pkg/apis/serving/k8s_validation.go b/pkg/apis/serving/k8s_validation.go
index 1aafa7716..ab5797c66 100644
--- a/pkg/apis/serving/k8s_validation.go
+++ b/pkg/apis/serving/k8s_validation.go
@@ -73,7 +73,7 @@ var (
 	)
 )
 
-func ValidateVolumes(vs []corev1.Volume) (sets.String, *apis.FieldError) {
+func ValidateVolumes(vs []corev1.Volume, mountedVolumes sets.String) (sets.String, *apis.FieldError) {
 	volumes := sets.NewString()
 	var errs *apis.FieldError
 	for i, volume := range vs {
@@ -83,6 +83,12 @@ func ValidateVolumes(vs []corev1.Volume) (sets.String, *apis.FieldError) {
 				Paths:   []string{"name"},
 			}).ViaIndex(i))
 		}
+		if !mountedVolumes.Has(volume.Name) {
+			errs = errs.Also((&apis.FieldError{
+				Message: fmt.Sprintf("volume with name %q not mounted", volume.Name),
+				Paths:   []string{"name"},
+			}).ViaIndex(i))
+		}
 		errs = errs.Also(validateVolume(volume).ViaIndex(i))
 		volumes.Insert(volume.Name)
 	}
@@ -259,7 +265,7 @@ func ValidatePodSpec(ctx context.Context, ps corev1.PodSpec) *apis.FieldError {
 
 	errs := apis.CheckDisallowedFields(ps, *PodSpecMask(&ps))
 
-	volumes, err := ValidateVolumes(ps.Volumes)
+	volumes, err := ValidateVolumes(ps.Volumes, AllMountedVolumes(ps.Containers))
 	if err != nil {
 		errs = errs.Also(err.ViaField("volumes"))
 	}
@@ -302,6 +308,16 @@ func validateContainers(ctx context.Context, containers []corev1.Container, volu
 	return errs
 }
 
+func AllMountedVolumes(containers []corev1.Container) sets.String {
+	volumeNames := sets.NewString()
+	for _, c := range containers {
+		for _, vm := range c.VolumeMounts {
+			volumeNames.Insert(vm.Name)
+		}
+	}
+	return volumeNames
+}
+
 // validateContainersPorts validates port when specified multiple containers
 func validateContainersPorts(containers []corev1.Container) *apis.FieldError {
 	var count int
@@ -462,13 +478,6 @@ func validateVolumeMounts(mounts []corev1.VolumeMount, volumes sets.String) *api
 		}
 
 	}
-
-	if missing := volumes.Difference(seenName); missing.Len() > 0 {
-		errs = errs.Also(&apis.FieldError{
-			Message: fmt.Sprintf("volumes not mounted: %v", missing.List()),
-			Paths:   []string{apis.CurrentField},
-		})
-	}
 	return errs
 }
 
diff --git a/pkg/apis/serving/v1alpha1/revision_validation.go b/pkg/apis/serving/v1alpha1/revision_validation.go
index bec47a86b..8cf0e4884 100644
--- a/pkg/apis/serving/v1alpha1/revision_validation.go
+++ b/pkg/apis/serving/v1alpha1/revision_validation.go
@@ -110,7 +110,7 @@ func (rs *RevisionSpec) Validate(ctx context.Context) *apis.FieldError {
 	case len(rs.PodSpec.Containers) > 0:
 		errs = errs.Also(rs.RevisionSpec.Validate(ctx))
 	case rs.DeprecatedContainer != nil:
-		volumes, err := serving.ValidateVolumes(rs.Volumes)
+		volumes, err := serving.ValidateVolumes(rs.Volumes, serving.AllMountedVolumes(append(rs.PodSpec.Containers, *rs.DeprecatedContainer)))
 		if err != nil {
 			errs = errs.Also(err.ViaField("volumes"))
 		}
diff --git a/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/controller.go b/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/controller.go
index 3b4632e18..2002d3e3a 100644
--- a/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/controller.go
+++ b/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/controller.go
@@ -102,6 +102,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.AgentName != "" {
 			agentName = opts.AgentName
 		}
+		if opts.SkipStatusUpdates {
+			rec.skipStatusUpdates = true
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/reconciler.go b/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/reconciler.go
index dd7d091a7..c1538d9c7 100644
--- a/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/reconciler.go
+++ b/pkg/client/injection/reconciler/autoscaling/v1alpha1/metric/reconciler.go
@@ -108,6 +108,10 @@ type reconcilerImpl struct {
 
 	// finalizerName is the name of the finalizer to reconcile.
 	finalizerName string
+
+	// skipStatusUpdates configures whether or not this reconciler automatically updates
+	// the status of the reconciled resource.
+	skipStatusUpdates bool
 }
 
 // Check that our Reconciler implements controller.Reconciler
@@ -258,20 +262,22 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
 	}
 
-	// Synchronize the status.
-	if equality.Semantic.DeepEqual(original.Status, resource.Status) {
-		// If we didn't change anything then don't call updateStatus.
-		// This is important because the copy we loaded from the injectionInformer's
-		// cache may be stale and we don't want to overwrite a prior update
-		// to status with this stale state.
-	} else if !isLeader {
-		logger.Warn("Saw status changes when we aren't the leader!")
-		// TODO: Consider logging the diff at Debug?
-	} else if err = r.updateStatus(original, resource); err != nil {
-		logger.Warnw("Failed to update resource status", zap.Error(err))
-		r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed",
-			"Failed to update status for %q: %v", resource.Name, err)
-		return err
+	if !r.skipStatusUpdates {
+		// Synchronize the status.
+		if equality.Semantic.DeepEqual(original.Status, resource.Status) {
+			// If we didn't change anything then don't call updateStatus.
+			// This is important because the copy we loaded from the injectionInformer's
+			// cache may be stale and we don't want to overwrite a prior update
+			// to status with this stale state.
+		} else if !isLeader {
+			logger.Warn("Saw status changes when we aren't the leader!")
+			// TODO: Consider logging the diff at Debug?
+		} else if err = r.updateStatus(original, resource); err != nil {
+			logger.Warnw("Failed to update resource status", zap.Error(err))
+			r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed",
+				"Failed to update status for %q: %v", resource.Name, err)
+			return err
+		}
 	}
 
 	// Report the reconciler event, if any.
diff --git a/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/controller.go b/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/controller.go
index 4993e2243..f51a920b2 100644
--- a/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/controller.go
+++ b/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/controller.go
@@ -106,6 +106,9 @@ func NewImpl(ctx context.Context, r Interface, classValue string, optionsFns ...
 		if opts.AgentName != "" {
 			agentName = opts.AgentName
 		}
+		if opts.SkipStatusUpdates {
+			rec.skipStatusUpdates = true
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/reconciler.go b/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/reconciler.go
index 96a1423bf..9e2dcaa5c 100644
--- a/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/reconciler.go
+++ b/pkg/client/injection/reconciler/autoscaling/v1alpha1/podautoscaler/reconciler.go
@@ -109,6 +109,10 @@ type reconcilerImpl struct {
 	// finalizerName is the name of the finalizer to reconcile.
 	finalizerName string
 
+	// skipStatusUpdates configures whether or not this reconciler automatically updates
+	// the status of the reconciled resource.
+	skipStatusUpdates bool
+
 	// classValue is the resource annotation[autoscaling.knative.dev/class] instance value this reconciler instance filters on.
 	classValue string
 }
@@ -269,20 +273,22 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
 	}
 
-	// Synchronize the status.
-	if equality.Semantic.DeepEqual(original.Status, resource.Status) {
-		// If we didn't change anything then don't call updateStatus.
-		// This is important because the copy we loaded from the injectionInformer's
-		// cache may be stale and we don't want to overwrite a prior update
-		// to status with this stale state.
-	} else if !isLeader {
-		logger.Warn("Saw status changes when we aren't the leader!")
-		// TODO: Consider logging the diff at Debug?
-	} else if err = r.updateStatus(original, resource); err != nil {
-		logger.Warnw("Failed to update resource status", zap.Error(err))
-		r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed",
-			"Failed to update status for %q: %v", resource.Name, err)
-		return err
+	if !r.skipStatusUpdates {
+		// Synchronize the status.
+		if equality.Semantic.DeepEqual(original.Status, resource.Status) {
+			// If we didn't change anything then don't call updateStatus.
+			// This is important because the copy we loaded from the injectionInformer's
+			// cache may be stale and we don't want to overwrite a prior update
+			// to status with this stale state.
+		} else if !isLeader {
+			logger.Warn("Saw status changes when we aren't the leader!")
+			// TODO: Consider logging the diff at Debug?
+		} else if err = r.updateStatus(original, resource); err != nil {
+			logger.Warnw("Failed to update resource status", zap.Error(err))
+			r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed",
+				"Failed to update status for %q: %v", resource.Name, err)
+			return err
+		}
 	}
 
 	// Report the reconciler event, if any.
diff --git a/pkg/client/injection/reconciler/serving/v1/configuration/controller.go b/pkg/client/injection/reconciler/serving/v1/configuration/controller.go
index f1eadbd3d..59ae09fbf 100644
--- a/pkg/client/injection/reconciler/serving/v1/configuration/controller.go
+++ b/pkg/client/injection/reconciler/serving/v1/configuration/controller.go
@@ -102,6 +102,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.AgentName != "" {
 			agentName = opts.AgentName
 		}
+		if opts.SkipStatusUpdates {
+			rec.skipStatusUpdates = true
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go b/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go
index 88d52aa5e..184fc66bf 100644
--- a/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/configuration/reconciler.go
@@ -108,6 +108,10 @@ type reconcilerImpl struct {
 
 	// finalizerName is the name of the finalizer to reconcile.
 	finalizerName string
+
+	// skipStatusUpdates configures whether or not this reconciler automatically updates
+	// the status of the reconciled resource.
+	skipStatusUpdates bool
 }
 
 // Check that our Reconciler implements controller.Reconciler
@@ -258,20 +262,22 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
 	}
 
-	// Synchronize the status.
-	if equality.Semantic.DeepEqual(original.Status, resource.Status) {
-		// If we didn't change anything then don't call updateStatus.
-		// This is important because the copy we loaded from the injectionInformer's
-		// cache may be stale and we don't want to overwrite a prior update
-		// to status with this stale state.
-	} else if !isLeader {
-		logger.Warn("Saw status changes when we aren't the leader!")
-		// TODO: Consider logging the diff at Debug?
-	} else if err = r.updateStatus(original, resource); err != nil {
-		logger.Warnw("Failed to update resource status", zap.Error(err))
-		r.Recorder.Eventf(resource, corev1.EventTypeWarning, "UpdateFailed",
-			"Failed to update status for %q: %v", resource.Name, err)
-		return err
+	if !r.skipStatusUpdates {
+		// Synchronize the status.
+		if equality.Semantic.DeepEqual(original.Status, resource.Status) {
+			// If we didn't change anything then don't call updateStatus.
+			// This is important because the copy we loaded from the injectionInformer's
+			// cache may be stale and we don't want to overwrite a prior update
+			// to status with this stale state.
+		} else if !isLeader {
+			logger.Warn("Saw status changes when we aren't the leader!")
+			// TODO: Consider logging the diff at Debug?
+		} else if err = r.updateStatus(original, resource); err != nil {
+			logger.Warnw("Failed to update resource status", zap.Error(err))
+			r.Recorder.Eventf(resource, corev1.EventTypeWarning, "UpdateFailed",
+				"Failed to update status for %q: %v", resource.Name, err)
+			return err
+		}
 	}
 
 	// Report the reconciler event, if any.
diff --git a/pkg/client/injection/reconciler/serving/v1/revision/controller.go b/pkg/client/injection/reconciler/serving/v1/revision/controller.go
index f75ab8e2b..a425471fb 100644
--- a/pkg/client/injection/reconciler/serving/v1/revision/controller.go
+++ b/pkg/client/injection/reconciler/serving/v1/revision/controller.go
@@ -102,6 +102,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.AgentName != "" {
 			agentName = opts.AgentName
 		}
+		if opts.SkipStatusUpdates {
+			rec.skipStatusUpdates = true
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go b/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
index 73805e893..6121a3c87 100644
--- a/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/revision/reconciler.go
@@ -108,6 +108,10 @@ type reconcilerImpl struct {
 
 	// finalizerName is the name of the finalizer to reconcile.
 	finalizerName string
+
+	// skipStatusUpdates configures whether or not this reconciler automatically updates
+	// the status of the reconciled resource.
+	skipStatusUpdates bool
 }
 
 // Check that our Reconciler implements controller.Reconciler
@@ -258,20 +262,22 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
 	}
 
-	// Synchronize the status.
-	if equality.Semantic.DeepEqual(original.Status, resource.Status) {
-		// If we didn't change anything then don't call updateStatus.
-		// This is important because the copy we loaded from the injectionInformer's
-		// cache may be stale and we don't want to overwrite a prior update
-		// to status with this stale state.
-	} else if !isLeader {
-		logger.Warn("Saw status changes when we aren't the leader!")
-		// TODO: Consider logging the diff at Debug?
-	} else if err = r.updateStatus(original, resource); err != nil {
-		logger.Warnw("Failed to update resource status", zap.Error(err))
-		r.Recorder.Eventf(resource, corev1.EventTypeWarning, "UpdateFailed",
-			"Failed to update status for %q: %v", resource.Name, err)
-		return err
+	if !r.skipStatusUpdates {
+		// Synchronize the status.
+		if equality.Semantic.DeepEqual(original.Status, resource.Status) {
+			// If we didn't change anything then don't call updateStatus.
+			// This is important because the copy we loaded from the injectionInformer's
+			// cache may be stale and we don't want to overwrite a prior update
+			// to status with this stale state.
+		} else if !isLeader {
+			logger.Warn("Saw status changes when we aren't the leader!")
+			// TODO: Consider logging the diff at Debug?
+		} else if err = r.updateStatus(original, resource); err != nil {
+			logger.Warnw("Failed to update resource status", zap.Error(err))
+			r.Recorder.Eventf(resource, corev1.EventTypeWarning, "UpdateFailed",
+				"Failed to update status for %q: %v", resource.Name, err)
+			return err
+		}
 	}
 
 	// Report the reconciler event, if any.
diff --git a/pkg/client/injection/reconciler/serving/v1/route/controller.go b/pkg/client/injection/reconciler/serving/v1/route/controller.go
index e567a6c8c..57549f136 100644
--- a/pkg/client/injection/reconciler/serving/v1/route/controller.go
+++ b/pkg/client/injection/reconciler/serving/v1/route/controller.go
@@ -102,6 +102,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.AgentName != "" {
 			agentName = opts.AgentName
 		}
+		if opts.SkipStatusUpdates {
+			rec.skipStatusUpdates = true
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/serving/v1/route/reconciler.go b/pkg/client/injection/reconciler/serving/v1/route/reconciler.go
index 17da3d796..a3713fa49 100644
--- a/pkg/client/injection/reconciler/serving/v1/route/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/route/reconciler.go
@@ -108,6 +108,10 @@ type reconcilerImpl struct {
 
 	// finalizerName is the name of the finalizer to reconcile.
 	finalizerName string
+
+	// skipStatusUpdates configures whether or not this reconciler automatically updates
+	// the status of the reconciled resource.
+	skipStatusUpdates bool
 }
 
 // Check that our Reconciler implements controller.Reconciler
@@ -258,20 +262,22 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
 	}
 
-	// Synchronize the status.
-	if equality.Semantic.DeepEqual(original.Status, resource.Status) {
-		// If we didn't change anything then don't call updateStatus.
-		// This is important because the copy we loaded from the injectionInformer's
-		// cache may be stale and we don't want to overwrite a prior update
-		// to status with this stale state.
-	} else if !isLeader {
-		logger.Warn("Saw status changes when we aren't the leader!")
-		// TODO: Consider logging the diff at Debug?
-	} else if err = r.updateStatus(original, resource); err != nil {
-		logger.Warnw("Failed to update resource status", zap.Error(err))
-		r.Recorder.Eventf(resource, corev1.EventTypeWarning, "UpdateFailed",
-			"Failed to update status for %q: %v", resource.Name, err)
-		return err
+	if !r.skipStatusUpdates {
+		// Synchronize the status.
+		if equality.Semantic.DeepEqual(original.Status, resource.Status) {
+			// If we didn't change anything then don't call updateStatus.
+			// This is important because the copy we loaded from the injectionInformer's
+			// cache may be stale and we don't want to overwrite a prior update
+			// to status with this stale state.
+		} else if !isLeader {
+			logger.Warn("Saw status changes when we aren't the leader!")
+			// TODO: Consider logging the diff at Debug?
+		} else if err = r.updateStatus(original, resource); err != nil {
+			logger.Warnw("Failed to update resource status", zap.Error(err))
+			r.Recorder.Eventf(resource, corev1.EventTypeWarning, "UpdateFailed",
+				"Failed to update status for %q: %v", resource.Name, err)
+			return err
+		}
 	}
 
 	// Report the reconciler event, if any.
diff --git a/pkg/client/injection/reconciler/serving/v1/service/controller.go b/pkg/client/injection/reconciler/serving/v1/service/controller.go
index 858ea780c..683ee3202 100644
--- a/pkg/client/injection/reconciler/serving/v1/service/controller.go
+++ b/pkg/client/injection/reconciler/serving/v1/service/controller.go
@@ -102,6 +102,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.AgentName != "" {
 			agentName = opts.AgentName
 		}
+		if opts.SkipStatusUpdates {
+			rec.skipStatusUpdates = true
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/pkg/client/injection/reconciler/serving/v1/service/reconciler.go b/pkg/client/injection/reconciler/serving/v1/service/reconciler.go
index 9558c6059..f600a9045 100644
--- a/pkg/client/injection/reconciler/serving/v1/service/reconciler.go
+++ b/pkg/client/injection/reconciler/serving/v1/service/reconciler.go
@@ -108,6 +108,10 @@ type reconcilerImpl struct {
 
 	// finalizerName is the name of the finalizer to reconcile.
 	finalizerName string
+
+	// skipStatusUpdates configures whether or not this reconciler automatically updates
+	// the status of the reconciled resource.
+	skipStatusUpdates bool
 }
 
 // Check that our Reconciler implements controller.Reconciler
@@ -258,20 +262,22 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
 	}
 
-	// Synchronize the status.
-	if equality.Semantic.DeepEqual(original.Status, resource.Status) {
-		// If we didn't change anything then don't call updateStatus.
-		// This is important because the copy we loaded from the injectionInformer's
-		// cache may be stale and we don't want to overwrite a prior update
-		// to status with this stale state.
-	} else if !isLeader {
-		logger.Warn("Saw status changes when we aren't the leader!")
-		// TODO: Consider logging the diff at Debug?
-	} else if err = r.updateStatus(original, resource); err != nil {
-		logger.Warnw("Failed to update resource status", zap.Error(err))
-		r.Recorder.Eventf(resource, corev1.EventTypeWarning, "UpdateFailed",
-			"Failed to update status for %q: %v", resource.Name, err)
-		return err
+	if !r.skipStatusUpdates {
+		// Synchronize the status.
+		if equality.Semantic.DeepEqual(original.Status, resource.Status) {
+			// If we didn't change anything then don't call updateStatus.
+			// This is important because the copy we loaded from the injectionInformer's
+			// cache may be stale and we don't want to overwrite a prior update
+			// to status with this stale state.
+		} else if !isLeader {
+			logger.Warn("Saw status changes when we aren't the leader!")
+			// TODO: Consider logging the diff at Debug?
+		} else if err = r.updateStatus(original, resource); err != nil {
+			logger.Warnw("Failed to update resource status", zap.Error(err))
+			r.Recorder.Eventf(resource, corev1.EventTypeWarning, "UpdateFailed",
+				"Failed to update status for %q: %v", resource.Name, err)
+			return err
+		}
 	}
 
 	// Report the reconciler event, if any.
diff --git a/pkg/leaderelection/config.go b/pkg/leaderelection/config.go
index 696e15260..74244d1f6 100644
--- a/pkg/leaderelection/config.go
+++ b/pkg/leaderelection/config.go
@@ -27,9 +27,11 @@ import (
 var (
 	validComponents = sets.NewString(
 		"controller",
+		"contour-ingress-controller",
 		"hpaautoscaler",
 		"certcontroller",
 		"istiocontroller",
+		"net-http01",
 		"nscontroller",
 		"webhook",
 	)
diff --git a/pkg/reconciler/accessor/core/secret.go b/pkg/reconciler/accessor/core/secret.go
index 2213e035f..3a0fa9488 100644
--- a/pkg/reconciler/accessor/core/secret.go
+++ b/pkg/reconciler/accessor/core/secret.go
@@ -26,6 +26,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes"
 	corev1listers "k8s.io/client-go/listers/core/v1"
+
 	"knative.dev/pkg/controller"
 	"knative.dev/pkg/kmeta"
 	kaccessor "knative.dev/serving/pkg/reconciler/accessor"
diff --git a/pkg/reconciler/gc/controller.go b/pkg/reconciler/gc/controller.go
index c0a444528..88fa69276 100644
--- a/pkg/reconciler/gc/controller.go
+++ b/pkg/reconciler/gc/controller.go
@@ -79,6 +79,8 @@ func NewController(
 		return controller.Options{
 			ConfigStore: configStore,
 			AgentName:   controllerAgentName,
+			// The GC reconciler shouldn't mutate the revision's status.
+			SkipStatusUpdates: true,
 		}
 	})
 }
diff --git a/pkg/reconciler/labeler/accessors.go b/pkg/reconciler/labeler/accessors.go
new file mode 100644
index 000000000..be168640f
--- /dev/null
+++ b/pkg/reconciler/labeler/accessors.go
@@ -0,0 +1,103 @@
+/*
+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 labeler
+
+import (
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/apimachinery/pkg/types"
+	"knative.dev/pkg/kmeta"
+
+	"knative.dev/serving/pkg/apis/serving"
+)
+
+// accessor defines an abstraction for manipulating labeled entity
+// (Configuration, Revision) with shared logic.
+type accessor interface {
+	get(ns, name string) (kmeta.Accessor, error)
+	list(ns, name string) ([]kmeta.Accessor, error)
+	patch(ns, name string, pt types.PatchType, p []byte) error
+}
+
+// revision is an implementation of accessor for Revisions
+type revision struct {
+	r *Reconciler
+}
+
+// revision implements accessor
+var _ accessor = (*revision)(nil)
+
+// get implements accessor
+func (r *revision) get(ns, name string) (kmeta.Accessor, error) {
+	return r.r.revisionLister.Revisions(ns).Get(name)
+}
+
+// list implements accessor
+func (r *revision) list(ns, name string) ([]kmeta.Accessor, error) {
+	rl, err := r.r.revisionLister.Revisions(ns).List(labels.SelectorFromSet(labels.Set{
+		serving.RouteLabelKey: name,
+	}))
+	if err != nil {
+		return nil, err
+	}
+	// Need a copy to change types in Go
+	kl := make([]kmeta.Accessor, 0, len(rl))
+	for _, r := range rl {
+		kl = append(kl, r)
+	}
+	return kl, err
+}
+
+// patch implements accessor
+func (r *revision) patch(ns, name string, pt types.PatchType, p []byte) error {
+	_, err := r.r.client.ServingV1().Revisions(ns).Patch(name, pt, p)
+	return err
+}
+
+// configuration is an implementation of accessor for Configurations
+type configuration struct {
+	r *Reconciler
+}
+
+// configuration implements accessor
+var _ accessor = (*configuration)(nil)
+
+// get implements accessor
+func (c *configuration) get(ns, name string) (kmeta.Accessor, error) {
+	return c.r.configurationLister.Configurations(ns).Get(name)
+}
+
+// list implements accessor
+func (c *configuration) list(ns, name string) ([]kmeta.Accessor, error) {
+	rl, err := c.r.configurationLister.Configurations(ns).List(labels.SelectorFromSet(labels.Set{
+		serving.RouteLabelKey: name,
+	}))
+	if err != nil {
+		return nil, err
+	}
+	// Need a copy to change types in Go
+	kl := make([]kmeta.Accessor, 0, len(rl))
+	for _, r := range rl {
+		kl = append(kl, r)
+	}
+	return kl, err
+}
+
+// patch implements accessor
+func (c *configuration) patch(ns, name string, pt types.PatchType, p []byte) error {
+	_, err := c.r.client.ServingV1().Configurations(ns).Patch(name, pt, p)
+	return err
+}
diff --git a/pkg/reconciler/labeler/controller.go b/pkg/reconciler/labeler/controller.go
index f19328b2f..29a4812a0 100644
--- a/pkg/reconciler/labeler/controller.go
+++ b/pkg/reconciler/labeler/controller.go
@@ -55,7 +55,12 @@ func NewController(
 		configurationLister: configInformer.Lister(),
 		revisionLister:      revisionInformer.Lister(),
 	}
-	impl := routereconciler.NewImpl(ctx, c)
+	impl := routereconciler.NewImpl(ctx, c, func(*controller.Impl) controller.Options {
+		return controller.Options{
+			// The labeler shouldn't mutate the route's status.
+			SkipStatusUpdates: true,
+		}
+	})
 
 	logger.Info("Setting up event handlers")
 	routeInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))
diff --git a/pkg/reconciler/labeler/labels.go b/pkg/reconciler/labeler/labels.go
index a6ddbbc96..132b38471 100644
--- a/pkg/reconciler/labeler/labels.go
+++ b/pkg/reconciler/labeler/labels.go
@@ -22,7 +22,6 @@ import (
 	"fmt"
 
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"knative.dev/pkg/kmeta"
@@ -31,14 +30,6 @@ import (
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
 )
 
-// accessor defines an abstraction for manipulating labeled entity
-// (Configuration, Revision) with shared logic.
-type accessor interface {
-	get(ns, name string) (kmeta.Accessor, error)
-	list(ns, name string) ([]kmeta.Accessor, error)
-	patch(ns, name string, pt types.PatchType, p []byte) error
-}
-
 // syncLabels makes sure that the revisions and configurations referenced from
 // a Route are labeled with route labels.
 func (c *Reconciler) syncLabels(ctx context.Context, r *v1.Route) error {
@@ -176,73 +167,3 @@ func setRouteLabel(acc accessor, elt kmeta.Accessor, routeName *string) error {
 
 	return acc.patch(elt.GetNamespace(), elt.GetName(), types.MergePatchType, patch)
 }
-
-// revision is an implementation of accessor for Revisions
-type revision struct {
-	r *Reconciler
-}
-
-// revision implements accessor
-var _ accessor = (*revision)(nil)
-
-// get implements accessor
-func (r *revision) get(ns, name string) (kmeta.Accessor, error) {
-	return r.r.revisionLister.Revisions(ns).Get(name)
-}
-
-// list implements accessor
-func (r *revision) list(ns, name string) ([]kmeta.Accessor, error) {
-	rl, err := r.r.revisionLister.Revisions(ns).List(labels.SelectorFromSet(labels.Set{
-		serving.RouteLabelKey: name,
-	}))
-	if err != nil {
-		return nil, err
-	}
-	// Need a copy to change types in Go
-	kl := make([]kmeta.Accessor, 0, len(rl))
-	for _, r := range rl {
-		kl = append(kl, r)
-	}
-	return kl, err
-}
-
-// patch implements accessor
-func (r *revision) patch(ns, name string, pt types.PatchType, p []byte) error {
-	_, err := r.r.client.ServingV1().Revisions(ns).Patch(name, pt, p)
-	return err
-}
-
-// configuration is an implementation of accessor for Configurations
-type configuration struct {
-	r *Reconciler
-}
-
-// configuration implements accessor
-var _ accessor = (*configuration)(nil)
-
-// get implements accessor
-func (c *configuration) get(ns, name string) (kmeta.Accessor, error) {
-	return c.r.configurationLister.Configurations(ns).Get(name)
-}
-
-// list implements accessor
-func (c *configuration) list(ns, name string) ([]kmeta.Accessor, error) {
-	rl, err := c.r.configurationLister.Configurations(ns).List(labels.SelectorFromSet(labels.Set{
-		serving.RouteLabelKey: name,
-	}))
-	if err != nil {
-		return nil, err
-	}
-	// Need a copy to change types in Go
-	kl := make([]kmeta.Accessor, 0, len(rl))
-	for _, r := range rl {
-		kl = append(kl, r)
-	}
-	return kl, err
-}
-
-// patch implements accessor
-func (c *configuration) patch(ns, name string, pt types.PatchType, p []byte) error {
-	_, err := c.r.client.ServingV1().Configurations(ns).Patch(name, pt, p)
-	return err
-}
diff --git a/third_party/cert-manager-0.12.0/net-certmanager.yaml b/third_party/cert-manager-0.12.0/net-certmanager.yaml
index ea4cbf934..694923017 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: "v20200626-029ac30"
+    serving.knative.dev/release: "v20200629-029ac30"
     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: "v20200626-029ac30"
+    serving.knative.dev/release: "v20200629-029ac30"
 webhooks:
 - admissionReviewVersions:
   - v1beta1
@@ -86,7 +86,7 @@ metadata:
   name: net-certmanager-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200626-029ac30"
+    serving.knative.dev/release: "v20200629-029ac30"
 
 ---
 # Copyright 2020 The Knative Authors
@@ -109,7 +109,7 @@ metadata:
   name: config-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200626-029ac30"
+    serving.knative.dev/release: "v20200629-029ac30"
     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: "v20200626-029ac30"
+    serving.knative.dev/release: "v20200629-029ac30"
     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: "v20200626-029ac30"
+        serving.knative.dev/release: "v20200629-029ac30"
     spec:
       serviceAccountName: controller
       containers:
@@ -208,7 +208,7 @@ kind: Service
 metadata:
   labels:
     app: networking-certmanager
-    serving.knative.dev/release: "v20200626-029ac30"
+    serving.knative.dev/release: "v20200629-029ac30"
     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: "v20200626-029ac30"
+    serving.knative.dev/release: "v20200629-029ac30"
 spec:
   selector:
     matchLabels:
@@ -258,7 +258,7 @@ spec:
       labels:
         app: net-certmanager-webhook
         role: net-certmanager-webhook
-        serving.knative.dev/release: "v20200626-029ac30"
+        serving.knative.dev/release: "v20200629-029ac30"
     spec:
       serviceAccountName: controller
       containers:
@@ -319,7 +319,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: net-certmanager-webhook
-    serving.knative.dev/release: "v20200626-029ac30"
+    serving.knative.dev/release: "v20200629-029ac30"
 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 ca235572b..2db246c35 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: "v20200624-8b83024"
+    serving.knative.dev/release: "v20200629-60921a3"
     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: "v20200624-8b83024"
+    serving.knative.dev/release: "v20200629-60921a3"
     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: "v20200624-8b83024"
+    serving.knative.dev/release: "v20200629-60921a3"
     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: "v20200624-8b83024"
+    serving.knative.dev/release: "v20200629-60921a3"
     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: "v20200624-8b83024"
+    serving.knative.dev/release: "v20200629-60921a3"
     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: "v20200624-8b83024"
+    serving.knative.dev/release: "v20200629-60921a3"
     networking.knative.dev/ingress-provider: istio
 
 ---
@@ -216,7 +216,7 @@ metadata:
   name: config-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200624-8b83024"
+    serving.knative.dev/release: "v20200629-60921a3"
     networking.knative.dev/ingress-provider: istio
 data:
   _example: |
@@ -286,7 +286,7 @@ metadata:
   name: networking-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200624-8b83024"
+    serving.knative.dev/release: "v20200629-60921a3"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -302,14 +302,14 @@ spec:
         sidecar.istio.io/inject: "false"
       labels:
         app: networking-istio
-        serving.knative.dev/release: "v20200624-8b83024"
+        serving.knative.dev/release: "v20200629-60921a3"
     spec:
       serviceAccountName: controller
       containers:
       - name: networking-istio
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/controller@sha256:7a2b855265e67c4b80639d600f54cad07adc72e24b86924a76f4350ed415c5d3
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/controller@sha256:be1b00e09673709c2d5d2cabe557332ee156865088d5e6d1c577a0771ab73b98
         resources:
           requests:
             cpu: 30m
@@ -361,7 +361,7 @@ metadata:
   name: istio-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200624-8b83024"
+    serving.knative.dev/release: "v20200629-60921a3"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -375,14 +375,14 @@ spec:
       labels:
         app: istio-webhook
         role: istio-webhook
-        serving.knative.dev/release: "v20200624-8b83024"
+        serving.knative.dev/release: "v20200629-60921a3"
     spec:
       serviceAccountName: controller
       containers:
       - name: webhook
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/webhook@sha256:3a1f8756550b14c803602deb222facdfba860f2618d9dda7b3a1d2cabe62a6a7
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/webhook@sha256:91d427dc9c5a6bc669c34ae861d5d5f807bd2cb6547eb2d15730ed716013b32e
         resources:
           requests:
             cpu: 20m
@@ -436,7 +436,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: istio-webhook
-    serving.knative.dev/release: "v20200624-8b83024"
+    serving.knative.dev/release: "v20200629-60921a3"
     networking.knative.dev/ingress-provider: istio
 spec:
   ports:
diff --git a/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/controller.go b/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/controller.go
index a21da03bb..fb5d0cf2f 100644
--- a/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/controller.go
+++ b/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/controller.go
@@ -100,6 +100,9 @@ func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsF
 		if opts.AgentName != "" {
 			agentName = opts.AgentName
 		}
+		if opts.SkipStatusUpdates {
+			rec.skipStatusUpdates = true
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/reconciler.go b/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/reconciler.go
index e7e27c017..2d116f8e5 100644
--- a/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/reconciler.go
+++ b/vendor/knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace/reconciler.go
@@ -107,6 +107,10 @@ type reconcilerImpl struct {
 
 	// finalizerName is the name of the finalizer to reconcile.
 	finalizerName string
+
+	// skipStatusUpdates configures whether or not this reconciler automatically updates
+	// the status of the reconciled resource.
+	skipStatusUpdates bool
 }
 
 // Check that our Reconciler implements controller.Reconciler
@@ -253,20 +257,22 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
 		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
 	}
 
-	// Synchronize the status.
-	if equality.Semantic.DeepEqual(original.Status, resource.Status) {
-		// If we didn't change anything then don't call updateStatus.
-		// This is important because the copy we loaded from the injectionInformer's
-		// cache may be stale and we don't want to overwrite a prior update
-		// to status with this stale state.
-	} else if !isLeader {
-		logger.Warn("Saw status changes when we aren't the leader!")
-		// TODO: Consider logging the diff at Debug?
-	} else if err = r.updateStatus(original, resource); err != nil {
-		logger.Warnw("Failed to update resource status", zap.Error(err))
-		r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed",
-			"Failed to update status for %q: %v", resource.Name, err)
-		return err
+	if !r.skipStatusUpdates {
+		// Synchronize the status.
+		if equality.Semantic.DeepEqual(original.Status, resource.Status) {
+			// If we didn't change anything then don't call updateStatus.
+			// This is important because the copy we loaded from the injectionInformer's
+			// cache may be stale and we don't want to overwrite a prior update
+			// to status with this stale state.
+		} else if !isLeader {
+			logger.Warn("Saw status changes when we aren't the leader!")
+			// TODO: Consider logging the diff at Debug?
+		} else if err = r.updateStatus(original, resource); err != nil {
+			logger.Warnw("Failed to update resource status", zap.Error(err))
+			r.Recorder.Eventf(resource, v1.EventTypeWarning, "UpdateFailed",
+				"Failed to update status for %q: %v", resource.Name, err)
+			return err
+		}
 	}
 
 	// Report the reconciler event, if any.
diff --git a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_controller.go b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_controller.go
index ac1c9fac3..177d99caa 100644
--- a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_controller.go
+++ b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_controller.go
@@ -245,6 +245,9 @@ func NewImpl(ctx {{.contextContext|raw}}, r Interface{{if .hasClass}}, classValu
 		if opts.AgentName != "" {
 			agentName = opts.AgentName
 		}
+		if opts.SkipStatusUpdates {
+			rec.skipStatusUpdates = true
+		}
 	}
 
 	rec.Recorder = createRecorder(ctx, agentName)
diff --git a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler.go b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler.go
index b3d3596a1..a32ce177d 100644
--- a/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler.go
+++ b/vendor/knative.dev/pkg/codegen/cmd/injection-gen/generators/reconciler_reconciler.go
@@ -255,6 +255,10 @@ type reconcilerImpl struct {
 	// finalizerName is the name of the finalizer to reconcile.
 	finalizerName string
 
+	// skipStatusUpdates configures whether or not this reconciler automatically updates
+	// the status of the reconciled resource.
+	skipStatusUpdates bool
+
 	{{if .hasClass}}
 	// classValue is the resource annotation[{{ .class }}] instance value this reconciler instance filters on.
 	classValue string
@@ -426,20 +430,22 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro
 		reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
 	}
 
-	// Synchronize the status.
-	if {{.equalitySemantic|raw}}.DeepEqual(original.Status, resource.Status) {
-		// If we didn't change anything then don't call updateStatus.
-		// This is important because the copy we loaded from the injectionInformer's
-		// cache may be stale and we don't want to overwrite a prior update
-		// to status with this stale state.
-	} else if !isLeader {
-		logger.Warn("Saw status changes when we aren't the leader!")
-		// TODO: Consider logging the diff at Debug?
-	} else if err = r.updateStatus(original, resource); err != nil {
-		logger.Warnw("Failed to update resource status", zap.Error(err))
-		r.Recorder.Eventf(resource, {{.corev1EventTypeWarning|raw}}, "UpdateFailed",
-			"Failed to update status for %q: %v", resource.Name, err)
-		return err
+	if !r.skipStatusUpdates {
+		// Synchronize the status.
+		if {{.equalitySemantic|raw}}.DeepEqual(original.Status, resource.Status) {
+			// If we didn't change anything then don't call updateStatus.
+			// This is important because the copy we loaded from the injectionInformer's
+			// cache may be stale and we don't want to overwrite a prior update
+			// to status with this stale state.
+		} else if !isLeader {
+			logger.Warn("Saw status changes when we aren't the leader!")
+			// TODO: Consider logging the diff at Debug?
+		} else if err = r.updateStatus(original, resource); err != nil {
+			logger.Warnw("Failed to update resource status", zap.Error(err))
+			r.Recorder.Eventf(resource, {{.corev1EventTypeWarning|raw}}, "UpdateFailed",
+				"Failed to update status for %q: %v", resource.Name, err)
+			return err
+		}
 	}
 
 	// Report the reconciler event, if any.
diff --git a/vendor/knative.dev/pkg/controller/options.go b/vendor/knative.dev/pkg/controller/options.go
index 6853839dc..b40b5a359 100644
--- a/vendor/knative.dev/pkg/controller/options.go
+++ b/vendor/knative.dev/pkg/controller/options.go
@@ -31,6 +31,10 @@ type Options struct {
 	// AgentName is the name of the agent this reconciler uses. This overrides
 	// the default controller's agent name.
 	AgentName string
+
+	// SkipStatusUpdates configures this reconciler to either do automated status
+	// updates (default) or skip them if this is set to true.
+	SkipStatusUpdates bool
 }
 
 // OptionsFn is a callback method signature that accepts an Impl and returns
diff --git a/vendor/knative.dev/pkg/hack/update-deps.sh b/vendor/knative.dev/pkg/hack/update-deps.sh
index a39fc858f..6ed007f02 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@master"
+  "knative.dev/test-infra@release-0.16"
 )
 
 # Parse flags to determine any we should pass to dep.
diff --git a/vendor/knative.dev/pkg/injection/README.md b/vendor/knative.dev/pkg/injection/README.md
index e23ff32f5..30d75866e 100644
--- a/vendor/knative.dev/pkg/injection/README.md
+++ b/vendor/knative.dev/pkg/injection/README.md
@@ -454,14 +454,14 @@ for that resource.
 
 #### Annotation based common logic
 
-**krshaped=true may become the default if omitted in the future**
+**krshapedlogic=false may be used to omit common reconciler logic**
 
 Reconcilers can handle common logic for resources that conform to the KRShaped
 interface. This allows the generated code to automatically increment
 ObservedGeneration.
 
 ```go
-// +genreconciler:krshapedlogic=true
+// +genreconciler
 ```
 
 Setting this annotation will emit the following in the generated reconciler.
diff --git a/vendor/knative.dev/pkg/injection/sharedmain/main.go b/vendor/knative.dev/pkg/injection/sharedmain/main.go
index fdc910ef0..6c4acf71e 100644
--- a/vendor/knative.dev/pkg/injection/sharedmain/main.go
+++ b/vendor/knative.dev/pkg/injection/sharedmain/main.go
@@ -241,7 +241,7 @@ func WebhookMainWithConfig(ctx context.Context, component string, cfg *rest.Conf
 	leConfig := leaderElectionConfig.GetComponentConfig(component)
 	if leConfig.LeaderElect {
 		// Signal that we are executing in a context with leader election.
-		ctx = kle.WithStandardLeaderElectorBuilder(ctx, kubeclient.Get(ctx), leConfig)
+		ctx = kle.WithDynamicLeaderElectorBuilder(ctx, kubeclient.Get(ctx), leConfig)
 	}
 
 	controllers, webhooks := ControllersAndWebhooksFromCtors(ctx, cmw, ctors...)
diff --git a/vendor/knative.dev/pkg/leaderelection/config.go b/vendor/knative.dev/pkg/leaderelection/config.go
index 8b7c02058..880dd8c2b 100644
--- a/vendor/knative.dev/pkg/leaderelection/config.go
+++ b/vendor/knative.dev/pkg/leaderelection/config.go
@@ -19,11 +19,15 @@ package leaderelection
 import (
 	"fmt"
 	"os"
+	"strconv"
+	"strings"
 	"time"
 
+	"github.com/kelseyhightower/envconfig"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/apimachinery/pkg/util/uuid"
+	"k8s.io/client-go/tools/leaderelection/resourcelock"
 
 	cm "knative.dev/pkg/configmap"
 )
@@ -34,7 +38,7 @@ const configMapNameEnv = "CONFIG_LEADERELECTION_NAME"
 // This is a variable so that it may be customized in the binary entrypoint.
 var MaxBuckets uint32 = 10
 
-var validResourceLocks = sets.NewString("leases", "configmaps", "endpoints")
+var validResourceLocks = sets.NewString(resourcelock.LeasesResourceLock)
 
 // NewConfigFromMap returns a Config for the given map, or an error.
 func NewConfigFromMap(data map[string]string) (*Config, error) {
@@ -61,7 +65,7 @@ func NewConfigFromMap(data map[string]string) (*Config, error) {
 		return nil, fmt.Errorf("buckets: value must be between %d <= %d <= %d", 1, config.Buckets, MaxBuckets)
 	}
 	if !validResourceLocks.Has(config.ResourceLock) {
-		return nil, fmt.Errorf(`resourceLock: invalid value %q: valid values are "leases","configmaps","endpoints"`, config.ResourceLock)
+		return nil, fmt.Errorf(`resourceLock: invalid value %q: valid values are "leases"`, config.ResourceLock)
 	}
 
 	return config, nil
@@ -126,12 +130,39 @@ type ComponentConfig struct {
 	RetryPeriod   time.Duration
 }
 
-// StatefulSetConfig represents the required information for a StatefulSet service.
-type StatefulSetConfig struct {
-	StatefulSetName string
-	ServiceName     string
-	Port            string
-	Protocol        string
+// statefulSetID is a envconfig Decodable controller ordinal and name.
+type statefulSetID struct {
+	ssName  string
+	ordinal int
+}
+
+func (ssID *statefulSetID) Decode(v string) error {
+	if i := strings.LastIndex(v, "-"); i != -1 {
+		ui, err := strconv.ParseUint(v[i+1:], 10, 64)
+		ssID.ordinal = int(ui)
+		ssID.ssName = v[:i]
+		return err
+	}
+	return fmt.Errorf("%q is not a valid stateful set controller ordinal", v)
+}
+
+var _ envconfig.Decoder = (*statefulSetID)(nil)
+
+// statefulSetConfig represents the required information for a StatefulSet service.
+type statefulSetConfig struct {
+	StatefulSetID statefulSetID `envconfig:"STATEFUL_CONTROLLER_ORDINAL" required:"true"`
+	ServiceName   string        `envconfig:"STATEFUL_SERVICE_NAME" required:"true"`
+	Port          string        `envconfig:"STATEFUL_SERVICE_PORT" default:"80"`
+	Protocol      string        `envconfig:"STATEFUL_SERVICE_PROTOCOL" default:"http"`
+}
+
+// newStatefulSetConfig builds a stateful set LE config.
+func newStatefulSetConfig() (*statefulSetConfig, error) {
+	ssc := &statefulSetConfig{}
+	if err := envconfig.Process("", ssc); err != nil {
+		return nil, err
+	}
+	return ssc, nil
 }
 
 func defaultComponentConfig(name string) ComponentConfig {
diff --git a/vendor/knative.dev/pkg/leaderelection/context.go b/vendor/knative.dev/pkg/leaderelection/context.go
index d273a572e..57b0586fb 100644
--- a/vendor/knative.dev/pkg/leaderelection/context.go
+++ b/vendor/knative.dev/pkg/leaderelection/context.go
@@ -33,6 +33,19 @@ import (
 	"knative.dev/pkg/system"
 )
 
+// WithDynamicLeaderElectorBuilder sets up the statefulset elector based on environment,
+// falling back on the standard elector.
+func WithDynamicLeaderElectorBuilder(ctx context.Context, kc kubernetes.Interface, cc ComponentConfig) context.Context {
+	logger := logging.FromContext(ctx)
+	ssc, err := newStatefulSetConfig()
+	if err == nil {
+		logger.Info("Running with StatefulSet leader election")
+		return withStatefulSetElectorBuilder(ctx, cc, *ssc)
+	}
+	logger.Info("Running with Standard leader election")
+	return WithStandardLeaderElectorBuilder(ctx, kc, cc)
+}
+
 // WithStandardLeaderElectorBuilder infuses a context with the ability to build
 // LeaderElectors with the provided component configuration acquiring resource
 // locks via the provided kubernetes client.
@@ -43,10 +56,10 @@ func WithStandardLeaderElectorBuilder(ctx context.Context, kc kubernetes.Interfa
 	})
 }
 
-// WithStatefulSetLeaderElectorBuilder infuses a context with the ability to build
+// withStatefulSetElectorBuilder infuses a context with the ability to build
 // Electors which are assigned leadership based on the StatefulSet ordinal from
 // the provided component configuration.
-func WithStatefulSetLeaderElectorBuilder(ctx context.Context, cc ComponentConfig, ssc StatefulSetConfig) context.Context {
+func withStatefulSetElectorBuilder(ctx context.Context, cc ComponentConfig, ssc statefulSetConfig) context.Context {
 	return context.WithValue(ctx, builderKey{}, &statefulSetBuilder{
 		lec: cc,
 		ssc: ssc,
@@ -71,9 +84,9 @@ func BuildElector(ctx context.Context, la reconciler.LeaderAware, name string, e
 	if val := ctx.Value(builderKey{}); val != nil {
 		switch builder := val.(type) {
 		case *standardBuilder:
-			return builder.BuildElector(ctx, la, name, enq)
+			return builder.buildElector(ctx, la, name, enq)
 		case *statefulSetBuilder:
-			return builder.BuildElector(ctx, la, enq)
+			return builder.buildElector(ctx, la, enq)
 		}
 	}
 
@@ -91,7 +104,8 @@ type standardBuilder struct {
 	lec ComponentConfig
 }
 
-func (b *standardBuilder) BuildElector(ctx context.Context, la reconciler.LeaderAware, name string, enq func(reconciler.Bucket, types.NamespacedName)) (Elector, error) {
+func (b *standardBuilder) buildElector(ctx context.Context, la reconciler.LeaderAware,
+	name string, enq func(reconciler.Bucket, types.NamespacedName)) (Elector, error) {
 	logger := logging.FromContext(ctx)
 
 	id, err := UniqueID()
@@ -143,8 +157,7 @@ func (b *standardBuilder) BuildElector(ctx context.Context, la reconciler.Leader
 				},
 			},
 			ReleaseOnCancel: true,
-
-			Name: rl.Identity(),
+			Name:            rl.Identity(),
 		})
 		if err != nil {
 			return nil, err
@@ -160,26 +173,22 @@ func (b *standardBuilder) BuildElector(ctx context.Context, la reconciler.Leader
 
 type statefulSetBuilder struct {
 	lec ComponentConfig
-	ssc StatefulSetConfig
+	ssc statefulSetConfig
 }
 
-func (b *statefulSetBuilder) BuildElector(ctx context.Context, la reconciler.LeaderAware, enq func(reconciler.Bucket, types.NamespacedName)) (Elector, error) {
+func (b *statefulSetBuilder) buildElector(ctx context.Context, la reconciler.LeaderAware, enq func(reconciler.Bucket, types.NamespacedName)) (Elector, error) {
 	logger := logging.FromContext(ctx)
-
-	ordinal, err := ControllerOrdinal()
-	if err != nil {
-		return nil, err
-	}
-
-	logger.Infof("%s will run in StatefulSet ordinal assignement mode with ordinal %d", b.lec.Component, ordinal)
+	ordinal := uint32(b.ssc.StatefulSetID.ordinal)
+	logger.Infof("%s will run in StatefulSet ordinal assignement mode with ordinal %d",
+		b.lec.Component, ordinal)
 
 	return &unopposedElector{
 		bkt: &bucket{
 			// The name is the full pod DNS of the owner pod of this bucket.
 			name: fmt.Sprintf("%s://%s-%d.%s.%s.svc.%s:%s", b.ssc.Protocol,
-				b.ssc.StatefulSetName, ordinal, b.ssc.ServiceName,
+				b.ssc.StatefulSetID.ssName, ordinal, b.ssc.ServiceName,
 				system.Namespace(), network.GetClusterDomainName(), b.ssc.Port),
-			index: uint32(ordinal),
+			index: ordinal,
 			total: b.lec.Buckets,
 		},
 		la:  la,
diff --git a/vendor/knative.dev/pkg/leaderelection/ordinal.go b/vendor/knative.dev/pkg/leaderelection/ordinal.go
index 94a3775a0..6ed957385 100644
--- a/vendor/knative.dev/pkg/leaderelection/ordinal.go
+++ b/vendor/knative.dev/pkg/leaderelection/ordinal.go
@@ -16,24 +16,12 @@ limitations under the License.
 
 package leaderelection
 
-import (
-	"fmt"
-	"os"
-	"strconv"
-	"strings"
-)
-
-// If run a process on Kubernetes, the value of this environment variable
-// should be set to the pod name via the downward API.
-const controllerOrdinalEnv = "CONTROLLER_ORDINAL"
-
 // ControllerOrdinal tries to get ordinal from the pod name of a StatefulSet,
 // which is provided from the environment variable CONTROLLER_ORDINAL.
-func ControllerOrdinal() (uint64, error) {
-	v := os.Getenv(controllerOrdinalEnv)
-	if i := strings.LastIndex(v, "-"); i != -1 {
-		return strconv.ParseUint(v[i+1:], 10, 64)
+func ControllerOrdinal() (int, error) {
+	ssc, err := newStatefulSetConfig()
+	if err != nil {
+		return 0, err
 	}
-
-	return 0, fmt.Errorf("ordinal not found in %s=%s", controllerOrdinalEnv, v)
+	return ssc.StatefulSetID.ordinal, nil
 }
diff --git a/vendor/knative.dev/pkg/version/version.go b/vendor/knative.dev/pkg/version/version.go
index 4fb8977d2..cadf90cda 100644
--- a/vendor/knative.dev/pkg/version/version.go
+++ b/vendor/knative.dev/pkg/version/version.go
@@ -30,7 +30,7 @@ const (
 	// the Kubernetes minimum version required by Knative.
 	KubernetesMinVersionKey = "KUBERNETES_MIN_VERSION"
 
-	defaultMinimumVersion = "v1.15.1"
+	defaultMinimumVersion = "v1.16.0"
 )
 
 func getMinimumVersion() string {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 332f29e96..f7c9fa3b7 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -986,7 +986,7 @@ k8s.io/utils/buffer
 k8s.io/utils/integer
 k8s.io/utils/pointer
 k8s.io/utils/trace
-# knative.dev/caching v0.0.0-20200606210318-787aec80f71c
+# knative.dev/caching v0.0.0-20200630172829-a78409990d76
 ## explicit
 knative.dev/caching/config
 knative.dev/caching/pkg/apis/caching
@@ -1007,7 +1007,7 @@ knative.dev/caching/pkg/client/injection/informers/caching/v1alpha1/image/fake
 knative.dev/caching/pkg/client/injection/informers/factory
 knative.dev/caching/pkg/client/injection/informers/factory/fake
 knative.dev/caching/pkg/client/listers/caching/v1alpha1
-# knative.dev/networking v0.0.0-20200625171028-5efff1e07309
+# knative.dev/networking v0.0.0-20200630174930-fdf40c82309e
 ## explicit
 knative.dev/networking/pkg/apis/config
 knative.dev/networking/pkg/apis/networking
@@ -1034,7 +1034,7 @@ knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/server
 knative.dev/networking/pkg/client/injection/reconciler/networking/v1alpha1/serverlessservice
 knative.dev/networking/pkg/client/istio/listers/networking/v1alpha3
 knative.dev/networking/pkg/client/listers/networking/v1alpha1
-# knative.dev/pkg v0.0.0-20200626022628-f1ee372577e1
+# knative.dev/pkg v0.0.0-20200630170034-2c1a029eb97f
 ## explicit
 knative.dev/pkg/apiextensions/storageversion
 knative.dev/pkg/apiextensions/storageversion/cmd/migrate
@@ -1153,7 +1153,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-20200625195728-62651036b559
+# knative.dev/test-infra v0.0.0-20200630141629-15f40fe97047
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/yaml v1.2.0


Test patch:
diff --git a/pkg/apis/serving/k8s_validation_test.go b/pkg/apis/serving/k8s_validation_test.go
index 14cc0d33a..a73ff308d 100644
--- a/pkg/apis/serving/k8s_validation_test.go
+++ b/pkg/apis/serving/k8s_validation_test.go
@@ -378,7 +378,122 @@ func TestPodSpecMultiContainerValidation(t *testing.T) {
 		},
 		cfgOpts: []configOption{withMultiContainer()},
 		want:    nil,
-	}}
+	}, {
+		name: "Volume mounts ok with single container",
+		ps: corev1.PodSpec{
+			Volumes: []corev1.Volume{
+				{Name: "the-name",
+					VolumeSource: corev1.VolumeSource{
+						Secret: &corev1.SecretVolumeSource{
+							SecretName: "foo",
+						},
+					}},
+			},
+			Containers: []corev1.Container{{
+				Image: "busybox",
+				VolumeMounts: []corev1.VolumeMount{{
+					MountPath: "/mount/path",
+					Name:      "the-name",
+					ReadOnly:  true,
+				}},
+			}},
+		},
+		cfgOpts: []configOption{withFieldRef()},
+	}, {
+		name: "Volume not mounted when having a single container",
+		ps: corev1.PodSpec{
+			Volumes: []corev1.Volume{
+				{Name: "the-name",
+					VolumeSource: corev1.VolumeSource{
+						Secret: &corev1.SecretVolumeSource{
+							SecretName: "foo",
+						},
+					}},
+			},
+			Containers: []corev1.Container{{
+				Image: "busybox",
+			}},
+		},
+		cfgOpts: []configOption{withFieldRef()},
+		want: &apis.FieldError{
+			Message: "volume with name \"the-name\" not mounted",
+			Paths:   []string{"volumes[0].name"}},
+	}, {
+		name: "Volume mounts ok when having multiple containers",
+		ps: corev1.PodSpec{
+			Volumes: []corev1.Volume{
+				{Name: "the-name1",
+					VolumeSource: corev1.VolumeSource{
+						Secret: &corev1.SecretVolumeSource{
+							SecretName: "foo1",
+						},
+					},
+				},
+				{Name: "the-name2",
+					VolumeSource: corev1.VolumeSource{
+						Secret: &corev1.SecretVolumeSource{
+							SecretName: "foo2",
+						},
+					}},
+			},
+			Containers: []corev1.Container{
+				{
+					Image: "busybox",
+					Ports: []corev1.ContainerPort{{ContainerPort: 8888}},
+					VolumeMounts: []corev1.VolumeMount{{
+						MountPath: "/mount/path",
+						Name:      "the-name1",
+						ReadOnly:  true,
+					}},
+				},
+				{
+					Image: "busybox",
+					VolumeMounts: []corev1.VolumeMount{{
+						MountPath: "/mount/path",
+						Name:      "the-name2",
+						ReadOnly:  true,
+					}},
+				},
+			},
+		},
+		cfgOpts: []configOption{withMultiContainer()},
+	}, {
+		name: "Volume not mounted when having multiple containers",
+		ps: corev1.PodSpec{
+			Volumes: []corev1.Volume{
+				{Name: "the-name1",
+					VolumeSource: corev1.VolumeSource{
+						Secret: &corev1.SecretVolumeSource{
+							SecretName: "foo1",
+						},
+					},
+				},
+				{Name: "the-name2",
+					VolumeSource: corev1.VolumeSource{
+						Secret: &corev1.SecretVolumeSource{
+							SecretName: "foo2",
+						},
+					}},
+			},
+			Containers: []corev1.Container{
+				{
+					Image: "busybox",
+					Ports: []corev1.ContainerPort{{ContainerPort: 8888}},
+					VolumeMounts: []corev1.VolumeMount{{
+						MountPath: "/mount/path",
+						Name:      "the-name1",
+						ReadOnly:  true,
+					}},
+				},
+				{Image: "busybox"},
+			},
+		},
+		cfgOpts: []configOption{withMultiContainer()},
+		want: &apis.FieldError{
+			Message: "volume with name \"the-name2\" not mounted",
+			Paths:   []string{"volumes[1].name"},
+		}},
+	}
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
@@ -743,16 +858,6 @@ func TestContainerValidation(t *testing.T) {
 			apis.ErrMissingField("readOnly").ViaFieldIndex("volumeMounts", 0)).Also(
 			apis.ErrMissingField("mountPath").ViaFieldIndex("volumeMounts", 0)).Also(
 			apis.ErrDisallowedFields("mountPropagation").ViaFieldIndex("volumeMounts", 0)),
-	}, {
-		name: "missing known volumeMounts",
-		c: corev1.Container{
-			Image: "foo",
-		},
-		volumes: sets.NewString("the-name"),
-		want: &apis.FieldError{
-			Message: "volumes not mounted: [the-name]",
-			Paths:   []string{"volumeMounts"},
-		},
 	}, {
 		name: "has known volumeMounts",
 		c: corev1.Container{
diff --git a/pkg/leaderelection/config_test.go b/pkg/leaderelection/config_test.go
index 52d0fdb5b..58ad7b0f9 100644
--- a/pkg/leaderelection/config_test.go
+++ b/pkg/leaderelection/config_test.go
@@ -79,7 +79,7 @@ func TestValidateConfig(t *testing.T) {
 			data["enabledComponents"] = "controller,frobulator"
 			return data
 		}(),
-		err: errors.New(`invalid enabledComponent "frobulator": valid values are ["certcontroller" "controller" "hpaautoscaler" "istiocontroller" "nscontroller" "webhook"]`),
+		err: errors.New(`invalid enabledComponent "frobulator": valid values are ["certcontroller" "contour-ingress-controller" "controller" "hpaautoscaler" "istiocontroller" "net-http01" "nscontroller" "webhook"]`),
 	}}
 
 	for _, tc := range cases {
@@ -95,6 +95,7 @@ func TestValidateConfig(t *testing.T) {
 		})
 	}
 }
+
 func TestServingConfig(t *testing.T) {
 	actual, example := ConfigMapsFromTestFile(t, "config-leader-election")
 	for _, test := range []struct {
diff --git a/pkg/reconciler/accessor/core/secret_test.go b/pkg/reconciler/accessor/core/secret_test.go
index 7ef63f8de..0170c5587 100644
--- a/pkg/reconciler/accessor/core/secret_test.go
+++ b/pkg/reconciler/accessor/core/secret_test.go
@@ -23,8 +23,9 @@ import (
 	"github.com/google/go-cmp/cmp"
 
 	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/util/wait"
 	"k8s.io/client-go/kubernetes"
 	corev1listers "k8s.io/client-go/listers/core/v1"
 
@@ -103,16 +104,6 @@ func TestReconcileSecretCreate(t *testing.T) {
 	ctx, cancel, _ := SetupFakeContextWithCancel(t)
 	kubeClient := fakekubeclient.Get(ctx)
 
-	h := NewHooks()
-	h.OnCreate(&kubeClient.Fake, "secrets", func(obj runtime.Object) HookResult {
-		got := obj.(*corev1.Secret)
-		if diff := cmp.Diff(got, desired); diff != "" {
-			t.Log("Unexpected Secret (-want, +got):", diff)
-			return HookIncomplete
-		}
-		return HookComplete
-	})
-
 	accessor, waitInformers := setup(ctx, []*corev1.Secret{}, kubeClient, t)
 	defer func() {
 		cancel()
@@ -121,8 +112,18 @@ func TestReconcileSecretCreate(t *testing.T) {
 
 	ReconcileSecret(ctx, ownerObj, desired, accessor)
 
-	if err := h.WaitForHooks(3 * time.Second); err != nil {
-		t.Errorf("Failed to Reconcile Secret: %v", err)
+	secretInformer := fakesecretinformer.Get(ctx)
+	if err := wait.PollImmediate(10*time.Millisecond, 3*time.Second, func() (bool, error) {
+		secret, err := secretInformer.Lister().Secrets(desired.Namespace).Get(desired.Name)
+		if err != nil {
+			if errors.IsNotFound(err) {
+				return false, nil
+			}
+			return false, err
+		}
+		return cmp.Equal(secret, desired), nil
+	}); err != nil {
+		t.Fatal("Failed to see secret propagation:", err)
 	}
 }
 
@@ -136,19 +137,19 @@ func TestReconcileSecretUpdate(t *testing.T) {
 		waitInformers()
 	}()
 
-	h := NewHooks()
-	h.OnUpdate(&kubeClient.Fake, "secrets", func(obj runtime.Object) HookResult {
-		got := obj.(*corev1.Secret)
-		if diff := cmp.Diff(got, desired); diff != "" {
-			t.Log("Unexpected Secret (-want, +got):", diff)
-			return HookIncomplete
-		}
-		return HookComplete
-	})
-
 	ReconcileSecret(ctx, ownerObj, desired, accessor)
-	if err := h.WaitForHooks(3 * time.Second); err != nil {
-		t.Errorf("Failed to Reconcile Secret: %v", err)
+	secretInformer := fakesecretinformer.Get(ctx)
+	if err := wait.PollImmediate(10*time.Millisecond, 3*time.Second, func() (bool, error) {
+		secret, err := secretInformer.Lister().Secrets(desired.Namespace).Get(desired.Name)
+		if err != nil {
+			if errors.IsNotFound(err) {
+				return false, nil
+			}
+			return false, err
+		}
+		return cmp.Equal(secret, desired), nil
+	}); err != nil {
+		t.Fatal("Failed to see secret propagation:", err)
 	}
 }
 
diff --git a/test/config/autotls/certmanager/http01/mesh-issuer.yaml b/test/config/autotls/certmanager/http01/mesh-issuer.yaml
index 7b5e615a1..6dd8aa3ed 100644
--- a/test/config/autotls/certmanager/http01/mesh-issuer.yaml
+++ b/test/config/autotls/certmanager/http01/mesh-issuer.yaml
@@ -28,4 +28,9 @@ spec:
             metadata:
               annotations:
                 sidecar.istio.io/inject: "true"
+              labels:
+                # Istio adds the Challenge name for the value of this label.
+                # And the challenge name is sometimes more than 63 characters.
+                # So we override this label with empty value.
+                service.istio.io/canonical-name: ""
         selector: {}
diff --git a/test/conformance.go b/test/conformance.go
index e5898947a..762ecb292 100644
--- a/test/conformance.go
+++ b/test/conformance.go
@@ -39,12 +39,16 @@ const (
 	SingleThreadedImage = "singlethreaded"
 	Timeout             = "timeout"
 	WorkingDir          = "workingdir"
+	ServingContainer    = "servingcontainer"
+	SidecarContainer    = "sidecarcontainer"
 
 	// Constants for test image output.
 	PizzaPlanetText1 = "What a spaceport!"
 	PizzaPlanetText2 = "Re-energize yourself with a slice of pepperoni!"
 	HelloWorldText   = "Hello World! How about some tasty noodles?"
 
+	MultiContainerResponse = "Yay!! multi-container works"
+
 	ConcurrentRequests = 200
 	// We expect to see 100% of requests succeed for traffic sent directly to revisions.
 	// This might be a bad assumption.
diff --git a/test/e2e-tests.sh b/test/e2e-tests.sh
index 71c3eedf9..10dbabf0f 100755
--- a/test/e2e-tests.sh
+++ b/test/e2e-tests.sh
@@ -59,6 +59,16 @@ function disable_tag_header_based_routing() {
   kubectl patch cm config-network -n "${SYSTEM_NAMESPACE}" -p '{"data":{"tagHeaderBasedRouting":"Disabled"}}'
 }
 
+function enable_multi_container_feature() {
+  echo -n "Enabling Multi Container Feature Flag"
+  kubectl patch cm config-features -n "${SYSTEM_NAMESPACE}" -p '{"data":{"multi-container":"Enabled"}}'
+}
+
+function disable_multi_container_feature() {
+  echo -n "Disabling Multi Container Feature Flag"
+  kubectl patch cm config-features -n "${SYSTEM_NAMESPACE}" -p '{"data":{"multi-container":"Disabled"}}'
+}
+
 # Script entry point.
 
 # Skip installing istio as an add-on
@@ -156,6 +166,11 @@ add_trap "disable_tag_header_based_routing" SIGKILL SIGTERM SIGQUIT
 go_test_e2e -timeout=2m ./test/e2e/tagheader || failed=1
 disable_tag_header_based_routing
 
+enable_multi_container_feature
+add_trap "disable_multi_container_feature" SIGKILL SIGTERM SIGQUIT
+go_test_e2e -timeout=2m ./test/e2e/multicontainer || failed=1
+disable_multi_container_feature
+
 # Certificate conformance tests must be run separately
 # because they need cert-manager specific configurations.
 kubectl apply -f ${TMP_DIR}/test/config/autotls/certmanager/selfsigned/
diff --git a/test/e2e/multicontainer/multicontainer_test.go b/test/e2e/multicontainer/multicontainer_test.go
new file mode 100644
index 000000000..1771d252b
--- /dev/null
+++ b/test/e2e/multicontainer/multicontainer_test.go
@@ -0,0 +1,77 @@
+// +build e2e
+
+/*
+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 multicontainer
+
+import (
+	"testing"
+
+	corev1 "k8s.io/api/core/v1"
+	pkgTest "knative.dev/pkg/test"
+	"knative.dev/pkg/test/logstream"
+	v1 "knative.dev/serving/pkg/apis/serving/v1"
+	"knative.dev/serving/test"
+	"knative.dev/serving/test/e2e"
+	v1test "knative.dev/serving/test/v1"
+)
+
+func TestMultiContainer(t *testing.T) {
+	t.Parallel()
+	cancel := logstream.Start(t)
+	defer cancel()
+
+	clients := e2e.Setup(t)
+
+	containers := []corev1.Container{{
+		Image: pkgTest.ImagePath(test.ServingContainer),
+		Ports: []corev1.ContainerPort{{
+			ContainerPort: 8881,
+		}},
+	}, {
+		Image: pkgTest.ImagePath(test.SidecarContainer),
+	}}
+
+	names := test.ResourceNames{
+		Service: test.ObjectNameForTest(t),
+	}
+
+	test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })
+	defer test.TearDown(clients, names)
+
+	t.Log("Creating a new Service")
+
+	resources, err := v1test.CreateServiceReadyForMultiContainer(t, clients, &names, func(svc *v1.Service) {
+		svc.Spec.Template.Spec.Containers = containers
+	})
+	if err != nil {
+		t.Fatalf("Failed to create initial Service: %v: %v", names.Service, err)
+	}
+
+	url := resources.Route.Status.URL.URL()
+	if _, err := pkgTest.WaitForEndpointState(
+		clients.KubeClient,
+		t.Logf,
+		url,
+		v1test.RetryingRouteInconsistency(pkgTest.MatchesAllOf(pkgTest.IsStatusOK, pkgTest.MatchesBody(test.MultiContainerResponse))),
+		"HelloWorldServesText",
+		test.ServingFlags.ResolvableDomain,
+		test.AddRootCAtoTransport(t.Logf, clients, test.ServingFlags.Https),
+	); err != nil {
+		t.Fatalf("The endpoint %s for Route %s didn't serve the expected text %q: %v", url, names.Route, test.MultiContainerResponse, err)
+	}
+}
diff --git a/test/test_images/multicontainer/README.md b/test/test_images/multicontainer/README.md
new file mode 100644
index 000000000..c6b5e6e2e
--- /dev/null
+++ b/test/test_images/multicontainer/README.md
@@ -0,0 +1,25 @@
+# MultiContainer test
+
+This directory contains the images used in the multicontainer e2e test.
+
+There are two images servingcontainer and sidecarcontainer.
+
+Both image contains a simple Go webserver and listens on port `8881`, `8882`
+respectively.
+
+Out of which one will be the serving (serves the request) container which
+exposes a service at `/` and the other will be the sidecar container.
+
+When called, the serving container emits a "Yay!! multi-container works" message
+by talking to sidecar container.
+
+## Trying out
+
+To run the image as a Service outisde of the test suite:
+
+`ko apply -f service.yaml`
+
+## Building
+
+For details about building and adding new images, see the
+[section about test images](/test/README.md#test-images).
diff --git a/test/test_images/multicontainer/service.yaml b/test/test_images/multicontainer/service.yaml
new file mode 100644
index 000000000..54c8e9dec
--- /dev/null
+++ b/test/test_images/multicontainer/service.yaml
@@ -0,0 +1,27 @@
+# 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: serving.knative.dev/v1
+kind: Service
+metadata:
+  name: multi-container-test
+  namespace: default
+spec:
+  template:
+    spec:
+      containers:
+      - image: ko://knative.dev/serving/test/test_images/multicontainer/servingcontainer
+        ports:
+          - containerPort: 8881
+      - image: ko://knative.dev/serving/test/test_images/multicontainer/sidecarcontainer
diff --git a/test/test_images/multicontainer/servingcontainer/servingcontainer.go b/test/test_images/multicontainer/servingcontainer/servingcontainer.go
new file mode 100644
index 000000000..b6900e3ab
--- /dev/null
+++ b/test/test_images/multicontainer/servingcontainer/servingcontainer.go
@@ -0,0 +1,46 @@
+/*
+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 main
+
+import (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+
+	"knative.dev/serving/test"
+)
+
+func handler(w http.ResponseWriter, r *http.Request) {
+	log.Println("serving container received a request.")
+	res, err := http.Get("http://127.0.0.1:8882")
+	if err != nil {
+		log.Fatal(err)
+	}
+	resp, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Fprintln(w, string(resp))
+}
+
+func main() {
+	flag.Parse()
+	log.Print("serving container started")
+	test.ListenAndServeGracefully(":8881", handler)
+}
diff --git a/test/test_images/multicontainer/sidecarcontainer/sidecarcontainer.go b/test/test_images/multicontainer/sidecarcontainer/sidecarcontainer.go
new file mode 100644
index 000000000..aabc38852
--- /dev/null
+++ b/test/test_images/multicontainer/sidecarcontainer/sidecarcontainer.go
@@ -0,0 +1,37 @@
+/*
+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 main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"net/http"
+
+	"knative.dev/serving/test"
+)
+
+func handler(w http.ResponseWriter, r *http.Request) {
+	log.Println("sidecar container received a request.")
+	fmt.Fprintln(w, "Yay!! multi-container works")
+}
+
+func main() {
+	flag.Parse()
+	log.Print("sidecar container started")
+	test.ListenAndServeGracefully(":8882", handler)
+}
diff --git a/test/upgrade/probe_test.go b/test/upgrade/probe_test.go
index 93fc2df00..021f98ad0 100644
--- a/test/upgrade/probe_test.go
+++ b/test/upgrade/probe_test.go
@@ -19,6 +19,7 @@ limitations under the License.
 package upgrade
 
 import (
+	"flag"
 	"io/ioutil"
 	"log"
 	"os"
@@ -30,6 +31,10 @@ import (
 	v1test "knative.dev/serving/test/v1"
 )
 
+var (
+	successFraction = flag.Float64("probe.success_fraction", 1.0, "Fraction of probes required to pass during upgrade.")
+)
+
 const pipe = "/tmp/prober-signal"
 
 func TestProbe(t *testing.T) {
@@ -63,7 +68,7 @@ func TestProbe(t *testing.T) {
 	// Use log.Printf instead of t.Logf because we want to see failures
 	// inline with other logs instead of buffered until the end.
 	prober := test.RunRouteProber(log.Printf, clients, url, test.AddRootCAtoTransport(t.Logf, clients, test.ServingFlags.Https))
-	defer test.AssertProberDefault(t, prober)
+	defer test.CheckSLO(*successFraction, t.Name(), prober)
 
 	// e2e-upgrade-test.sh will close this pipe to signal the upgrade is
 	// over, at which point we will finish the test and check the prober.
diff --git a/test/v1/service.go b/test/v1/service.go
index 05831787a..500b1be42 100644
--- a/test/v1/service.go
+++ b/test/v1/service.go
@@ -89,6 +89,20 @@ func GetResourceObjects(clients *test.Clients, names test.ResourceNames) (*Resou
 	}, nil
 }
 
+// CreateServiceReadyForMultiContainer creates a new Service for multi container in 'Ready' state . This function expects Service
+// name passed in through 'names'. Names is updated with the Route and Configuration created by the Service
+// and ResourceObjects is returned with the Service, Route, and Configuration objects.
+// Returns error if the service does not come up correctly.
+func CreateServiceReadyForMultiContainer(t pkgTest.T, clients *test.Clients, names *test.ResourceNames, fopt ...rtesting.ServiceOption) (*ResourceObjects, error) {
+	t.Log("Creating a new Service", "service", names.Service)
+	svc := rtesting.ServiceWithoutNamespace(names.Service, fopt...)
+	svc, err := createService(t, clients, svc)
+	if err != nil {
+		return nil, err
+	}
+	return getResourceObjects(t, clients, names, svc)
+}
+
 // CreateServiceReady creates a new Service in state 'Ready'. This function expects Service and Image name
 // passed in through 'names'.  Names is updated with the Route and Configuration created by the Service
 // and ResourceObjects is returned with the Service, Route, and Configuration objects.
@@ -97,13 +111,15 @@ func CreateServiceReady(t pkgTest.T, clients *test.Clients, names *test.Resource
 	if names.Image == "" {
 		return nil, fmt.Errorf("expected non-empty Image name; got Image=%v", names.Image)
 	}
-
 	t.Log("Creating a new Service", "service", names.Service)
 	svc, err := CreateService(t, clients, *names, fopt...)
 	if err != nil {
 		return nil, err
 	}
+	return getResourceObjects(t, clients, names, svc)
+}
 
+func getResourceObjects(t pkgTest.T, clients *test.Clients, names *test.ResourceNames, svc *v1.Service) (*ResourceObjects, error) {
 	// Populate Route and Configuration Objects with name
 	names.Route = serviceresourcenames.Route(svc)
 	names.Config = serviceresourcenames.Configuration(svc)
@@ -119,7 +135,7 @@ func CreateServiceReady(t pkgTest.T, clients *test.Clients, names *test.Resource
 	}
 
 	t.Log("Checking to ensure Service Status is populated for Ready service")
-	err = validateCreatedServiceStatus(clients, names)
+	err := validateCreatedServiceStatus(clients, names)
 	if err != nil {
 		return nil, err
 	}
@@ -134,7 +150,11 @@ func CreateServiceReady(t pkgTest.T, clients *test.Clients, names *test.Resource
 
 // CreateService creates a service in namespace with the name names.Service and names.Image
 func CreateService(t pkgTest.T, clients *test.Clients, names test.ResourceNames, fopt ...rtesting.ServiceOption) (*v1.Service, error) {
-	service := Service(names, fopt...)
+	svc := Service(names, fopt...)
+	return createService(t, clients, svc)
+}
+
+func createService(t pkgTest.T, clients *test.Clients, service *v1.Service) (*v1.Service, error) {
 	test.AddTestAnnotation(t, service.ObjectMeta)
 	LogResourceObject(t, ResourceObjects{Service: service})
 	return clients.ServingClient.Services.Create(service)
diff --git a/third_party/contour-latest/net-contour.yaml b/third_party/contour-latest/net-contour.yaml
index b6ab7c6b4..6f0d3afe5 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: "v20200626-d2e1fe6d"
+    serving.knative.dev/release: "v20200629-d2e1fe6d"
 data:
   _example: |
     ################################
diff --git a/vendor/knative.dev/pkg/test/zipkin/util.go b/vendor/knative.dev/pkg/test/zipkin/util.go
index 1ff2050a7..b5b574911 100644
--- a/vendor/knative.dev/pkg/test/zipkin/util.go
+++ b/vendor/knative.dev/pkg/test/zipkin/util.go
@@ -23,11 +23,18 @@ import (
 	"fmt"
 	"io/ioutil"
 	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
 	"sync"
+	"testing"
 	"time"
 
+	tracingconfig "knative.dev/pkg/tracing/config"
+
 	"github.com/openzipkin/zipkin-go/model"
 	"go.opencensus.io/trace"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes"
 	"knative.dev/pkg/test/logging"
 	"knative.dev/pkg/test/monitoring"
@@ -46,10 +53,7 @@ const (
 
 	// App is the name of this component.
 	// This will be used as a label selector.
-	app = "zipkin"
-
-	// istioNS is the namespace we are using for istio components.
-	istioNS = "istio-system"
+	appLabel = "zipkin"
 )
 
 var (
@@ -65,26 +69,66 @@ var (
 	teardownOnce sync.Once
 )
 
+// SetupZipkinTracingFromConfigTracing setups zipkin tracing like SetupZipkinTracing but retrieving the zipkin configuration
+// from config-tracing config map
+func SetupZipkinTracingFromConfigTracing(kubeClientset *kubernetes.Clientset, logf logging.FormatLogger, configMapNamespace string) error {
+	cm, err := kubeClientset.CoreV1().ConfigMaps(configMapNamespace).Get("config-tracing", metav1.GetOptions{})
+	if err != nil {
+		return fmt.Errorf("error while retrieving config-tracing config map: %w", err)
+	}
+	c, err := tracingconfig.NewTracingConfigFromConfigMap(cm)
+	if err != nil {
+		return fmt.Errorf("error while parsing config-tracing config map: %w", err)
+	}
+	zipkinEndpointURL, err := url.Parse(c.ZipkinEndpoint)
+	if err != nil {
+		return fmt.Errorf("error while parsing the zipkin endpoint in config-tracing config map: %w", err)
+	}
+	unparsedPort := zipkinEndpointURL.Port()
+	port := uint64(80)
+	if unparsedPort != "" {
+		port, err = strconv.ParseUint(unparsedPort, 10, 16)
+		if err != nil {
+			return fmt.Errorf("error while parsing the zipkin endpoint port in config-tracing config map: %w", err)
+		}
+	}
+
+	namespace, err := parseNamespaceFromHostname(zipkinEndpointURL.Host)
+	if err != nil {
+		return fmt.Errorf("error while parsing the Zipkin endpoint in config-tracing config map: %w", err)
+	}
+
+	return SetupZipkinTracing(kubeClientset, logf, int(port), namespace)
+}
+
+// SetupZipkinTracingFromConfigTracingOrFail is same as SetupZipkinTracingFromConfigTracing, but fails the test if an error happens
+func SetupZipkinTracingFromConfigTracingOrFail(t testing.TB, kubeClientset *kubernetes.Clientset, configMapNamespace string) {
+	if err := SetupZipkinTracingFromConfigTracing(kubeClientset, t.Logf, configMapNamespace); err != nil {
+		t.Fatalf("Error while setup Zipkin tracing: %v", err)
+	}
+}
+
 // SetupZipkinTracing sets up zipkin tracing which involves:
 // 1. Setting up port-forwarding from localhost to zipkin pod on the cluster
 //    (pid of the process doing Port-Forward is stored in a global variable).
 // 2. Enable AlwaysSample config for tracing for the SpoofingClient.
-func SetupZipkinTracing(kubeClientset *kubernetes.Clientset, logf logging.FormatLogger) bool {
+// The zipkin deployment must have the label app=zipkin
+func SetupZipkinTracing(kubeClientset *kubernetes.Clientset, logf logging.FormatLogger, zipkinRemotePort int, zipkinNamespace string) (err error) {
 	setupOnce.Do(func() {
-		if err := monitoring.CheckPortAvailability(ZipkinPort); err != nil {
-			logf("Zipkin port not available on the machine: %v", err)
+		if e := monitoring.CheckPortAvailability(zipkinRemotePort); e != nil {
+			err = fmt.Errorf("Zipkin port not available on the machine: %w", err)
 			return
 		}
 
-		zipkinPods, err := monitoring.GetPods(kubeClientset, app, istioNS)
-		if err != nil {
-			logf("Error retrieving Zipkin pod details: %v", err)
+		zipkinPods, e := monitoring.GetPods(kubeClientset, appLabel, zipkinNamespace)
+		if e != nil {
+			err = fmt.Errorf("error retrieving Zipkin pod details: %w", err)
 			return
 		}
 
-		zipkinPortForwardPID, err = monitoring.PortForward(logf, zipkinPods, ZipkinPort, ZipkinPort, istioNS)
-		if err != nil {
-			logf("Error starting kubectl port-forward command: %v", err)
+		zipkinPortForwardPID, e = monitoring.PortForward(logf, zipkinPods, ZipkinPort, zipkinRemotePort, zipkinNamespace)
+		if e != nil {
+			err = fmt.Errorf("error starting kubectl port-forward command: %w", err)
 			return
 		}
 
@@ -93,9 +137,15 @@ func SetupZipkinTracing(kubeClientset *kubernetes.Clientset, logf logging.Format
 		// Applying AlwaysSample config to ensure we propagate zipkin header for every request made by this client.
 		trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
 		logf("Successfully setup SpoofingClient for Zipkin Tracing")
-		ZipkinTracingEnabled = true
 	})
-	return ZipkinTracingEnabled
+	return
+}
+
+// SetupZipkinTracingOrFail is same as SetupZipkinTracing, but fails the test if an error happens
+func SetupZipkinTracingOrFail(t testing.TB, kubeClientset *kubernetes.Clientset, zipkinRemotePort int, zipkinNamespace string) {
+	if err := SetupZipkinTracing(kubeClientset, t.Logf, zipkinRemotePort, zipkinNamespace); err != nil {
+		t.Fatalf("Error while setup zipkin tracing: %v", err)
+	}
 }
 
 // CleanupZipkinTracingSetup cleans up the Zipkin tracing setup on the machine. This involves killing the process performing port-forward.
@@ -187,3 +237,11 @@ func jsonTrace(traceID string) ([]model.SpanModel, error) {
 	}
 	return models, nil
 }
+
+func parseNamespaceFromHostname(hostname string) (string, error) {
+	parts := strings.Split(hostname, ".")
+	if len(parts) < 3 || parts[2] != "svc" {
+		return "", fmt.Errorf("could not extract namespace/name from %s", hostname)
+	}
+	return parts[1], nil
+}
diff --git a/vendor/knative.dev/test-infra/scripts/dummy.go b/vendor/knative.dev/test-infra/scripts/dummy.go
index 809b3a607..82c9edfa9 100644
--- a/vendor/knative.dev/test-infra/scripts/dummy.go
+++ b/vendor/knative.dev/test-infra/scripts/dummy.go
@@ -1,9 +1,12 @@
 /*
 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.
@@ -11,13 +14,6 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
+// Package scripts is a placeholder that allows us to pull the shell scripts
+// via go mod vendor.
 package scripts
-
-import (
-	"fmt"
-)
-
-func main() {
-	fmt.Println("This is a dummy go file so `go dep` can be used with knative/test-infra/scripts")
-	fmt.Println("This file can be safely removed if one day this directory contains real, useful go code")
-}
diff --git a/vendor/knative.dev/test-infra/scripts/library.sh b/vendor/knative.dev/test-infra/scripts/library.sh
index 1ed038116..da54be8b9 100644
--- a/vendor/knative.dev/test-infra/scripts/library.sh
+++ b/vendor/knative.dev/test-infra/scripts/library.sh
@@ -528,10 +528,19 @@ function run_go_tool() {
 # Run kntest tool, error out and ask users to install it if it's not currently installed.
 # Parameters: $1..$n - parameters passed to the tool.
 function run_kntest() {
-  if [[ -z "$(which kntest)" ]]; then
-    echo "--- FAIL: kntest not installed, please clone test-infra repo and run \`go install ./kntest/cmd/kntest\` to install it"; return 1;
+  # If the current repo is test-infra, run kntest from source.
+  if [[ "${REPO_NAME}" == "test-infra" ]]; then
+    # Each parameter can possibly be in the format of "--xxx yyy", using $@ can automatically split them into multiple positional arguments for the command.
+    # shellcheck disable=SC2068
+    go run "${REPO_ROOT_DIR}"/kntest/cmd/kntest $@
+  # Otherwise kntest must be installed.
+  else
+    if [[ ! -x "$(command -v kntest)" ]]; then
+      echo "--- FAIL: kntest not installed, please clone test-infra repo and run \`go install ./kntest/cmd/kntest\` to install it"; return 1;
+    fi
+    # shellcheck disable=SC2068
+    kntest $@
   fi
-  kntest "$@"
 }
 
 # Run go-licenses to update licenses.