aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2023-02-21 14:08:30 +0100
committerAleksandr Nogikh <wp32pw@gmail.com>2023-02-21 14:56:46 +0100
commit47536162e02e8ba9aadd6e06184cb044f2eb069b (patch)
tree48542cdb8ad4a674aca61d73a1521ab2f89c99f0
parentf949448d9137ce181301419253c1bb224fcfea28 (diff)
dashboard: introduce a subsystem page
Display the list of open bugs, subsystem info and stats there. Make the URL look nice, e.g. /upstream/s/fs
-rw-r--r--dashboard/app/main.go85
-rw-r--r--dashboard/app/main_test.go26
-rw-r--r--dashboard/app/subsystem_page.html28
3 files changed, 118 insertions, 21 deletions
diff --git a/dashboard/app/main.go b/dashboard/app/main.go
index fa31a7ab2..cbe3ca7df 100644
--- a/dashboard/app/main.go
+++ b/dashboard/app/main.go
@@ -22,6 +22,7 @@ import (
"github.com/google/syzkaller/dashboard/dashapi"
"github.com/google/syzkaller/pkg/email"
"github.com/google/syzkaller/pkg/html"
+ "github.com/google/syzkaller/pkg/subsystem"
"github.com/google/syzkaller/pkg/vcs"
"golang.org/x/net/context"
"golang.org/x/sync/errgroup"
@@ -61,6 +62,7 @@ func initHTTPHandlers() {
http.Handle("/"+ns+"/repos", handlerWrapper(handleRepos))
http.Handle("/"+ns+"/bug-stats", handlerWrapper(handleBugStats))
http.Handle("/"+ns+"/subsystems", handlerWrapper(handleSubsystemsList))
+ http.Handle("/"+ns+"/s/", handlerWrapper(handleSubsystemPage))
}
http.HandleFunc("/cron/cache_update", cacheUpdate)
http.HandleFunc("/cron/deprecate_assets", handleDeprecateAssets)
@@ -145,6 +147,12 @@ type uiRepo struct {
Alias string
}
+type uiSubsystemPage struct {
+ Header *uiHeader
+ Info *uiSubsystem
+ Groups []*uiBugGroup
+}
+
type uiSubsystemsPage struct {
Header *uiHeader
List []*uiSubsystem
@@ -437,6 +445,40 @@ func handleInvalid(c context.Context, w http.ResponseWriter, r *http.Request) er
})
}
+func handleSubsystemPage(c context.Context, w http.ResponseWriter, r *http.Request) error {
+ hdr, err := commonHeader(c, r, w, "")
+ if err != nil {
+ return err
+ }
+ service := getSubsystemService(c, hdr.Namespace)
+ if service == nil {
+ return fmt.Errorf("the namespace does not have subsystems")
+ }
+ var subsystem *subsystem.Subsystem
+ if pos := strings.Index(r.URL.Path, "/s/"); pos != -1 {
+ subsystem = service.ByName(r.URL.Path[pos+3:])
+ }
+ if subsystem == nil {
+ return fmt.Errorf("the subsystem is not found")
+ }
+ groups, err := fetchNamespaceBugs(c, accessLevel(c, r),
+ hdr.Namespace, &userBugFilter{
+ Subsystem: subsystem.Name,
+ })
+ if err != nil {
+ return err
+ }
+ cached, err := CacheGet(c, r, hdr.Namespace)
+ if err != nil {
+ return err
+ }
+ return serveTemplate(w, "subsystem_page.html", &uiSubsystemPage{
+ Header: hdr,
+ Info: createUISubsystem(hdr.Namespace, subsystem, cached),
+ Groups: groups,
+ })
+}
+
func handleRepos(c context.Context, w http.ResponseWriter, r *http.Request) error {
hdr, err := commonHeader(c, r, w, "")
if err != nil {
@@ -805,27 +847,7 @@ func handleSubsystemsList(c context.Context, w http.ResponseWriter, r *http.Requ
}
list := []*uiSubsystem{}
for _, item := range service.List() {
- stats := cached.Subsystems[item.Name]
- list = append(list, &uiSubsystem{
- Name: item.Name,
- Lists: strings.Join(item.Lists, ", "),
- Maintainers: strings.Join(item.Maintainers, ", "),
- Open: uiSubsystemStats{
- Count: stats.Open,
- Link: html.AmendURL("/"+hdr.Namespace,
- "subsystem", item.Name),
- },
- Fixed: uiSubsystemStats{
- Count: stats.Fixed,
- Link: html.AmendURL("/"+hdr.Namespace+"/fixed",
- "subsystem", item.Name),
- },
- Invalid: uiSubsystemStats{
- Count: stats.Invalid,
- Link: html.AmendURL("/"+hdr.Namespace+"/invalid",
- "subsystem", item.Name),
- },
- })
+ list = append(list, createUISubsystem(hdr.Namespace, item, cached))
}
sort.Slice(list, func(i, j int) bool { return list[i].Name < list[j].Name })
return serveTemplate(w, "subsystems.html", &uiSubsystemsPage{
@@ -834,6 +856,27 @@ func handleSubsystemsList(c context.Context, w http.ResponseWriter, r *http.Requ
})
}
+func createUISubsystem(ns string, item *subsystem.Subsystem, cached *Cached) *uiSubsystem {
+ stats := cached.Subsystems[item.Name]
+ return &uiSubsystem{
+ Name: item.Name,
+ Lists: strings.Join(item.Lists, ", "),
+ Maintainers: strings.Join(item.Maintainers, ", "),
+ Open: uiSubsystemStats{
+ Count: stats.Open,
+ Link: html.AmendURL("/"+ns, "subsystem", item.Name),
+ },
+ Fixed: uiSubsystemStats{
+ Count: stats.Fixed,
+ Link: html.AmendURL("/"+ns+"/fixed", "subsystem", item.Name),
+ },
+ Invalid: uiSubsystemStats{
+ Count: stats.Invalid,
+ Link: html.AmendURL("/"+ns+"/invalid", "subsystem", item.Name),
+ },
+ }
+}
+
// handleText serves plain text blobs (crash logs, reports, reproducers, etc).
func handleTextImpl(c context.Context, w http.ResponseWriter, r *http.Request, tag string) error {
var id int64
diff --git a/dashboard/app/main_test.go b/dashboard/app/main_test.go
index e69dd0275..8f732ac3d 100644
--- a/dashboard/app/main_test.go
+++ b/dashboard/app/main_test.go
@@ -210,3 +210,29 @@ func TestSubsystemsList(t *testing.T) {
assert.Contains(t, string(reply), "subsystemA")
assert.Contains(t, string(reply), "subsystemB")
}
+
+func TestSubsystemPage(t *testing.T) {
+ c := NewCtx(t)
+ defer c.Close()
+
+ client := c.client
+ build := testBuild(1)
+ client.UploadBuild(build)
+
+ crash1 := testCrash(build, 1)
+ crash1.Title = "test crash title"
+ crash1.GuiltyFiles = []string{"a.c"}
+ client.ReportCrash(crash1)
+ client.pollBug()
+
+ crash2 := testCrash(build, 2)
+ crash2.GuiltyFiles = []string{"b.c"}
+ client.ReportCrash(crash2)
+ crash2.Title = "crash that must not be present"
+ client.updateBug(client.pollBug().ID, dashapi.BugStatusInvalid, "")
+
+ reply, err := c.AuthGET(AccessAdmin, "/test1/s/subsystemA")
+ c.expectOK(err)
+ assert.Contains(t, string(reply), crash1.Title)
+ assert.NotContains(t, string(reply), crash2.Title)
+}
diff --git a/dashboard/app/subsystem_page.html b/dashboard/app/subsystem_page.html
new file mode 100644
index 000000000..ea108b596
--- /dev/null
+++ b/dashboard/app/subsystem_page.html
@@ -0,0 +1,28 @@
+{{/*
+Copyright 2023 syzkaller project authors. All rights reserved.
+Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+The list of polled trees.
+*/}}
+
+<!doctype html>
+<html>
+<head>
+ {{template "head" .Header}}
+ <title>syzbot: {{.Info.Name}} subsystem</title>
+</head>
+<body>
+ {{template "header" .Header}}
+ <h2>{{.Info.Name}} subsystem</h2><br>
+ {{if .Info.Lists}}
+ <b>List(s):</b> {{.Info.Lists}}<br>
+ {{end}}
+ {{if .Info.Maintainers}}
+ <b>Maintainer(s):</b> {{.Info.Maintainers}}<br>
+ {{end}}
+ <b>Fixed bugs:</b> {{link .Info.Fixed.Link (printf "%d" .Info.Fixed.Count)}}<br>
+ {{range $group := $.Groups}}
+ {{template "bug_list" $group}}
+ {{end}}
+</body>
+</html>