[buildbucket] report build count metrics

Before this CL is rolled out, PyBB needs to be updated to
stop reporting global metrics.

Change-Id: Iea643ebfc2ce4f8cf70e2f311cc1b99bc4c5c9e8
Bug: 1249664
Reviewed-on: https://chromium-review.googlesource.com/c/infra/luci/luci-go/+/3307576
Reviewed-by: smut <smut@google.com>
Commit-Queue: Scott Lee <ddoman@chromium.org>
diff --git a/buildbucket/appengine/internal/metrics/builder.go b/buildbucket/appengine/internal/metrics/builder.go
index 78b6f5b..b1fb8a2 100644
--- a/buildbucket/appengine/internal/metrics/builder.go
+++ b/buildbucket/appengine/internal/metrics/builder.go
@@ -220,6 +220,27 @@
 
 // reportBuildCount computes and reports # of builds with SCHEDULED and STARTED.
 func reportBuildCount(ctx context.Context, project, bucket, legacyBucket, builder string) error {
-	// TODO(ddoman): implement me
+	var nScheduled, nStarted int64
+	q := datastore.NewQuery(model.BuildKind).
+		Eq("bucket_id", protoutil.FormatBucketID(project, bucket)).
+		Eq("experimental", false).
+		Eq("tags", "builder:"+builder)
+	eg, ctx := errgroup.WithContext(ctx)
+	eg.Go(func() (err error) {
+		nScheduled, err = datastore.Count(ctx, q.Eq("status_v2", pb.Status_SCHEDULED))
+		return
+	})
+	eg.Go(func() (err error) {
+		nStarted, err = datastore.Count(ctx, q.Eq("status_v2", pb.Status_STARTED))
+		return
+	})
+	if err := eg.Wait(); err != nil {
+		return err
+	}
+
+	V1.BuildCount.Set(ctx, nScheduled, legacyBucket, builder, pb.Status_name[int32(pb.Status_SCHEDULED)])
+	V1.BuildCount.Set(ctx, nStarted, legacyBucket, builder, pb.Status_name[int32(pb.Status_STARTED)])
+	V2.BuildCount.Set(ctx, nScheduled, pb.Status_name[int32(pb.Status_SCHEDULED)])
+	V2.BuildCount.Set(ctx, nStarted, pb.Status_name[int32(pb.Status_STARTED)])
 	return nil
 }
diff --git a/buildbucket/appengine/internal/metrics/builder_test.go b/buildbucket/appengine/internal/metrics/builder_test.go
index d4f1ef0..4071500 100644
--- a/buildbucket/appengine/internal/metrics/builder_test.go
+++ b/buildbucket/appengine/internal/metrics/builder_test.go
@@ -51,9 +51,9 @@
 		store := tsmon.Store(ctx)
 		prj, bkt := "infra", "ci"
 		task := &target.Task{
-			ServiceName: "service",
+			ServiceName: "svc",
 			JobName:     "job",
-			HostName:    "ins-0",
+			HostName:    "ins",
 			TaskNum:     0,
 		}
 		store.SetDefaultTarget(task)
diff --git a/buildbucket/appengine/internal/metrics/v1.go b/buildbucket/appengine/internal/metrics/v1.go
index 346652d..4457f32 100644
--- a/buildbucket/appengine/internal/metrics/v1.go
+++ b/buildbucket/appengine/internal/metrics/v1.go
@@ -36,11 +36,13 @@
 		"failure_reason":       field.String("failure_reason"),
 		"must_be_never_leased": field.Bool("must_be_never_leased"),
 		"result":               field.String("result"),
+		"status":               field.String("status"),
 		"user_agent":           field.String("user_agent"),
 	}
 
 	// V1 is a collection of metric objects for V1 metrics.
 	V1 = struct {
+		BuildCount              metric.Int
 		BuildCountCreated       metric.Counter
 		BuildCountStarted       metric.Counter
 		BuildCountCompleted     metric.Counter
@@ -49,6 +51,11 @@
 		BuildDurationScheduling metric.CumulativeDistribution
 		MaxAgeScheduled         metric.Float
 	}{
+		BuildCount: metric.NewInt(
+			"buildbucket/builds/count",
+			"Number of pending/running prod builds", nil,
+			bFields("status")...,
+		),
 		BuildCountCreated: metric.NewCounter(
 			"buildbucket/builds/created",
 			"Build creation", nil,
diff --git a/buildbucket/appengine/internal/metrics/v2.go b/buildbucket/appengine/internal/metrics/v2.go
index e3797f4..40db0a7 100644
--- a/buildbucket/appengine/internal/metrics/v2.go
+++ b/buildbucket/appengine/internal/metrics/v2.go
@@ -15,6 +15,7 @@
 package metrics
 
 import (
+	"go.chromium.org/luci/common/tsmon/field"
 	"go.chromium.org/luci/common/tsmon/metric"
 	"go.chromium.org/luci/common/tsmon/types"
 )
@@ -22,9 +23,17 @@
 var (
 	// V2 is a collection of metric objects for V2 metrics.
 	V2 = struct {
+		BuildCount      metric.Int
 		BuilderPresence metric.Bool
 		MaxAgeScheduled metric.Float
 	}{
+		BuildCount: metric.NewIntWithTargetType(
+			"buildbucket/v2/builds/count",
+			(&Builder{}).Type(),
+			"Number of pending/running prod builds",
+			nil,
+			field.String("status"),
+		),
 		BuilderPresence: metric.NewBoolWithTargetType(
 			"buildbucket/v2/builder/presence",
 			(&Builder{}).Type(),