cmd/prober: check for stdlib in search

Add a probe that verifies that standard library packages are being
returned from relevant searches.

To do this, add a general way to check for a string in the response
body.

Also, add a flag to turn off metrics collection, to simplify running
locally.

Change-Id: I1ac0222d4c7d6e7ed87a79fe79aa0f91674f6941
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/342673
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
diff --git a/cmd/prober/main.go b/cmd/prober/main.go
index 6a5b9ce..9d238e6 100644
--- a/cmd/prober/main.go
+++ b/cmd/prober/main.go
@@ -31,7 +31,10 @@
 	"golang.org/x/pkgsite/internal/log"
 )
 
-var credsFile = flag.String("creds", "", "filename for credentials, when running locally")
+var (
+	credsFile     = flag.String("creds", "", "filename for credentials, when running locally")
+	exportMetrics = flag.Bool("metrics", true, "export metrics")
+)
 
 // A Probe represents a single HTTP GET request.
 type Probe struct {
@@ -46,6 +49,9 @@
 	// Whether or not to set a header that causes the frontend to skip the redis
 	// cache.
 	BypassCache bool
+
+	// If non-empty, the body should contain this string.
+	Contains string
 }
 
 var probes = []*Probe{
@@ -149,6 +155,12 @@
 		RelativeURL: "search?q=github",
 		BypassCache: true,
 	},
+	{
+		Name:        "search-http",
+		RelativeURL: "search?q=http",
+		BypassCache: true,
+		Contains:    "net/http",
+	},
 }
 
 func init() {
@@ -244,13 +256,15 @@
 	if err := view.Register(firstByteLatencyDistribution, probeCount); err != nil {
 		log.Fatalf(ctx, "view.Register: %v", err)
 	}
-	metricExporter, err = dcensus.NewViewExporter(cfg)
-	if err != nil {
-		log.Fatal(ctx, err)
-	}
+	if *exportMetrics {
+		metricExporter, err = dcensus.NewViewExporter(cfg)
+		if err != nil {
+			log.Fatal(ctx, err)
+		}
 
-	// To export metrics immediately, we use a metric reader.  See runProbes, below.
-	metricReader = metricexport.NewReader()
+		// To export metrics immediately, we use a metric reader.  See runProbes, below.
+		metricReader = metricexport.NewReader()
+	}
 
 	http.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
 		http.ServeFile(w, r, "static/shared/icon/favicon.ico")
@@ -320,9 +334,11 @@
 		s := runProbe(ctx, p)
 		statuses = append(statuses, s)
 	}
-	metricReader.ReadAndExport(metricExporter)
-	metricExporter.Flush()
-	log.Info(ctx, "metrics exported to StackDriver")
+	if metricReader != nil {
+		metricReader.ReadAndExport(metricExporter)
+		metricExporter.Flush()
+		log.Info(ctx, "metrics exported to StackDriver")
+	}
 	return statuses
 }
 
@@ -385,6 +401,11 @@
 		record("FAILED wrong body")
 		return status
 	}
+	if p.Contains != "" && !bytes.Contains(body, []byte(p.Contains)) {
+		status.Text = fmt.Sprintf("FAILED: body does not contain %q", p.Contains)
+		record("FAILED wrong body")
+		return status
+	}
 	status.Text = "OK"
 	record("200 OK")
 	return status