Repository: knative/serving
Pull request #8798 (2020-07-24 09:00:15)

Propagate labels and annotations from Route to placeholder service
This patch changes to propagate placeholder service's labels and
annotations from Route.

Currently there are no way to add annotation and labels to the
placeholder service except for manual.

/lint

**Release Note**

```release-note
Placeholder service's labels and annotations are propagated from Route.
```


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

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

- ~~[pkg/reconciler/route/OWNERS](https://github.com/knative/serving/blob/master/pkg/reconciler/route/OWNERS)~~ [nak3]

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":[]} -->
nak3: This is based on user's request: https://knative.slack.com/archives/CA9RHBGJX/p1595390943143500

And one note:
I didn't change it to reconcile the placeholder service aggressively.
It means that even if we updated the label to the route, the placeholder service is not reconciled automatically. It is the same behavior with current KIngress.


Patch:
diff --git a/cmd/activator/main.go b/cmd/activator/main.go
index 81c4172a5..37ad183cb 100644
--- a/cmd/activator/main.go
+++ b/cmd/activator/main.go
@@ -68,9 +68,6 @@ import (
 const (
 	component = "activator"
 
-	// Add enough buffer to not block request serving on stats collection
-	requestCountingQueueLength = 100
-
 	// The port on which autoscaler WebSocket server listens.
 	autoscalerPort = ":8080"
 )
@@ -168,9 +165,6 @@ func main() {
 	statCh := make(chan []asmetrics.StatMessage)
 	defer close(statCh)
 
-	reqCh := make(chan network.ReqEvent, requestCountingQueueLength)
-	defer close(reqCh)
-
 	// Start throttler.
 	throttler := activatornet.NewThrottler(ctx, env.PodIP)
 	go throttler.Run(ctx)
@@ -198,13 +192,13 @@ func main() {
 	go statReporter(statSink, statCh, logger)
 
 	// Create and run our concurrency reporter
-	cr := activatorhandler.NewConcurrencyReporter(ctx, env.PodName, reqCh, statCh)
-	go cr.Run(ctx.Done())
+	concurrencyReporter := activatorhandler.NewConcurrencyReporter(ctx, env.PodName, statCh)
+	go concurrencyReporter.Run(ctx.Done())
 
 	// Create activation handler chain
 	// Note: innermost handlers are specified first, ie. the last handler in the chain will be executed first
 	var ah http.Handler = activatorhandler.New(ctx, throttler)
-	ah = activatorhandler.NewRequestEventHandler(reqCh, ah)
+	ah = concurrencyReporter.Handler(ah)
 	ah = tracing.HTTPSpanMiddleware(ah)
 	ah = configStore.HTTPMiddleware(ah)
 	reqLogHandler, err := pkghttp.NewRequestLogHandler(ah, logging.NewSyncFileWriter(os.Stdout), "",
diff --git a/cmd/queue/main.go b/cmd/queue/main.go
index 39215cc18..b91e18d64 100644
--- a/cmd/queue/main.go
+++ b/cmd/queue/main.go
@@ -329,7 +329,7 @@ func buildServer(env config, healthState *health.State, rp *readiness.Probe, sta
 	httpProxy.Transport = buildTransport(env, logger)
 	httpProxy.ErrorHandler = pkgnet.ErrorHandler(logger)
 	httpProxy.BufferPool = network.NewBufferPool()
-	httpProxy.FlushInterval = -1
+	httpProxy.FlushInterval = network.FlushInterval
 	activatorutil.SetupHeaderPruning(httpProxy)
 
 	breaker := buildBreaker(env)
diff --git a/go.mod b/go.mod
index 56ee6a681..80baef609 100644
--- a/go.mod
+++ b/go.mod
@@ -40,8 +40,8 @@ require (
 	k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29
 	knative.dev/caching v0.0.0-20200723145958-d031ce9de55d
 	knative.dev/networking v0.0.0-20200723155758-cc457d7322d6
-	knative.dev/pkg v0.0.0-20200723172857-6167cd9c1f2b
-	knative.dev/test-infra v0.0.0-20200723141258-383af8175387
+	knative.dev/pkg v0.0.0-20200724055557-c36f46cc8c80
+	knative.dev/test-infra v0.0.0-20200723182457-517b66ba19c1
 )
 
 replace (
diff --git a/go.sum b/go.sum
index 9d6397b1b..404461dda 100644
--- a/go.sum
+++ b/go.sum
@@ -1639,13 +1639,13 @@ knative.dev/networking v0.0.0-20200723155758-cc457d7322d6 h1:E3icpGu2JG84UXvrG/3
 knative.dev/networking v0.0.0-20200723155758-cc457d7322d6/go.mod h1:Xd6H9nzGwHI/tfEnvrm7dQjYP1s/CbZL8J941HyUaSE=
 knative.dev/pkg v0.0.0-20200207155214-fef852970f43/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
 knative.dev/pkg v0.0.0-20200723060257-ae9c3f7fa8d3/go.mod h1:pOJ+tu5k/SgHPGmecWqFguxJcIZozyDWlTlyldGcYGM=
-knative.dev/pkg v0.0.0-20200723172857-6167cd9c1f2b h1:H3BgBC4oIKseilY6RaCl8f3H0JiG3LeR9L79NvbIbtw=
-knative.dev/pkg v0.0.0-20200723172857-6167cd9c1f2b/go.mod h1:GtoTEupsOzadgRKT4GgPWukbhAcINwDDGcKibTyd1Fk=
+knative.dev/pkg v0.0.0-20200724055557-c36f46cc8c80 h1:rA0vidKv+Xr4hVsFV4KSenD0Ytalwlbs83RPuvTIBnI=
+knative.dev/pkg v0.0.0-20200724055557-c36f46cc8c80/go.mod h1:GtoTEupsOzadgRKT4GgPWukbhAcINwDDGcKibTyd1Fk=
 knative.dev/test-infra v0.0.0-20200721175154-c98db9bd4d5d h1:GFzUmKNa8SVL3uMwa3R+NOjaSY1oUzxNdNWW3j5HrZ4=
 knative.dev/test-infra v0.0.0-20200721175154-c98db9bd4d5d/go.mod h1:kzRhTm5L08eDQFRl8NKSAN93lz6IZWQMs+2TjTCN+VA=
 knative.dev/test-infra v0.0.0-20200722142057-3ca910b5a25e/go.mod h1:oHmDsPmq+zcc3b+Z94Kgmrz1JnmZEz36jmKuvL2Lw7o=
-knative.dev/test-infra v0.0.0-20200723141258-383af8175387 h1:9asfovmD7n5kydyrUTV9XWbyu2LKfPCR1w9b3vycnck=
-knative.dev/test-infra v0.0.0-20200723141258-383af8175387/go.mod h1:Y3sNbLSTA11kRcLvTzRsZCxO+GQuw7KV7+PL+iD+9CA=
+knative.dev/test-infra v0.0.0-20200723182457-517b66ba19c1 h1:auvu/bG10qo06+InTTHMa0BO0P++Pe937kjRi8zGFps=
+knative.dev/test-infra v0.0.0-20200723182457-517b66ba19c1/go.mod h1:Y3sNbLSTA11kRcLvTzRsZCxO+GQuw7KV7+PL+iD+9CA=
 modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
diff --git a/pkg/activator/handler/concurrency_reporter.go b/pkg/activator/handler/concurrency_reporter.go
index ccad0640b..3621d52db 100644
--- a/pkg/activator/handler/concurrency_reporter.go
+++ b/pkg/activator/handler/concurrency_reporter.go
@@ -19,6 +19,7 @@ package handler
 import (
 	"context"
 	"math"
+	"net/http"
 	"sync"
 	"time"
 
@@ -29,6 +30,7 @@ import (
 	"knative.dev/pkg/logging"
 	pkgmetrics "knative.dev/pkg/metrics"
 	"knative.dev/serving/pkg/activator"
+	"knative.dev/serving/pkg/activator/util"
 	"knative.dev/serving/pkg/apis/serving"
 	asmetrics "knative.dev/serving/pkg/autoscaler/metrics"
 	revisioninformer "knative.dev/serving/pkg/client/injection/informers/serving/v1/revision"
@@ -44,8 +46,6 @@ type ConcurrencyReporter struct {
 	logger  *zap.SugaredLogger
 	podName string
 
-	// Ticks with every request arrived/completed respectively
-	reqCh chan network.ReqEvent
 	// Stat reporting channel
 	statCh chan []asmetrics.StatMessage
 
@@ -65,12 +65,10 @@ type ConcurrencyReporter struct {
 
 // NewConcurrencyReporter creates a ConcurrencyReporter which listens to incoming
 // ReqEvents on reqCh and ticks on reportCh and reports stats on statCh.
-func NewConcurrencyReporter(ctx context.Context, podName string,
-	reqCh chan network.ReqEvent, statCh chan []asmetrics.StatMessage) *ConcurrencyReporter {
+func NewConcurrencyReporter(ctx context.Context, podName string, statCh chan []asmetrics.StatMessage) *ConcurrencyReporter {
 	return &ConcurrencyReporter{
 		logger:  logging.FromContext(ctx),
 		podName: podName,
-		reqCh:   reqCh,
 		statCh:  statCh,
 		rl:      revisioninformer.Get(ctx).Lister(),
 
@@ -154,13 +152,11 @@ func (cr *ConcurrencyReporter) report(now time.Time) []asmetrics.StatMessage {
 		messages = append(messages, asmetrics.StatMessage{
 			Key: key,
 			Stat: asmetrics.Stat{
-				// Stat time is unset by design. The receiver will set the time.
 				PodName:                   cr.podName,
 				AverageConcurrentRequests: adjustedConcurrency,
 				RequestCount:              adjustedCount,
 			},
 		})
-		cr.reportToMetricsBackend(key, report.AverageConcurrency)
 	}
 	cr.reportedFirstRequest = make(map[types.NamespacedName]float64)
 
@@ -192,10 +188,11 @@ func (cr *ConcurrencyReporter) Run(stopCh <-chan struct{}) {
 func (cr *ConcurrencyReporter) run(stopCh <-chan struct{}, reportCh <-chan time.Time) {
 	for {
 		select {
-		case event := <-cr.reqCh:
-			cr.handleEvent(event)
 		case now := <-reportCh:
 			msgs := cr.report(now)
+			for _, msg := range msgs {
+				cr.reportToMetricsBackend(msg.Key, msg.Stat.AverageConcurrentRequests)
+			}
 			if len(msgs) > 0 {
 				cr.statCh <- msgs
 			}
@@ -204,3 +201,17 @@ func (cr *ConcurrencyReporter) run(stopCh <-chan struct{}, reportCh <-chan time.
 		}
 	}
 }
+
+// Handler returns a handler that records requests coming in/being finished in the stats
+// machinery.
+func (cr *ConcurrencyReporter) Handler(next http.Handler) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		revisionKey := util.RevIDFrom(r.Context())
+		cr.handleEvent(network.ReqEvent{Key: revisionKey, Type: network.ReqIn, Time: time.Now()})
+		defer func() {
+			cr.handleEvent(network.ReqEvent{Key: revisionKey, Type: network.ReqOut, Time: time.Now()})
+		}()
+
+		next.ServeHTTP(w, r)
+	}
+}
diff --git a/pkg/activator/handler/handler.go b/pkg/activator/handler/handler.go
index 9d789a024..246bd17fe 100644
--- a/pkg/activator/handler/handler.go
+++ b/pkg/activator/handler/handler.go
@@ -115,7 +115,7 @@ func (a *activationHandler) proxyRequest(logger *zap.SugaredLogger, w http.Respo
 	if tracingEnabled {
 		proxy.Transport = a.tracingTransport
 	}
-	proxy.FlushInterval = -1
+	proxy.FlushInterval = network.FlushInterval
 	proxy.ErrorHandler = pkgnet.ErrorHandler(logger)
 	util.SetupHeaderPruning(proxy)
 
diff --git a/pkg/activator/handler/requestevent_handler.go b/pkg/activator/handler/requestevent_handler.go
deleted file mode 100644
index ab29a2f00..000000000
--- a/pkg/activator/handler/requestevent_handler.go
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-Copyright 2018 The Knative Authors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package handler
-
-import (
-	"net/http"
-	"time"
-
-	"knative.dev/serving/pkg/activator/util"
-	"knative.dev/serving/pkg/network"
-)
-
-// NewRequestEventHandler creates a handler that sends events
-// about incoming/closed http connections to the given channel.
-func NewRequestEventHandler(reqChan chan network.ReqEvent, next http.Handler) *RequestEventHandler {
-	handler := &RequestEventHandler{
-		nextHandler: next,
-		reqChan:     reqChan,
-	}
-
-	return handler
-}
-
-// RequestEventHandler sends events to the given channel.
-type RequestEventHandler struct {
-	nextHandler http.Handler
-	reqChan     chan network.ReqEvent
-}
-
-func (h *RequestEventHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	revisionKey := util.RevIDFrom(r.Context())
-
-	h.reqChan <- network.ReqEvent{Key: revisionKey, Type: network.ReqIn, Time: time.Now()}
-	defer func() {
-		h.reqChan <- network.ReqEvent{Key: revisionKey, Type: network.ReqOut, Time: time.Now()}
-	}()
-	h.nextHandler.ServeHTTP(w, r)
-}
diff --git a/pkg/network/network.go b/pkg/network/network.go
index 818b77d5d..110150c17 100644
--- a/pkg/network/network.go
+++ b/pkg/network/network.go
@@ -26,6 +26,7 @@ import (
 	"net/url"
 	"strings"
 	"text/template"
+	"time"
 
 	lru "github.com/hashicorp/golang-lru"
 	corev1 "k8s.io/api/core/v1"
@@ -157,6 +158,13 @@ const (
 
 	// ProtoAcceptContent is the content type to be used when autoscaler scrapes metrics from the QP
 	ProtoAcceptContent = "application/protobuf"
+
+	// FlushInterval controls the time when we flush the connection in the
+	// reverse proxies (Activator, QP).
+	// NB: having it equal to 0 is a problem for streaming requests
+	// since the data won't be transfered in chunks less than 4kb, if the
+	// reverse proxy fails to detect streaming (gRPC, e.g.).
+	FlushInterval = 20 * time.Millisecond
 )
 
 // DomainTemplateValues are the available properties people can choose from
diff --git a/pkg/reconciler/route/resources/service.go b/pkg/reconciler/route/resources/service.go
index c287f83d9..0c0cfdead 100644
--- a/pkg/reconciler/route/resources/service.go
+++ b/pkg/reconciler/route/resources/service.go
@@ -112,7 +112,8 @@ func makeK8sService(ctx context.Context, route *v1.Route, targetName string) (*c
 				// This service is owned by the Route.
 				*kmeta.NewControllerRef(route),
 			},
-			Labels: svcLabels,
+			Labels:      kmeta.UnionMaps(route.GetLabels(), svcLabels),
+			Annotations: route.GetAnnotations(),
 		},
 	}, nil
 }
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 6df0ed18d..e3201695d 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1052,7 +1052,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-20200723172857-6167cd9c1f2b
+# knative.dev/pkg v0.0.0-20200724055557-c36f46cc8c80
 ## explicit
 knative.dev/pkg/apiextensions/storageversion
 knative.dev/pkg/apiextensions/storageversion/cmd/migrate
@@ -1176,7 +1176,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-20200723141258-383af8175387
+# knative.dev/test-infra v0.0.0-20200723182457-517b66ba19c1
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/yaml v1.2.0


Test patch:
diff --git a/pkg/activator/handler/concurrency_reporter_test.go b/pkg/activator/handler/concurrency_reporter_test.go
index e78f47692..662183191 100644
--- a/pkg/activator/handler/concurrency_reporter_test.go
+++ b/pkg/activator/handler/concurrency_reporter_test.go
@@ -18,6 +18,9 @@ package handler
 
 import (
 	"context"
+	"fmt"
+	"net/http"
+	"net/http/httptest"
 	"sort"
 	"strconv"
 	"testing"
@@ -29,6 +32,7 @@ import (
 	"knative.dev/pkg/metrics/metricstest"
 	_ "knative.dev/pkg/metrics/testing"
 	rtesting "knative.dev/pkg/reconciler/testing"
+	"knative.dev/serving/pkg/activator/util"
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
 	"knative.dev/serving/pkg/autoscaler/metrics"
 	asmetrics "knative.dev/serving/pkg/autoscaler/metrics"
@@ -309,23 +313,22 @@ func TestStats(t *testing.T) {
 
 	for _, tc := range tt {
 		t.Run(tc.name, func(t *testing.T) {
-			s, cr, ctx, cancel := newTestStats(t)
+			cr, _, cancel := newTestReporter(t)
 			defer cancel()
-			go func() {
-				cr.run(ctx.Done(), s.reportBiChan)
-				close(s.reportBiChan)
-			}()
 
 			// Apply request operations
 			for _, op := range tc.ops {
 				time := time.Time{}.Add(time.Duration(op.time) * time.Millisecond)
 				switch op.op {
 				case requestOpStart:
-					s.reqChan <- network.ReqEvent{Key: op.key, Type: network.ReqIn, Time: time}
+					cr.handleEvent(network.ReqEvent{Key: op.key, Type: network.ReqIn, Time: time})
 				case requestOpEnd:
-					s.reqChan <- network.ReqEvent{Key: op.key, Type: network.ReqOut, Time: time}
+					cr.handleEvent(network.ReqEvent{Key: op.key, Type: network.ReqOut, Time: time})
 				case requestOpTick:
-					s.reportBiChan <- time
+					stats := cr.report(time)
+					if len(stats) > 0 {
+						cr.statCh <- stats
+					}
 				}
 			}
 
@@ -333,7 +336,7 @@ func TestStats(t *testing.T) {
 			stats := make([]metrics.StatMessage, 0, len(tc.expectedStats))
 			for len(stats) < len(tc.expectedStats) {
 				select {
-				case x := <-s.statChan:
+				case x := <-cr.statCh:
 					stats = append(stats, x...)
 				case <-time.After(time.Second):
 					t.Fatal("Timed out waiting for the event")
@@ -342,7 +345,7 @@ func TestStats(t *testing.T) {
 
 			// Verify we're not getting extra events.
 			select {
-			case x := <-s.statChan:
+			case x := <-cr.statCh:
 				t.Fatal("Extra events received:", x)
 			case <-time.After(5 * time.Millisecond):
 				// Lookin' good.
@@ -366,22 +369,116 @@ func TestStats(t *testing.T) {
 	}
 }
 
+func TestConcurrencyReporterRun(t *testing.T) {
+	cr, ctx, cancel := newTestReporter(t)
+	defer cancel()
+
+	reportCh := make(chan time.Time)
+	go func() {
+		cr.run(ctx.Done(), reportCh)
+		close(reportCh)
+	}()
+
+	now := time.Time{}
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn, Time: now})
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn, Time: now})
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn, Time: now})
+
+	reportCh <- now.Add(1)
+
+	want := []metrics.StatMessage{{
+		Key: rev1,
+		Stat: metrics.Stat{
+			AverageConcurrentRequests: 1,
+			RequestCount:              1,
+			PodName:                   activatorPodName,
+		},
+	}, {
+		Key: rev1,
+		Stat: metrics.Stat{
+			AverageConcurrentRequests: 2, // Discounted via the from 0 stat.
+			RequestCount:              2, // Discounted via the from 0 stat.
+			PodName:                   activatorPodName,
+		},
+	}}
+
+	reportCh <- time.Now()
+
+	got := make([]metrics.StatMessage, 0, len(want))
+	got = append(got, <-cr.statCh...) // Scale from 0.
+	got = append(got, <-cr.statCh...) // Actual report.
+	if !cmp.Equal(got, want) {
+		t.Errorf("Unexpected stats (-want +got): %s", cmp.Diff(want, got))
+	}
+}
+
+func TestConcurrencyReporterHandler(t *testing.T) {
+	cr, ctx, cancel := newTestReporter(t)
+	defer cancel()
+
+	reportCh := make(chan time.Time)
+	go func() {
+		cr.run(ctx.Done(), reportCh)
+		close(reportCh)
+	}()
+
+	baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
+	handler := cr.Handler(baseHandler)
+
+	resp := httptest.NewRecorder()
+	req := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
+	rCtx := util.WithRevID(context.Background(), rev1)
+
+	// Send a few requests.
+	handler.ServeHTTP(resp, req.WithContext(rCtx))
+	handler.ServeHTTP(resp, req.WithContext(rCtx))
+	handler.ServeHTTP(resp, req.WithContext(rCtx))
+
+	want := []metrics.StatMessage{{
+		Key: rev1,
+		Stat: metrics.Stat{
+			AverageConcurrentRequests: 1,
+			RequestCount:              1,
+			PodName:                   activatorPodName,
+		},
+	}, {
+		Key: rev1,
+		Stat: metrics.Stat{
+			AverageConcurrentRequests: 0, // Discounted via the from 0 stat.
+			RequestCount:              2, // Discounted via the from 0 stat.
+			PodName:                   activatorPodName,
+		},
+	}}
+
+	reportCh <- time.Now()
+
+	got := make([]metrics.StatMessage, 0, len(want))
+	got = append(got, <-cr.statCh...) // Scale from 0.
+	got = append(got, <-cr.statCh...) // Actual report.
+	if !cmp.Equal(got, want) {
+		t.Errorf("Unexpected stats (-want +got): %s", cmp.Diff(want, got))
+	}
+}
+
 func TestMetricsReported(t *testing.T) {
 	reset()
-	s, cr, ctx, cancel := newTestStats(t)
+	cr, ctx, cancel := newTestReporter(t)
 	defer cancel()
+
+	reportCh := make(chan time.Time)
 	go func() {
-		cr.run(ctx.Done(), s.reportBiChan)
-		close(s.reportBiChan)
+		cr.run(ctx.Done(), reportCh)
+		close(reportCh)
 	}()
 
-	s.reqChan <- network.ReqEvent{Key: rev1, Type: network.ReqIn}
-	s.reqChan <- network.ReqEvent{Key: rev1, Type: network.ReqIn}
-	s.reqChan <- network.ReqEvent{Key: rev1, Type: network.ReqIn}
-	s.reqChan <- network.ReqEvent{Key: rev1, Type: network.ReqIn}
-	s.reportBiChan <- time.Time{}.Add(1 * time.Millisecond)
-	<-s.statChan // The scale from 0 quick-report
-	<-s.statChan // The actual report we want to see
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn})
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn})
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn})
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn})
+
+	reportCh <- time.Now()
+	<-cr.statCh
+	<-cr.statCh
 
 	wantTags := map[string]string{
 		metricskey.LabelRevisionName:      rev1.Name,
@@ -391,31 +488,23 @@ func TestMetricsReported(t *testing.T) {
 		metricskey.PodName:                "the-best-activator",
 		metricskey.ContainerName:          "activator",
 	}
-	metricstest.CheckLastValueData(t, "request_concurrency", wantTags, 4)
-}
+	metricstest.CheckLastValueData(t, "request_concurrency", wantTags, 3)
 
-// Test type to hold the bi-directional time channels
-type testStats struct {
-	reqChan      chan network.ReqEvent
-	statChan     chan []metrics.StatMessage
-	reportBiChan chan time.Time
+	reportCh <- time.Now()
+	<-cr.statCh
+
+	metricstest.CheckLastValueData(t, "request_concurrency", wantTags, 4)
 }
 
-func newTestStats(t *testing.T) (*testStats, *ConcurrencyReporter, context.Context, context.CancelFunc) {
-	reportBiChan := make(chan time.Time)
-	ts := &testStats{
-		reqChan: make(chan network.ReqEvent),
-		// Buffered channel permits avoiding sending the test commands on the separate go routine
-		// simplifying main test process.
-		statChan:     make(chan []metrics.StatMessage, 10),
-		reportBiChan: reportBiChan,
-	}
+func newTestReporter(t *testing.T) (*ConcurrencyReporter, context.Context, context.CancelFunc) {
 	ctx, cancel, _ := rtesting.SetupFakeContextWithCancel(t)
 	revisionInformer(ctx, revision(rev1.Namespace, rev1.Name),
 		revision(rev2.Namespace, rev2.Name), revision(rev3.Namespace, rev3.Name))
 
-	return ts, NewConcurrencyReporter(ctx, activatorPodName,
-		ts.reqChan, ts.statChan), ctx, cancel
+	// Buffered channel permits avoiding sending the test commands on the separate go routine
+	// simplifying main test process.
+	statCh := make(chan []metrics.StatMessage, 10)
+	return NewConcurrencyReporter(ctx, activatorPodName, statCh), ctx, cancel
 }
 
 func revisionInformer(ctx context.Context, revs ...*v1.Revision) {
@@ -433,9 +522,8 @@ func BenchmarkConcurrencyReporter(b *testing.B) {
 	defer cancel()
 
 	// Buffer equal to the activator.
-	reqCh := make(chan network.ReqEvent, 100)
 	statCh := make(chan []asmetrics.StatMessage)
-	cr := NewConcurrencyReporter(ctx, activatorPodName, reqCh, statCh)
+	cr := NewConcurrencyReporter(ctx, activatorPodName, statCh)
 
 	stopCh := make(chan struct{})
 	defer close(stopCh)
@@ -452,29 +540,25 @@ func BenchmarkConcurrencyReporter(b *testing.B) {
 		}
 	}()
 
+	fake := fakeservingclient.Get(ctx)
+	revisions := fakerevisioninformer.Get(ctx)
+
 	// Spread the load across 100 revisions.
 	keys := make([]types.NamespacedName, 0, 100)
 	for i := 0; i < cap(keys); i++ {
-		keys = append(keys, types.NamespacedName{
+		key := types.NamespacedName{
 			Namespace: testNamespace,
 			Name:      testRevName + strconv.Itoa(i),
-		})
-	}
-
-	requestChannel := func(key types.NamespacedName) {
-		reqCh <- network.ReqEvent{
-			Time: time.Now(),
-			Type: network.ReqIn,
-			Key:  key,
-		}
-		reqCh <- network.ReqEvent{
-			Time: time.Now(),
-			Type: network.ReqOut,
-			Key:  key,
 		}
+		keys = append(keys, key)
+
+		// Create revisions in the fake clients to trigger report logic.
+		rev := revision(key.Namespace, key.Name)
+		fake.ServingV1().Revisions(rev.Namespace).Create(rev)
+		revisions.Informer().GetIndexer().Add(rev)
 	}
 
-	requestDirect := func(key types.NamespacedName) {
+	request := func(key types.NamespacedName) {
 		cr.handleEvent(network.ReqEvent{
 			Time: time.Now(),
 			Type: network.ReqIn,
@@ -487,39 +571,71 @@ func BenchmarkConcurrencyReporter(b *testing.B) {
 		})
 	}
 
-	b.Run("sequential-direct", func(b *testing.B) {
+	b.Run("sequential", func(b *testing.B) {
 		for j := 0; j < b.N; j++ {
 			key := keys[j%len(keys)]
-			requestDirect(key)
+			request(key)
 		}
 	})
 
-	b.Run("sequential-channel", func(b *testing.B) {
-		for j := 0; j < b.N; j++ {
-			key := keys[j%len(keys)]
-			requestChannel(key)
-		}
-	})
-
-	b.Run("parallel-direct", func(b *testing.B) {
+	b.Run("parallel", func(b *testing.B) {
 		b.RunParallel(func(pb *testing.PB) {
 			var j int
 			for pb.Next() {
 				key := keys[j%len(keys)]
-				requestDirect(key)
+				request(key)
 				j++
 			}
 		})
 	})
+}
 
-	b.Run("parallel-channel", func(b *testing.B) {
-		b.RunParallel(func(pb *testing.PB) {
-			var j int
-			for pb.Next() {
-				key := keys[j%len(keys)]
-				requestChannel(key)
-				j++
+// BenchmarkConcurrencyReporterReport benchmarks the report function specifically
+// to get a feeling for how expensive it is as it'll lock the mutex and thus block
+// requests for the respective time too.
+func BenchmarkConcurrencyReporterReport(b *testing.B) {
+	for _, revs := range []int{1, 5, 10, 50, 100, 200} {
+		b.Run(fmt.Sprintf("revs-%d", revs), func(b *testing.B) {
+			ctx, cancel, _ := rtesting.SetupFakeContextWithCancel(b)
+			defer cancel()
+
+			// Buffer equal to the activator.
+			statCh := make(chan []asmetrics.StatMessage)
+			cr := NewConcurrencyReporter(ctx, activatorPodName, statCh)
+
+			// Just read and ignore all stat messages.
+			go func() {
+				for {
+					select {
+					case <-statCh:
+					}
+				}
+			}()
+
+			fake := fakeservingclient.Get(ctx)
+			revisions := fakerevisioninformer.Get(ctx)
+			for i := 0; i < revs; i++ {
+				key := types.NamespacedName{
+					Namespace: testNamespace,
+					Name:      testRevName + strconv.Itoa(i),
+				}
+
+				// Create revisions in the fake clients to trigger report logic.
+				rev := revision(key.Namespace, key.Name)
+				fake.ServingV1().Revisions(rev.Namespace).Create(rev)
+				revisions.Informer().GetIndexer().Add(rev)
+
+				// Send a dummy request for each revision to make sure it's reported.
+				cr.handleEvent(network.ReqEvent{
+					Time: time.Now(),
+					Type: network.ReqIn,
+					Key:  key,
+				})
+			}
+
+			for j := 0; j < b.N; j++ {
+				cr.report(time.Now())
 			}
 		})
-	})
+	}
 }
diff --git a/pkg/activator/handler/requestevent_handler_test.go b/pkg/activator/handler/requestevent_handler_test.go
deleted file mode 100644
index 59e431340..000000000
--- a/pkg/activator/handler/requestevent_handler_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-Copyright 2018 The Knative Authors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-package handler
-
-import (
-	"bytes"
-	"context"
-	"net/http"
-	"net/http/httptest"
-	"testing"
-	"time"
-
-	"k8s.io/apimachinery/pkg/types"
-	"knative.dev/serving/pkg/activator/util"
-	"knative.dev/serving/pkg/network"
-)
-
-func TestRequestEventHandler(t *testing.T) {
-	namespace := "testspace"
-	revision := "testrevision"
-	timeOffset := 1 * time.Millisecond
-
-	baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		w.WriteHeader(http.StatusOK)
-		time.Sleep(timeOffset)
-	})
-
-	reqChan := make(chan network.ReqEvent, 2)
-	handler := NewRequestEventHandler(reqChan, baseHandler)
-
-	resp := httptest.NewRecorder()
-	req := httptest.NewRequest(http.MethodPost, "http://example.com", bytes.NewBufferString(""))
-	key := types.NamespacedName{Namespace: namespace, Name: revision}
-	ctx := util.WithRevID(context.Background(), key)
-	handler.ServeHTTP(resp, req.WithContext(ctx))
-
-	in := <-handler.reqChan
-	if in.Key != key {
-		t.Errorf("in.Key = %v, want %v", in.Key, key)
-	}
-	if in.Type != network.ReqIn {
-		t.Errorf("in.Type = %v, want %v", in.Type, network.ReqIn)
-	}
-	if in.Time.IsZero() {
-		t.Error("in.Time = Time{}, want something real")
-	}
-
-	out := <-handler.reqChan
-	if out.Key != key {
-		t.Errorf("out.Key = %v, want %v", in.Key, key)
-	}
-	if out.Type != network.ReqOut {
-		t.Errorf("in.Type = %v, want %v", in.Type, network.ReqIn)
-	}
-	if out.Time.Sub(in.Time) < timeOffset {
-		t.Errorf("out.Time.Sub(in.Time) = %v, want > %v", out.Time.Sub(in.Time), timeOffset)
-	}
-}
diff --git a/pkg/reconciler/route/resources/service_test.go b/pkg/reconciler/route/resources/service_test.go
index 1adbb4f65..383edd4f7 100644
--- a/pkg/reconciler/route/resources/service_test.go
+++ b/pkg/reconciler/route/resources/service_test.go
@@ -252,6 +252,7 @@ func TestMakeK8sPlaceholderService(t *testing.T) {
 		name           string
 		expectedSpec   corev1.ServiceSpec
 		expectedLabels map[string]string
+		expectedAnnos  map[string]string
 		wantErr        bool
 		route          *v1.Route
 	}{{
@@ -266,6 +267,23 @@ func TestMakeK8sPlaceholderService(t *testing.T) {
 			serving.RouteLabelKey: "test-route",
 		},
 		wantErr: false,
+	}, {
+		name: "labels and annotations are propagated from the route",
+		route: Route("test-ns", "test-route",
+			WithRouteLabel(map[string]string{"route-label": "foo"}), WithRouteAnnotation(map[string]string{"route-anno": "bar"})),
+		expectedSpec: corev1.ServiceSpec{
+			Type:            corev1.ServiceTypeExternalName,
+			ExternalName:    "foo-test-route.test-ns.example.com",
+			SessionAffinity: corev1.ServiceAffinityNone,
+		},
+		expectedLabels: map[string]string{
+			serving.RouteLabelKey: "test-route",
+			"route-label":         "foo",
+		},
+		expectedAnnos: map[string]string{
+			"route-anno": "bar",
+		},
+		wantErr: false,
 	}, {
 		name:  "cluster local route",
 		route: Route("test-ns", "test-route", WithRouteLabel(map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal})),
@@ -275,7 +293,8 @@ func TestMakeK8sPlaceholderService(t *testing.T) {
 			SessionAffinity: corev1.ServiceAffinityNone,
 		},
 		expectedLabels: map[string]string{
-			serving.RouteLabelKey: "test-route",
+			serving.RouteLabelKey:      "test-route",
+			serving.VisibilityLabelKey: serving.VisibilityClusterLocal,
 		},
 	}}
 	for _, tt := range tests {
@@ -301,6 +320,9 @@ func TestMakeK8sPlaceholderService(t *testing.T) {
 			if !cmp.Equal(tt.expectedLabels, got.ObjectMeta.Labels) {
 				t.Errorf("Unexpected Labels (-want +got): %s", cmp.Diff(tt.expectedLabels, got.ObjectMeta.Labels))
 			}
+			if !cmp.Equal(tt.expectedAnnos, got.ObjectMeta.Annotations) {
+				t.Errorf("Unexpected Annotations (-want +got): %s", cmp.Diff(tt.expectedAnnos, got.ObjectMeta.Annotations))
+			}
 			if !cmp.Equal(tt.expectedSpec, got.Spec) {
 				t.Errorf("Unexpected ServiceSpec (-want +got): %s", cmp.Diff(tt.expectedSpec, got.Spec))
 			}
diff --git a/third_party/contour-latest/contour.yaml b/third_party/contour-latest/contour.yaml
index dc3c0f31b..d7773ec62 100644
--- a/third_party/contour-latest/contour.yaml
+++ b/third_party/contour-latest/contour.yaml
@@ -1432,7 +1432,7 @@ spec:
         # if we change this on each version, you can no longer upgrade
         # just by applying the deployment YAML.
         # See #2423, #2395, #2150, and #2030 for earlier questions about this.
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         command:
         - contour
@@ -1672,7 +1672,7 @@ spec:
         - --contour-key-file=/certs/tls.key
         - --config-path=/config/contour.yaml
         command: ["contour"]
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         name: contour
         ports:
@@ -1764,7 +1764,7 @@ spec:
         args:
         - envoy
         - shutdown-manager
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         lifecycle:
           preStop:
@@ -1840,7 +1840,7 @@ spec:
         - --envoy-key-file=/certs/tls.key
         command:
         - contour
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         name: envoy-initconfig
         volumeMounts:
@@ -3305,7 +3305,7 @@ spec:
         # if we change this on each version, you can no longer upgrade
         # just by applying the deployment YAML.
         # See #2423, #2395, #2150, and #2030 for earlier questions about this.
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         command:
         - contour
@@ -3545,7 +3545,7 @@ spec:
         - --contour-key-file=/certs/tls.key
         - --config-path=/config/contour.yaml
         command: ["contour"]
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         name: contour
         ports:
@@ -3637,7 +3637,7 @@ spec:
         args:
         - envoy
         - shutdown-manager
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         lifecycle:
           preStop:
@@ -3713,7 +3713,7 @@ spec:
         - --envoy-key-file=/certs/tls.key
         command:
         - contour
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         name: envoy-initconfig
         volumeMounts:
diff --git a/third_party/contour-latest/net-contour.yaml b/third_party/contour-latest/net-contour.yaml
index 0faaf082d..5f0c2c7b8 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: "v20200722-25763217"
+    serving.knative.dev/release: "v20200724-a051c55c"
 data:
   _example: |
     ################################
@@ -94,7 +94,7 @@ spec:
       - name: controller
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:522db3dea868014143ad78f85c8134edd0991176107295701f9c564d91637e52
+        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:6735fea99a7966d7830caa8b7c73052ce82efde86786cd9c9d0535a3d6dbaad8
         resources:
           requests:
             cpu: 40m
diff --git a/vendor/knative.dev/pkg/reconciler/testing/table.go b/vendor/knative.dev/pkg/reconciler/testing/table.go
index a8bb76199..6059fefec 100644
--- a/vendor/knative.dev/pkg/reconciler/testing/table.go
+++ b/vendor/knative.dev/pkg/reconciler/testing/table.go
@@ -98,6 +98,8 @@ type TableRow struct {
 	// testing framework. Instead it is used in the test method. E.g. setting up the responses for a
 	// mock client can go in here.
 	OtherTestData map[string]interface{}
+
+	CmpOpts []cmp.Option
 }
 
 func objKey(o runtime.Object) string {
@@ -175,7 +177,7 @@ func (r *TableRow) Test(t *testing.T, factory Factory) {
 			t.Errorf("Unexpected action[%d]: %#v", i, got)
 		}
 
-		if diff := cmp.Diff(want, obj, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty()); diff != "" {
+		if diff := cmp.Diff(want, obj, append(r.CmpOpts, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty())...); diff != "" {
 			t.Errorf("Unexpected create (-want, +got): %s", diff)
 		}
 	}
@@ -196,7 +198,7 @@ func (r *TableRow) Test(t *testing.T, factory Factory) {
 				continue
 			}
 			t.Errorf("Missing update for %s (-want, +prevState): %s", key,
-				cmp.Diff(wo, oldObj, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty()))
+				cmp.Diff(wo, oldObj, append(r.CmpOpts, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty())...))
 			continue
 		}
 
@@ -209,7 +211,7 @@ func (r *TableRow) Test(t *testing.T, factory Factory) {
 		// Update the object state.
 		objPrevState[objKey(got)] = got
 
-		if diff := cmp.Diff(want.GetObject(), got, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty()); diff != "" {
+		if diff := cmp.Diff(want.GetObject(), got, append(r.CmpOpts, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty())...); diff != "" {
 			t.Errorf("Unexpected update (-want, +got): %s", diff)
 		}
 	}
@@ -231,7 +233,7 @@ func (r *TableRow) Test(t *testing.T, factory Factory) {
 				continue
 			}
 			t.Errorf("Missing status update for %s (-want, +prevState): %s", key,
-				cmp.Diff(wo, oldObj, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty()))
+				cmp.Diff(wo, oldObj, append(r.CmpOpts, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty())...))
 			continue
 		}
 
@@ -240,7 +242,7 @@ func (r *TableRow) Test(t *testing.T, factory Factory) {
 		// Update the object state.
 		objPrevState[objKey(got)] = got
 
-		if diff := cmp.Diff(want.GetObject(), got, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty()); diff != "" {
+		if diff := cmp.Diff(want.GetObject(), got, append(r.CmpOpts, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty())...); diff != "" {
 			t.Errorf("Unexpected status update (-want, +got): %s\nFull: %v", diff, got)
 		}
 	}
@@ -254,7 +256,7 @@ func (r *TableRow) Test(t *testing.T, factory Factory) {
 				continue
 			}
 			t.Errorf("Extra status update for %s (-extra, +prevState): %s", key,
-				cmp.Diff(wo, oldObj, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty()))
+				cmp.Diff(wo, oldObj, append(r.CmpOpts, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty())...))
 		}
 	}
 
diff --git a/vendor/knative.dev/test-infra/scripts/presubmit-tests.sh b/vendor/knative.dev/test-infra/scripts/presubmit-tests.sh
index bd0402c51..37fca7be4 100644
--- a/vendor/knative.dev/test-infra/scripts/presubmit-tests.sh
+++ b/vendor/knative.dev/test-infra/scripts/presubmit-tests.sh
@@ -28,7 +28,7 @@ readonly PRESUBMIT_TEST_FAIL_FAST=${PRESUBMIT_TEST_FAIL_FAST:-0}
 readonly NO_PRESUBMIT_FILES=(\.png \.gitignore \.gitattributes ^OWNERS ^OWNERS_ALIASES ^AUTHORS)
 
 # Flag if this is a presubmit run or not.
-(( IS_PROW )) && [[ -n "${PULL_PULL_SHA}" ]] && IS_PRESUBMIT=1 || IS_PRESUBMIT=0
+(( IS_PROW )) && [[ ${JOB_TYPE} == "presubmit" ]] && IS_PRESUBMIT=1 || IS_PRESUBMIT=0
 readonly IS_PRESUBMIT
 
 # List of changed files on presubmit, LF separated.
@@ -109,7 +109,8 @@ function run_build_tests() {
 # Parameters: $1 - report name.
 #             $2... - command (test) to run.
 function report_build_test() {
-  local report="$(mktemp)"
+  local report
+  report="$(mktemp)"
   local report_name="$1"
   shift
   local errors=""
@@ -163,7 +164,8 @@ function default_build_test_runner() {
   [[ -z "${go_pkg_dirs}" ]] && return ${failed}
   # Ensure all the code builds
   subheader "Checking that go code builds"
-  local report="$(mktemp)"
+  local report
+  report="$(mktemp)"
   local errors_go1=""
   local errors_go2=""
   if ! capture_output "${report}" go build -v ./... ; then
@@ -172,10 +174,12 @@ function default_build_test_runner() {
     errors_go1="$(grep -v '^\(github\.com\|knative\.dev\)/' "${report}" | sort | uniq)"
   fi
   # Get all build tags in go code (ignore /vendor, /hack and /third_party)
-  local tags="$(grep -r '// +build' . \
+  local tags
+  tags="$(grep -r '// +build' . \
     | grep -v '^./vendor/' | grep -v '^./hack/' | grep -v '^./third_party' \
     | cut -f3 -d' ' | sort | uniq | tr '\n' ' ')"
-  local tagged_pkgs="$(grep -r '// +build' . \
+  local tagged_pkgs
+  tagged_pkgs="$(grep -r '// +build' . \
     | grep -v '^./vendor/' | grep -v '^./hack/' | grep -v '^./third_party' \
     | grep ":// +build " | cut -f1 -d: | xargs dirname \
     | sort | uniq | tr '\n' ' ')"
@@ -190,7 +194,8 @@ function default_build_test_runner() {
     rm -f e2e.test
   done
 
-  local errors_go="$(echo -e "${errors_go1}\n${errors_go2}" | uniq)"
+  local errors_go
+  errors_go="$(echo -e "${errors_go1}\n${errors_go2}" | uniq)"
   create_junit_xml _build_tests Build_Go "${errors_go}"
   # Check that we don't have any forbidden licenses in our images.
   subheader "Checking for forbidden licenses"
@@ -266,18 +271,16 @@ function run_integration_tests() {
 
 # Default integration test runner that runs all `test/e2e-*tests.sh`.
 function default_integration_test_runner() {
-  # options is always empty.
-  # TODO: remove it or indeed allow passing options.
-  local options=""
   local failed=0
-  for e2e_test in $(find test/ -name e2e-*tests.sh); do
+  find test/ ! -name "$(printf "*\n*")" -name "e2e-*tests.sh" > tmp
+  while IFS= read -r e2e_test
+  do
     echo "Running integration test ${e2e_test}"
-    if ! ${e2e_test} ${options}; then
+    if ! ${e2e_test}; then
       failed=1
-      step_failed "${e2e_test} ${options}"
+      step_failed "${e2e_test}"
     fi
-  done
-  return ${failed}
+  done < tmp
 }
 
 # Options set by command-line flags.
@@ -324,7 +327,7 @@ function main() {
     # (https://github.com/kubernetes/test-infra/blob/09bd4c6709dc64308406443f8996f90cf3b40ed1/jenkins/bootstrap.py#L588)
     # TODO(chaodaiG): follow up on https://github.com/kubernetes/test-infra/blob/0fabd2ea816daa8c15d410c77a0c93c0550b283f/prow/initupload/run.go#L49
     echo ">> node name"
-    echo "$(curl -H "Metadata-Flavor: Google" 'http://169.254.169.254/computeMetadata/v1/instance/name' 2> /dev/null)"
+    curl -H "Metadata-Flavor: Google" 'http://169.254.169.254/computeMetadata/v1/instance/name' 2> /dev/null
     echo ">> pod name"
     echo "${HOSTNAME}"
   fi
@@ -336,6 +339,15 @@ function main() {
   while [[ $# -ne 0 ]]; do
     local parameter=$1
     case ${parameter} in
+      --help|-h)
+        echo "Usage: ./presubmit-tests.sh [options...]"
+        echo "  --build-tests: run build tests."
+        echo "  --unit-tests: run unit tests."
+        echo "  --integration-tests: run integration tests, basically all the e2e-*tests.sh."
+        echo "  --all-tests: run build tests, unit tests and integration tests in sequence."
+        echo "  --run-test: run custom tests. Can be used to run multiple tests that need different args."
+        echo "              For example, ./presubmit-tests.sh --run-test \"e2e-tests1.sh arg1\" \"e2e-tests2.sh arg2\"."
+        ;;
       --build-tests) RUN_BUILD_TESTS=1 ;;
       --unit-tests) RUN_UNIT_TESTS=1 ;;
       --integration-tests) RUN_INTEGRATION_TESTS=1 ;;