dashboard: report the running build to admins !119
2 files changed, +62 −0
Layout: unified · split
e2e/dashboardbuild_test.go added +48
| @@ -0,0 +1,48 @@ | |||
| 1 | package e2e | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "strings" | ||
| 5 | "testing" | ||
| 6 | ) | ||
| 7 | |||
| 8 | // The dashboard reports which build is serving, so the running commit is | ||
| 9 | // visible without reading the journal. It is admin-only: the exact build a | ||
| 10 | // host runs narrows down which known issues apply to it. | ||
| 11 | func TestDashboardReportsTheServerBuild(t *testing.T) { | ||
| 12 | inst := startInstance(t) | ||
| 13 | |||
| 14 | adminKey := inst.newKey(t, "root") | ||
| 15 | userKey := inst.newKey(t, "plain") | ||
| 16 | inst.admin(t, "admin", "user", "create", "root", "--key", adminKey+".pub", "--admin") | ||
| 17 | inst.admin(t, "admin", "user", "create", "plain", "--key", userKey+".pub") | ||
| 18 | |||
| 19 | out, errOut, code := inst.ssh(t, adminKey, "", "dashboard", "--json") | ||
| 20 | if code != 0 { | ||
| 21 | t.Fatalf("admin dashboard: %s", errOut) | ||
| 22 | } | ||
| 23 | if !strings.Contains(out, `"server":{"commit":"`) { | ||
| 24 | t.Fatalf("admin dashboard did not report the build:\n%s", out) | ||
| 25 | } | ||
| 26 | |||
| 27 | // A non-admin gets the same dashboard without it. omitempty drops the key | ||
| 28 | // entirely rather than reporting an empty string. | ||
| 29 | out, errOut, code = inst.ssh(t, userKey, "", "dashboard", "--json") | ||
| 30 | if code != 0 { | ||
| 31 | t.Fatalf("user dashboard: %s", errOut) | ||
| 32 | } | ||
| 33 | if strings.Contains(out, `"server"`) { | ||
| 34 | t.Fatalf("a non-admin was told the server build:\n%s", out) | ||
| 35 | } | ||
| 36 | if !strings.Contains(out, `"review_queue"`) { | ||
| 37 | t.Fatalf("user dashboard is missing its usual contents:\n%s", out) | ||
| 38 | } | ||
| 39 | |||
| 40 | // Human output carries it too, for the operator who did not ask for JSON. | ||
| 41 | out, errOut, code = inst.ssh(t, adminKey, "", "dashboard") | ||
| 42 | if code != 0 { | ||
| 43 | t.Fatalf("admin dashboard (human): %s", errOut) | ||
| 44 | } | ||
| 45 | if !strings.Contains(out, "server:") || !strings.Contains(out, "build ") { | ||
| 46 | t.Fatalf("human dashboard did not report the build:\n%s", out) | ||
| 47 | } | ||
| 48 | } | ||
internal/control/dashboard.go +14
| @@ -6,6 +6,7 @@ import ( | |||
| 6 | "io" | 6 | "io" |
| 7 | "strconv" | 7 | "strconv" |
| 8 | 8 | ||
| 9 | "gitbay.org/gitbay/internal/buildinfo" | ||
| 9 | "gitbay.org/gitbay/internal/gitutil" | 10 | "gitbay.org/gitbay/internal/gitutil" |
| 10 | "gitbay.org/gitbay/internal/policy" | 11 | "gitbay.org/gitbay/internal/policy" |
| 11 | "gitbay.org/gitbay/internal/protocol" | 12 | "gitbay.org/gitbay/internal/protocol" |
| @@ -52,6 +53,12 @@ func runDashboard(c *Ctx, args []string) int { | |||
| 52 | CreatedAt string `json:"created_at"` | 53 | CreatedAt string `json:"created_at"` |
| 53 | FinishedAt string `json:"finished_at,omitempty"` | 54 | FinishedAt string `json:"finished_at,omitempty"` |
| 54 | } | 55 | } |
| 56 | // serverOut is admin-only. The exact build a host is running narrows down | ||
| 57 | // which known issues apply to it, so it is not everyone's to read; the | ||
| 58 | // person who needs it is the operator. | ||
| 59 | type serverOut struct { | ||
| 60 | Commit string `json:"commit"` | ||
| 61 | } | ||
| 55 | type out struct { | 62 | type out struct { |
| 56 | Reviews []dashboardItem `json:"review_queue"` | 63 | Reviews []dashboardItem `json:"review_queue"` |
| 57 | Assigned []dashboardItem `json:"assigned_issues"` | 64 | Assigned []dashboardItem `json:"assigned_issues"` |
| @@ -60,6 +67,7 @@ func runDashboard(c *Ctx, args []string) int { | |||
| 60 | Pinned []pinnedOut `json:"pinned"` | 67 | Pinned []pinnedOut `json:"pinned"` |
| 61 | Activity []feedOut `json:"recent_activity"` | 68 | Activity []feedOut `json:"recent_activity"` |
| 62 | Builds []buildOut `json:"builds"` | 69 | Builds []buildOut `json:"builds"` |
| 70 | Server *serverOut `json:"server,omitempty"` | ||
| 63 | } | 71 | } |
| 64 | d := out{ | 72 | d := out{ |
| 65 | Reviews: []dashboardItem{}, Assigned: []dashboardItem{}, MRs: []dashboardItem{}, | 73 | Reviews: []dashboardItem{}, Assigned: []dashboardItem{}, MRs: []dashboardItem{}, |
| @@ -127,6 +135,9 @@ func runDashboard(c *Ctx, args []string) int { | |||
| 127 | for _, b := range builds { | 135 | for _, b := range builds { |
| 128 | d.Builds = append(d.Builds, buildOut{b.RepoPath, b.Number, b.Job, b.Status, b.SHA, b.Ref, b.CreatedAt, b.FinishedAt}) | 136 | d.Builds = append(d.Builds, buildOut{b.RepoPath, b.Number, b.Job, b.Status, b.SHA, b.Ref, b.CreatedAt, b.FinishedAt}) |
| 129 | } | 137 | } |
| 138 | if c.User.IsAdmin { | ||
| 139 | d.Server = &serverOut{Commit: buildinfo.String()} | ||
| 140 | } | ||
| 130 | 141 | ||
| 131 | return c.emit(d, func(w io.Writer) { | 142 | return c.emit(d, func(w io.Writer) { |
| 132 | fmt.Fprintln(w, "waiting on your review:") | 143 | fmt.Fprintln(w, "waiting on your review:") |
| @@ -153,6 +164,9 @@ func runDashboard(c *Ctx, args []string) int { | |||
| 153 | for _, b := range d.Builds { | 164 | for _, b := range d.Builds { |
| 154 | fmt.Fprintf(w, " %s\t%d\t%s\t%s\t%.10s\t%s\n", b.Repo, b.Number, b.Job, b.Status, b.SHA, b.Ref) | 165 | fmt.Fprintf(w, " %s\t%d\t%s\t%s\t%.10s\t%s\n", b.Repo, b.Number, b.Job, b.Status, b.SHA, b.Ref) |
| 155 | } | 166 | } |
| 167 | if d.Server != nil { | ||
| 168 | fmt.Fprintf(w, "server:\n build %s\n", d.Server.Commit) | ||
| 169 | } | ||
| 156 | }) | 170 | }) |
| 157 | } | 171 | } |
| 158 | 172 | ||