cmd/ejobs: add an Estimated Time Left column to list command

- Add rudimentary ETF calculation: (enqueued - finished)/ processing
  rate.
- Jobs that have been cancelled or are finished are marked as Done.

Change-Id: I3f4d892b8ca4811dc95f9fce7697a42bad37b2f3
Reviewed-on: https://go-review.googlesource.com/c/pkgsite-metrics/+/697756
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Ethan Lee <ethanalee@google.com>
Reviewed-by: Neal Patel <nealpatel@google.com>
diff --git a/cmd/ejobs/main.go b/cmd/ejobs/main.go
index b98e5cb..fce4f00 100644
--- a/cmd/ejobs/main.go
+++ b/cmd/ejobs/main.go
@@ -211,18 +211,33 @@
 	d7 := -time.Hour * 24 * 7
 	weekBefore := time.Now().Add(d7)
 	tw := tabwriter.NewWriter(os.Stdout, 2, 8, 1, ' ', 0)
-	fmt.Fprintf(tw, "ID\tUser\tStart Time\tStarted\tFinished\tTotal\tCanceled\n")
+	fmt.Fprintf(tw, "ID\tUser\tStart Time\tStarted\tFinished\tTotal\tCanceled\tEstimated Time Left\n")
 	for _, j := range *joblist {
 		if userFilter != "" && j.User != userFilter {
 			continue
 		}
 		if j.StartedAt.After(weekBefore) {
-			fmt.Fprintf(tw, "%s\t%s\t%s\t%d\t%d\t%d\t%t\n",
+			done := j.NumFinished()
+			etaLabel := func(d time.Duration) string {
+				if d >= 0 {
+					return d.String()
+				}
+				return "Done"
+			}
+			var eta time.Duration
+			if !(done >= j.NumEnqueued || j.Canceled) {
+				elapsed := time.Since(j.StartedAt)
+				rate := float64(done) / elapsed.Seconds()
+				remainingTasks := float64(j.NumEnqueued - done)
+				eta = time.Duration(remainingTasks / rate * float64(time.Second)).Round(time.Minute)
+			}
+			fmt.Fprintf(tw, "%s\t%s\t%s\t%d\t%d\t%d\t%t\t%s\n",
 				j.ID(), j.User, j.StartedAt.Format(time.RFC3339),
 				j.NumStarted,
 				j.NumSkipped+j.NumFailed+j.NumErrored+j.NumSucceeded,
 				j.NumEnqueued,
-				j.Canceled)
+				j.Canceled,
+				etaLabel(eta))
 		}
 	}
 	return tw.Flush()