diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2023-07-17 12:07:58 +0200 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2023-07-17 11:17:04 +0000 |
| commit | c842da34cdd3b56628cb00b5f1f024dabc798716 (patch) | |
| tree | 8ed37230fa4620f2d4e0f8d87d9e7c7caafda57c | |
| parent | e5f1088910d12c083d40dd1d9e3f62d4713faa6b (diff) | |
dashboard: show per-bisect type job lists for admins
Currently we only show running/pending/recent jobs. Let admins also see
the list of the latest cause/fix bisections.
| -rw-r--r-- | dashboard/app/admin.html | 5 | ||||
| -rw-r--r-- | dashboard/app/index.yaml | 6 | ||||
| -rw-r--r-- | dashboard/app/main.go | 87 | ||||
| -rw-r--r-- | dashboard/app/main_test.go | 41 | ||||
| -rw-r--r-- | dashboard/app/templates.html | 2 |
5 files changed, 115 insertions, 26 deletions
diff --git a/dashboard/app/admin.html b/dashboard/app/admin.html index cc66f7d91..8ada1c64b 100644 --- a/dashboard/app/admin.html +++ b/dashboard/app/admin.html @@ -47,8 +47,13 @@ Main page. {{end}} {{template "manager_list" $.Managers}} + + {{if $.FixBisectionsLink}}<a href="{{$.FixBisectionsLink}}">[Fix Bisections]</a>{{end}} + {{if $.CauseBisectionsLink}}<a href="{{$.CauseBisectionsLink}}">[Cause Bisections]</a>{{end}} + {{if $.JobOverviewLink}}<a href="{{$.JobOverviewLink}}">[Jobs Overview]</a>{{end}} {{template "job_list" $.RunningJobs}} {{template "job_list" $.PendingJobs}} {{template "job_list" $.RecentJobs}} + {{template "job_list" $.TypeJobs}} </body> </html> diff --git a/dashboard/app/index.yaml b/dashboard/app/index.yaml index 7b18993f2..f904e3062 100644 --- a/dashboard/app/index.yaml +++ b/dashboard/app/index.yaml @@ -227,3 +227,9 @@ indexes: - name: Type - name: Finished direction: desc + +- kind: Job + properties: + - name: Type + - name: Finished + direction: desc diff --git a/dashboard/app/main.go b/dashboard/app/main.go index d7655fd47..9fe31b0ef 100644 --- a/dashboard/app/main.go +++ b/dashboard/app/main.go @@ -179,13 +179,17 @@ type uiSubsystemStats struct { } type uiAdminPage struct { - Header *uiHeader - Log []byte - Managers *uiManagerList - RecentJobs *uiJobList - PendingJobs *uiJobList - RunningJobs *uiJobList - MemcacheStats *memcache.Statistics + Header *uiHeader + Log []byte + Managers *uiManagerList + RecentJobs *uiJobList + PendingJobs *uiJobList + RunningJobs *uiJobList + TypeJobs *uiJobList + FixBisectionsLink string + CauseBisectionsLink string + JobOverviewLink string + MemcacheStats *memcache.Statistics } type uiManager struct { @@ -644,6 +648,7 @@ func handleAdmin(c context.Context, w http.ResponseWriter, r *http.Request) erro recentJobs []*uiJob pendingJobs []*uiJob runningJobs []*uiJob + typeJobs []*uiJob ) g, _ := errgroup.WithContext(context.Background()) g.Go(func() error { @@ -661,21 +666,33 @@ func handleAdmin(c context.Context, w http.ResponseWriter, r *http.Request) erro errorLog, err = fetchErrorLogs(c) return err }) - g.Go(func() error { - var err error - recentJobs, err = loadRecentJobs(c) - return err - }) - g.Go(func() error { - var err error - pendingJobs, err = loadPendingJobs(c) - return err - }) - g.Go(func() error { - var err error - runningJobs, err = loadRunningJobs(c) - return err - }) + if r.FormValue("job_type") != "" { + value, err := strconv.Atoi(r.FormValue("job_type")) + if err != nil { + return fmt.Errorf("%w: %v", ErrClientBadRequest, err) + } + g.Go(func() error { + var err error + typeJobs, err = loadJobsOfType(c, JobType(value)) + return err + }) + } else { + g.Go(func() error { + var err error + recentJobs, err = loadRecentJobs(c) + return err + }) + g.Go(func() error { + var err error + pendingJobs, err = loadPendingJobs(c) + return err + }) + g.Go(func() error { + var err error + runningJobs, err = loadRunningJobs(c) + return err + }) + } err = g.Wait() if err != nil { return err @@ -684,11 +701,18 @@ func handleAdmin(c context.Context, w http.ResponseWriter, r *http.Request) erro Header: hdr, Log: errorLog, Managers: makeManagerList(managers, hdr.Namespace), - RecentJobs: &uiJobList{Title: "Recent jobs:", Jobs: recentJobs}, - RunningJobs: &uiJobList{Title: "Running jobs:", Jobs: runningJobs}, - PendingJobs: &uiJobList{Title: "Pending jobs:", Jobs: pendingJobs}, MemcacheStats: memcacheStats, } + if r.FormValue("job_type") != "" { + data.TypeJobs = &uiJobList{Title: "Last jobs:", Jobs: typeJobs} + data.JobOverviewLink = "/admin" + } else { + data.RecentJobs = &uiJobList{Title: "Recent jobs:", Jobs: recentJobs} + data.RunningJobs = &uiJobList{Title: "Running jobs:", Jobs: runningJobs} + data.PendingJobs = &uiJobList{Title: "Pending jobs:", Jobs: pendingJobs} + data.FixBisectionsLink = html.AmendURL("/admin", "job_type", fmt.Sprintf("%d", JobBisectFix)) + data.CauseBisectionsLink = html.AmendURL("/admin", "job_type", fmt.Sprintf("%d", JobBisectCause)) + } return serveTemplate(w, "admin.html", data) } @@ -1926,6 +1950,19 @@ func loadRunningJobs(c context.Context) ([]*uiJob, error) { return getUIJobs(c, keys, jobs), nil } +func loadJobsOfType(c context.Context, t JobType) ([]*uiJob, error) { + var jobs []*Job + keys, err := db.NewQuery("Job"). + Filter("Type=", t). + Order("-Finished"). + Limit(50). + GetAll(c, &jobs) + if err != nil { + return nil, err + } + return getUIJobs(c, keys, jobs), nil +} + func getUIJobs(c context.Context, keys []*db.Key, jobs []*Job) []*uiJob { var results []*uiJob for i, job := range jobs { diff --git a/dashboard/app/main_test.go b/dashboard/app/main_test.go index fff94d7f9..efbf60ef6 100644 --- a/dashboard/app/main_test.go +++ b/dashboard/app/main_test.go @@ -6,6 +6,7 @@ package main import ( "bytes" "testing" + "time" "github.com/google/syzkaller/dashboard/dashapi" "github.com/stretchr/testify/assert" @@ -295,3 +296,43 @@ func TestMultiLabelFilter(t *testing.T) { assert.NotContains(t, string(reply), "/access-public-email?label=subsystems:subsystemA\"") assert.NotContains(t, string(reply), "/access-public-email?label=prop:low\"") } + +func TestAdminJobList(t *testing.T) { + c := NewCtx(t) + defer c.Close() + + client := c.client2 + build := testBuild(1) + client.UploadBuild(build) + + crash := testCrash(build, 1) + crash.Title = "some bug title" + crash.GuiltyFiles = []string{"a.c"} + crash.ReproOpts = []byte("repro opts") + crash.ReproSyz = []byte("repro syz") + crash.ReproC = []byte("repro C") + client.ReportCrash(crash) + client.pollEmailBug() + + c.advanceTime(24 * time.Hour) + + pollResp := client.pollSpecificJobs(build.Manager, dashapi.ManagerJobs{BisectCause: true}) + c.expectNE(pollResp.ID, "") + + causeJobsLink := "/admin?job_type=1" + fixJobsLink := "/admin?job_type=2" + reply, err := c.AuthGET(AccessAdmin, "/admin") + c.expectOK(err) + assert.Contains(t, string(reply), causeJobsLink) + assert.Contains(t, string(reply), fixJobsLink) + + // Verify the bug is in the bisect cause jobs list. + reply, err = c.AuthGET(AccessAdmin, causeJobsLink) + c.expectOK(err) + assert.Contains(t, string(reply), crash.Title) + + // Verify the bug is NOT in the fix jobs list. + reply, err = c.AuthGET(AccessAdmin, fixJobsLink) + c.expectOK(err) + assert.NotContains(t, string(reply), crash.Title) +} diff --git a/dashboard/app/templates.html b/dashboard/app/templates.html index 6278c1f7d..e9738e785 100644 --- a/dashboard/app/templates.html +++ b/dashboard/app/templates.html @@ -423,7 +423,7 @@ Use of this source code is governed by Apache 2 LICENSE that can be found in the {{/* List of jobs, invoked with *uiJobList */}} {{define "job_list"}} -{{if $.Jobs}} +{{if and $ $.Jobs}} <table class="list_table"> <caption id="jobs"><a class="plain" href="#jobs">{{$.Title}}</a></caption> <thead> |
